当前位置: 首页 > news >正文

QT - QT开发进阶合集

基础知识

此合集是对c++有基础知识同学,可以快速对QT进行理解。每个小章节,都会有对应的代码,进行示例。以下所有项目使用的QT版本为6.9.1,所谓一通百通,大家不用纠结与某个版本的问题,版本大体都兼容,只有某些函数发生了改变而已。

1. 信号与槽

信号:
对象发出的事件,

槽函数:
对象发出的事件后,要执行的函数

connect:
连接信号与槽函数的方式,五个参数

connect参数说明
     参数一:信号源,值发送信号方
     参数二:信号源发送的信号,即button的动作,这里是点击后这个事件发生的动作
     参数三:信号的接收方,即接收信号的对象
     参数四:接收信号的对象的槽函数,即要事件发生后执行的函数,
     参数五:额外的参数(一般不写),通常是指连接类型,即信号和槽的连接方式
    类型:
       Qt::AutoConnection:自动连接,默认值,通常在主线程中使用
       Qt::DirectConnection:直接连接,信号和槽在同一线程中执行
       Qt::QueuedConnection:队列连接,信号和槽在不同线程中执行
       Qt::BlockingQueuedConnection:阻塞队列连接,信号和槽在不同线程中执行,但会阻塞发送信号的线程,直到槽函数执行完毕
       Qt::UniqueConnection:唯一连接,确保信号和槽只连接一次
       Qt::ConnectionType:连接类型,指定信号和槽的连接方式
    原因:
       线程安全:在多线程环境下,选择合适的连接类型非常重要,以避免死锁或崩溃
       性能:不同的连接类型对性能有不同的影响,选择合适的连接类型可以优化程序的性能
 

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <qlabel.h>
#include <qpushbutton.h>
#include <QLineEdit>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = nullptr);~Dialog();private:QLabel *lab1, *lab2;QLineEdit *edit;QPushButton *btn;private:void AreaCal(int x); // 计算面积
};
#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include <QGridLayout> //布局的头文件
const static double PI = 3.14;Dialog::Dialog(QWidget *parent): QDialog(parent)
{// 实例化对应的按钮标签, 编辑框// lab1 = new QLabel("请输入圆半径",this);// 提示标签lab1 = new QLabel(this);lab1->setText("请输入圆半径");// 输入框,获取框内的值edit = new QLineEdit(this);// 动作按钮btn = new QPushButton(this);btn->setText("计算");/*connect参数说明参数一:信号源,值发送信号方参数二:信号源发送的信号,即button的动作,这里是点击后这个事件发生的动作参数三:信号的接收方,即接收信号的对象参数四:接收信号的对象的槽函数,即要事件发生后执行的函数,参数五:额外的参数(一般不写),通常是指连接类型,即信号和槽的连接方式类型:Qt::AutoConnection:自动连接,默认值,通常在主线程中使用Qt::DirectConnection:直接连接,信号和槽在同一线程中执行Qt::QueuedConnection:队列连接,信号和槽在不同线程中执行Qt::BlockingQueuedConnection:阻塞队列连接,信号和槽在不同线程中执行,但会阻塞发送信号的线程,直到槽函数执行完毕Qt::UniqueConnection:唯一连接,确保信号和槽只连接一次Qt::ConnectionType:连接类型,指定信号和槽的连接方式原因:线程安全:在多线程环境下,选择合适的连接类型非常重要,以避免死锁或崩溃性能:不同的连接类型对性能有不同的影响,选择合适的连接类型可以优化程序的性能*/connect(btn, &QPushButton::clicked, this, [this](){ QString inputText = edit->text();int x = inputText.toInt();AreaCal(x); },Qt::AutoConnection );// 显示面积标签lab2 = new QLabel(this);// 布局调整QGridLayout *m = new QGridLayout(this);m->addWidget(lab1, 0, 0);m->addWidget(edit, 20, 0);m->addWidget(btn, 40, 0);m->addWidget(lab2, 60, 0);
}
Dialog::~Dialog() {}void Dialog::AreaCal(int x)
{// 圆的面积 --- Π*r*r;double area = PI * x * x;lab2->setText("面积为:" + QString::number(area));
}

