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 model
setColumnCount(2);
setHorizontalHeaderLabels(QStringList()<< "属性" << "值") ;
// Root item
QStandardItem *rootItem = invisibleRootItem();
QStandardItem *classItem = new QStandardItem("1班");
rootItem->appendRow(classItem);
// Students
QStringList students = {"张三", "李四", "王五"};
for (const QString &student : students)
{
QStandardItem *studentItem = new QStandardItem(student);
classItem->appendRow(studentItem);
// Subjects
QStringList subjects = {"语文", "数学", "英语", "性别"};
for (const QString &subject : subjects)
{
QStandardItem *subjectItem = new QStandardItem(subject);
subjectItem->setEditable(false); // Property column is not editable
QStandardItem *valueItem = new QStandardItem(subject == "性别"?"女":"100.0");
valueItem->setEditable(true); // Value column is editable for level 2
studentItem->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 editable
return 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_OBJECT
public:
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_OBJECT
public:
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();
}