Qt 5.2 Model-View-Pattern:如何通知模型对象有关底层数据结构的更改 [英] Qt 5.2 Model-View-Pattern: How to inform model object about changes in underlying data structure

查看:120
本文介绍了Qt 5.2 Model-View-Pattern:如何通知模型对象有关底层数据结构的更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a class used for permanent storage of some item that are organized in a table-like manner. This class is totally unrelated to Qt and comes from a different library. Lets call this class DataContainer for the rest of this question. It provides std-c++ compatible iterators to access and manipulate the content.

我需要通过Qt GUI显示和修改该数据.我的想法是创建一个从QAbstractTableModel继承并存储指向DataContainer对象的指针的类DataContainerQtAdaptor. DataContainerQtAdaptor用作DataContainer对象的适配器,并且从我的Qt应用程序内部进行的所有操作都通过此适配器进行.然后,我使用QTableView小部件显示信息.

I need to display and modify that data through a Qt GUI. My idea was to create a class DataContainerQtAdaptor that inherits from QAbstractTableModel and stores a pointer to the DataContainer object. The DataContainerQtAdaptor serves as an adaptor to the DataContainer object and all manipulation from inside of my Qt app is done through this adaptor. Then I use a QTableView widget to display the information.

不幸的是,DataContainer可能会被线程/进程更改. (例如,将DataContainer视为封装数据库连接的某些C ++类,并且该数据库可能会被其他人更改.)

Unfortunately the DataContainer might be changed by threads/processes. (For example think of DataContainer as some C++ class that encapsulates a database connection and that database might be changed by someone else.)

问题:

1)假设每次更改DataContainer对象的内部结构时,都会调用一个函数.必须调用QAbstractTableModel的正确函数是什么,以告知模型基础更改?我需要类似亲爱的模型,您的持久性存储后端已更改.请更新自己并向每个附加的视图发出信号,以反映此更改."

1) Assume I have a function that is called everytime the internal structur of the DataContainer object has been changed. What is the correct function of the QAbstractTableModel that must be called to inform the model of the underlying change? I need something like "Dear Model, your persistent storage backend changed. Please, update yourself and emit a signal to every attached view in order to reflect this change".

2)假设1)已解决.在通过GUI触发更改的情况下,避免双重" GUI更新的最佳方法是什么?例如:用户点击表格小部件中的单元格->表格小部件调用模型的setData->模型将更改推送到后端->后端触发其自己的"onUpdate"功能->模型重新读取完整的后端(尽管它已经知道更改)-> GUI第二次更新

2) Lets say 1) is solved. What is the best way to avoid a "double" GUI update in case the change was triggered through the GUI? E.g: User clicks on a cell in the table widget -> table widget calls setData of the model -> model pushes change to backend -> backend triggers its own "onUpdate" function -> model re-reads complete backend (although it already knows the change) -> GUI is updated a second time

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

3) The user should be able to insert new rows/columns through the GUI and put data into it. But the position is detemined by this data, because the backend keeps the data sorted. Hence, I have the following problem: The user decides to create a new row at the end and the new data is pushed to the backend. When the backend/model is re-read this data is normally not at the last position, but has been inserted somewhere in the middle and all other data has been moved forward. Ho do I keep all the properties of the the table view widget like "selection of a cell" in sync?

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

I believe, there must be some simple standard solution to all these question, because it is the same way as QFileSystemModel works. The user selects a file and some other process creates a new file. The new file is displayed in the view and all subsequent rows move forward. The selection moves forward, too.

Matthias

推荐答案

模型语义

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

Model Semantics

First of all, you must ensure that the QAbstractItemModel cannot be in an inconsistent state. This means that there are some signals that must be fired on the model before certain changes to the underlying data are done.

结构更改和数据更改之间存在根本区别.结构更改是要添加或删除的模型的行/列.数据更改仅影响现有数据项的值.

There is a fundamental difference between changes to structure and changes to data. Structure changes are the rows/columns of the model being added or removed. Data changes affect the value of existing data items only.

  • 结构更改需要在修改周围调用beginXxxendXxx.您不能修改任何结构,然后再调用beginXxx.完成结构更改后,请调用endXxx. Xxx是以下内容之一:InsertColumnsMoveColumnsRemoveColumnsInsertRowsMoveRowsRemoveRowsResetModel.

  • Structural changes require calling beginXxx and endXxx around the modification. You cannot modify any structure before calling beginXxx. When you're done changing the structure, call endXxx. Xxx is one of: InsertColumns, MoveColumns, RemoveColumns, InsertRows, MoveRows, RemoveRows, ResetModel.

如果更改影响许多不连续的行/列,则指示模型重置会更便宜-但请注意,对视图的选择可能无法幸免.

If the changes affect many discontiguous rows/columns, it's cheaper to indicate a model reset - but be wary that selections on the views might not survive it.

保持结构完整的数据更改仅要求在修改基础数据后 发送dataChanged.这意味着在一段时间内,对data的调用可能会在查询模型的对象收到dataChanged之前返回新值.

Data changes that keep the structure intact merely require that dataChanged is sent after the underlying data was modified. This means that there is a window of time when a call to data might return a new value before dataChanged is received by the object that queries the model.

这也意味着非QObject类中的非常量模型几乎是无用的,除非您当然使用观察者或类似的模式来实现桥接功能.

This also implies that non-constant models are almost useless from non-QObject classes, unless of course you implement bridge functionality using observer or similar patterns.

