使用自定义QItemDelegate为QTableView提供Qt界面技术

19

我按照Qt提供的SpinBox Delegate教程,尝试实现自己的QItemDelegate。它将用于指定一个QComboBox来表示QTableView单元格中的数据,但它没有起作用。

enter image description here

我的最大问题是我不知道何时会使用我的QItemDelegate

  • 当使用itemModel->setData()itemModel->setItem()时,我会怀疑使用setItem(),因为我重新实现了QItemDelegate(重点在于“Item”),但教程使用setData()并且可以正常工作。

  • 我知道如果指定的QItemDelegate不起作用,则使用默认值,但我如何知道我指定的代理没有起作用?

  • 应该在什么情况下怀疑QTableView使用我的委托。我想为每个单元格指定要使用的委托。这是可能的吗,还是QTableView只使用一个委托?

  • QTableView显示QComboBox时,我该如何指定要填充的项目?

这里是我实现的QItemDelegate

  • 我尝试添加应该使用QComboBox的单元格的部分在mainwindow.cpp下面的“Enabled”注释中。

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

主窗口.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);
}
3个回答

17

首先,您应该对模型列进行描述:

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

如果您操作正确,当您尝试编辑其值时,将在最后一列出现一个组合框。

我应该在什么时候调用ui->tableView->setItemDelegate(new ComboBoxDelegate);?我在SetupUI()中调用它,这是在我向tableView添加任何内容之前调用的,并且在您进行更改后,它仍然无法正常工作。我还尝试在appendRow()之后调用它,但它仍然无法正常工作。我已经在QComboxItemDelegate中放置了一些调试语句以查看它进行到哪个阶段,但createEditor()没有被调用。 - arnm
在这条语句之后,哪个对象将负责释放(new ComboBoxDelegate)?答案:ui->tableView - AAEM
还有,这个类的析构函数(destructor())应该在什么时候被调用?我在里面放了一个调试语句,但没有输出。 - AAEM
我一直在努力弄清楚项目委托是如何与特定列关联的 - 现在我得出的结论是每个表格只能有一个委托,我的说法正确吗?我想在我的表格中放置两个按钮。一个按钮用于执行某个操作,下一个按钮用于执行其他操作。我以为项目委托可能是答案,但不知道如何让它们起作用 抓狂 - Seth D. Fulmer

4
更简单的方法是使用QTableView::setItemDelegateForColumn()来处理列。例如,在您的MainWindow中,您可以创建一个成员变量:
QComboBoxItemDelegate dgtComboDelegate;

然后,在您的构造函数或init()中,您可以有
ui->tableView->setItemDelegateForColumn(2, dgtComboDelegate);

如果您只想针对单个单元格进行此操作,那么您需要测试index.column()和index.row()。
您知道,您不必创建QTableView来执行此操作。例如,参见?: Qt-在QTable中居中复选框 OP没有给出表部件或视图的声明;但是它具有QTableView标记。它应该对任何一种都同样有效。
在前一种情况下,您可以执行ui->tableWidget->setItemDelegateForColumn(2, dgtComboDelegate);而无需制作自己的模型。只需使用setData()在创建的项目上(或甚至以后)初始化其值即可。

2

2
如果你通过 Hank 解决了问题,应该给他以功劳,而不是将正确答案标记为自己的并提交。你可以编辑或评论他/她的回答。 - refuzee
4
为了避免类似问题的发生,我强烈建议使用 C++11 中的 override 关键字。请参考链接 http://en.cppreference.com/w/cpp/language/override。 - Joel Bodenmann
考虑到我已经编辑了8个单元格,我想在每个单元格被编辑后释放QComboBox,那么在哪里删除它们? - AAEM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接