Qt新手入门指南 - 如何创建模型/视图(四)

news2024/11/15 11:09:47

每个UI开发人员都应该了解ModelView编程,本教程的目标是为大家提供一个简单易懂的介绍。

Qt 是目前最先进、最完整的跨平台C++开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。

在上文中,我们主要为大家介绍了如何创建一个简单的Model/View(模型/视图)的应用(点击这里回顾>>),本文将继续为大家介绍如何实现中间主题。

点击获取Qt Widget组件下载(Q技术交流:166830288)

3. 中间主题
3.1 TreeView

开发人员可以将上面的示例转换为具有树视图的应用程序,简单地将QTableView 替换为QTreeView,这将产生一个读/写树。不必对模型进行任何更改,树不会有任何层次结构,因为模型本身没有任何层次结构。

QListView、QTableView和QTreeView都使用一个模型抽象,它是一个合并的列表、表和树,这使得从同一个模型中使用几种不同类型的视图类成为可能。

这是我们的示例模型到目前为止的样子:

为了建立一个模型,我们把数据封装在上面的示例中。这次使用QStandardItemModel,它是一个层次数据的容器,也实现了QAbstractItemModel。要显示树,QStandardItemModel必须用QStandardItems填充,QStandardItems能够容纳项目的所有标准属性,如文本、字体、复选框或笔刷。

(文件来源:examples/widgets/tutorials/modelview/6_treeview/mainwindow.cpp)

// modelview.cpp
#include "mainwindow.h"

#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, treeView(new QTreeView(this))
, standardModel(new QStandardItemModel(this))
{
setCentralWidget(treeView);

QList<QStandardItem *> preparedRow = prepareRow("first", "second", "third");
QStandardItem *item = standardModel->invisibleRootItem();
// adding a row to the invisible root item produces a root element
item->appendRow(preparedRow);

QList<QStandardItem *> secondRow = prepareRow("111", "222", "333");
// adding a row to an item starts a subtree
preparedRow.first()->appendRow(secondRow);

treeView->setModel(standardModel);
treeView->expandAll();
}

QList<QStandardItem *> MainWindow::prepareRow(const QString &first,
const QString &second,
const QString &third) const
{
return {new QStandardItem(first),
new QStandardItem(second),
new QStandardItem(third)};
}

我们简单地实例化一个QStandardItemModel,并向构造函数添加两个QStandardItems,然后可以创建一个层次数据结构,因为一个QStandardItem 可以容纳其他QStandardItems,节点在视图中折叠和展开。

3.2 使用选择

我们希望访问选定项的内容,以便将其与层次结构级别一起输出到窗口标题中。

所以创建两个项目:

(文件来源:examples/widgets/tutorials/modelview/7_selections/mainwindow.cpp)

#include "mainwindow.h"

#include <QTreeView>
#include <QStandardItemModel>
#include <QItemSelectionModel>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, treeView(new QTreeView(this))
, standardModel(new QStandardItemModel(this))
{
setCentralWidget(treeView);
auto *rootNode = standardModel->invisibleRootItem();

// defining a couple of items
auto *americaItem = new QStandardItem("America");
auto *mexicoItem = new QStandardItem("Canada");
auto *usaItem = new QStandardItem("USA");
auto *bostonItem = new QStandardItem("Boston");
auto *europeItem = new QStandardItem("Europe");
auto *italyItem = new QStandardItem("Italy");
auto *romeItem = new QStandardItem("Rome");
auto *veronaItem = new QStandardItem("Verona");

// building up the hierarchy
rootNode-> appendRow(americaItem);
rootNode-> appendRow(europeItem);
americaItem-> appendRow(mexicoItem);
americaItem-> appendRow(usaItem);
usaItem-> appendRow(bostonItem);
europeItem-> appendRow(italyItem);
italyItem-> appendRow(romeItem);
italyItem-> appendRow(veronaItem);

// register the model
treeView->setModel(standardModel);
treeView->expandAll();

// selection changes shall trigger a slot
QItemSelectionModel *selectionModel = treeView->selectionModel();
connect(selectionModel, &QItemSelectionModel::selectionChanged,
this, &MainWindow::selectionChangedSlot);
}

