qt - How to update geometry of item delegate - Stack Overflow

Qt 6.8.0, Ubuntu 24.10.I wrote a simple delegate for a QSqlTableModel to be shown in a QTableView.It

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
  • Use doc.qt.io/qt-6/qstyleditemdelegate.html#sizeHint to return the size you need – chehrlic Commented Jan 29 at 17:01
  • Also note that, if you're using QStyledItemDelegate (and you normally should, but we don't know that as you are not showing your implementation of the delegate), properly overriding initStyleOption() (or at least displayText()) 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:27
  • Sorry, I fot to add the source code of the delegate!!! I will edit the question as soon as possible – Mark Commented Jan 29 at 19:55
  • In any case, remember that, usually, every UI related aspect (drawing, geometry update and interaction) should normally happen only within the rect 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 as resizeColumnsToContents(). – musicamante Commented Jan 30 at 2:54
  • The exception is for functions that query about the possible contents (eventually based on a given one, but that's normally an exception), which is exactly the case of QAbstractItemDelegate::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
 |  Show 1 more comment

1 Answer 1

Reset to default 1

Assuming 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

相关推荐

  • qt - How to update geometry of item delegate - Stack Overflow

    Qt 6.8.0, Ubuntu 24.10.I wrote a simple delegate for a QSqlTableModel to be shown in a QTableView.It

    3小时前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信