QTreeView 与 QTreeWidget 例子

server/2024/12/17 1:55:31/

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

在这里插入图片描述


http://www.ppmy.cn/server/150772.html

相关文章

Scala的正则表达式3

贪婪模式与非贪婪模式 object test { //正则表达式 def main(args: Array[String]): Unit { // 贪婪模式 // 正则匹配默认是贪婪模式的 // ? 非贪婪模式,加在量词的后面 //在如下字符串中 查找 满足正则表达式要求的内容 // 找全部的手机号 // 规则&#xff1a; // 1.11位数…

scala列表

1 不可变 List 说明 &#xff08;1&#xff09;List 默认为不可变集合 &#xff08;2&#xff09;创建一个 List&#xff08;数据有顺序&#xff0c;可重复&#xff09; &#xff08;3&#xff09;遍历 List &#xff08;4&#xff09;List 增加数据 &#xff08;5&#…

数据库系统原理 第六章 关系数据库的规范化理论

文章目录 1.问题的提出1.1概念回顾1.2.关系模式的形式化定义1.3.什么是数据依赖1.4.数据依赖对关系模式的影响 2.规范化2.1函数依赖2.2码2.3.范式(Normal-Form) 3.数据依赖的公理系统3.1ArmStrong公理系统3.2闭包3.3计算关系R的属性集X的闭包的步骤如下3.4候选码求解理论与算法…

20分钟入门solidity(1)

1. Solidity简介 Solidity是一种静态类型编程语言&#xff0c;专门用于在以太坊区块链上编写智能合约。它借鉴了JavaScript、Python和C的语法&#xff0c;非常适合开发在以太坊虚拟机&#xff08;EVM&#xff09;上运行的应用程序。 智能合约&#xff1a;表达商业、法律关系的…

17.Java 正则表达式(正则表达式底层实现、元字符、正则表达式常用类)

一、正则表达式引入 1、概述 正则表达式&#xff08;regular expression&#xff0c;简称 RegExp&#xff09;是处理文本的利器&#xff0c;是对字符串执行模式匹配的技术 例如&#xff0c;使用传统方式提取内容中的所有英文单词就要使用遍历&#xff0c;代码量大&#xff0c…

es有string类型字段吗

在较新的版本的 Elasticsearch (ES) 中,string 类型已经被移除,取而代之的是更具体的 text 和 keyword 类型。这一改变是在 Elasticsearch 5.0 版本引入的,目的是为了提供更好的性能和更明确的数据处理方式。 Text 类型 用途:text 类型用于全文搜索。当你有一个字段需要进…

【工具】linux matlab 的使用

问题1 - 复制图表 在使用linux matlab画图后&#xff0c;无法保存figure。 例如在windows下 但是在linux下并没有这个“Copy Figure”的选项。 这是因为 “ The Copy Figure option is not available on Linux systems. Use the programmatic alternative.” 解决方案&…

《Vue进阶教程》第十课:其它函数

往期内容&#xff1a; 《Vue零基础入门教程》合集&#xff08;完结&#xff09; 《Vue进阶教程》第一课&#xff1a;什么是组合式API 《Vue进阶教程》第二课&#xff1a;为什么提出组合式API 《Vue进阶教程》第三课&#xff1a;Vue响应式原理 《Vue进阶教程》第四课&#…