Qt Использование пользовательского QItemDelegate для QTableView

Я следовал руководству по Spin Box Delegate, которое предоставляет Qt, чтобы попытаться реализовать мой собственный QItemDelegate. Он будет использоваться для указания QComboBox для представления данных в ячейке QTableView но он не работает.

enter image description here

Моя самая большая проблема в том, что я не знаю, когда мой QItemDelegate будет использоваться.

  • когда используется itemModel->setData() или когда itemModel->setItem(). Я бы заподозрил setItem() потому что я переопределил QItemDelegate (акцент на "Item"), но в учебнике используется setData() и он отлично работает.

  • Я знаю, что если указанный QItemDelegate не работает, он использует по умолчанию, но как мне теперь, если указанный мной не работает?

  • когда я должен заподозрить использование QTableView моего делегата. Я хотел бы указать, какие делегаты использовать для каждой ячейки. Это возможно, или QTableView использует только один делегат?

  • Как бы я указал элементы для заполнения QComboBox только он отобразится в QTableView?

Я реализовал QItemDelegate здесь:

  • часть, где я пытаюсь добавить ячейку, которая должна использовать QComboBox находится под комментарием "Включено" в mainwindow.cpp далее в этом посте.

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: здесь я инициализирую 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: здесь я обновляю 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);
}

Редактировать 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);
}

Ответ 1

Во-первых, у вас должно быть описание столбцов вашей модели:

enum Columns
{
    COL_NAME,
    COL_TIME,
    COL_STATUS
}

Ваш делегат должен работать только для последнего столбца.

Вот пример того, как вы можете заполнить свою модель:

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

Как я уже сказал, ваш делегат должен работать только для последнего столбца. Таким образом, все методы, которые вы переопределили, должны иметь проверку столбцов следующим образом:

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

Вы должны добавить эту проверку к другим методам: если текущий столбец не является столбцом состояния, следует использовать реализацию базового класса (QItemDelegate).

Затем вы устанавливаете свой делегат на ваш взгляд:

ui->tableView->setItemDelegate(new ComboBoxDelegate);

Если вы все сделаете правильно, в последнем столбце появится поле со списком, если вы попытаетесь изменить его значения.

Ответ 2

Итак, я понял, что я не отменял правильные прототипы функций..! Я забыл, что они имел const в прототипе, означающем, что я не перекрывал никаких функций, поэтому использовал стандартные. Вот верные виртуальные функции, которые необходимо выполнить повторно: http://qt-project.org/doc/qt-5.0/qtwidgets/qitemdelegate.html

Ответ 3

Еще проще; Я обнаружил, что QTableView :: setItemDelegateForColumn() работает превосходно для столбца. Например, в вашем MainWindow вы можете сделать член:

QComboBoxItemDelegate dgtComboDelegate;

Затем в вашем ctor или init() вы могли бы иметь

ui->tableView->setItemDelegateForColumn(2, dgtComboDelegate);

Если вы хотите, чтобы это происходило с одной ячейкой, тогда вам нужно протестировать на index.column() и index.row().

Вы знаете, вам не нужно создавать QTableView, чтобы сделать это тоже. Например, увидеть?

Qt - Центрирование флажка в QTable

OP не дает объявление для виджета или представления таблицы; но у него есть тег QTableView. Это должно работать одинаково хорошо для обоих.

В последнем случае вы можете сделать ui->tableWidget->setItemDelegateForColumn(2, dgtComboDelegate); и никогда не придется делать свою собственную модель. Просто используйте setData() для элементов, которые вы создаете (или даже позже), чтобы инициализировать их значения.