1. 先举个例子
1班有3个学生:张三、李四、王五
4个学生属性:语文 数学 英语 性别。
语文 数学 英语使用QDoubleSpinBox* 编辑,范围为0到100,1位小数
性别使用QComboBox* 编辑,选项为:男、女
实现效果:
2. 按照例子实现
2.1 自定义一个QStandardItemModel 来存数据
#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QStyledItemDelegate>
#include <QHeaderView>class CustomStandardItemModel : public QStandardItemModel
{
public:CustomStandardItemModel(QObject *parent = nullptr);Qt::ItemFlags flags(const QModelIndex &index) const override;
};
class CustomStandardItemModel : public QStandardItemModel
{
public:CustomStandardItemModel(QObject *parent = nullptr);Qt::ItemFlags flags(const QModelIndex &index) const override;
};CustomStandardItemModel::CustomStandardItemModel(QObject *parent) : QStandardItemModel(parent)
{// Set up the modelsetColumnCount(2);setHorizontalHeaderLabels(QStringList()<< "属性" << "值") ;// Root itemQStandardItem *rootItem = invisibleRootItem();QStandardItem *classItem = new QStandardItem("1班");rootItem->appendRow(classItem);// StudentsQStringList students = {"张三", "李四", "王五"};for (const QString &student : students){QStandardItem *studentItem = new QStandardItem(student);classItem->appendRow(studentItem);// SubjectsQStringList subjects = {"语文", "数学", "英语", "性别"};for (const QString &subject : subjects){QStandardItem *subjectItem = new QStandardItem(subject);subjectItem->setEditable(false); // Property column is not editableQStandardItem *valueItem = new QStandardItem(subject == "性别"?"女":"100.0");valueItem->setEditable(true); // Value column is editable for level 2studentItem->appendRow(QList<QStandardItem*>() << subjectItem << valueItem);}}
}Qt::ItemFlags CustomStandardItemModel::flags(const QModelIndex &index) const
{if (index.column() == 1 && index.parent().isValid()) {QStandardItem *item = itemFromIndex(index);if (item && item->hasChildren()) {// If the item has children, it's a student node, make value column not editablereturn QAbstractItemModel::flags(index) & ~Qt::ItemIsEditable;}}return QStandardItemModel::flags(index);
}
2.2 自定义一个QStyledItemDelegate 来显示不同的QWidget控件
class CustomDelegate : public QStyledItemDelegate
{
public:CustomDelegate(QObject *parent = nullptr) ;QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;};
CustomDelegate::CustomDelegate(QObject *parent) : QStyledItemDelegate(parent)
{}QWidget *CustomDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{if (index.column() == 1 && index.parent().isValid()){QString property = index.sibling(index.row(), 0).data().toString();if (property == "语文" || property == "数学" || property == "英语"){QDoubleSpinBox *spinBox = new QDoubleSpinBox(parent);spinBox->setRange(0, 100);spinBox->setDecimals(1);return spinBox;} else if (property == "性别") {QComboBox *comboBox = new QComboBox(parent);comboBox->addItem("男");comboBox->addItem("女");return comboBox;}}return QStyledItemDelegate::createEditor(parent, option, index);
}
2.3 使用 QTreeView 来显示
int main(int argc, char *argv[])
{QApplication a(argc, argv);CustomStandardItemModel* model;CustomDelegate* delegate;QTreeView *treeView;treeView = new QTreeView();treeView->setObjectName(QString::fromUtf8("treeView"));treeView->setGeometry(QRect(40, 30, 241, 501));model = new CustomStandardItemModel();delegate = new CustomDelegate();treeView->setModel(model);treeView->setItemDelegate(delegate);treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);treeView->expandAll();treeView->show();bool ret = a.exec();delete model;delete delegate;delete treeView;return ret;
}
2.4 运行效果
修改语文、数学、英语时,双击后,变成QDoubleSpinBox 控件
修改性别时,双击后,变成QComboBox控件
3. 增加修改的信号
要在 CustomDelegate 中实现值修改时发送信号,通知告知是哪个学生的哪个属性的值变成了多少,可以按照以下步骤进行修改:
定义信号:在 CustomDelegate 类中添加一个信号,用于在值修改时发送。
捕获编辑器值的变化:在 setModelData 方法中捕获编辑器的值变化,并发出信号。
修改代码:
class CustomDelegate : public QStyledItemDelegate
{Q_OBJECT
public:CustomDelegate(QObject *parent = nullptr) ;QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;signals:void valueChanged( QString &student, QString &property, QVariant value) const;
};
CustomDelegate::CustomDelegate(QObject *parent) : QStyledItemDelegate(parent)
{}QWidget *CustomDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{if (index.column() == 1 && index.parent().isValid()) {QString property = index.sibling(index.row(), 0).data().toString();if (property == "语文" || property == "数学" || property == "英语") {QDoubleSpinBox *spinBox = new QDoubleSpinBox(parent);spinBox->setRange(0, 100);spinBox->setDecimals(1);return spinBox;} else if (property == "性别") {QComboBox *comboBox = new QComboBox(parent);comboBox->addItem("男");comboBox->addItem("女");return comboBox;}}return QStyledItemDelegate::createEditor(parent, option, index);
}void CustomDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{if (index.column() == 1 && index.parent().isValid()) {QString property = index.sibling(index.row(), 0).data().toString();QString student = index.parent().data().toString();if (QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox*>(editor)){double value = spinBox->value();model->setData(index, value);emit valueChanged(student, property, QVariant::fromValue(value));}else if (QComboBox *comboBox = qobject_cast<QComboBox*>(editor)){QString value = comboBox->currentText();model->setData(index, value);emit valueChanged(student, property, QVariant::fromValue(value));}}else{QStyledItemDelegate::setModelData(editor, model, index);}
}
连接信号和槽
// 连接信号槽QObject::connect(delegate, &CustomDelegate::valueChanged, [&](const QString &student, const QString &property, QVariant value) {if (property == "性别"){qDebug() << "学生:" << student << "属性:" << property << "值:" << value.toString();}else{qDebug() << "学生:" << student << "属性:" << property << "值:" << value.toDouble();}});
4. 上面例子改为QTreeWidget 实现
基本步骤也差不多,就是少了QStandardItemModel
#include <QApplication>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QHeaderView>
#include <QLineEdit>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QAbstractItemView>
#include <QEvent>
#include <QMouseEvent>
#include <QItemDelegate>
#include <QDebug>
#include <cmath> // 用于std::fabs函数
#include <iostream>class MyTreeWidgetDelegate : public QItemDelegate {Q_OBJECTpublic:MyTreeWidgetDelegate(QObject* parent = nullptr) : QItemDelegate(parent) {}QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override {if (index.column() == 1){QTreeWidgetItem* item = static_cast<QTreeWidgetItem*>(index.internalPointer());if (item && item->parent() && item->parent()->parent()) {const QString attr = item->text(0);if (attr == "语文" || attr == "数学" || attr == "英语") {QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);spinBox->setRange(0, 100);spinBox->setDecimals(1);spinBox->setSingleStep(0.1);return spinBox;}else if (attr == "性别") {QComboBox* comboBox = new QComboBox(parent);comboBox->addItems({ "男", "女" });return comboBox;}}}return QItemDelegate::createEditor(parent, option, index);}void setEditorData(QWidget* editor, const QModelIndex& index) const override {if (index.column() == 1) {QTreeWidgetItem* item = static_cast<QTreeWidgetItem*>(index.internalPointer());if (item && item->parent() && item->parent()->parent()) {const QString attr = item->text(0);if (attr == "语文" || attr == "数学" || attr == "英语") {QDoubleSpinBox* spinBox = qobject_cast<QDoubleSpinBox*>(editor);if (spinBox) {spinBox->setValue(item->text(1).toDouble());}}else if (attr == "性别") {QComboBox* comboBox = qobject_cast<QComboBox*>(editor);if (comboBox) {comboBox->setCurrentText(item->text(1));}}}}else {QItemDelegate::setEditorData(editor, index);}}void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override {if (index.column() == 1) {QString property = index.sibling(index.row(), 0).data().toString();QString student = index.parent().data().toString();if (QDoubleSpinBox* spinBox = qobject_cast<QDoubleSpinBox*>(editor)){double oldValue = index.sibling(index.row(), 1).data().toDouble();double value = spinBox->value();if (std::fabs(oldValue - value) > 1e-6){model->setData(index, value, Qt::EditRole);emit valueChanged(student, property, QVariant::fromValue(value));}}else if (QComboBox* comboBox = qobject_cast<QComboBox*>(editor)){QString oldValue = index.sibling(index.row(), 1).data().toString();QString value = comboBox->currentText();if (oldValue != value){model->setData(index, value, Qt::EditRole);emit valueChanged(student, property, QVariant::fromValue(value));}}}else {QItemDelegate::setModelData(editor, model, index);}}void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override {editor->setGeometry(option.rect);}
signals:void valueChanged(QString& student, QString& property, QVariant value) const;
};class CustomTreeWidget : public QTreeWidget {Q_OBJECTpublic:CustomTreeWidget(QWidget* parent = nullptr) : QTreeWidget(parent) {setColumnCount(2);setHeaderLabels({ "属性", "值" });// 设置属性列不可编辑header()->setSectionResizeMode(0, QHeaderView::Stretch);header()->setSectionResizeMode(1, QHeaderView::Stretch);// 创建班级节点QTreeWidgetItem* classItem = new QTreeWidgetItem(this);classItem->setText(0, "1班");classItem->setFlags(classItem->flags() & ~Qt::ItemIsEditable);// 创建学生节点QStringList students = { "张三", "李四", "王五" };for (const QString& student : students) {QTreeWidgetItem* studentItem = new QTreeWidgetItem(classItem);studentItem->setText(0, student);studentItem->setFlags(studentItem->flags() & ~Qt::ItemIsEditable);// 创建学生属性节点QStringList attributes = { "语文", "数学", "英语", "性别" };for (const QString& attr : attributes) {QTreeWidgetItem* attrItem = new QTreeWidgetItem(studentItem);attrItem->setText(0, attr);if (attr == "语文" || attr == "数学" || attr == "英语"){attrItem->setText(1, "100");}else{attrItem->setText(1, "男");}attrItem->setFlags(attrItem->flags() & ~Qt::ItemIsEditable);if (attr == "语文" || attr == "数学" || attr == "英语" || attr == "性别") {attrItem->setFlags(attrItem->flags() | Qt::ItemIsEditable);}else {attrItem->setFlags(attrItem->flags() & ~Qt::ItemIsEditable);}}}// 设置编辑策略m_delegate = new MyTreeWidgetDelegate(this);setItemDelegateForColumn(1, m_delegate);setEditTriggers(QAbstractItemView::DoubleClicked);// 连接信号槽QObject::connect(m_delegate, &MyTreeWidgetDelegate::valueChanged, [&](const QString& student, const QString& property, QVariant value){if (property == "性别"){qDebug() << "学生:" << student << "属性:" << property << "值:" << value.toString();}else{qDebug() << "学生:" << student << "属性:" << property << "值:" << value.toDouble();}});}protected:void mousePressEvent(QMouseEvent* event) override {QTreeWidgetItem* item = itemAt(event->pos());if (item && item->columnCount() > 1){int column = columnAt(event->x());if (column == 1 && item->parent() && item->parent()->parent()){editItem(item, column);return;}}QTreeWidget::mousePressEvent(event);}
private:MyTreeWidgetDelegate* m_delegate;
};int main(int argc, char* argv[]) {QApplication a(argc, argv);CustomTreeWidget w;w.setGeometry(QRect(40, 30, 241, 501));w.expandAll();w.show();return a.exec();
}