I figured out that I had a lot spaghetti code. I can't get in words what was wrong.
After thinking a lot I have a better view of the problem. Basically what I do now is, for the underlying data, manually creating a tree structure based on what I want to show on the screen. Now, I have a "complex" method that is responsible to know how to create the tree structure based on my internal data, whatever that data is it. So now, in other words, the tree structure acts as a "view" to the internal data, and as a "model" for the QAbstractItemModel.
To the model (QAbstractItemModel), that tree structure is the data. Nodes hides behavior of the internal data. Simple as that. A node is a class with a name, value, parent, children. In the future I will add other attributes to the node (bytes, start, end), but that doesn't matter now. The tree structure expects that each node has a name, a value, a parent and zero or more children. Whatever else it has, doesn't matter. So NetworkEvent nodes will not have "start", "end" or "bytes" attributes, to the tree that is indifferent.
So this is an example of the tree structure behind the model.
Qt Code:
Node(name='Network Events', value=object<list>) |-- Node(name='0', value=object<NetworkEvent>) | |-- Node(name='timestamp', value='2019-Jan-13 01:34:27') | |-- Node(name='server', value='195.232.44.140:5005') | |-- Node(name='source', value='Server') | +-- Node(name='packets', value=object<list>) | +-- Node(name='0', value=object<Packet>) | |-- Node(name='header', value=object<Header>, start=0, end=5, bytes=...) ... (continues expanding) | +-- Node(name='content', value=object<Content>, start=6, end=., bytes=...) ... (continues expanding) |-- Node(name='1', value=object<NetworkEvent>) | |-- Node(name='timestamp', value='2019-Jan-13 01:34:27') | |-- Node(name='server', value='195.232.44.140:5005') | |-- Node(name='source', value='Client') | +-- Node(name='packets', value=object<list>) | +-- Node(name='0', value=object<Packet>) | |-- Node(name='header', value=object<Header>, start=0, end=5, bytes=...) ... (continues expanding) | +-- Node(name='content', value=object<Content>, start=6, end=., bytes=...) ... (continues expanding)To copy to clipboard, switch view to plain text mode
Now I don't understand why you would recommend to use multiple models instead of a single one with different proxy models, apart of maybe being a little more complex. While having different models makes things easier, it also has its cons. In the GUI, when a user selects a network event from the network event list view, it should show its packet tree on the QTreeView. Problem is, everytime I change the QTreeView model because a user selected a different NetworkEvent, it closes every expanded node. That leads to a poor user experience. So, if I expand packet 0 from network event 0, then I go to network event 1, and again to network event 0, now packet 0 is collapsed. I want it to remember expanded/collapsed nodes in the current session.
[ 1 ] Maybe what I am doing wrong is, setting only "packets" node as data of the treeview model, instead using the entire tree structure as treeview model and then selecting the QModelIndex corresponding to "packets" node of the clicked NetworkEvent. This way the QTreeView will know which packets were collapsed/expanded.
Another way I was thinking is defining a base model with the entire tree structure, then create a proxy model to generate the network event list. When the user press a NetworkEvent on the list, pass its "packets" children node to the QTreeView model, being it responsible to recover the corresponding QModelIndex to its model (proxy or base model, doesn't matter), similar to what you said about having ways to address nodes without QModelIndex objects.
This way I avoid making use of mapFromSource and mapToSource with QModelIndex when interacting between views elements, and instead, replicate this behavior internally but with nodes. So won't be calling QTreeView.setRootIndex but instead MyCustomTreeView.setRootNode(node), and internally it will execute MyCustomTreeViewModel.getIndexFromNode(node), then setRootIndex with that index.
But this approach, the more I think, it seems like having different models with the same entire tree in the background (different models, sharing the entire background data, as you suggest).
And I guess filtering with QSortFiltProxyModels would mean invalidating all QModelIndex's of models so it doesnt matter if I filter the QAbstractItemModel using the "qt" method or I implement the filter on the tree structure then change the model, both ways would mean that the view wouldn't know which nodes were expanded/collapsed.
So my new question is, is there any visual/user experience/whatever difference, between attacking this problem, with proxy models or different models that I should take in account thinking on what I may want to implement in the future? As I see now (after realizing
[ 1 ]), I guess there is not, apart of the ease of use of not having to mess with QModelIndex mapping.
I suppose those differences may appear when trying to add up other things to the problem, like syncing changes between different QAbstractItemModels that work over the same underlying data. But this is beyond my current knowledge.
Thanks for your time, I really appreciate it. Sorry for the confusing wall of text.
Bookmarks