PDA

View Full Version : QTreeView and expandable items



SiLiZiUMM
28th April 2008, 20:46
Hello,

I am converting a item-based QTreeWidget into a model-based QTreeView.

I am rendering a QTreeView using a custom model based on QAbstractItemModel. My data model has a bool property called "expanded": that property indicate whether if the node must be expanded or collapsed.

Previously, when the QTreeWidgetItems were created in a for-loop, it was easy to call setExpanded(data->isExpanded()). Now that my data is rendered using a view, I am confused on how I should accomplish this.

Up to now, after I call setModel() on the model object, I use a for-loop to iterate through my model (if the property isExpanded is set in the data model, I call expand() on the tree view).

Works well, but it doesn't look like the optimal solution here. I would have thought that a delegate would take care of this, but as far as I understand it, it is the View's job to manage the tree items... do I have it right? If so, do I have to subclass QTreeView in order to do what I want (The view is for the display only, when the model is loaded, the nodes are reset to the state indicated in the data, we do not save the node state afterwards)?

wysota
28th April 2008, 21:08
I am rendering a QTreeView using a custom model based on QAbstractItemModel. My data model has a bool property called "expanded": that property indicate whether if the node must be expanded or collapsed.
Hmm??? Why? Expansion state is the property of the view, not the model...


Up to now, after I call setModel() on the model object, I use a for-loop to iterate through my model (if the property isExpanded is set in the data model, I call expand() on the tree view).
Well... that's exactly the same as you were doing before, only that instead of iterating items you are now iterating indexes. I'd suggest to keep the state in the view though...


Works well, but it doesn't look like the optimal solution here.
The operation has O(n) complexity, you can't do better if you don't have a list of expanded items stored anywhere.


I would have thought that a delegate would take care of this,
The delegate? It is responsible for rendering a single item, not the whole tree. And it doesn't track nor change the state of data in the model.


but as far as I understand it, it is the View's job to manage the tree items... do I have it right?
Yes, the view is responsible for it.


If so, do I have to subclass QTreeView in order to do what I want
No, just "load" the list of expanded items from wherever you have it stored and expand those indexes that need expanding.


(The view is for the display only, when the model is loaded, the nodes are reset to the state indicated in the data, we do not save the node state afterwards)?

Why do you keep the expansion state in the model? It's against any logic relevant to model-view architecture... Visualization state of the model is not related to the data. If I expand or collapse an item in one of the views, there is no change to the data. What if I have two views of the same data and in one of them an index is collapsed and in the other it is expanded? If you really want to break the architecture, then subclass the view, reimplement its setModel() method and hook to the dataChanged signal so that you can react on changes to the model, make the model emit dataChanged when its item changes expansion state and make the view operate on a custom role of the model with setData while expanding or collapsing the item. It will work, although I don't really see a point of doing that....

SiLiZiUMM
28th April 2008, 21:27
Hmm??? Why? Expansion state is the property of the view, not the model...
Simple: my UI is generated from a XML file, describing the UI entirely. The XML file is parsed, and stored in the data structure. Therefore, I need to set the items as described in the file...



Well... that's exactly the same as you were doing before, only that instead of iterating items you are now iterating indexes. I'd suggest to keep the state in the view though...

The only "disadvantage" I see now is that the iteration must be done after the items were created, instead of when they are created (I see this as a second strep, re-iterating through the elements)...



The delegate? It is responsible for rendering a single item, not the whole tree. And it doesn't track nor change the state of data in the model.
Thanks for clarifying me this point :)


No, just "load" the list of expanded items from wherever you have it stored and expand those indexes that need expanding.
This might be a solution, I'll check if that can be done easily!


Why do you keep the expansion state in the model? It's against any logic relevant to model-view architecture... Visualization state of the model is not related to the data. If I expand or collapse an item in one of the views, there is no change to the data. What if I have two views of the same data and in one of them an index is collapsed and in the other it is expanded?

That's why I was saying that this "expanded" property is just used when building the tree. Think of this property as read-only in the data model: the view can use it to render the tree the first time, and the user can modify the state, but the state is not saved back into the data model.

Hope it clears a little bit why I am doing that :D

wysota
28th April 2008, 21:49
Simple: my UI is generated from a XML file, describing the UI entirely. The XML file is parsed, and stored in the data structure. Therefore, I need to set the items as described in the file...
But it doesn't imply you should keep the state in the data model. I don't know what the xml describes - does it describe items that are to be visualized using a tree view?



The only "disadvantage" I see now is that the iteration must be done after the items were created, instead of when they are created (I see this as a second strep, re-iterating through the elements)...
With the model there is really no concept of "creating" items. They are just there... When you load data from the file, you can build a separate data structure that will serve only as a means to expand the tree. Then you'll just iterate the structure and expand appropriate items.

Keep in mind that the model is only an interface to *any* data, nothing more, nothing less. If you have an XML file - don't read/parse/create structures. Just make your model operate on the XML directly. Otherwise you're ending up with an unnecessary redundancy and lag.

SiLiZiUMM
28th April 2008, 21:59
But it doesn't imply you should keep the state in the data model.
I don't keep the current state, but only the default state in the XML -- how should the tree item be displayed when loaded.


I don't know what the xml describes - does it describe items that are to be visualized using a tree view?
Yes. Simple XML example:



<browserTree name="MyTree">
<treeNode name="RootNode" expanded="1">
<treeNode name="ChildNode" expanded="1" />
</treeNode>
</browserTree>




Keep in mind that the model is only an interface to *any* data, nothing more, nothing less. If you have an XML file - don't read/parse/create structures. Just make your model operate on the XML directly. Otherwise you're ending up with an unnecessary redundancy and lag.

Well the way the app is done right now, the parser (SaX) reads the XML file and creates the appropriate objects on the fly (for the GUI, those objects only describes the elements of the GUI to be generated. The view currently operate on the created objects.

wysota
28th April 2008, 23:06
The view currently operate on the created objects.

You have redundancy then. Why not operate directly on the xml? How many items do you have? If the amount is not very big, you might use DOM instead of SAX and operate on the DOM tree directly. Of course I don't encourage you to do that if you already have a working SAX based version.

SiLiZiUMM
29th April 2008, 14:21
You have redundancy then. Why not operate directly on the xml? How many items do you have? If the amount is not very big, you might use DOM instead of SAX and operate on the DOM tree directly. Of course I don't encourage you to do that if you already have a working SAX based version.

That's exactly the situation: the parser class exists since a while and is part of another project, therefore cannot be swapped easily :p

But thanks for the infos, very pleasing to read your posts as usual! :cool: