首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Qt5.2模型-视图-模式:如何将底层数据结构的变化通知模型对象

Qt5.2模型-视图-模式:如何将底层数据结构的变化通知模型对象
EN

Stack Overflow用户
提问于 2014-01-31 03:15:40
回答 2查看 5.8K关注 0票数 4

我有一个类,用于永久存储一些以表格方式组织的项目。这个类与Qt完全无关,来自不同的库。让我们为这个问题的其余部分调用这个类DataContainer。它提供了std-c++兼容的迭代器来访问和操作内容。

我需要通过Qt来显示和修改这些数据。我的想法是创建一个类DataContainerQtAdaptor,该类继承自QAbstractTableModel,并存储指向DataContainer对象的指针。DataContainerQtAdaptor充当DataContainer对象的适配器,我的Qt应用程序内部的所有操作都是通过这个适配器完成的。然后我使用一个QTableView小部件来显示信息。

不幸的是,DataContainer可能被线程/进程更改。(例如,将DataContainer看作封装数据库连接的某个C++类,而该数据库可能被其他人更改)。

问题:

1)假设我有一个函数,每次更改DataContainer对象的内部结构时都调用它。必须调用的QAbstractTableModel的正确功能是什么,以通知底层更改的模型?我需要“亲爱的模型,您的持久存储后端更改。请,更新自己,并发出信号给每个附加的视图,以反映这一变化”。

( 2)假设1)解。在通过GUI触发更改时,避免“双”GUI更新的最佳方法是什么?例如:用户单击表小部件->表小部件中的一个单元格,调用模型的setData ->模型将更改推送到后端->后端触发它自己的"onUpdate“函数->模型重新读取完整的后端(尽管它已经知道了更改) -> GUI再次更新。

3)用户应该能够通过GUI插入新的行/列并将数据放入其中。但是位置是由这些数据决定的,因为后端保持数据的排序。因此,我有以下问题:用户决定在末尾创建一个新行,并将新数据推送到后端。当重新读取后端/模型时,数据通常不在最后一个位置,而是插入到中间的某个位置,所有其他数据都已向前移动。我是否将表视图小部件的所有属性保持同步,比如“选择一个单元格”?

我相信,必须有一些简单的标准解决方案来解决所有这些问题,因为它与QFileSystemModel的工作方式相同。用户选择一个文件,其他一些进程创建一个新文件。新文件将显示在视图中,随后的所有行都向前移动。选择也向前推进。

马提亚斯

EN

回答 2

Stack Overflow用户

发布于 2014-01-31 10:46:08

模型语义

首先,您必须确保QAbstractItemModel不能处于不一致的状态。这意味着在对底层数据进行某些更改之前,必须在模型上触发一些信号。

对结构的更改与对数据的更改有根本的区别。结构更改是要添加或删除模型的行/列。数据更改仅影响现有数据项的值。

  • 结构更改需要围绕修改调用beginXxxendXxx。在调用beginXxx之前,不能修改任何结构。当您完成结构更改后,请调用endXxxXxxInsertColumnsMoveColumnsRemoveColumnsInsertRowsMoveRowsRemoveRowsResetModel之一。 如果更改影响到许多不连续的行/列,则表示模型重置成本更低--但要小心,视图上的选择可能无法存活。
  • 保持结构完整的数据更改只要求在底层数据被修改后发送dataChanged。这意味着对data的调用可能会在查询模型的对象接收到dataChanged之前返回一个新值。

这也意味着非常量模型对非QObject类几乎毫无用处,当然,除非您使用观察者或类似的模式实现桥功能。

断续更新环

在模型上处理update循环的Qt-惯用方法是利用项角色。这完全取决于你的模型如何解释角色。QStringListModel实现的一个简单而有用的行为就是将角色从setData调用转发到dataChanged,否则忽略角色。

股票视图小部件只对dataChangedDisplayRole作出反应。然而,当他们编辑数据时,他们用EditRole调用EditRole。这就打破了循环。这种方法既适用于查看小部件,也适用于Qt快速视图项。

将数据插入排序模型

只要模型在完成排序时正确地发出更改信号,您就可以了。

行动顺序如下:

  1. 视图添加一行并调用模型的insertRow方法。模型可以将空行添加到基础容器中,也可以不添加。关键是必须暂时保留空行索引。
  2. 编辑从行中的项开始。视图状态更改为Editing
  3. 编辑在项目上完成。视图退出编辑状态,并设置模型上的数据。
  4. 模型根据项目的内容确定项目的最终位置。
  5. 模型调用beginMoveRows
  6. 模型通过在正确的位置插入项目来更改容器。
  7. 模型调用endMoveRows

在这一点上,一切都如你所料。视图可以自动跟踪被移动的项目,如果它在被移动之前被聚焦。在默认情况下,编辑的项都是集中的,因此工作正常。

必需的容器功能

您的DataContainer没有足够的功能使其工作,除非所有对它的访问都是通过模型完成的。如果您想直接访问容器,那么要么让容器显式继承QAbstractXxxxModel,要么向容器添加通知系统。前者是一个更容易的选择。