2. 七类实用控件

在此,因为没有时间把所有的写完整,这次,只写了六个,但是其他控件的原理与规则,与这个其实差不多少,这个模块其实更在意的是槽与函数的使用,即怎么写槽函数,怎么连接槽函数,知道每个空间的信号。其实,信号应该是可以自定义的。我暂时不知道。后续会给出答案。

1. ComboBox & FontComboBox

ComboBox:或者理解为下拉框选择器

FontCOmboBox:即字体样式选择器,我没有写代码,因为原理一样

1.1 拖控件

1.2 代码实现
cpp代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QMessageBox>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 主窗口的宽高this->setGeometry(200, 100, 1000, 800);// 实例化cmb = new QComboBox(this);cmb->setGeometry(50, 50, 150, 50);// 添加到队列的方式共有两种,// 1.addItem 在ComboBox的末尾添加一个新选项。// 2.insertItem 在ComboBox的指定索引位置插入一个新选项。cmb->insertItem(0, "小学");cmb->insertItem(1, "初中");cmb->insertItem(2, "高中");cmb->insertItem(3, "专科");cmb->insertItem(4, "本科");cmb->insertItem(5, "硕士");cmb->insertItem(6, "博士");// 设置当前索引cmb->setCurrentIndex(0);// 连接信号和槽connect(cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(cmbIndexChange(int)));
}
MainWindow::~MainWindow()
{delete ui;
}
// 再此的问题是,如何知道了索引发生改变
void MainWindow::cmbIndexChange(int)
{// 控制台输出内容qDebug() << "当前索引发生改变,索引为:" << cmb->currentIndex()<< ", 当前选项为:" << cmb->currentText();// 弹框QMessageBox::information(this, "提示", QString("当前索引发生改变,索引为:%1, 当前选项为:%2").arg(cmb->currentIndex()).arg(cmb->currentText()));
}
h头文件代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>// 添加控件QComBoBox
#include <QComboBox>QT_BEGIN_NAMESPACE
namespace Ui
{class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;// 1.声明QComboBox对象QComboBox *cmb;
public slots:// QComboBox对象的槽函数// combox, 有内置发生改变的信号void cmbIndexChange(int);
};
#endif // MAINWINDOW_H
效果展示:

2. LineEdit & TextEdit 

LineEdit:行文本编辑

TextEdit:可以理解为块文本编辑

1.1 拖控件

1.2 代码实现
cpp代码
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 参数:设置窗口的左上角位置,参数2:设置窗口的宽度,参数3:设置窗口的高度this->setGeometry(200, 100, 1000, 800);lineEdit = new QLineEdit(this);lineEdit->setGeometry(50, 50, 120, 40);connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(onLineEditEditingFinished()));textEdit = new QTextEdit(this);textEdit->setGeometry(50, 200, 120, 80);// QTextEdit没有editingFinished信号,使用textChanged信号代替connect(textEdit, SIGNAL(textChanged()), this, SLOT(onTextEditEditingFinished()));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::onLineEditEditingFinished()
{// 获取lineEdit的文本内容QString text = lineEdit->text();// 在textEdit中显示lineEdit的文本内容textEdit->setText(text);
}void MainWindow::onTextEditEditingFinished()
{// 获取textEdit的文本内容QString text = textEdit->toPlainText();// 在lineEdit中显示textEdit的文本内容lineEdit->setText(text);
}
h头文件代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QLineEdit>
#include <QTextEdit>QT_BEGIN_NAMESPACE
namespace Ui
{class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;QLineEdit *lineEdit;QTextEdit *textEdit;private slots:// lineEdit编辑完成void onLineEditEditingFinished();// textEdit编辑完成void onTextEditEditingFinished();
};
#endif // MAINWINDOW_H
效果展示:

上方的为行编辑,即输入完,点击enter键,可以使得textEdit获得同步,信号为editfinished

下方的为文本编辑块,输入完,可以使得上方的lineEdit同步,信号为textchanged

