Using setData(Qt::EditRole, xxx) with QTableWidgetItem
-
There are different approaches here. Do you need a deterministic number of decimals or do you need as many decimals as the number has?
e.g.
data: 12.34500do you have a 4 digits fixed so 12.3450 or as little digits as necessary to represent the number in full 12.345?
-
@VRonin I set the data with Qt::EditRole, it's double. When I set the data, the needed decimal count is determined, too. If the data is 12.345, and I need 4 decimal, it should displayed as '12.3450'. If I only need 2 decimal, then it should displayed as '12.35'.
Them problem now is, I want some cells show 4, some cells show 3, and some cells show 2, etc.
-
Just use a role
class DecimalsDelegate : public QStyledItemDelegate{ Q_OBJECT public: enum { NumOfDecimalsRole = Qt::UserRole + 888 }; /* 888 is just a random number, you can use whatever you want if it conflicts */ explicit DecimalsDelegate(QObject* parent = nullptr) :QStyledItemDelegate(parent) {} virtual ~DecimalsDelegate() = default; DecimalsDelegate(const DecimalsDelegate&) = delete; DecimalsDelegate& operator=(const DecimalsDelegate&) = delete; virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { Q_ASSERT(index.isValid()); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QVariant numOfDecimals = index.data(NumOfDecimalsRole ); if (numOfDecimals.isValid() && !numOfDecimals.isNull() && numOfDecimals.canConvert(QMetaType::Int) && ( opt.features & QStyleOptionViewItem::HasDisplay) ) { const QVariant value = index.data(Qt::DisplayRole); if(value.canConvert(QMetaType::Double)) opt.text = opt.locale.toString(value.toDouble(),'f',numOfDecimals.toInt()); } const QWidget *widget = option.widget; const QStyle* const style = widget ? widget->style() : QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); } }
now after you set the Qt::EditRole just set NumOfDecimalsRole to the number of decimals you need
Edit: fixed copy/paste error
-
@VRonin Great, it work.
I have another idea. Because I can't know the current index of the model in the displayText function. So, I can't change the decimal count by the 'value' 's 'index'. But in 'setEditorData' and 'setModelData', and 'createEditor', I can know the 'index'. So, when I set the widget item, I use '
item->setData(Qt::EditRole, QString::number(ratio_error * 1000, 'f', 2)); ui.testTableWidget->setItem(row, 7, item);
And my derived class is as below:
virtual QWidget *createEditor(QWidget *parent, QStyleOptionViewItem const &option, QModelIndex const &index) const override { if (index.data(Qt::EditRole).isValid() && ((index.column() == 6) || (index.column() == 7))) { QDoubleSpinBox *spinbox = new QDoubleSpinBox(parent); QTableWidget *table = qobject_cast<QTableWidget *>(this->parent()); if (index.column() == 6) { spinbox->setDecimals(2); } else if (index.column() == 7) { spinbox->setDecimals(3); } return spinbox; } else { return QStyledItemDelegate::createEditor(parent, option, index); } } virtual void iStyledItemDelegate::updateEditorGeometry(QWidget *editor, QStyleOptionViewItem const &option, QModelIndex const &index) const override { editor->setGeometry(option.rect); } virtual void iStyledItemDelegate::setEditorData(QWidget *editor, QModelIndex const &index) const override { if (index.data(Qt::EditRole).isValid() && ((index.column() == 6) || (index.column() == 7))) { double value = index.model()->data(index, Qt::EditRole).toString().toDouble(); QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox*>(editor); spinBox->setValue(value); } else { QStyledItemDelegate::setEditorData(editor, index); } } virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { if (index.data(Qt::EditRole).isValid() && ((index.column() == 6) || (index.column() == 7))) { QDoubleSpinBox *spin = qobject_cast<QDoubleSpinBox *>(editor); spin->interpretText(); double value = spin->value(); int decimals = spin->decimals(); QString text = QString::number(value, 'f', decimals); model->setData(index, text, Qt::EditRole); } else { QStyledItemDelegate::setModelData(editor, model, index); } }
For the column 7 (the last column), it works as expected, but for column 6, when I double click the cell, the value goes to "0.00", and I debuged it, find for column 6, the invoke sequence is:
createEditor -> setEditorData -> setModelData, when invoke the 'setData' in this function, it invoke 'setEditorData' once more. It's right?
-
@VRonin Finally, I found the reason, I forgot set the minimum and maximum of the spinbox, it seem it's default minimum value is "0", and I just need show a minus float number in column 6. So it is "rounded" to zero.
Can you check my code below, if it's a 'right' usage case?
class iStyledItemDelegate : public QStyledItemDelegate { public: explicit iStyledItemDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} virtual ~iStyledItemDelegate() = default; virtual QWidget *createEditor(QWidget *parent, QStyleOptionViewItem const &option, QModelIndex const &index) const override { if ((index.column() == 6) || (index.column() == 7)) { QDoubleSpinBox *spinbox = new QDoubleSpinBox(parent); spinbox->installEventFilter(const_cast<iStyledItemDelegate*>(this)); QTableWidget *table = qobject_cast<QTableWidget *>(this->parent()); spinbox->setFrame(false); spinbox->setMinimum(-DBL_MAX); spinbox->setMaximum(DBL_MAX); if (index.column() == 6) { spinbox->setDecimals(2); } else if (index.column() == 7) { spinbox->setDecimals(3); } return spinbox; } else { return QStyledItemDelegate::createEditor(parent, option, index); } } virtual void updateEditorGeometry(QWidget *editor, QStyleOptionViewItem const &option, QModelIndex const &index) const override { editor->setGeometry(option.rect); } virtual void setEditorData(QWidget *editor, QModelIndex const &index) const override { QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox*>(editor); if (spinBox && ((index.column() == 6) || (index.column() == 7))) { double value = index.data(Qt::EditRole).toString().toDouble(); spinBox->setValue(value); } else { QStyledItemDelegate::setEditorData(editor, index); } } virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { QDoubleSpinBox *spin = qobject_cast<QDoubleSpinBox *>(editor); if (spin && ((index.column() == 6) || (index.column() == 7))) { spin->interpretText(); model->setData(index, spin->text(), Qt::EditRole); } else { QStyledItemDelegate::setModelData(editor, model, index); } } };
-
@VRonin Yes, I know it's not 'good' for 're-use'. But I can't find a method to set delegate to individual cells. In my case, take for example column 6. Some rows may need show 3, some rows may need show 2 decimals, depending on the column 2's contents. So I make this class dedicated to this. Any suggestions to implement my goal but no depending to 'index' in the derived class?
-
https://forum.qt.io/topic/71692/using-setdata-qt-editrole-xxx-with-qtablewidgetitem/10
Then set the NumOfDecimalsRole in a slot connected to the dataChanged of the model
Btw, looking at the code above it looks like you need 2 decimals in column 6 and 3 decimals in column 7 and this can be achieved with 2 delegates set on 2 columns with
setItemDelegeateForColumn
P.S.
model->setData(index, spin->text(), Qt::EditRole);
this will set a string, not a number so the decimals in a string are meaningless
-
@VRonin :),
- In my previous example, I simplified my usage case.
- I use strings instead of numbers, because the displayText function won't abandon the trailing zeros of the number strings. I think if I use 'model->setData(index, spin->value(), Qt::EditRole);' , then the displayText() will format the numbers for me, and I can't access 'index' in it, so it will always abandon the trailing zeros.
- So, my solution is actually avoid using numbers, but use text, and when create editor, I format the text to number, when user finish the editing, I format the values back to text and store it in the model.
-
It turns out to be a very good method which I seeks for a really long time. But now I use Qt 6.0.0, so the last return statement does not work well. So it needs to be changed into:
return QItemEditorFactory::createEditor(userType, parent);
And it is done!