您的核心问题归结为:我可以在不实现模型通知API的某些变体的情况下拥有模型功能吗?显而易见的答案是:不,对不起,你不能-从定义上来说。如果您不希望容器是一个QObject,那么您将需要您的模型shim。没有办法可以绕过它。

文件系统通知QFileSystemModel有关已更改的各个目录条目。您的容器也必须这样做--这相当于提供某种形状或形式的dataChanged信号。如果模型中有被移动或添加/删除的项--其结构更改--则必须通过调用相关的xxxAboutToBeYyyendZzz方法来发出beginZzzendZzz信号。

索引

QModelIndex最重要的未得到说明的方面是:它的实例只有在模型的结构没有改变的情况下才有效。如果您的模型通过了一个在结构更改之前生成的索引,您就可以自由地以一种未定义的方式进行操作(崩溃、启动核打击等)。

QModelIndex::internalPointer()存在的全部原因是您有一个底层的、复杂的索引数据容器的用例。模型的createIndex方法的实现必须生成以某种形式存储对DataContainer索引的引用的索引实例。如果这些索引适合指针,则不需要分配堆上的数据。如果需要在堆上分配容器索引存储,则必须保留指向该数据的指针,并在容器的结构更改时随时删除它。您可以自由地这样做,因为在结构更改后,没有人应该使用索引实例。

票数 8
EN

Stack Overflow用户

发布于 2014-01-31 03:47:20

从方法bool QAbstractItemModel::insertRows(int row, int count, const QModelIndex & parent = QModelIndex())的文档

如果您实现自己的模型,如果希望支持插入,则可以重新实现此函数。或者,您可以提供自己的API来修改数据。在这两种情况下,您都需要调用beginInsertRows()和endInsertRows()来通知其他组件模型已经更改。

removeRows()moveRows()也是如此(他们有自己的begin*()end*()方法)。为了修改现有项目的数据,有一个dataChanged()信号。

是这样的(问题1的答案):

实现您自己的插入/删除/修改数据的方法,其中每个方法必须如下所示:

代码语言:javascript
运行
AI代码解释
复制
beginInsertRows(parentIndex, beginRow, endRow);
// code that modifies underlying data
endInsertRows();

必须提供beginRowendRow,以通知将插入哪些行以及其中的行数(endRow- where )。

对于beginDeleteRows()beginMoveRows()来说,情况是一样的。

当您有一个简单地修改现有项中的数据的方法时,该方法必须在末尾发出信号:dataChanged()

如果您对数据进行了大量更改,有时只需在执行此巨大修改的方法中调用beginResetModel()endResetModel()就更简单了。它将导致所有视图刷新其中的所有数据。

对问题2的回答:

这取决于视图类实现,如果它将“双更新”。当您在视图中输入数据时,数据将通过模型中的一个版本方法(insertRows()setData()等)发送到模型。这些方法的默认实现总是使用begin*()end*()方法,因此模型会发出适当的通知信号。所有视图都会监听这些信号,包括输入数据的信号,因此将执行“双更新”。

定义此行为的唯一方法是继承视图并重新实现其受保护的插槽(如dataChanged()和类似的),以避免在检测到该视图提供的值时进行更新。

我不确定Qt视图是否已经这样做了。要想解决这个问题,需要有人在Qt内部有更多的知识,或者研究Qt源代码(我目前还没有)。如果有人知道这一点,请评论,我会更新答案。

我认为从模型中重新加载数据并没有那么糟糕--它保证了您所看到的确实是来自模型的值。您可以避免编辑器和视图错误可能出现的问题。

对问题3的回答:

当你重新加载整个模型,那么就没有简单的方法来跟踪选择。在这种情况下,您需要询问view->selectionModel()当前选择的情况,并在重新加载之后尝试恢复它。

但是,如果您执行部分刷新(使用我在答案1中描述的方法),则视图将为您跟踪所选内容。没什么好担心的。

最后评论:

如果您想编辑来自模型类外部的数据,您可以这样做。只要将begin*()end*()方法公开为公共API,其他编辑数据的代码就可以通知模型和视图更改。

虽然这是可以做到的,但这不是一个好的做法。这可能会导致错误,因为无论您修改数据,都很容易忘记调用通知。如果您必须调用model来通知更改,为什么不将所有编辑代码移到模型内部并公开编辑API呢?

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21478746