处理模型上更新循环的Qt惯用方式是利用项目角色.您的模型如何解释角色完全取决于您. QStringListModel实现的一种简单而有用的行为就是将角色从setData调用转发到dataChanged,否则忽略该角色.

The Qt-idiomatic way of dealing with update loops on the model is by leveraging the item roles. It's entirely up to you how your model interprets the roles. A simple and useful behavior implemented by QStringListModel is simply to forward the role from the setData call to dataChanged, otherwise ignoring the role.

库存视图小部件仅对dataChangedDisplayRole起反应.但是,当他们编辑数据时,他们用EditRole调用setData.这打破了循环.该方法适用于查看窗口小部件和Qt Quick查看项.

The stock view widgets react only to dataChanged with the DisplayRole. Yet, when they edit the data, they call setData with the EditRole. This breaks the loop. The approach is applicable both to view widgets and to Qt Quick view items.

只要排序完成后模型正确发出更改信号,就可以了.

As long as the model properly emits the change signals when the sorting is done, you'll be fine.

操作顺序为:

  1. 该视图添加一行并调用模型的insertRow方法.模型可以将此空行添加到基础容器,也可以不添加.关键是必须暂时保留空行索引.

  1. The view adds a row and calls model's insertRow method. The model can either add this empty row to the underlying container or not. The key is that the empty row index must be kept for now.

编辑将从该行中的项目开始.视图状态更改为Editing.

The editing starts on an item in the row. The view state changes to Editing.

已完成对项目的编辑.视图将退出编辑状态,并在模型上设置数据.

Editing is done on the item. The view exits the editing state, and sets the data on the model.

该模型根据项目的内容确定项目的最终位置.

The model determines the final position of the item, based on its contents.

模型调用beginMoveRows.

模型通过将项目插入正确的位置来更改容器.

The model changes the container by inserting the item at the correct location.

模型调用endMoveRows.

在这一点上,一切都与您期望的一样.如果在移动之前将其聚焦,则视图可以自动跟随其移动.默认情况下,编辑后的项目会处于焦点状态,这样就可以正常工作.

At this point, everything is as you expect it to be. The views can automatically follow the moved item if it was focused prior to being moved. The edited items are focused by default, so that works fine.

您的DataContainer没有足够的功能来使其正常工作,除非所有对它的访问都必须通过模型进行.如果要直接访问该容器,请使该容器显式继承QAbstractXxxxModel,或者必须向该容器添加一个通知系统.前者是一个更简单的选择.

Your DataContainer doesn't have enough functionality to make it work unless all access to it were to be done through the model. If you want to access the container directly, either make the container explicitly inherit QAbstractXxxxModel, or you'll have to add a notification system to the container. The former is an easier option.

您的核心问题简化为:我可以在没有实现模型通知API某些变体的情况下具有模型功能.显而易见的答案是:不,抱歉,您不能-按照定义.功能是否存在或不存在.如果您不希望容器成为QObject,则可以使用观察者模式实现通知API-然后,您将需要模型shim类.真的没有办法解决.

Your core question reduces to: can I have model functionality without implementing some variant of the model notification API. The obvious answer is: no, sorry, you can't - by definition. Either the functionality is there, or it isn't. You can implement the notification API using an observer pattern if you don't want the container to be a QObject - then you'll need your model shim class. There's really no way around it.

QFileSystemModel由文件系统通知有关已更改的各个目录条目.您的容器必须执行相同的操作-这相当于以某种形状或形式提供dataChanged信号.如果模型具有可移动或添加/删除的项目-结构发生了变化-它必须通过调用相关的beginZzzendZzz方法来发出xxxAboutToBeYyyxxxYyy信号.

The QFileSystemModel gets notified by the filesystem about individual directory entries that have changed. Your container has to do the same - and this amounts to providing a dataChanged signal, in some shape or form. If the model has items that get moved around or added/removed - its structure changes - it has to emit the xxxAboutToBeYyy and xxxYyy signals, by calling the relevant beginZzz and endZzz methods.

QModelIndex的最重要的文献不足的方面是:其实例仅在模型的结构未更改的情况下才有效.如果您的模型传递了在结构更改之前生成的索引,则您可以自由地以未定义的方式运行(崩溃,发动核打击等).

The most important underdocumented aspect of QModelIndex is: its instances are only valid for as long as the model's structure hasn't changed. If your model is passed an index that was generated prior to a structure change, you're free to behave in an undefined way (crash, launch a nuclear strike, whatever).

QModelIndex::internalPointer()存在的全部原因是您拥有一个基础的,复杂索引的数据容器的用例.模型的createIndex方法的实现必须生成索引实例,该索引实例以某种形式存储对DataContainer索引的引用.如果这些索引适合指针,则无需在堆上分配数据.如果需要在堆上分配容器索引存储,则必须保留指向此数据的指针,并在容器结构发生任何变化时将其删除.您可以自由地执行此操作,因为在结构更改后没有人可以使用索引实例.

The whole reason for the existence of QModelIndex::internalPointer() is your use case of having an underlying, complex-indexed data container. Your implementation of the model's createIndex method must generate index instances that store references to the DataContainer's indices in some form. If those indices fit in a pointer, you don't need to allocate the data on the heap. If you need to allocate the container index storage on the heap, you must retain a pointer to this data and delete it any time the container's structure changes. You're free to do it, since nobody is supposed to use the index instance after a structure change.

这篇关于Qt 5.2 Model-View-Pattern:如何通知模型对象有关底层数据结构的更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