QStyledItemDelegate: commit QComboBox value to model on click - c++

I am setting a QStyledItemDelegate on my model for a particular field, and returning a QComboBox from QStyledItemDelegate::createEditor
QComboBox* createEditor(QWidget* parent)
{
QComboBox* cb = new QComboBox(parent);
cb->addItem("UNDEFINED");
cb->addItem("TEST");
cb->addItem("OSE");
cb->addItem("TSE");
return cb;
}
void setEditorData(QWidget* editor, const QModelIndex& index)
{
QComboBox* cb = qobject_cast<QComboBox*>(editor);
if (!cb)
throw std::logic_error("editor is not a combo box");
QString value = index.data(Qt::EditRole).toString();
int idx = cb->findText(value);
if (idx >= 0)
cb->setCurrentIndex(idx);
cb->showPopup();
}
This is working fine, and when I select the field in question I am shown a combo box.
When I select an option from the drop-down list, the combobox closes and the item is displayed with a drop-down icon next to it:
At this point I would like the QStyledItemDelegate::setModelData function to be called, so that selecting an item in the list commits the data to the model.
However, I am required to first press Enter to commit the data (whereby the drop-down icon disappears)
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index)
{
QComboBox* cb = qobject_cast<QComboBox*>(editor);
if (!cb)
throw std::logic_error("editor is not a combo box");
model->setData(index, cb->currentText(), Qt::EditRole);
}
Question:
How can I configure my QComboBox to automatically commit the data when the user selects an item in the list and the combobox list closes, rather than requiring the additional press of Enter?

You have to issue the signal commitData and closeEditor when an item is selected as shown in the following example:
#include <QApplication>
#include <QStandardItemModel>
#include <QListView>
#include <QStyledItemDelegate>
#include <QComboBox>
class ComboBoxDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
Q_UNUSED(option)
Q_UNUSED(index)
QComboBox* editor = new QComboBox(parent);
connect(editor, QOverload<int>::of(&QComboBox::activated),
this, &ComboBoxDelegate::commitAndCloseEditor);
editor->addItems({"UNDEFINED", "TEST", "OSE", "TSE"});
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const{
QComboBox* cb = qobject_cast<QComboBox*>(editor);
if (!cb)
throw std::logic_error("editor is not a combo box");
QString value = index.data(Qt::EditRole).toString();
int idx = cb->findText(value);
if (idx >= 0)
cb->setCurrentIndex(idx);
cb->showPopup();
}
private:
void commitAndCloseEditor(){
QComboBox *editor = qobject_cast<QComboBox *>(sender());
emit commitData(editor);
emit closeEditor(editor);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListView view;
QStandardItemModel model;
for(int i=0; i<10; i++){
model.appendRow(new QStandardItem("UNDEFINED"));
}
view.setItemDelegate(new ComboBoxDelegate(&view));
view.setModel(&model);
view.show();
return a.exec();
}

Related

QListView set Custom Editor via QStyledItemDelegate::createEditor

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().

Add QCombobox inside QTreeview specific cell

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

How to add Autocompletion for QTableWidget