3. 高级控件精进

1. Tree View 

简而言之,是一种数据与控件分离的架构,不同于treeWidget直接有内置可以直接操作,他需要引入相关的模型。

1. treeView树形控件原理分析

QTreeView 的 Model-View 架构原理

1. 分离关注点的设计模式

// 数据模型 - 负责数据存储和管理standardItemModel = new QStandardItemModel(ui->treeView);// 视图控件 - 负责数据显示和用户交互ui->treeView->setModel(standardItemModel);

这是 Qt 的 Model-View 架构,将数据(Model)和显示(View)完全分离:

  • Model:管理数据的存储、结构、增删改查
  • View:负责数据的可视化呈现和用户交互

2. 为什么要用 QStandardItemModel?

TreeView 本身只是一个"空壳"显示控件,它不存储任何数据。所有的数据都存储在 Model 中:

  • QTreeView:只负责绘制界面、处理用户点击、滚动等UI交互
  • QStandardItemModel:存储树形结构的实际数据、节点关系、节点属性等

3. 这样设计的优势

  1. 数据与显示解耦

    • 同一份数据可以用不同的 View 显示(TreeView、ListView、TableView)
    • 数据变化时,View 会自动更新显示
  2. 内存效率

    • View 只渲染可见的部分,不会为所有数据创建UI对象
    • 大数据量时性能更好
  3. 灵活性

    • 可以轻松切换不同的显示方式
    • 可以自定义 Model 实现复杂的数据逻辑
2. 实现

cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// ==================== 初始化TreeView数据模型 ====================// 1. 创建标准数据模型对象// QStandardItemModel是Qt提供的标准模型类,用于管理树形/表格数据// 参数ui->treeView指定父对象,这样当treeView销毁时会自动销毁modelstandardItemModel = new QStandardItemModel(ui->treeView);// 2. 将数据模型绑定到TreeView控件// Model-View架构的核心:将数据(Model)与显示(View)分离// setModel()建立了Model和View之间的连接,数据变化时View会自动更新ui->treeView->setModel(standardItemModel);// 3. 设置表头列的自动调整模式// QHeaderView::Stretch:列宽自动拉伸填满整个控件宽度// 这样无论窗口如何调整大小,列都会自动适应宽度ui->treeView->header()->setSectionResizeMode(QHeaderView::Stretch);// 4. 设置表头标题// QStringList创建字符串列表,这里只有一列所以只有一个标题// 如果是多列树形控件,可以添加多个标题:QStringList() << "名称" << "类型" << "大小"standardItemModel->setHorizontalHeaderLabels(QStringList() << "节点名称");// ==================== 优化TreeView显示效果 ====================// 5. 设置选择行为:选择整行而不是单个单元格ui->treeView->setSelectionBehavior(QAbstractItemView::SelectRows);// 6. 设置选择模式:单选模式(一次只能选择一行)ui->treeView->setSelectionMode(QAbstractItemView::SingleSelection);// 7. 启用右键菜单(如果需要的话)ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_insert_top_btn_clicked()
{// ==================== 添加顶级节点(一级节点)====================// 1. 获取当前模型中已有的顶级节点数量// rowCount()返回顶级节点的数量,用于生成新节点的序号int index = standardItemModel->rowCount();// 2. 创建QList容器来存储一行的数据项// 虽然这里只有一列,但QStandardItemModel的appendRow()需要QList参数// 这样设计是为了支持多列的情况(如:名称、类型、大小等多列)QList<QStandardItem *> toplist;// 3. 创建新的标准数据项// QStandardItem是模型中的基本数据单元,可以存储文本、图标、用户数据等// QString::arg()是字符串格式化方法,%1会被替换为(index + 1)的值// 例如:当index=0时,显示"一级节点:1-1"toplist << new QStandardItem(QString("一级节点:%1-1").arg(index + 1));// 4. 为节点设置自定义数据(用于标识节点类型)// setData()可以存储额外的数据,这里存储-1表示这是顶级节点// Qt::UserRole + 1 是自定义角色,Qt预留了UserRole之后的角色供用户使用// 这样我们可以通过data(Qt::UserRole + 1)来获取节点类型toplist[0]->setData(-1, Qt::UserRole + 1); // -1表示顶级节点类型// 5. 设置节点图标(可选)// 可以为不同类型的节点设置不同图标,增强视觉效果// toplist[0]->setIcon(QIcon(":/icons/folder.png"));// 6. 设置节点为可编辑(可选)// 默认情况下节点是可编辑的,用户可以双击修改节点名称toplist[0]->setEditable(true);// 7. 将新创建的行添加到数据模型的根节点下// appendRow()会自动触发视图更新,新节点会立即显示在TreeView中// 这是Model-View架构的优势:数据变化时视图自动同步standardItemModel->appendRow(toplist);// 8. 可选:自动选中新添加的节点QModelIndex newIndex = standardItemModel->index(index, 0);ui->treeView->setCurrentIndex(newIndex);
}void MainWindow::on_insert_child_btn_clicked()
{// ==================== 添加子节点(二级节点)====================// 1. 获取当前选中的节点索引// selectionModel()返回选择模型,管理TreeView中的选择状态// currentIndex()获取当前选中项的模型索引QModelIndex currentIndex = ui->treeView->selectionModel()->currentIndex();// 2. 检查是否有选中的节点if (!currentIndex.isValid()){// 如果没有选中任何节点,显示提示消息QMessageBox::warning(this, "警告", "请先选择一个父节点!");return;}// 3. 通过模型索引获取对应的标准数据项// itemFromIndex()将QModelIndex转换为QStandardItem指针// 这样我们就可以直接操作数据项,添加子节点QStandardItem *parentItem = standardItemModel->itemFromIndex(currentIndex);// 4. 检查父节点是否有效if (!parentItem){QMessageBox::warning(this, "错误", "无法获取父节点!");return;}// 5. 获取父节点已有的子节点数量// rowCount()返回该节点下子节点的数量,用于生成子节点序号int childCount = parentItem->rowCount();// 6. 创建子节点QList<QStandardItem *> childList;// 7. 生成子节点名称// 格式:父节点名称 + "-子节点" + 序号QString parentText = parentItem->text();QString childText = QString("%1-子节点%2").arg(parentText).arg(childCount + 1);childList << new QStandardItem(childText);// 8. 为子节点设置类型标识// 这里用父节点在顶级的位置作为类型标识// 例如:第一个顶级节点的子节点类型为0,第二个为1,以此类推int parentRow = currentIndex.row();childList[0]->setData(parentRow, Qt::UserRole + 1); // 存储父节点的行号作为类型// 9. 设置子节点属性childList[0]->setEditable(true); // 可编辑// childList[0]->setIcon(QIcon(":/icons/file.png"));  // 可设置不同图标// 10. 将子节点添加到父节点下// appendRow()添加到指定父节点下,而不是根节点parentItem->appendRow(childList);// 11. 展开父节点以显示新添加的子节点// expand()展开指定的节点,让用户能看到新添加的子节点ui->treeView->expand(currentIndex);// 12. 选中新添加的子节点QModelIndex newChildIndex = standardItemModel->index(childCount, 0, currentIndex);ui->treeView->setCurrentIndex(newChildIndex);
}void MainWindow::on_delete_btn_clicked()
{// ==================== 删除选中的节点 ====================// 1. 获取当前选中的节点索引QModelIndex currentIndex = ui->treeView->selectionModel()->currentIndex();// 2. 检查是否有选中的节点if (!currentIndex.isValid()){QMessageBox::warning(this, "警告", "请先选择要删除的节点!");return;}// 3. 获取选中节点的数据项QStandardItem *selectedItem = standardItemModel->itemFromIndex(currentIndex);if (!selectedItem){QMessageBox::warning(this, "错误", "无法获取选中的节点!");return;}// 4. 确认删除操作// 获取节点文本用于确认对话框QString itemText = selectedItem->text();// 检查是否有子节点int childCount = selectedItem->rowCount();QString message;if (childCount > 0){message = QString("确定要删除节点 \"%1\" 及其所有 %2 个子节点吗?").arg(itemText).arg(childCount);}else{message = QString("确定要删除节点 \"%1\" 吗?").arg(itemText);}// 5. 显示确认对话框QMessageBox::StandardButton reply = QMessageBox::question(this,"确认删除",message,QMessageBox::Yes | QMessageBox::No, // 按钮选项QMessageBox::No                     // 默认按钮);// 6. 如果用户确认删除if (reply == QMessageBox::Yes){// 7. 执行删除操作// 获取父节点QStandardItem *parentItem = selectedItem->parent();if (parentItem){// 如果有父节点,说明这是子节点// 从父节点中移除该行int row = selectedItem->row();parentItem->removeRow(row);}else{// 如果没有父节点,说明这是顶级节点// 从模型根节点中移除该行int row = currentIndex.row();standardItemModel->removeRow(row);}// 8. 删除成功提示(可选)// QMessageBox::information(this, "成功", "节点删除成功!");}
}void MainWindow::on_get_btn_clicked()
{// ==================== 获取并显示选中节点的详细信息 ====================// 1. 获取当前选中的节点索引QModelIndex currentIndex = ui->treeView->selectionModel()->currentIndex();// 2. 检查是否有选中的节点if (!currentIndex.isValid()){QMessageBox::warning(this, "警告", "请先选择一个节点!");return;}// 3. 获取选中节点的数据项QStandardItem *selectedItem = standardItemModel->itemFromIndex(currentIndex);if (!selectedItem){QMessageBox::warning(this, "错误", "无法获取选中的节点!");return;}// 4. 收集节点的详细信息QString nodeInfo;// 基本信息nodeInfo += "=== 节点详细信息 ===\n\n";nodeInfo += QString("节点名称: %1\n").arg(selectedItem->text());nodeInfo += QString("节点行号: %1\n").arg(selectedItem->row());nodeInfo += QString("节点列号: %1\n").arg(selectedItem->column());// 层级信息int level = 0;QStandardItem *parent = selectedItem->parent();while (parent){level++;parent = parent->parent();}nodeInfo += QString("节点层级: %1\n").arg(level == 0 ? "顶级节点" : QString("第%1级").arg(level + 1));// 自定义数据QVariant userData = selectedItem->data(Qt::UserRole + 1);if (userData.isValid()){nodeInfo += QString("节点类型: %1\n").arg(userData.toString());}// 子节点信息int childCount = selectedItem->rowCount();nodeInfo += QString("子节点数量: %1\n").arg(childCount);if (childCount > 0){nodeInfo += "\n子节点列表:\n";for (int i = 0; i < childCount; ++i){QStandardItem *child = selectedItem->child(i, 0);if (child){nodeInfo += QString("  - %1\n").arg(child->text());}}}// 父节点信息QStandardItem *parentItem = selectedItem->parent();if (parentItem){nodeInfo += QString("\n父节点: %1\n").arg(parentItem->text());// 兄弟节点信息int siblingCount = parentItem->rowCount();nodeInfo += QString("兄弟节点数量: %1\n").arg(siblingCount - 1); // 减去自己if (siblingCount > 1){nodeInfo += "\n兄弟节点列表:\n";for (int i = 0; i < siblingCount; ++i){QStandardItem *sibling = parentItem->child(i, 0);if (sibling && sibling != selectedItem){nodeInfo += QString("  - %1\n").arg(sibling->text());}}}}else{nodeInfo += "\n父节点: 无(顶级节点)\n";// 同级顶级节点信息int topLevelCount = standardItemModel->rowCount();nodeInfo += QString("同级节点数量: %1\n").arg(topLevelCount - 1);if (topLevelCount > 1){nodeInfo += "\n同级节点列表:\n";for (int i = 0; i < topLevelCount; ++i){QStandardItem *topItem = standardItemModel->item(i, 0);if (topItem && topItem != selectedItem){nodeInfo += QString("  - %1\n").arg(topItem->text());}}}}// 路径信息(从根节点到当前节点的完整路径)QStringList pathList;QStandardItem *current = selectedItem;while (current){pathList.prepend(current->text());current = current->parent();}nodeInfo += QString("\n节点路径: %1\n").arg(pathList.join(" -> "));// 5. 显示信息对话框QMessageBox::information(this, "节点信息", nodeInfo);
}void MainWindow::on_pushButton_5_clicked()
{// ==================== 展开/折叠所有节点的切换功能 ====================// 1. 检查是否有节点数据if (standardItemModel->rowCount() == 0){QMessageBox::information(this, "提示", "当前没有任何节点!");return;}// 2. 检查当前的展开状态// 我们通过检查第一个顶级节点是否展开来判断当前状态bool isExpanded = false;QModelIndex firstIndex = standardItemModel->index(0, 0);if (firstIndex.isValid()){isExpanded = ui->treeView->isExpanded(firstIndex);}// 3. 根据当前状态执行相反操作if (isExpanded){// 如果当前是展开状态,则折叠所有节点ui->treeView->collapseAll();// 可选:显示操作提示// QMessageBox::information(this, "操作完成", "已折叠所有节点!");}else{// 如果当前是折叠状态,则展开所有节点ui->treeView->expandAll();// 可选:显示操作提示// QMessageBox::information(this, "操作完成", "已展开所有节点!");}// 4. 高级功能:也可以实现更复杂的展开逻辑/*// 替代方案1:总是展开所有节点ui->treeView->expandAll();// 替代方案2:只展开到指定层级(例如只展开前2层)expandToLevel(2);// 替代方案3:递归展开有子节点的节点expandNodesWithChildren();*/
}// ==================== 辅助函数实现 ====================void MainWindow::expandToLevel(int level)
{// 展开到指定层级的递归函数// level: 要展开到的层级(0表示只显示顶级节点,1表示展开一层子节点)if (level < 0)return;// 递归函数:展开指定深度std::function<void(const QModelIndex &, int)> expandRecursively =[this, &expandRecursively](const QModelIndex &index, int currentLevel){if (currentLevel >= 0){ui->treeView->expand(index);// 继续展开子节点int rowCount = standardItemModel->rowCount(index);for (int i = 0; i < rowCount; ++i){QModelIndex childIndex = standardItemModel->index(i, 0, index);expandRecursively(childIndex, currentLevel - 1);}}};// 从根节点开始展开int topLevelCount = standardItemModel->rowCount();for (int i = 0; i < topLevelCount; ++i){QModelIndex topIndex = standardItemModel->index(i, 0);expandRecursively(topIndex, level);}
}void MainWindow::expandNodesWithChildren()
{// 只展开有子节点的节点std::function<void(const QModelIndex &)> expandIfHasChildren =[this, &expandIfHasChildren](const QModelIndex &index){int childCount = standardItemModel->rowCount(index);if (childCount > 0){ui->treeView->expand(index);// 递归处理子节点for (int i = 0; i < childCount; ++i){QModelIndex childIndex = standardItemModel->index(i, 0, index);expandIfHasChildren(childIndex);}}};// 处理所有顶级节点int topLevelCount = standardItemModel->rowCount();for (int i = 0; i < topLevelCount; ++i){QModelIndex topIndex = standardItemModel->index(i, 0);expandIfHasChildren(topIndex);}
}int MainWindow::getNodeLevel(QStandardItem *item)
{// 获取节点在树中的层级// 返回值:0表示顶级节点,1表示第二层,以此类推if (!item)return -1;int level = 0;QStandardItem *parent = item->parent();while (parent){level++;parent = parent->parent();}return level;
}
h头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QStandardItemModel> // 数据模型的类
#include <QMessageBox>        // 消息框
#include <QInputDialog>       // 输入对话框
#include <QModelIndex>        // 模型索引
#include <functional>         // 用于std::functionQT_BEGIN_NAMESPACE
namespace Ui
{class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_insert_top_btn_clicked();   // 添加顶级节点void on_insert_child_btn_clicked(); // 添加子节点void on_delete_btn_clicked();       // 删除节点void on_get_btn_clicked();          // 获取节点信息void on_pushButton_5_clicked();     // 展开/折叠所有节点private:Ui::MainWindow *ui;QStandardItemModel *standardItemModel; // 树形数据模型// 辅助函数(可选,用于扩展功能)void expandToLevel(int level);         // 展开到指定层级void expandNodesWithChildren();        // 展开有子节点的节点int getNodeLevel(QStandardItem *item); // 获取节点层级
};
#endif // MAINWINDOW_H