复制
相关文章
WPF XAML 为项目设置全局样式
正确的做法是封装统一风格的所有控件。 (例如按钮,统一高宽,字体,字体大小,然后申明到独立的资源字典中, 在App.xaml中引用)
zls365
2021/10/19
1.8K0
WPF全局样式设置
/Resources/OverwrideDefaultControlStyles.xaml
码客说
2021/07/30
1.6K0
WPF设置托盘图标
正文 添加工具类 using System; using System.Drawing; using System.Windows.Forms; namespace ColorPicker.Utils { public class SystemTray { public static SystemTray Instance; /// <summary> /// 静态构造函数,在类第一次被创建或者静态成员被调用的时候调用 //
码客说
2022/03/13
1.1K0
WPF Textbox设置Placeholder
将一个与占位符绑定的TextBlock放入VisualBrush内,在TextBox的Text为空时使用VisualBrush绘制背景,不为空时背景设为Null。
码客说
2023/02/10
2K0
WPF开源项目:WPF-ControlBase
仓库README很素,但看作者README贴的几篇博文介绍,你会喜欢上它的,废话不多说,上介绍目录:
独立观察员
2022/12/06
3.5K0
WPF开源项目:WPF-ControlBase
WPF开源项目:WPF-ControlBase
仓库README很素,但看作者README贴的几篇博文介绍,你会喜欢上它的,废话不多说,上介绍目录:
沙漠尽头的狼
2021/12/01
3.6K0
WPF开源项目:WPF-ControlBase
WPF 设置输入只能英文
然后在构造使用 System.Windows.Input.InputMethod 可以设置 IME 和输入是否可以是中文。
林德熙
2019/03/13
2.1K0
WPF开源项目:AIStudio.Wpf.AClient
本框架使用Prism做MVVM,优点咱就不说了,主要了容器注入,消息和DI,比自己写省很多事。网上有很多标准的MVVM的使用方法,但是没有形成一个系统级的框架。本框架从登录到具体业务的使用,还有自动升级都搭建完成,没有大神写的那么好,只是起个抛砖引玉的作用。
沙漠尽头的狼
2021/12/01
1.5K0
WPF开源项目:AIStudio.Wpf.AClient
WPF 设置纯软件渲染
最近看到有小伙伴说 WPF 使用硬件渲染,如何让 WPF 不使用硬件渲染,因为他觉得性能太好了。万一这个版本发布了,产品经理说下个版本要提升性能就不好了。于是就找到一个快速的方法,让程序不使用硬件渲染这样下个版本要优化就让程序使用硬件渲染。
林德熙
2018/09/18
9590
设置WPF窗体全屏显示:
//全屏代码: private void Window_Loaded(object sender, RoutedEventArgs e) { // 设置全屏 this.WindowState = System.Windows.WindowState.Normal; this.WindowStyle = System.Windows.WindowStyle.None; this.ResizeMode = System.Windows.ResizeMode.NoResize;
hbbliyong
2018/03/05
3.9K0
WPF 设置纯软件渲染
最近看到有小伙伴说 WPF 使用硬件渲染,如何让 WPF 不使用硬件渲染,因为他觉得性能太好了。万一这个版本发布了,产品经理说下个版本要提升性能就不好了。于是就找到一个快速的方法,让程序不使用硬件渲染这样下个版本要优化就让程序使用硬件渲染。
林德熙
2022/08/04
5950
WPF 如何在应用程序调试启动
如果在一些无法使用源代码编译的电脑,调试一个exe无法启动,那么需要使用本文的技术。
林德熙
2019/03/13
8910
WPF 如何在应用程序调试启动
WPF 给应用程序添加水印
例如我有一个应用,我在主页面添加了功能页面,在功能页面的最上层需要一个水印,这个水印不能被用户点击到,例如我的功能页面是一个用户控件放在页面
林德熙
2020/02/18
1.2K0
WPF 如何在应用程序调试启动
如果在一些无法使用源代码编译的电脑,调试一个exe无法启动,那么需要使用本文的技术。
林德熙
2022/08/04
8860
WPF 如何在应用程序调试启动
如果在一些无法使用源代码编译的电脑,调试一个exe无法启动,那么需要使用本文的技术。
林德熙
2018/09/19
1.1K0
WPF 如何在应用程序调试启动
WPF项目实战WeMail - 项目准备
http://mpvideo.qpic.cn/0bf22maayaaamqapbe2ogzqvbu6dbtjqadaa.f10002.mp4?dis_k=33606d935d05bee0acb788d
宿春磊Charles
2022/03/29
3490
【愚公系列】2022年01月 WPF控件专题 WPF应用程序组成
Windows Presentation Foundation 由两个主要部分组成:引擎和编程框架。
愚公搬代码
2022/12/01
5240
【愚公系列】2022年01月 WPF控件专题 WPF应用程序组成
【愚公系列】2023年09月 WPF控件专题 WPF应用程序组成
Windows Presentation Foundation 由两个主要部分组成:引擎和编程框架。
愚公搬代码
2023/09/17
3830
C# WPF项目实战
好久没写原创了,今天心血来潮,打算写一篇,关于特定的知识点之前写过很多,今天呢就写一篇综合性的偏应用的一个小的项目实战.
用户9127601
2021/11/01
8950
WPF 项目文件不加 -windows 的引用 WPF 框架方式
默认情况下的 WPF 项目都是带 -windows 的 TargetFramework 方式,但有一些项目是不期望加上 -windows 做平台限制的,本文将介绍如何实现不添加 -windows 而引用 WPF 框架
林德熙
2023/07/24
3680

相似问题

层次分类法列表页

10

层次分类法术语视图

20

层次分类法比较的工作原理

10

如何使用层次分类法来组织内容?

10

层次分类法术语不起作用

20
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文