Click to Update Database, from editable QAbstractTableModel - c++

An editable table view is connected to a custom model (a subclass of QAbstractTableModel) that populates it from a database. The table view's edited values are temporarily held in a 2D QVector.
The goal is for a push button below the table view to trigger an update of the database.
The problem is that I can't seem to figure out the SIGNAL&SLOT mechanism for this. It seems to be a problem of the scope of the push button, I think, but I don't know how to fix it.
First two files just for overview, the problem is (probably) in the third, in setData().
I'm still a beginner in this framework so apologies for a potentially silly question.
a tableView("menuTable") and a pushButton("menuTableUpdateButton") are made in Design Mode.
mainwindow.cpp (opening a db conection, querying db, placing result into QVector and initializing and assigning the model)
...
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//database connection
...
//opening database
if(db.open())
{
...
//2D array to hold the query to pass into model
QVector< QVector<QString> > sqlQueryVector;
...
//querying and storing the result
...
//initialize QAbstractTableModel and pass it into the menu table
//passing an id(int) and a 2D QVector that holds the sql query result to the model
menuTableModel* modelMenu = new menuTableModel(id, sqlQueryVector);
ui->menuTable->setModel(modelMenu);
}
else
{
...
}
}
menutablemodel.h (basic initializations)
#ifndef MENUTABLEMODEL_H
#define MENUTABLEMODEL_H
#include <QAbstractTableModel>
#include <QVector>
class menuTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit menuTableModel(const int &monId, const QVector< QVector<QString> > &qry, QObject *parent = 0)
: QAbstractTableModel(parent), mondayId(monId),query(qry) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const ;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex & index) const ;
void setQuery(const QVector< QVector<QString> > &query);
void setId(const int &stId);
private:
int mondayId;
QVector< QVector<QString> > query;
signals:
void editCompleted(const QString &);
slots:
void updateDatabase();
};
#endif // MENUTABLEMODEL_H
menutablemodel.cpp (assigning data into table, editing and SAVING TO DATABASE)
#include "menutablemodel.h"
#include <QDebug>
#include <QSqlQuery>
int menuTableModel::rowCount(const QModelIndex & /*parent*/) const {...}
int menuTableModel::columnCount(const QModelIndex & /*parent*/) const {...}
QVariant menuTableModel::data(const QModelIndex &index, int role) const {...}
QVariant menuTableModel::headerData(int section, Qt::Orientation orientation, int role) const {...}
//edit and save into database
bool menuTableModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if (role == Qt::EditRole)
{
query[index.row()][index.column()] = value.toString();
QString result;
for(int row= 0; row < query.size(); row++)
{
for(int col= 0; col < query[0].size(); col++)
{
result += query[row][col] + " ";
}
}
emit editCompleted( result );
qDebug() << "result is: " << result;
//update database here with connect??
//connect(menuTableUpdateButton, SIGNAL (clicked()), this, SLOT (updateDatabase()));
}
return true;
}
Qt::ItemFlags menuTableModel::flags(const QModelIndex & /*index*/) const {...}
void menuTableModel::setQuery(const QVector< QVector<QString> > &qry) {
query = qry;
}
void menuTableModel::setId(const int &stId) {...}
void menuTableModel::updateDatabase() {
//write SqlQuery here
...
}
My main question is where and how do I define the SIGNAL&SLOT command to connect the button being clicked to executing an SQL query to the database?

In your MainWindow constructor, you have the model and the button, so that's the best place to connect them:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//opening database
if(db.open()) {
//...
menuTableModel* modelMenu = new menuTableModel(id, sqlQueryVector);
ui->menuTable->setModel(modelMenu);
// Connect button click to model update
connect(ui->menuTableUpdateButton, SIGNAL(clicked()),
modelMenu, SLOT(updateDatabase()));
}
}

Related

QComboBox in QItemDelegate to show only the portion corresponding to other column