视图在单独的选择模型中管理选择,可以使用selectionModel() 方法检索,检索选择模型是为了将一个槽连接到它的selectionChanged() 信号。

(文件来源:examples/widgets/tutorials/modelview/7_selections/mainwindow.cpp)

void MainWindow::selectionChangedSlot(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/)
{
// get the text of the selected item
const QModelIndex index = treeView->selectionModel()->currentIndex();
QString selectedText = index.data(Qt::DisplayRole).toString();
// find out the hierarchy level of the selected item
int hierarchyLevel = 1;
QModelIndex seekRoot = index;
while (seekRoot.parent().isValid()) {
seekRoot = seekRoot.parent();
hierarchyLevel++;
}
QString showString = QString("%1, Level %2").arg(selectedText)
.arg(hierarchyLevel);
setWindowTitle(showString);
}

通过调用treeView->selectionModel()->currentIndex()来获得与选择相对应的模型索引,并通过使用模型索引来获得字段的字符串,然后只需计算该项的hierarchyLevel。顶级项没有父项,parent()方法将返回一个默认构造的QModelIndex(),这就是为什么使用parent()方法迭代到顶层,同时计算迭代期间执行的步骤。

选择模型(如上所示)可以检索,但也可以使用QAbstractItemView::setSelectionModel进行设置。这就是为什么有3个视图类具有同步选择,因为只使用了选择模型的一个实例。要在3个视图之间共享选择模型,请使用selectionModel() 并使用setSelectionModel()将结果分配给第二个和第三个视图类。

3.3 预定义模型

使用模型/视图的典型方法是封装特定的数据,使其可用于视图类。但是Qt也为公共底层数据结构提供了预定义的模型,如果其中一种可用的数据结构适合您的应用程序,那么预定义模型可能是一个不错的选择。

  • QStringListModel:存储字符串列表

  • QStandardItemModel:存储任意层次项

  • QFileSystemModel:封装本地文件系统

  • QSqlQueryModel:封装SQL结果集

  • QSqlTableModel:封装SQL表

  • QSqlRelationalTableModel:用外键封装SQL表

  • QSortFilterProxyModel:对另一个模型进行排序和/或筛选

3.4 Delegates

在迄今为止的所有示例中,数据在单元格中以文本或复选框的形式呈现,并以文本或复选框的形式进行编辑,提供这些表示和编辑服务的组件称为delegate。一起来看一个名为Star Delegate的示例:

该视图有一个setItemDelegate()方法,用于替换默认delegate并安装自定义delegate。一个新的delegate可以通过创建一个继承自QStyledItemDelegate的类来编写,为了编写一个显示星号且没有输入功能的delegate并安装自定义delegate。一个新的delegate,我们只需要重写2个方法。

class StarDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
StarDelegate(QWidget *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const;
};

paint()根据底层数据的内容绘制星号,可以通过调用index.data()来查找数据。delegate的sizeHint()方法用于获取每个星星的尺寸,因此单元格将提供足够的高度和宽度来容纳这些星星。

如果您想在视图类的网格中使用自定义图形表示方式显示数据,那么编写自定义delegates是正确的选择。如果想要离开网格,不会使用自定义delegates,可以使用自定义视图类。

3.5 使用ModelTest进行调试

模型的被动特性为程序员提供了新的挑战,模型中的不一致可能导致应用程序崩溃。由于模型受到来自视图的大量调用的影响,因此很难找出哪个调用使应用程序崩溃,以及哪个操作引入了问题。

Qt Labs提供了一种名为ModelTest的软件,可以在程序运行时检查模型。每当模型被更改时,ModelTest都会扫描模型并使用断言报告错误。这对于树模型尤其重要,因为它们的层次性质为微妙的不一致留下了许多可能性。

与视图类不同,ModelTest使用超出范围的索引来测试模型。这意味着您的应用程序可能会在使用ModelTest时崩溃,即使没有它它也可以完美地运行。因此在使用ModelTest时,您还需要处理所有超出范围的索引。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/376582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AJAX介绍及其应用

1.1 AJAX 简介 AJAX全称为 Asynchronous JavaScript and XML &#xff0c;就是异步的js和xml。通过AJAX可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff0c;无刷新获取数据。AJAX不是新的编程语言&#xff0c;而是一种现有的标准组合再一起使用的新方式 应…

scanpy 单细胞分析API接口使用案例

参考&#xff1a;https://zhuanlan.zhihu.com/p/537206999 https://scanpy.readthedocs.io/en/stable/api.html scanpy python包主要分四个模块&#xff1a; 1&#xff09;read 读写模块、 https://scanpy.readthedocs.io/en/stable/api.html#reading 2&#xff09;pp Prepr…

springBoot自动装配原理探究springBoot配置类Thymeleaf模板引擎

微服务 微服务是一种架构风格&#xff0c;由于单体架构不利于团队协作完成并且代码量较大&#xff0c;后期维护成本较高&#xff0c;逐渐有了微服务架构。微服务是将一个项目拆分成不同的服务&#xff0c;各个服务之间相互独立互不影响&#xff0c;互相通过轻量级机制通信比如…

(转载)STM32与LAN9252构建EtherCAT从站

目录 &#xff08;一&#xff09;&#xff1a;项目简介 EtherCAT及项目简述 LAN9252工作模式 整体开发流程 移植要处理的问题 代码层面的工作 开发中使用的工具 &#xff08;二&#xff09;&#xff1a;SSC的使用 SSC简介和下载 SSC构建协议栈文件和XML &#xff08…

爬虫数据解析-正则表达式

数据解析-正则表达式 正则表达式 正则编写规则简介 字符含义.匹配除换行符以外的任意字符|A|B表示&#xff1a;匹配正则表达式条件A或B^匹配字符串的开始(在集合[]里表示"非"&#xff09;的意思$匹配字符串的结束{n}重复n次{,n}重复小于n次{n,}重复n次或更多次{n,…

2023软件测试金三银四常见的软件测试面试题-【抓包和网络协议篇】

八、抓包与网络协议 8.1 抓包工具怎么用 我原来的公司对于抓包这块&#xff0c;在App的测试用得比较多。我们会使用fiddler抓取数据检查结果&#xff0c;定位问题&#xff0c;测试安全&#xff0c;制造弱网环境; 如&#xff1a;抓取数据通过查看请求数据&#xff0c;请求行&…

经验 // 指标异常了怎么办?

本文参考了数据万花筒的文章&#xff0c;结合我自己工作经验。希望给大家一些帮助。 指标异常排查&#xff0c;是数据分析师的工作重点之一&#xff0c;是各行各业数据分析师都绕不开的话题。 本文试图回答&#xff1a; 1、指标波动的影响因素有哪些&#xff1f; 2、如何快速…

Web3中文|泰勒·斯威夫特演唱会票务闹乌龙,NFT票务急需普及

2022年底&#xff0c;美国艺人Taylor Swift&#xff08;泰勒斯威夫特&#xff09;的2023年巡回演唱会Eras Tour门票开始出票。作为当今世界最受欢迎的流行歌手之一&#xff0c;四年多没举办大型巡演无疑积攒了大量的粉丝需求。但是在2022年11月15日开放预售的当天&#xff0c;售…

数据驱动下的物种保护,拯救生命的“特效药”

如果给出这样      一张猎豹的图片      我们能否通过图中有限的信息      判断它的年龄、健康状况      以及所属族群?      如果你是一名研究动物的专家,你可能会从其花纹和斑点中获取一定量的信息,但对于大多数人以及一线的动物保护者来说,它可能只是一…

imx6ull——I2C驱动

I2C基本介绍 SCL 为高电平&#xff0c;SDA 出现下降沿:起始位 SCL 位高电平&#xff0c;SDA出现上升沿:停止位 主机——从机地址&#xff08;ack&#xff09;——寄存器地址&#xff08;ack&#xff09;——数据&#xff08;ack&#xff09; 重点&#xff1a;先是写&#xff0c…