What i want to do is to start typing some data in a table cell and it shows completion suggestions but so far no success.
I tried adding QLineEdit in a cell but is there a way to accomplish that without using QLineEdit as a cellWidget?
Edit:
Made it work by overriding QItemDelegate class
Autocomplete_Delegate.h
#include <QItemDelegate>
#include <QModelIndex>
#include <QLineEdit>
#include <QCompleter>
class Autocomplete_Delegate : public QItemDelegate {
public:
Autocomplete_Delegate(QObject *parent, QStringList model);
~Autocomplete_Delegate();
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
private:
QStringList model;
};
Autocomplete_Delegate..cpp
Autocomplete_Delegate::Autocomplete_Delegate(QObject *parent, QStringList model) : QItemDelegate(parent), model(model) {}
Autocomplete_Delegate::~Autocomplete_Delegate() {
model.clear();
}
QWidget *Autocomplete_Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QWidget *editor = QItemDelegate::createEditor(parent, option, index); //* Create the editor so it looks native to the tablewidget
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); //* create a linedit so it behaves like a line edit and cast the editor to line edit
QCompleter *completer = new QCompleter(model, parent); //* make a completer and pass in the wordlist
completer->setCaseSensitivity(Qt::CaseInsensitive); //* set the case senstivity
lineEdit->setCompleter(completer); //* set the completor on line edit
return lineEdit;
}
void Autocomplete_Delegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
QString data = index.model()->data(index, Qt::EditRole).toString(); //* get the data from the model -> the cell
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(data); //* set the data in the editor
}
Thanks to #eyllanesc for the idea.
You can use a role associated with the QTableWidgetItem, and use a delegate to create a QCompleter where your model is established and updated.
#include <QtWidgets>
enum CustomRoles{
ListCompleterRole = Qt::UserRole + 1000
};
class CompletedDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QWidget* editor = QStyledItemDelegate::createEditor(parent, option, index);
if(QLineEdit *le = qobject_cast<QLineEdit *>(editor)){
QStringListModel *model = new QStringListModel(le);
QCompleter *completer = new QCompleter(le);
completer->setModel(model);
le->setCompleter(completer);
}
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const{
QStyledItemDelegate::setEditorData(editor, index);
if(QLineEdit *le = qobject_cast<QLineEdit *>(editor)){
if(QCompleter *completer = le->completer()){
if(QStringListModel *model = qobject_cast<QStringListModel *>(completer->model())){
QVariant v = index.data(CustomRoles::ListCompleterRole);
if (v.canConvert<QStringList>()){
QStringList options = v.value<QStringList>();
model->setStringList(options);
}
}
}
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStringList words = {
"astonishing",
"agreement",
"appeal",
"autonomy",
"accompany",
"articulate",
"article",
"amuse",
"advertise",
"admiration"
};
QTableWidget w(1, 1);
CompletedDelegate *delegate = new CompletedDelegate(&w);
w.setItemDelegate(delegate);
QTableWidgetItem *item = new QTableWidgetItem;
item->setData(CustomRoles::ListCompleterRole, words); // update options
w.setItem(0, 0, item);
w.show();
return a.exec();
}

QListView items also showing the delegate editor even though the delegate is only added to the QTableView

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);
}

Qt Using Custom QItemDelegate for QTableView

