PDA

View Full Version : Updating a View when Model Changes Externally



jstark
12th September 2009, 17:48
Hello,

I'm trying to learn how to use Qt's Model/View framework, and i am stuck with a "simple" problem. I've a 'composite' class called Composite that contains other classes, either Composite(s) or Leaf(s). I have subclassed a QAbstractItemModel and rendered my hierarchy of Composite items in a QTreeView. What i would like to ask, is how to update a view when i know that the model (or some part of it) has changed outside the view. If a single Composite gets changed, deleted or something else, externally, how can i inform the view ? Should i reload the model ? There is a signal called dataChanged but it needs two QModelIndex arguments which i don't have, am i correct ? Am i missing something ?

wysota
14th September 2009, 08:49
You should avoid situations where a model can change "externally". You should encapsulate the data directly in your model and provide some API to modify it so that you can make sure proper signals will get emitted.

http://blog.wysota.eu.org/index.php/2007/12/17/itemviews-data-redundancy/

negritot
14th September 2009, 21:53
There is also modelReset(), if you can't know specifically what portion of the model changed.

jstark
16th September 2009, 20:58
Thanks for your answers, but let me make the problem a little more specific. Let's say we have a drawing program, and the user has created a set of points (x,y,z). He can see and manipulate the points in a QListView. However, due to 'legacy' code, these actual 'point' data structures are not in a QAbstractItemModel, but in a container of some form that we cannot alter. Now, while the list is open, he can alter the points from the list, and graphic view should update accordingly. Also, while the list is open, he can pick points from the graphic area, move them, delete them, or alter them somehow, and the list should update also. How would you deal with such a case ? I assumed that it is a good idea to gather all events (delete, edit, copy etc) in a central class (something like a controller class, or mediator) and then propagate the events to the model. So, if a point is altered from the graphic area, a specific class would receive an 'ALTER' event and the pointer to the point. Then what ? How could i ask the model to update this point only, since i have only a pointer to the point struct ? Should i hold a mapping of some sort, from pointers to QModelIndex ?

ps: Updating the whole list could be an option if all altered points could be received in a batch, and not one after the other which is the case.

ecanela
21st November 2009, 07:45
try to use the signal dataChanged() in the QAbstractItemModel class for enforce a update in the views.

if the data was modified for a external class. emit a signal to notify the change and connect whit one slot in the model. and emit the signal dataChanged. the only problem are indicate what modelIndex was modified.

i hope you found this useful.

wysota
21st November 2009, 10:04
I somehow missed this post earlier...


I assumed that it is a good idea to gather all events (delete, edit, copy etc) in a central class (something like a controller class, or mediator) and then propagate the events to the model.
The model is your central/controller class. Just make everything go through the model emitting proper signals. You can extend the model with your own methods - as long as you'll be emitting signals, everything will be fine. That's what the link I provided in the previous post explains.

ghorwin
22nd November 2009, 19:24
Hi All,

it appears to me that this kind of problem actually occurs quite often in real-world problems. For example, in our code we have quite a large data model (thermal simulation model for a whole building including detailed geometry and appliances) which is built-up hierarchically. In our user interface, we have different views that only show certain parts of the model, sometimes in table views and sometimes in tree views.

To fill the different table and tree views with data, we have several implementations of QAbstractItemModel and QAbstractTableModel which source the data directly from our object hierarchy, this from the model's point of view the data is external.

Now often we have the problem that part of the data in our object hierarchy changes, for example through interaction with one model. Other models that show the same data in a different representation do not know that the external source data has changed and we have the same problem as mentioned in the thread here, that we need to tell each model implementation which part of the data has changed.

This is difficult to do properly, so here are some options:

Use brute force and tell every model to reset(). This is bad because selection and item states are lost. It works ok, when the complete object hierarchy is newly populated (e.g. after loading the project data from file), but for minor changes it is overkill.
Use each model's data() function to determine which model indices have changed. This is slow and doesn't work well in many cases when type conversions are done inside the data() function.
Let each model cache the data it shows so that the models can always check for differences between the model's data representation and the source data and emit the dataChanged() signal accordingly. This is probably the best way, however, it has quite a memory overhead and is sometimes tricky to implement for non-trivial models.


Maybe you could comment on these and provide alternatives if possible?

wysota
22nd November 2009, 20:47
You should have one model inheriting QAbstractItemModel and all your "submodels" should just be proxies over the base model. If needed, you can add your own methods (and signals) to those classes, just don't separate the models - otherwise you will immediately run into synchronization problems. And make sure proper signals from QAbstractItemModel API are always emitted from the custom methods where appropriate.