context.Context

context.Context前言一、为什么要context二、context有什么用三、基本数据结构3.1、context包的整体工作机制3.2 基本接口和结构体3.3 API函数3.4 辅助函数3.5 context用法3.6 使用 context 传递数据的争议总结参考资料前言 context是go语言的一个并发包&#xff0c;一个标准库…

平台总线开发(id和设备树匹配)

目录 一、ID匹配之框架代码 二、ID匹配之led驱动​​​​​​​ 三、设备树匹配 四、设备树匹配之led驱动 五、一个编写驱动用的宏 一、ID匹配之框架代码 id匹配&#xff08;可想象成八字匹配&#xff09;&#xff1a;一个驱动可以对应多个设备 ------优先级次低 注意事项…

河南农信社数字化转型实践方案

农信机构立足地方“三农”&#xff0c;普遍规模较小&#xff0c;高地域集中度在县域和农村地区&#xff0c;容易受到地方农村经济脆弱性的影响。 农信机构由于信贷项目要求多&#xff0c;单笔贷款业务批复的额度相对较小&#xff0c;在信用档案登记环节&#xff0c;造成业务量…

SQL Server主流版本生命周期管理

SQL Server 生命周期 每个版本的 SQL Server 都有至少 10 年的支持期限&#xff0c;其中包括五年的主要支持和五年的扩展支持&#xff1a; “主要支持” 包括功能、性能、可伸缩性和安全更新。“扩展支持” 仅包含安全更新。 “终止支持” &#xff08;有时也称为生命周期结束…

udp套接字编程(超详细带你逐步实现)

我自己在学习UDP服务器的时候&#xff0c;有着太多的不解&#xff0c;我不明白一个udp服务器是如何设计出来的。我在网上找了很多的资料&#xff0c;不过绝大多数都是把代码往哪里一放&#xff0c;具体的设计流程完全不提&#xff0c;这让我看了之后一头雾水。或许对于刚刚开始…

scikit-learn实现近邻算法分类的示例

scikit-learn库 scikit-learn已经封装好很多数据挖掘的算法 现介绍数据挖掘框架的搭建方法 转换器&#xff08;Transformer&#xff09;用于数据预处理&#xff0c;数据转换流水线&#xff08;Pipeline&#xff09;组合数据挖掘流程&#xff0c;方便再次使用&#xff08;封装…

SVN配置使用(钩子配置、updata忽略指定文件)

参考链接&#xff1a; svn(svnsync)实时同步备份及问题解答 SVN常用命令之update SVN钩子就是一个脚本&#xff0c;在SVN更新前、后、或者变化前后等等状态&#xff0c;触发的脚本。据此可以有多种用途&#xff0c;如&#xff1a;1、在版本提交前要求必须对更新进行说明&#…

20分钟6个示例4个动图教你学会Async Hooks

序幕 async_hooks模块提供了一个全新的功能世界,但作为 Node.js 爱好者,我最感兴趣的是,它可以让您轻松了解我们在应用程序中经常执行的一些任务的幕后情况。 在本文中,我将尝试借助async_hooks模块来演示和解释一个典型的异步资源的生命周期。 Async Hooks API 简介 as…

【源码解析】SpringBoot自动装配的实现原理

什么是SpringBoot的自动装配 SpringBoot在启动的时候会扫描外部jar包中的META-INF/spring.factories文件&#xff0c;将文件中配置的类信息按照条件装配到Spring容器中。 实现原理 核心注解SpringBootApplication Target({ElementType.TYPE}) Retention(RetentionPolicy.R…

时间序列分析 | BiLSTM双向长短期记忆神经网络时间序列预测(Matlab完整程序)

时间序列分析 | BiLSTM双向长短期记忆神经网络时间序列预测(Matlab完整程序) 目录 时间序列分析 | BiLSTM双向长短期记忆神经网络时间序列预测(Matlab完整程序)预测结果评价指标基本介绍完整程序参考资料预测结果 评价指标 训练集数据的R2为:0.99302 测试集数据的R2为&…