Qt Using Custom QItemDelegate for QTableView
Asked Answered
K

3

19

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.

enter image description here

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);
}
Kine answered 21/5, 2013 at 0:27 Comment(0)
H
17

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.

Historiography answered 21/5, 2013 at 7:21 Comment(4)
When am I suppose to call ui->tableView->setItemDelegate(new ComboBoxDelegate);? I call it in my SetupUI() which is called before I add anything to the tableView and after making your changes it still does not work. I also tested calling it after the appendRow() but it still does not work. I have placed some debug statements in the QComboxItemDelegate to see how far along it was going but createEditor() is not getting calledKine
after this statement: "ui->tableView->setItemDelegate(new ComboBoxDelegate);" which object will be responsible of de-allocating the (new ComboBoxDelegate)?Fionafionna
also when should the destructor() of this class should be called, i put a debug statement inside it and there was no outputFionafionna
I've been trying to figure out how an Item Delegate is linked to a particular column - Now I'm gathering that there can only be 1 delegate per table, Am I correct in that assertion? I am trying to put 2 buttons in my table. One button to do one thing and the next button next column to do something else. I thought maybe ItemDelegates were the answer but don't know how to get them to work pulls hair outMontane
F
4

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.

Faceharden answered 7/11, 2018 at 19:28 Comment(0)
K
2

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

Kine answered 21/5, 2013 at 14:52 Comment(3)
if you figured out your problem thanks to hank you should give him credit and instead of giving yourself the correct answer check, edit and / or comment his/her answer.Sulfatize
To prevent those kind of issues in the future, I highly recommend using the override keyword of C++11. en.cppreference.com/w/cpp/language/overrideBlacklist
consider that i have edited 8 cells, i want to deallocate the qcombobox after each one has been edited, so where to delete them?Fionafionna

© 2022 - 2024 — McMap. All rights reserved.