[SOLVED] Delegate won't call setModelData with a QWidget/TableView
-
I subclassed the AbstractTableModel and I created a TableView and myown Delegate
If I create a QCheckBox or a QRadioButton etc.. and return it, the delegate works calling all functions like setEditorData, setModelData.
But as I would move the control to the center of the cell, I tried the following codeQWidget *Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) return QStyledItemDelegate::createEditor(parent, option, index); QWidget *w = new QWidget(parent); w->setAutoFillBackground(true); QVBoxLayout *layout = new QVBoxLayout; w->setLayout(layout); QCheckBox *cb = new QCheckBox(); layout->addWidget(cb); return w; }
With this code, the delegate calls UpdateGeometry, setEditorData, but never setModelData.
If I use a TableWidget instead of a TableView, the delegate works fine.
-
http://doc.qt.io/qt-5/qstyleditemdelegate.html#setModelData
Do you have a user property defined in your editor? If not, have you overriden the
setModelData
method of the delegate? -
this is my overriden method:
void Delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
if (!index.isValid())
return QStyledItemDelegate::setModelData(editor, model, index);qDebug() << "setModelData"; QCheckBox *cb = editor->findChild<QCheckBox*>(); model->setData(index, cb->isChecked());
}
I don't have defined a User Property in my editor. How can I fix it?
Thanks!
-
@giupigna
The user property is optional. In any case I noticed you don't commit the data from the editor and I believe this is why yoursetModelData
method is not called.QWidget *Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { // ... QCheckBox *cb = new QCheckBox(); layout->addWidget(cb); //< You should commit the data when it's changed. QSignalMapper * mapper = new QSignalMapper(cb); mapper->setMapping(cb, w); QObject::connect(cb, SIGNAL(toggled(bool)), mapper, SLOT(map())); QObject::connect(mapper, SIGNAL(mapped(QWidget *)), this, SIGNAL(commitData(QWidget *))); }
PS:
You could look at the Star delegate example for further guidance -
Yes I saw the Star delegate Example. In that code, the editor is a subclass of QWidget, like I want in my application.
But the table in that case is a TableWidget and not a TableView. Infact I tried my code using a TableWidget and it works, but using a tableView it stops to work.
I also connected the checkbox with a signal and slot to commitData to model, but "the program has unexpectedly finished"I don't understand why also if I create and return a QCheckbox, it works fine and I don't need any signal to emit.
I've installed Qt 5.6.
-
But the table in that case is a TableWidget and not a TableView.
QTableWidget
is a convenience class that extendsQTableView
, it should work either way.I also connected the checkbox with a signal and slot to commitData to model, but "the program has unexpectedly finished"
You should debug that. Probably you're getting a segmentation fault somewhere. If I had to guess this line:
QCheckBox *cb = editor->findChild<QCheckBox*>();
in
Delegate::setModelData
returnsNULL
and then callingcb->isChecked()
is crashing (dereferencingNULL
pointer is not possible).I don't understand why also if I create and return a QCheckbox, it works fine and I don't need any signal to emit.
QCheckBox
is derived fromQAbstractButton
, which in turn has the optional user property, so Qt knows what property change signifies that the data should be written to the model. -
@kshegunov
Ok, I fixed the bug crash. It was in the createEditor method. Now it's working.
But if I would create a complex widget as editor, how can I create it using the USER property?class MyWidget : public QWidget { Q_OBJECT Q_PROPERTY(bool check READ check WRITE setCheck NOTIFY checkChanged USER true) public: explicit MyWidget(QWidget *parent = 0); bool check() const; void setCheck(bool value) const; Q_SIGNALS: void checkChanged(bool); private: QCheckBox *m_check; };
where check and setCheck methods return and set the value of checkbox.
How does the delegate realize that the bool variable is changed? Is it correct written in this way?
Thanks so much!
-
@giupigna said:
How does the delegate realize that the bool variable is changed? Is it correct written in this way?
Your code is correct (only remove the const modifier for
setCheck
). Do remember, however, to emit the notification signal from thesetCheck
method (or connect it to thetoggled
signal of the checkbox).void MyWidget::setCheck(bool value) { m_check->setChecked(value); emit checkChanged(value); //< This is how Qt knows when the property has changed }
Or:
MyWidget::MyWidget(QWidget * parent = NULL) : QWidget(parent), m_check(new QCheckBox(this)) { QObject::connect(m_check, SIGNAL(toggled(bool)), this, SIGNAL(checkChanged(bool))); //< Delegate the signal } void MyWidget::setCheck(bool value) { m_check->setChecked(value); //< Should be sufficient, as the signal will be emitted when the checkbox's notification is raised }
Thanks so much!
No problem.
-
Hi,
@kshegunov , in your first implementation of setCheck, there's a check missing. You'll be emitting checkChanged even if m_check's current value is already matching the value of
value
.