I try to follow an example in the book "Foundations of Qt Development" to create a custom delegate.
The goal is to create a table with two columns.
The first is just the row number.
The second column is some arbitrary number but show in terms of a bar style.
How the program just crashed after I run it.
Here is my code:
The MainWindow Class
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
Table = new QTableView(this);
Model = new QStandardItemModel(this);
dataInit(Model);
Table->setModel(Model);
setCentralWidget(Table);
// If I comment out these two lines
// the program works well
// A table view with number shows
BarDelegate delegate;
Table->setItemDelegateForColumn(1, &delegate);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::dataInit(QStandardItemModel* Model)
{
// Fill the Model with data
for(int r = 1; r < 11; ++r)
{
QStandardItem* item = new QStandardItem(QString("0%1").arg(r));
item->setEditable(false);
Model->setItem(r - 1, 0, item);
Model->setItem(r - 1, 1, new QStandardItem(QString(QString::number(r*17%100))));
}
}
The custom delegate class
#include "bardelegate.h"
BarDelegate::BarDelegate(QObject *parent)
{
}
QSize BarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return QSize(30, 15);
}
void BarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
int value = index.model()->data(index, Qt::DisplayRole).toInt();
double factor = (double) value/100.0;
painter->save();
if(factor > 1)
{
painter->setBrush(Qt::red);
factor = 1;
}
else
painter->setBrush(QColor(0, (int)(factor*255), 255-(int)(factor*255)));
painter->setPen(Qt::black);
painter->drawRect(option.rect.x()+2, option.rect.y()+2, (int)(factor*(option.rect.width()-5)), (int)(factor*(option.rect.height()-5)));
painter->restore();
}
What is the problem?
The problem might be in these two lines:
BarDelegate delegate;
Table->setItemDelegateForColumn(1, &delegate);
You allocate delegate in the stack and pass its address to the setItemDelegateForColumn function. However delegate is deleted as soon as the execution leaves the scope of MainWindow contructor. Thus your table view gets an invalidated delegate. To fix this you need to use a pointer to your delegate. I.e. declare BarDelegate delegate; as MainWindow class member and:
delegate = new BarDelegate(this);
Table->setItemDelegateForColumn(1, delegate);
Related
I want to show custom widget in each QListView cells (3 labels width different fonts and 2 tool buttons). The widget must handle mouse events for correct handling of the hover events and button clicks. (Therefore I cannot just draw it in QStyledItemDelegate::paint()).
Here is what I want each row in a list view looks like:
The main idea: QAbstractItemView::openPersistentEditor().
#include <QApplication>
#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QVBoxLayout>
#include <QDateTime>
#include <QListView>
#include <QStringListModel>
#include <QStyledItemDelegate>
class Form : public QWidget
{
//Q_OBJECT
public:
explicit Form(QWidget *parent = nullptr)
:QWidget(parent)
{
verticalLayout = new QVBoxLayout(this);
horizontalLayout = new QHBoxLayout();
labelTitle = new QLabel(this);
labelTitle->setFont(QFont("Calibri", 12, QFont::Bold));
horizontalLayout->addWidget(labelTitle);
toolButtonEdit = new QToolButton(this);
toolButtonEdit->setText("E");
horizontalLayout->addWidget(toolButtonEdit);
toolButtonRemove = new QToolButton(this);
toolButtonRemove->setText("R");
horizontalLayout->addWidget(toolButtonRemove);
verticalLayout->addLayout(horizontalLayout);
labelDate = new QLabel(this);
labelDate->setFont(QFont("Calibri", 8));
verticalLayout->addWidget(labelDate);
labelText = new QLabel(this);
labelText->setFont(QFont("Calibri", 10));
verticalLayout->addWidget(labelText);
verticalLayout->setStretch(2, 1);
setMinimumSize(QSize(300, 50));
}
public:
QVBoxLayout *verticalLayout;
QHBoxLayout *horizontalLayout;
QLabel *labelTitle;
QToolButton *toolButtonEdit;
QToolButton *toolButtonRemove;
QLabel *labelDate;
QLabel *labelText;
};
class MyDelegate : public QStyledItemDelegate
{
public:
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
auto editor = new Form(parent);
return editor;
}
void setEditorData(QWidget *ed, const QModelIndex &index) const override
{
QVariant var = index.model()->data(index, Qt::DisplayRole);
if (Form *editor = dynamic_cast<Form*>(ed))
{
editor->labelTitle->setText("SYMBOL");
editor->labelDate->setText("date-time");
editor->labelText->setText(var.toString());
}
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem& option, const QModelIndex &)const override
{
editor->setGeometry(option.rect);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Form form(nullptr);
form.labelTitle->setText("TITLE");
form.labelDate->setText(QDateTime::currentDateTime().toString());
form.labelText->setText("text body");
form.show();
auto model = new QStringListModel;
model->setStringList(QStringList()
<< "text body 1"
<< "text body 2"
<< "text body 3");
auto view = new QListView(nullptr);
view->setModel(model);
view->setItemDelegate(new MyDelegate);
int rowCount = model->rowCount();
for (int row = 0; row < rowCount; ++row)
{
QModelIndex index = model->index(row, 0);
view->openPersistentEditor(index);
}
view->show();
return a.exec();
}
Here is how the list view actually looks:
What how can one set such a custom widget to show view cells?
Note that while you are defining your own delegate MyDelegate you never actually use it (i.e. by calling QAbstractItemView::setItemDelegate(). Therefore you see the default delegate (a simple QLineEdit for data of type QString) when calling openPersistentEditor().
I was trying to insert a QCombobox only in some specific cells of my QTreeview. As I read, I think that I need to create my delegate (that I've created). But I don't understand how to insert this in my treeview.
I want to realize this:
This is my code:
#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>
#include "mainwindow.h"
#include "comboboxdelegate.h"
const int ROWS = 2;
const int COLUMNS = 3;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
treeView = new QTreeView(this);
setCentralWidget(treeView);
standardModel = new QStandardItemModel ;
standardModel->setColumnCount(2);
QStandardItem *root = new QStandardItem("ROOT");
root->setCheckable(true);
root->setCheckState(Qt::Checked);
root->setEditable(false);
standardModel->setItem(0, 0, root);
QList< QStandardItem * > listOne ;
QStandardItem *f1 = new QStandardItem( "Field_1" );
f1->setCheckable(true);
f1->setCheckState(Qt::Checked);
f1->setEditable(false);
listOne.append(f1) ;
listOne.append( new QStandardItem( "<Free text>" ) ) ;
root->appendRow(listOne);
QList< QStandardItem * > listTwo ;
QStandardItem *f2 = new QStandardItem( "Field_2" );
listTwo.append(f2) ;
listTwo.append( new QStandardItem( "<HERE COMBOBOX!>" ) ) ;
root->appendRow(listTwo);
treeView->setModel(standardModel);
treeView->expandAll();
}
I managed to create an entire column with QCombobox (using custom delegate). But I don't know how to set only specific cell. Anyone can help me?
QTreeWidget makes widget items convenient.
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
treeWidget = new QTreeWidget(this);
setCentralWidget(treeWidget);
treeWidget->setColumnCount(2);
auto root = new QTreeWidgetItem({"Root"});
root->setCheckState(0, Qt::Checked);
treeWidget->addTopLevelItem(root);
auto child1 = new QTreeWidgetItem({"Field 1", "<Free Text>"});
child1->setCheckState(0, Qt::Checked);
child1->setFlags(child1->flags() | Qt::ItemIsEditable);
root->addChild(child1);
auto child2 = new QTreeWidgetItem({"Field 2"});
child2->setFlags(child2->flags() | Qt::ItemIsEditable);
root->addChild(child2);
auto comboBox = new QComboBox();
comboBox->addItems({"Red", "Blue", "Yellow"});
treeWidget->setItemWidget(child2, 1, comboBox);
connect(treeWidget, &QTreeWidget::itemDoubleClicked, treeWidget, &QTreeWidget::editItem);
treeWidget->expandAll();
}
There are a few differences to note.
You'll need QTreeWidget* treeWidget; in your class declaration. And include the QTreeWidget header.
By default, TreeWidgetItems aren't checkable (no checkbox), but calling QTreeWidgetItem::setCheckState with Qt::Checked or Qt::Unchecked will make it checkable.
Items are not editable by default. Whole rows can be made editable by calling treeWidgetItem->setFlags(treeWidgetItem->flags() | Qt::ItemIsEditable). To filter what rows/columns can be edited, you can define your own itemDoubleClicked slot and use an if-statement (example).
You need to store combobox items in model item, for example using Qt::UserRole
QStringList options = {"one", "two", "three"};
QStandardItem* item = new QStandardItem(options[0]);
item->setData(QVariant(options),Qt::UserRole);
listTwo.append(item);
Then you need assign delegate to view. You can assign it for the whole table and return default delegate if index.data(Qt::UserRole).isNull().
Delegate* delegate = new Delegate(treeView);
treeView->setItemDelegate(delegate);
It's probably a good idea to set edit triggers to all, so dropdown occurs not only on doubleclick but also on single click
treeView->setEditTriggers(QAbstractItemView::AllEditTriggers);
Delegate must implement createEditor, setEditorData and setModelData
QWidget *Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.data(Qt::UserRole).isNull()) {
return QStyledItemDelegate::createEditor(parent, option, index);
}
return new QComboBox(parent);
}
void Delegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
if (!comboBox) {
return QStyledItemDelegate::setEditorData(editor, index);
}
QStringList options = index.data(Qt::UserRole).toStringList();
comboBox->addItems(options);
QString value = index.data().toString();
int current = options.indexOf(value);
if (current > -1) {
comboBox->setCurrentIndex(current);
}
comboBox->showPopup();
}
void Delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
if (!comboBox) {
return QStyledItemDelegate::setModelData(editor, model, index);
}
model->setData(index, comboBox->currentText());
}
By default delegate doesn't change how item is displayed and show editor only if edit is triggered: no combobox is shown. But you can override it with custom paintEvent.
void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.data(Qt::UserRole).isNull()) {
return QStyledItemDelegate::paint(painter, option, index);
}
QStyle* style = qApp->style();
QStyleOptionComboBox opt;
opt.rect = option.rect;
opt.currentText = index.data().toString();
opt.palette = option.palette;
opt.state = option.state;
opt.subControls = QStyle::SC_All;
opt.activeSubControls = QStyle::SC_All;
opt.editable = false;
opt.frame = true;
style->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, 0);
style->drawControl(QStyle::CE_ComboBoxLabel, &opt, painter, 0);
}
Full source here: combobox-delegate
I have a QComboBox with a popup list (a QAbstractItemView) showing different items (QStandardItems). Now, I want the item in the list to show a different text than if the item is selected.
Background:
I am creating a word-processor like style chooser where one can choose, say, "1.1 Heading 2" from the list, indicating the numbering and the style name, but when an item is chosen the combobox should only show the style name, say "Heading 2".
I thought the following question was exactly about what I was asking for but apparently an answer was chosen that does not work (apparently even according to the person asking the question): Can a QComboBox display a different value than whats in it's list?
Solution
Since QComboBox uses a list view to display the values, probably the "Qt'iest" way to achieve the desired effect, is to use a custom delegate and modify the text within its paint method, using a hash map (QHash) to get the corresponding string.
Example
Here is a simple example I have prepared for you to demonstrate how the proposed solution could be implemented:
Delegate.h this is where the magic is happening
#include <QStyledItemDelegate>
#include <QApplication>
class Delegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit Delegate(QObject *parent = nullptr) :
QStyledItemDelegate(parent) {}
void setHash(const QHash<int, QString> &hash) {
m_hash = hash;
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
if (!index.isValid())
return;
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
opt.text = m_hash.value(index.row());
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
}
private:
QHash<int, QString> m_hash;
};
MainWindow.h only for demo purposes
#include <QWidget>
#include <QBoxLayout>
#include <QComboBox>
#include <QStandardItemModel>
#include "Delegate.h"
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr)
: QWidget(parent)
{
auto *l = new QVBoxLayout(this);
auto *cmbBox = new QComboBox(this);
auto *model = new QStandardItemModel(this);
auto *delegate = new Delegate(this);
QHash<int, QString> hash;
for (int n = 0; n < 5; n++) {
// For demo purposes I am using "it#" and "item #"
// Feel free to set those strings to whatever you need
model->appendRow(new QStandardItem(tr("it%1").arg(QString::number(n))));
hash.insert(n, tr("item %1").arg(QString::number(n)));
}
delegate->setHash(hash);
cmbBox->setModel(model);
cmbBox->setItemDelegate(delegate);
l->addWidget(cmbBox);
resize(600, 480);
}
};
Result
The example produces the following result:
The easiest way to do is to set the ComboBox as editable and then when the current item changes, you change the text to whatever you want. Example:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStringList a = {"Red", "Green", "Blue"};
aModel.setStringList(a);
ui->comboBox->setModel(&aModel);
ui->comboBox->setEditable(true);
}
void MainWindow::on_comboBox_currentIndexChanged(const QString &arg1)
{
if (arg1 == "Green") {
ui->comboBox->setCurrentText("Green on");
} else if (arg1 == "Red") {
ui->comboBox->setCurrentText("Red on");
}
}
ui->comboBox->setCurrentText("Green on"); will only change the text when the item is selected, when you reopen the combobox, the text will be reverted back to original. This is somewhat similar to my answer here.
Another way to do this would be to inherit the QComboBox class and then reimplement the mousePressEvent to change the model whenever the mouse is pressed, and switch it back after releasing. This will probably be more difficult to get right or may not even work as I have not tried it myself
I have been researching for a while how to store images loaded on a QGraphicsView into rows of a QTableView using a QPushButton in a programmatic way but the information I found so far are not that many.
I have 1 QGraphicsView, a QPushButton (Send button) and a QTableView and a QLineEdit. When I upload images using the load button I show them both on the QGraphicsView and on the QLineEidt (I show the path of the image), if I click the Send button, the text of the QLineEdit should be added in the first row of the QTableView (which is happening) and the image should be stored inside the QTableView.
However, the image on the QGraphicsView is not being stored to the QTableView and nothing is being passed.
Currently this is what happens:
The expected behavior would be:
I created an ItemDelegate class that takes care of the resizing of the image on the QGraphicsView to be stored inside the QTableView
That part is shown below:
This is the mainwindow.h
#include <QGraphicsView>
#include <QGraphicsScene>
#include "imagedelegate.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void addData();
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
private slots:
void on_sendBtn_clicked();
void on_loadBtn_clicked();
private:
Ui::MainWindow *ui;
QStandardItemModel *model;
QGraphicsScene *leftScene;
};
#endif // MAINWINDOW_H
and here is the mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "imagedelegate.h"
#include <QGraphicsPixmapItem>
#include <QBuffer>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
leftScene = new QGraphicsScene(this);
ui->graphicsView->setScene(leftScene);
ui->graphicsView->show();
model = new QStandardItemModel();
ui->tableView->setModel(model);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::addData()
{
// Path on the first column
QStandardItem *pathAItem = new QStandardItem(ui->lineEdit->text());
// Image on the second column - not working yet
//QStandardItem *image1 = new QStandardItem(/*ui->graphicsView->*/);
QPixmap image1;
QByteArray img1Array;
QBuffer buffer1(&img1Array);
buffer1.open(QIODevice::WriteOnly);
image1.save(&buffer1, "PNG");
QList<QStandardItem*> row;
row << pathAItem;
model->setColumnCount(1);
model->appendRow(row);
}
void MainWindow::on_sendBtn_clicked()
{
addData();
}
void MainWindow::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QString colName = index.model()->headerData(index.column(), Qt::Horizontal).toString();
if(colName == "image1")
{
QPixmap iconPix;
if(!iconPix.loadFromData(index.model()->data(index).toByteArray())) {
}
iconPix = iconPix.scaledToHeight(32);
painter->drawPixmap(option.rect.x(),option.rect.y(),iconPix);
} else {
// QStyledItemDelegate::paint(painter, option, index);
}
}
The entire code will compile if you copy and paste so that you can see the issue I have.
Please shed light on this matter.
I wanted to answer to this question hoping that could also be useful to others. As suggested by Jeremy Friesner, the best (and fast in comparison to a QItemDelegate) way to send images into a QTableView using a QPushButton is to modify the void MainWindow::addData() function by using a QImage and pass it to a setData(QVariant(QPixmap::fromImage), Qt::DecorationRole) so that the entire function can be written as follows:
FIRST OPTION:
void MainWindow::on_sendBtn_clicked()
{
addData();
}
void MainWindow::addData()
{
QStandardItem *pathAItem = new QStandardItem(ui->pathLineEdit_A->text());
QStandardItem *pathBItem = new QStandardItem(ui->pathLineEdit_B->text());
QImage image1(ui->graphicsViewLeft->grab().toImage());
QStandardItem *item1 = new QStandardItem();
item1->setData(QVariant(QPixmap::fromImage(image1.scaled(42,42, Qt::KeepAspectRatio,Qt::SmoothTransformation))), Qt::DecorationRole);
ui->bookMarkTableView->setModel(model);
QImage image2(ui->graphicsViewRight->grab().toImage());
QStandardItem *item2 = new QStandardItem();
item2->setData(QVariant(QPixmap::fromImage(image2.scaled(42,42, Qt::KeepAspectRatio,Qt::SmoothTransformation))), Qt::DecorationRole);
ui->bookMarkTableView->setModel(model);
QList<QStandardItem*> row;
row << pathAItem << pathBItem << item1 << item2;
model->appendRow(row);
}
SECOND OPTION
If it is necessary to use a QItemDelgate I am posting that part of the code too (it is working as I already tried it):
In the imagedelegate.h is necessary to provide a QSize as follows:
class ImageDelegate : public QStyledItemDelegate
{
public:
ImageDelegate(QObject * parent = nullptr);
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const;
After that on your imagedelegate.cpp the implementation is:
#include "imagedelegate.h"
ImageDelegate::ImageDelegate(QObject * parent) : QStyledItemDelegate(parent)
{}
QSize ImageDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
return QSize(32,32);
Q_UNUSED(option);
Q_UNUSED(index);
}
void ImageDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
qDebug() << (index.model()->headerData(index.column(), Qt::Horizontal).toString());
QString colName = index.model()->headerData(index.column(), Qt::Horizontal).toString();
if(colName == "image1" || colName == "image2")
{
QPixmap iconPix;
if(!iconPix.loadFromData(index.model()->data(index).toByteArray())) {
}
iconPix = iconPix.scaledToHeight(32);
painter->drawPixmap(option.rect.x(),option.rect.y(),iconPix);
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}
In my case I had two columns in which I needed to save the images, so you can expand it for how many columns as you like and I also set a QSize of (32,32) but this is up to the developer.
I hope this will save your programming time and this is the final result! :)
I have a QListView and a QTableView both have the same QStandardItemModel. I have added a custom delegator to the QTableView. Now when I go to my QTableView and double-click an item I see the delegate editor widget, now if I go to my QListView and double-click the same item I see the delegate editor widget there as well. Point to be noted is that I see the editor widget only for those items in QListView which have been double-clicked in QTableView already. Whats going on here? Why do QListView items also showing the delegate editor widget even though the delegate is only added to the QTableView?
For reference, I am having below code:
#include <QtWidgets/QApplication>
#include <QtGui>
#include <QCombobox>
#include <QListview>
#include <QTableview>
#include <QLayout>
#include <QColor>
#include <QStyledItemDelegate>
#include <QSpinbox>
class SpinBoxDeligate : public QStyledItemDelegate {
public:
QWidget * createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override {
auto w = new QSpinBox(parent);
w->setFrame(false);
w->setMinimum(0);
w->setMaximum(100);
return w;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override {
static_cast<QSpinBox*>(editor)->setValue(index.data(Qt::EditRole).toInt());
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
model->setData(index, static_cast<QSpinBox*>(editor)->value(), Qt::EditRole);
}
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QStandardItemModel model(3, 1);
for (int r = 0; r < 3; ++r)
{
auto text = QString("%0").arg(r);
QStandardItem* item = new QStandardItem(text);
item->setFlags(Qt::ItemIsUserCheckable
| Qt::ItemIsEnabled
| Qt::ItemIsEditable
);
item->setData(Qt::Unchecked, Qt::CheckStateRole);
item->setData(text, Qt::ToolTipRole);
item->setData(QSize(100, 30), Qt::SizeHintRole);
item->setData(QIcon(":/QtMVC/Desert.jpg"), Qt::DecorationRole);
model.setItem(r, 0, item);
}
QComboBox* combo = new QComboBox();
combo->setModel(&model);
QListView* list = new QListView();
list->setModel(&model);
QTableView* table = new QTableView();
table->setModel(&model);
table->setItemDelegate(new SpinBoxDeligate());
QWidget w;
QVBoxLayout* containerLayout = new QVBoxLayout();
w.setLayout(containerLayout);
containerLayout->addWidget(combo);
containerLayout->addWidget(list);
containerLayout->addWidget(table);
w.show();
return app.exec();
}
The problem is really simple, if the data saved in the model are numbers the delegate is a QSpinBox by default, ie the delegate you see is the QListView is not the SpinBoxDeligate, but the delegate by default.
And why is it generated if you do not keep a number?
It's because the SpinBoxDeligate saves the data as a number.
So the solution is to save the data obtained by the SpinBoxDeligate as text:
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
model->setData(index, static_cast<QSpinBox*>(editor)->text(), Qt::EditRole);
}