PDA

View Full Version : QTreeView: hide parents but show children?



ce_nort
14th September 2016, 21:57
I am trying to figure out if it is possible to hide certain parents in a QTreeView (or TreeWidget, possibly) without hiding the children. For example:


Cars
|_Toyota
| |_Camry
| |_Corolla
|
|_Nissan
|_Altima
|_Pathfinder
|_Versa

Is it possible to hide items in such a way that all that is show is:


Camry
Corolla
Altima
Pathfinder
Versa

This is a simplified example to illustrate. Effectively the functionality that I'm trying to achieve is as follows:
A "parent" item represents a class of object, and all of it's child objects share certain traits and vary on certain traits (in the car example, they might all have the same engine-type and location of manufacture, but be different sizes and colors). For someone creating this [editable table] I would like to be able have the parent there to pass the overlapping traits on to their children, and have the children individually editable to fill in the traits on which they vary. However, for ease of manually copying and pasting large swaths of data into the treeview from external spreadsheets that only contain information on the children, I would like to have the parents (e.g., Toyota, Nissan) disappear. The best way to describe it would be turning the TreeView with parents and children into a TableView with only the lowest level children. What is the best approach? I'd like to toggle between the two views easily, which is why my initial idea was to simply maniuplate the TreeView. But I am definitely open to other ideas. I have read http://www.qtcentre.org/threads/46896-Hide-Parent-and-show-only-children-in-QTreeView and http://www.qtcentre.org/threads/29834-QTreeView-How-to-hide-only-parent-s-row-and-show-its-children . The first one doesn't work for me since there are multiple parents to hide, and the second one went unanswered. Any and all thoughts welcome! I can also explain what I'm looking for in more detail if it's not clear from what I already wrote, just let me know if you need more info.

Edit:
Would I be best served to apply a Proxy Model of some sort? I have had a hard time understanding how to use proxy models or what they do, so I'm not sure if that would be the appropriate application here...

wysota
15th September 2016, 07:36
You need to define a custom proxy model that will do what you want. In this particular situation it is quite easy since you only want to show leaves of the tree but in a general case it becomes more complex. All in all you have to find a mapping between nodes in the source model and in the proxy model by implementing mapFromSource() and mapToSource() in the proxy. Based on how your model keeps its data internally it might be easier to have a separate model (not a proxy) operating on the same data as the primary model (you need to ensure proper synchronization between models then).

anda_skoa
15th September 2016, 09:27
I think the https://inqlude.org/libraries/kitemmodels.html has something for that, KDescendantsProxyModel

Cheers,
_

ce_nort
15th September 2016, 16:11
Thank you both for the helpful info! Unfortunately I don't think the KDescendantsProxyModel will quite work for me since it turns the result into a list, when really I need a table (not clear from my initial post, apologies). What I left out in my simplified example is that I still need all the extra data associated with each child (e.g. color, price, etc.). It seems that perhaps the best solution is to create a custom proxy model and put the resulting children into a table view rather than a tree view (at least, I'm assuming it should be a tableview since there won't be nodes). I spent a few hours yesterday trying to wrap my head around proxy models, and I think it will be the best solution. I believe that the relationship between the parents and children is simple enough that I shouldn't need two separate models.

wysota
16th September 2016, 09:59
If it's a proxy model then it shouldn't care whether you have multiple columns or not. You should give it a try.

ce_nort
16th September 2016, 16:56
Cool, I will look into it!

WorstNoob
6th December 2017, 21:05
Cool, I will look into it!

Did you by any chance come up with a solution to the problem?
I am facing the very same need to only display the last level of children from every parent and I would really hate having to duplicate the entire model data (minus the parents) just for being able to display it that way at times. Seems strange that something this simple has to be this complicated, the only "solution" I see currently is data duplication, which sucks. In the meantime I could've completed the data duplication three times over but I really don't want to.

ce_nort
6th December 2017, 21:13
The project that I was needing this for ended up getting postponed indefinitely, so I stopped exploring the solution. It's been a long time since I was looking into this, but if the thread tells the tale correctly, then the concept of proxy models should help you: http://doc.qt.io/qt-5/qsortfilterproxymodel.html , http://doc.qt.io/qt-5/qabstractproxymodel.html . Wish I could be more helpful!

WorstNoob
6th December 2017, 21:19
Unfortunately so far it was of no particular use.
I do have a proxyfiltermodel in there, to filter the view and having the parents still displayed when any children matches is working just fine.
But I can't seem to get behind on how one could possibly make it ignore the parent items, but still display the children that are matching. If the parent is not accepted, then the children never get examined.

Thought about overriding mapTo/FromSource and somehow keeping a mapping but for some reason, even if there are no filters set for the proxyfiltermodel, it still doesn't map 1:1 to the source model, at least row of the proxy / source indexes don't match and I cannot see a pattern either. This unexpected behavior is throwing me off.
Plus would have to somehow delete that mapping when the source changes or generate it on the fly somewhere, which I don't know where yet.

haiku
20th March 2023, 11:38
I've been looking into how to do this and have found quite a nice way so long has your willing to use some private apis.

The issue is as WorstNoob states that there is no way to show only the children for a tree model using a QSortFilterProxyModel (that I could find), however you can use an internal Qt model called QQmlTreeModelToTableModel (QtQmlModels/private/qqmltreemodeltotablemodel_p_p.h) which as the name suggests proxies a tree model and converts it into a table model. Now you can proxy this table model and filter out parent rows as they are technically no longer parents. This is the model that Qts QML TreeView uses internally for what its worth but it should work whatever your usecase. It also preserves child parent relationships albeit in a different way so you still have that info available should you need it. One gotcha to look out for is that you need to manually expand the rows, otherwise it will only show the first row in the model.

d_stranz
20th March 2023, 15:24
Several years ago, the KDE project had a FlatProxyModel class. It doesn't seem to be a part of the current project, but you can still find it online (https://code.woboq.org/kde/calligra/plan/libs/models/) as part of some other projects in the kptflatproxymodel.[h, cpp] files.

There is now a KDescendantsProxyModel (https://api.kde.org/frameworks/kitemmodels/html/classKDescendantsProxyModel.html) in the KDE Frameworks (KItemModels section) (https://api.kde.org/frameworks/kitemmodels/html/index.html) which looks like it might be the replacement for the FlatProxyModel class.

These are both KDE "Tier 1" classes, meaning they have no dependence on other parts of KDE, only on Qt.

These do essentially the same thing as your private model does - flatten a tree into a table by turning each leaf into a row. Parents are added as new columns at the start of the row. It has the advantage that it isn't going to break your code in the future when it is modified. Private classes are private because the authors of Qt want to be able to change them while keeping the external public API the same.