I followed the Spin Box Delegate tutorial, which Qt provides, to try to implement my own QItemDelegate. It would be used to specify a QComboBox to represent data in a QTableView cell but it is not working.
My biggest problem is that I don't know when my QItemDelegate is going to be utilized.
when itemModel->setData() is used or when itemModel->setItem(). I would suspect setItem() because I reimplemented a QItemDelegate (emphasis on the "Item") but the tutorial uses setData() and it works fine.
I know that if the specified QItemDelegate does not work it uses the default one but how do I now that the one I specified did not work?
when should I suspect for QTableView to use my delegate. I would like to specify which delegates to use for each cell. Is this possible or does the QTableView only use one delegate throughout?
How would I specify the items to populate the QComboBox once it gets displayed by the QTableView?
I implemented QItemDelegate here:
the part where I try to add the cell which is suppose to use the QComboBox is under the comment "Enabled" in mainwindow.cpp further down this post.
qcomboboxitemdelegate.h
#ifndef QCOMBOBOXITEMDELEGATE_H
#define QCOMBOBOXITEMDELEGATE_H
#include <QItemDelegate>
#include <QComboBox>
class QComboBoxItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit QComboBoxItemDelegate(QObject *parent = 0);
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index);
void setEditorData(QWidget *editor, const QModelIndex &index);
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index);
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index);
signals:
private:
};
#endif // QCOMBOBOXITEMDELEGATE_H
qcomboboxitemdelegate.cpp
#include "qcomboboxitemdelegate.h"
#include <QDebug>
QComboBoxItemDelegate::QComboBoxItemDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) {
// create widget for use
QComboBox* comboBox = new QComboBox(parent);
return comboBox;
}
void QComboBoxItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) {
// update model widget
QString value = index.model()->data(index, Qt::EditRole).toString();
qDebug() << "Value:" << value;
QComboBox* comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentIndex(comboBox->findText(value));
}
void QComboBoxItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) {
// store edited model data to model
QComboBox* comboBox = static_cast<QComboBox*>(editor);
QString value = comboBox->currentText();
model->setData(index, value, Qt::EditRole);
}
void QComboBoxItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) {
editor->setGeometry(option.rect);
}
mainwindow.cpp : this is where I initialize the QStandardItemModel
void MainWindow::init() {
itemModel = new QStandardItemModel(this);
}
void MainWindow::setupUi() {
this->setWindowTitle("QAlarmClock");
QStringList labelList;
labelList << "Alarm Name" << "Time" << "Enabled";
itemModel->setHorizontalHeaderLabels(labelList);
ui->tableView->setModel(itemModel);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableView->setItemDelegate(comboBoxItemDelegate);
}
void MainWindow::on_actionNew_triggered() {
alarmDialog = new AlarmDialog(this);
connect(alarmDialog, SIGNAL(on_close()), this, SLOT(on_alarmDialog_close()));
alarmDialog->exec();
}
mainwindow.cpp : this is where I update QStandardItemModel
void MainWindow::on_alarmDialog_close() {
QString alarmName = alarmDialog->getAlarmName();
QDateTime alarmDateTime = alarmDialog->getDateTime();
itemModel->insertRow(itemModel->rowCount());
int rowCount = itemModel->rowCount();
// Alarm Name
QStandardItem* alarmItem = new QStandardItem(QIcon("res/alarmclock.ico"), alarmName);
itemModel->setItem(rowCount - 1 , 0, alarmItem);
// Date Time
QStandardItem* dateTimeItem = new QStandardItem();
dateTimeItem->setText(alarmDateTime.toString());
dateTimeItem->setEditable(false);
itemModel->setItem(rowCount - 1, 1, dateTimeItem);
// Enabled
QStandardItem* enabledItem = new QStandardItem();
QList<QStandardItem*> optionList;
optionList << new QStandardItem("Enabled") << new QStandardItem("Disabled");
enabledItem->appendRows(optionList);
itemModel->setItem(rowCount - 1, 2, enabledItem);
}
Edit 1
qcomboboxdelegate.cpp
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) {
// create widget for use
qDebug() << "Column: " << index.column();
if (index.column() == 2) {
QComboBox* comboBox = new QComboBox(parent);
QStringList values;
values << "Enabled" << "Disabled";
comboBox->addItems(values);
return comboBox;
} else {
return QItemDelegate::createEditor(parent, option, index);
}
}
mainwindow.cpp
void MainWindow::on_alarmDialog_close() {
QList<QStandardItem*> row;
QString alarmName = alarmDialog->getAlarmName();
QDateTime alarmDateTime = alarmDialog->getDateTime();
QString status = "Enabled";
// Alarm Name
QStandardItem* alarmItem = new QStandardItem(QIcon("res/alarmclock.ico"), alarmName);
row << alarmItem;
// Date Time
QStandardItem* dateTimeItem = new QStandardItem();
dateTimeItem->setText(alarmDateTime.toString());
dateTimeItem->setEditable(false);
row << dateTimeItem;
// Enabled
QStandardItem* statusItem = new QStandardItem(status);
row << statusItem;
itemModel->appendRow(row);
}
First, you should have a description of your model columns:
enum Columns
{
COL_NAME,
COL_TIME,
COL_STATUS
}
Your delegate should only work for the last column.
Here is an example of how you can populate your model:
for (int i = 0; i < 5; ++i)
{
QStandardItem *itemName = new QStandardItem(QString("name %1").arg(i));
QStandardItem *itemTime = new QStandardItem(QString("time %1").arg(i));
QString status;
if (i % 2 == 0)
{
status = "Enabled";
}
else
{
status = "Disabled";
}
QStandardItem *itemStatus = new QStandardItem(status);
QList<QStandardItem*> row;
row << itemName << itemTime << itemStatus;
model->appendRow(row);
}
As I said, your delegate should only work for the last column. So all methods you have reimplemented should have a column check like this:
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index)
{
if (index.column() == COL_STATUS)
{
QStringList values;
values << "Enabled" << "Disabled";
QComboBox* comboBox = new QComboBox(parent);
comboBox->addItems(values);
return comboBox;
}
else
{
return QItemDelegate::createEditor(parent, option, index);
}
}
You should add this check to the other methods: if the current column is not the status column, the base class (QItemDelegate) implementation should be used.
Then you set your delegate to your view:
ui->tableView->setItemDelegate(new ComboBoxDelegate);
If you do everything right, a combo Box will appear in the last column if you try to edit its values.
Even more simply; I found QTableView::setItemDelegateForColumn() to work admirably for a column. For example, in your MainWindow, you could make a member:
QComboBoxItemDelegate dgtComboDelegate;
Then, in your ctor, or init(), you could have
ui->tableView->setItemDelegateForColumn(2, dgtComboDelegate);
If you wanted that to happen for a single cell, that's when you need to test on the index.column() and index.row().
You know, you don't have to create a QTableView to do this either. E.g., see the ?:
Qt - Centering a checkbox in a QTable
The OP doesn't give the declaration for a table widget or view; but it does have the QTableView tag. It should work equally well for either.
In the former case, you can do ui->tableWidget->setItemDelegateForColumn(2, dgtComboDelegate); and never have to make your own model. Just use setData() on the items you create (or even later, for that matter,) to initialize their values.
So I figured out that I did not override the correct function prototypes..! I forgot that they
had const in the prototype meaning that I was not overriding any functions so it was using the default ones. Here are the correct virtual functions that have to be re-implemented: http://qt-project.org/doc/qt-5.0/qtwidgets/qitemdelegate.html