I have two MySQL tables with one-to-many relations.
Table si:
id integer primary key auto_increment,
...
cur_verify_date date,
...
Table verify:
...
si_id integer, -- id in 'si'
verify_date date,
...
So I need a combobox delegate for a table si in QTableView to choose items of verify_date only with si_id for corresponding id, different items in each row. However, the delegates can have, as I suppose, only identical values for each row. Is there a way to do it in the way I need?
Yes, you can do that with QItemDelegate, I would say that's your main solution when you want to show combobox in QTableView.
Check is signature of createEditor method from QAbstractItemDelegate:
https://doc.qt.io/qt-5/qabstractitemdelegate.html#createEditor
You can use QModelIndex parameter to define custom values for each row in your combobox.
While actual implementation will depend on your implementation of your model, you can check this simple example of that idea.
#include <QAbstractTableModel>
#include <QApplication>
#include <QTableView>
#include <QComboBox>
#include <QStyledItemDelegate>
class DummyModel : public QAbstractTableModel {
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
return 5;
}
int columnCount(const QModelIndex& parent = QModelIndex()) const override
{
return 2;
}
Qt::ItemFlags flags(const QModelIndex& index) const override
{
return __super::flags(index) | Qt::ItemIsEditable;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
{
if (role == Qt::DisplayRole && index.column() == 0)
return index.row();
return {};
}
};
class ComboBoxItemDelegate : public QStyledItemDelegate {
public:
ComboBoxItemDelegate(QObject* parent = nullptr)
: QStyledItemDelegate(parent) {};
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
QComboBox* cb = new QComboBox(parent);
//place to query database for actual values
const auto value = index.model()->index(index.row(), 0).data(Qt::DisplayRole).toString();
cb->addItem(value);
cb->addItem(value);
return cb;
}
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
DummyModel model;
QTableView table_view;
table_view.setModel(&model);
ComboBoxItemDelegate cb_delegate;
table_view.setItemDelegateForColumn(1, &cb_delegate);
table_view.show();
return app.exec();
}

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

How to pass images on QGraphicsView to QTableView programmatically using QPushButton

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! :)

Dragging from a QAbstractItemModel drops empty string

I derived a custem model from QAbstractItemModel and implemented drag&drop support by
overloading a flags() method that returns Qt::ItemIsDragEnabled where applicable,
overloading supportedDropActions and
implementing mimeData() to properly return a QMimeData object with some string set for the text/plain format.
When I setup this model in a QTreeView and enable dragging via setDragEnabled(true), I can pick items from the tree view and drag them. For example, I can drag an item and drop it onto a QLineEdit, which in turn will display the string that the model returned via mimeData().
However, when I drag an item and drop it onto another application's widgets (such as notepad or simply a second instance of my application) an empty string gets dropped.
I found another clue using the Drop site example shipped with Qt: When I drag an item over it, it correctly shows that there is text/plain available and it even displays the string associated with it. Yet, when I drop the item I get an empty string again in the dropEvent()...
Sourcecode
ssce.pro:
QT += core gui widgets
TARGET = ssce
TEMPLATE = app
SOURCES += main.cpp
main.cpp:
#include <QtCore>
#include <QtWidgets>
struct Model: public QAbstractItemModel {
int rowCount(const QModelIndex &parent) const {
return parent.isValid() ? 0 : 1;
}
int columnCount(const QModelIndex &parent) const {
return 1;
}
QModelIndex index(int row, int column, const QModelIndex &parent) const {
return parent.isValid() ? QModelIndex() : createIndex(row, column, static_cast<void *>(0));
}
QModelIndex parent(const QModelIndex &child) const {
return QModelIndex();
}
Qt::ItemFlags flags(const QModelIndex &index) const {
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
QVariant data(const QModelIndex &index, int role) const {
return (role == Qt::DisplayRole) ? "Test item" : QVariant();
}
QStringList mimeTypes() const {
return QStringList("text/plain");
}
QMimeData *mimeData(const QModelIndexList &indexes) const {
QMimeData *d = new QMimeData();
d->setText("hello world");
return d;
}
Qt::DropActions supportedDropActions() const {
return Qt::CopyAction;
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QDialog d;
QVBoxLayout l;
QLineEdit e;
l.addWidget(&e);
QTreeView v;
Model m;
v.setModel(&m);
v.setDragEnabled(true);
l.addWidget(&v);
d.setLayout(&l);
d.show();
return a.exec();
}
Description:
Run this and you can drag the item from the tree view into the line edit, it will display hello world then. Drop the item anywhere else, including the line edit of a second instance of the sample application, and nothing will happen.

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