2. Tree Widget

常规模式

由于TreeWidget内置数据模型,直接调用即可。

使用的方式也比较简单,即反复套即可,

cpp代码(部分)
 exampleWidget = new example(this);// 实例化控件对象treeWidget = new QTreeWidget(this);// 设置框与页面大小this->setGeometry(100, 200, 1000, 800);treeWidget->setGeometry(200, 150, 600, 500);// 添加头标签treeWidget->setHeaderLabel("Tree Widget 示例");// 设置树形控件的根节点-根节点的父节点为treeWidgetQTreeWidgetItem *item1 = new QTreeWidgetItem(treeWidget);item1->setText(0, "根节点 1");// 添加子节点,子节点的父节点为item1QTreeWidgetItem *child1 = new QTreeWidgetItem(item1);child1->setText(0, "子节点 1");QTreeWidgetItem *child2 = new QTreeWidgetItem(item1);child2->setText(0, "子节点 2");// 展开根节点item1->setExpanded(true);QTreeWidgetItem *item2 = new QTreeWidgetItem(treeWidget);item2->setText(0, "根节点 2");QTreeWidgetItem *child3 = new QTreeWidgetItem(item2);child3->setText(0, "子节点 3");QTreeWidgetItem *child4 = new QTreeWidgetItem(item2);child4->setText(0, "子节点 4");item2->setExpanded(true);
