Qt 6.8.0, Ubuntu 24.10.
I wrote a simple delegate for a QSqlTableModel
to be shown in a QTableView
.
It formats a floating-point numbers with a given number of decimals.
Issue When the table adjusts the columns' width to the content it ignores the new content size (large than the initial one, due to the new decimals added).
Working and complete MRE
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSqlRelationalTableModel>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QSqlRelationalTableModel *_model = nullptr;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "delegatereal.h"
#include <QSqlError>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSqlRelationalDelegate>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "mydb");
db.setDatabaseName("mydb.sql");
db.open();
QSqlQuery query("DROP TABLE IF EXISTS foo", db);
query.exec();
QString sql = "CREATE TABLE foo \
(name TEXT NOT NULL, \
v FLOAT NOT NULL, \
PRIMARY KEY (name));";
query.prepare(sql);
query.exec();
_model = new QSqlRelationalTableModel(this, db);
_model->setTable("foo");
_model->setEditStrategy(QSqlRelationalTableModel::OnRowChange);
ui->table->setSortingEnabled(false);
ui->table->setModel(_model);
ui->table->setItemDelegate(new QSqlRelationalDelegate(this));
ui->table->setItemDelegateForColumn(1, new DelegateReal(5));
ui->table->setFocusPolicy(Qt::TabFocus);
query.exec("INSERT INTO foo (name, v) VALUES ('aaa', 1.234);");
query.exec("INSERT INTO foo (name, v) VALUES ('bbb', 2.345);");
query.exec("INSERT INTO foo (name, v) VALUES ('ccc', 3.465);");
_model->select();
ui->table->resizeColumnsToContents();
}
MainWindow::~MainWindow()
{
delete ui;
}
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="2">
<widget class="QTableView" name="table"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
delegatereal.h
#ifndef DELEGATEREAL_H
#define DELEGATEREAL_H
#include <QStyledItemDelegate>
#include <QDoubleSpinBox>
#include <QApplication>
#include <QDebug>
class DelegateReal : public QStyledItemDelegate
{
Q_OBJECT
public:
DelegateReal(int decimals, QObject *parent = nullptr) : QStyledItemDelegate(parent), _decimals(decimals)
{
}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
editor->setFrame(false);
editor->setDecimals(_decimals);
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QDoubleSpinBox *w = static_cast<QDoubleSpinBox*>(editor);
w->setValue(value);
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
{
QDoubleSpinBox *w = static_cast<QDoubleSpinBox*>(editor);
w->interpretText();
double value = w->value();
model->setData(index, value, Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
editor->setGeometry(option.rect);
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
opt.text = opt.locale.toString(opt.locale.toFloat(opt.text), 'f', _decimals);
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
}
private:
int _decimals;
};
#endif // DELEGATEREAL_H
Results
Expected behavior
Trials
I played with the sizes of the QRect
in the updateEditorGeometry()
, but this affects the editor only, of course (that is when I edit the cell):
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QRect rect(option.rect);
rect.setWidth(100);
editor->setGeometry(rect);
}
I also tried to set the width inside the paint()
function:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
opt.text = opt.locale.toString(opt.locale.toFloat(opt.text), 'f', _decimals);
opt.rect.setWidth(100);
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
}
It actually enlarges the painted area, but still the columns are resized wrongly:
Question
What I need to change in order to update the geometry of the content?
Qt 6.8.0, Ubuntu 24.10.
I wrote a simple delegate for a QSqlTableModel
to be shown in a QTableView
.
It formats a floating-point numbers with a given number of decimals.
Issue When the table adjusts the columns' width to the content it ignores the new content size (large than the initial one, due to the new decimals added).
Working and complete MRE
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSqlRelationalTableModel>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QSqlRelationalTableModel *_model = nullptr;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "delegatereal.h"
#include <QSqlError>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSqlRelationalDelegate>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "mydb");
db.setDatabaseName("mydb.sql");
db.open();
QSqlQuery query("DROP TABLE IF EXISTS foo", db);
query.exec();
QString sql = "CREATE TABLE foo \
(name TEXT NOT NULL, \
v FLOAT NOT NULL, \
PRIMARY KEY (name));";
query.prepare(sql);
query.exec();
_model = new QSqlRelationalTableModel(this, db);
_model->setTable("foo");
_model->setEditStrategy(QSqlRelationalTableModel::OnRowChange);
ui->table->setSortingEnabled(false);
ui->table->setModel(_model);
ui->table->setItemDelegate(new QSqlRelationalDelegate(this));
ui->table->setItemDelegateForColumn(1, new DelegateReal(5));
ui->table->setFocusPolicy(Qt::TabFocus);
query.exec("INSERT INTO foo (name, v) VALUES ('aaa', 1.234);");
query.exec("INSERT INTO foo (name, v) VALUES ('bbb', 2.345);");
query.exec("INSERT INTO foo (name, v) VALUES ('ccc', 3.465);");
_model->select();
ui->table->resizeColumnsToContents();
}
MainWindow::~MainWindow()
{
delete ui;
}
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="2">
<widget class="QTableView" name="table"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
delegatereal.h
#ifndef DELEGATEREAL_H
#define DELEGATEREAL_H
#include <QStyledItemDelegate>
#include <QDoubleSpinBox>
#include <QApplication>
#include <QDebug>
class DelegateReal : public QStyledItemDelegate
{
Q_OBJECT
public:
DelegateReal(int decimals, QObject *parent = nullptr) : QStyledItemDelegate(parent), _decimals(decimals)
{
}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
editor->setFrame(false);
editor->setDecimals(_decimals);
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QDoubleSpinBox *w = static_cast<QDoubleSpinBox*>(editor);
w->setValue(value);
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
{
QDoubleSpinBox *w = static_cast<QDoubleSpinBox*>(editor);
w->interpretText();
double value = w->value();
model->setData(index, value, Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
editor->setGeometry(option.rect);
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
opt.text = opt.locale.toString(opt.locale.toFloat(opt.text), 'f', _decimals);
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
}
private:
int _decimals;
};
#endif // DELEGATEREAL_H
Results
Expected behavior
Trials
I played with the sizes of the QRect
in the updateEditorGeometry()
, but this affects the editor only, of course (that is when I edit the cell):
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QRect rect(option.rect);
rect.setWidth(100);
editor->setGeometry(rect);
}
I also tried to set the width inside the paint()
function:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
opt.text = opt.locale.toString(opt.locale.toFloat(opt.text), 'f', _decimals);
opt.rect.setWidth(100);
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
}
It actually enlarges the painted area, but still the columns are resized wrongly:
Question
What I need to change in order to update the geometry of the content?
Share Improve this question edited Jan 30 at 6:13 Mark asked Jan 29 at 16:51 MarkMark 5,23511 gold badges73 silver badges153 bronze badges 6 | Show 1 more comment1 Answer
Reset to default 1Assuming the point of the delegate is to be able to set the number of decimals (or otherwise alter the displayed text), you could reimplement QStyledItemDelegate::displayText()
instead of paint()
. This in turn is called from the default initStyleOption()
implementation. In turn, initStyleOption()
is called by both paint()
and sizeHint()
methods.
If displayText()
doesn't cover the needs, it may still be simpler to reimplement initStyleOption()
and change the text value there instead of in paint()
, for same reason that the sizeHint()
would pick that up automatcially.
https://codebrowser.dev/qt6/qtbase/src/widgets/itemviews/qstyleditemdelegate.cpp.html#_ZNK19QStyledItemDelegate11displayTextERK8QVariantRK7QLocale
For further reference, here's how QTableView
calculates the size hint for a column, depending on if editor is open or not.
https://codebrowser.dev/qt6/qtbase/src/widgets/itemviews/qtableview.cpp.html#_ZNK17QTableViewPrivate17widthHintForIndexERK11QModelIndexiRK20QStyleOptionViewItem
PS. If changing style options still isn't enough and paint()
does need to be reimplemented then the delegate will also need to return an appropriate custom sizeHint()
. Example of this can be seen in Qt's "Star Delegate Example".
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745288891a4620735.html
initStyleOption()
(or at leastdisplayText()
) should be enough to let the default Qt implementation to compute an appropriate size based on the display text. – musicamante Commented Jan 29 at 19:27rect
of the given option. As you already found out,updateEditorGeometry()
is inappropriate in this case: you need to let the view know the required size of an item, while that function is used to eventually change the geometry of an editor, based on the current (and already set) size of the item, which will obviously not affect the size hint of that item, required by functions such asresizeColumnsToContents()
. – musicamante Commented Jan 30 at 2:54QAbstractItemDelegate::sizeHint()
that is queried about the preferred item size: Qt item views have various ways to adjust geometries of their items, depending on the view implementation. One of the common sources for those hints are delegates (which are also considered by table headers), and QStyledItemDelegate normally takes care of that by default. – musicamante Commented Jan 30 at 2:59