数据驱动模式(json & 容器数据 & 结构体)

数据驱动,即先写好数据,这个数据可以是个list,也可以是个map,也可以是个map<QString,map<QString,QString>>,若想实现多级,可使用这个多级嵌套,稍微麻烦,

,所以json无疑是一个非常好的选择,或者使用结构体(class)来定义,真正的开发中,肯定是从数据库中拿到对应数值,再展示对应的数据,我更推荐json结构体(class)的方式,但是本次使用的是嵌套的集合(c++中叫containner,即容器,无伤大雅,一个意思。)

cpp代码:
#include "example.h"
#include "ui_example.h"
#include <mainwindow.h>example::example(QWidget *parent): QMainWindow(parent), ui(new Ui::example)
{ui->setupUi(this);this->setWindowTitle("使用数据驱动方式");// 实例化控件对象treeWidget = new QTreeWidget(this);// 设置框与页面大小this->setGeometry(100, 200, 1000, 800);treeWidget->setGeometry(200, 150, 600, 500);// 添加头标签treeWidget->setHeaderLabel("TreeWidget 数据驱动方法");// 使用数据驱动创建树createTreeFromData();
}example::~example()
{delete ui;
}void example::on_pushButton_clicked()
{this->hide();// 返回到主窗口MainWindow *mainWindow = qobject_cast<MainWindow *>(this->parent());if (mainWindow){mainWindow->show();}// emit requestClose(); // 发射信号,让父窗口处理,在子类写个信号,主窗口连接这个信号,执行转换窗口的操作。
}
void example::createTreeFromData()
{// 定义结构化数据 categories是一级分类,items是二级分类QStringList categories = {"编程语言", "开发工具", "框架库", "数据库"};// 使用 QMap 存储每个分类下的项目,是二级分类QMap<QString, QStringList> items = {{"编程语言", {"C++", "Python", "JavaScript", "Java"}},{"开发工具", {"Visual Studio", "Qt Creator", "Git", "Docker"}},{"框架库", {"Qt", "React", "Django", "Spring"}},{"数据库", {"MySQL", "PostgreSQL", "MongoDB", "Redis"}}};// 三级分类或者多级分类,可以使用容器嵌套,或者使用结构体定义,或者json数据。// 批量创建树节点,使用迭代器,便利map容器之中的值for (const QString &category : categories){// addTreeItem(nullptr, category);这一步是为了创造一级分类且,设置父节点为空,即当前category的数据都是顶级节点QTreeWidgetItem *categoryItem = addTreeItem(nullptr, category);// 根据键值对匹配,创建二级分类节点for (const QString &item : items[category]){addTreeItem(categoryItem, item); // categoryItem 作为父节点传入,创建子节点}categoryItem->setExpanded(true); // 展开一级分类节点}
}
QTreeWidgetItem *example::addTreeItem(QTreeWidgetItem *parent, const QString &text)
{QTreeWidgetItem *item;if (parent){item = new QTreeWidgetItem(parent);}else{item = new QTreeWidgetItem(treeWidget);}item->setText(0, text);return item;
}

4. View 与 Widget

核心区别对比表

特性View类(Model/View)Widget类(Item-Based)
数据管理通过Model管理直接管理Item对象
灵活性高度灵活简单直观
性能大数据量时更好小数据量时足够
学习曲线较陡峭平缓
自定义能力强大有限

在此根据treeView与treeWidget的区别,直接引出所有的View与Widget的区别,

  • View类:基于Model/View架构,适合复杂应用
  • Widget类:基于Item操作,适合简单应用

目前的话,treeView 与 ListView 其实是一样的设计模式,或者说,其他的也是一样的设计模式,在此,我再过多的叙述,大家,可以根据上方的代码,进行之后的代码的设计。

5. QMessageBox

不必过多赘述

进阶知识

1. QDockWidget

停靠窗口,页面可以包容各种插件,例如TextEdit,或者日历什么的

Dock停靠页面 2025-08-15 10-01-50.mp4

2. QStackedWidget

堆栈窗口

3. QSplitter

分割窗口

项目

http://www.dtcms.com/a/331668.html

相关文章:

  • 0814 TCP和DUP通信协议
  • 【DFS系列 | 暴力搜索与回溯剪枝】DFS问题实战:如何通过剪枝优化暴力搜索效率
  • Java Map集合精讲:键值对高效操作指南
  • (LeetCode 每日一题) 1780. 判断一个数字是否可以表示成三的幂的和 (数学、三进制数)
  • 【lucene】DocumentsWriterFlushControl
  • Linux与Windows文件共享:Samba配置指南
  • Linux软件编程:进程
  • GoLand 项目从 0 到 1:第八天 ——GORM 命名策略陷阱与 Go 项目启动慢问题攻坚
  • Go 并发控制利器 ants 使用文档
  • Uniapp 中的 uni.vibrate 震动 API 使用指南
  • 4. 索引数据的增删改查
  • ATAM:基于场景的软件架构权衡分析法
  • C语言指针使用
  • 机器翻译:Hugging Face库详解
  • Qwen-Image深度解析:突破文本渲染与图像编辑的视觉革命
  • 网站突然崩了,此站点遇到了致命错误!
  • 从零开始学习:深度学习(基础入门版)(第2天)
  • RCL 2025 | LLM采样机制的新视角:来自处方性偏移的解释
  • 区块链技术原理(10)-以太坊帐户
  • ​​vdbench 存储性能测试工具​​的详细使用教程,结合安装部署、参数配置、测试执行及结果分析
  • 电池模组奇异值分解降阶模型
  • Pandas数据处理与分析实战:Pandas数据转换与处理基础课程
  • 既然是长连接 ,资源已经占用,已经存在。那抢购就直接用长连接不更好?
  • 前端八股文-HTML5篇
  • AI绘画:从算法原理解读其风格、质量与效率变革
  • RLHF综述-GRPO之前
  • 《SeeClick: Harnessing GUI Grounding for Advanced Visual GUI Agents》论文精读笔记
  • 机器学习算法篇(八)-------svm支持向量机
  • 机器人“ChatGPT 时刻”倒计时
  • 码上爬第九题【协程+webpack】