PDA

View Full Version : QTreewidget with checkboxes



avery96
11th August 2020, 07:35
I created a treewidget in Qt. My goal is to create a simple library with CMakeLists.txt files. I have collected the project files in a directory. My goal is to access CMakeLists.txt files in project files and show them in a treewidget. There is no problem in this part.
In step 2, I have to add checkboxes to the parents (each parent corresponds to a project) and put links to them. There are about 40 parents, each parent's child has other parent's listed. When I check any parent's, other parents listed in the child need to be checked as well. How can I do that?

d_stranz
11th August 2020, 18:55
I think you need to separate the data structure you use to represent the CMakeLists files and their parent-child relationships from the QTreeWidget you use to display this data structure.

One problem with QTreeWidget is that the model it uses cannot directly represent children that have more than one parent. Each QTreeWidgetItem has only zero or one parent, but can have zero or more children. With CMakeLists, a subproject, like a library, can be used in multiple higher level CMakeLists, so you don't have a simple parent-child relationship but a many-many relationship. It is a network, not a tree.

You can model this with your own network data structure, where each node (representing a CMakeLists.txt file) can have zero or more parent nodes and zero or more child nodes. You need to be able to travel through this data structure from top to bottom (following parent -> child links) or bottom to top (following child -> parent links). Your data structure will look more like a mesh than a tree with a root and branches. Each element in this tree needs to have a "checked" flag (in addition to whatever other data you store).

To map this into a QTreeWidget, you have to start at any top-level node of your data structure and work downwards, creating QTreeWidgetItem instances and inserting them into the tree widget. You need to store a pointer to the node in your data structure inside the QTreeWidgetItem, either as a data() member or by deriving a class from QTreeWidgetItem and storing it as a member variable. Note that in your QTreeWidget, every QTreeWidgetItem will be a unique instance, but the same data pointer from your data structure might be stored in more than one QTreeWidgetItem. This is how you handle the multiple-parent problem of your data structure in a tree that supports only a single parent for each item. When you look at your QTreeWidget, you could see the same CMakeLists file displayed at multiple places in it.

So now your problem of checking projects comes down to setting the "checked" flag in your data structure and then traversing the data structure to set the checked flag in all of the parent nodes that depend on that one.

Once that is done, you map the data structure checks to tree widget checks. You walk through your QTreeWidget from top to bottom. For each top-level QTreeWidgetItem, you retrieve the pointer to your data structure node, and set the QTreeWidgetItem's checked status appropriately, then follow the QTreeWidgetItem links to its children, retrieve their data structure pointers, set the check state, and so on, until you reach the end of that path through the tree. You then go back to the next top-level node and do it again for that one, and so on until you have handled all of the top level nodes and all of their children.

If you are interacting with the QTreeWidget to check / uncheck nodes, you do this same process - when changing the check state of a node, you retrieve the pointer to the data structure and change -its- check state (and the state of everything it links to), then you use this information to update the tree widget. Don't do it the other way around (using the tree widget to update the data structure) because the tree widget doesn't contain all of the necessary information - it only holds -one- of the parents of any node.

One more gotcha: A parent CMakeLists might depend on more than one checked child. So when you uncheck a child, you can't uncheck the parent automatically. You have to first check all of the other children of that parent to see if any of them are still checked. If they are, then you can't uncheck the parent.

In CMakeLists terms, if a parent depends on two subprojects and both of them are checked, unchecking one project still means the parent has to be build, because building the other subproject will force it.

By the way, the QAbstractItemModel class does not put any restrictions on the parent-child relationships in the model. It is very general in this regard. The specialization to QTreeWidget / QTreeView adds the restriction of one parent per child. If Qt supplied a widget that could display QAbstractItemModels of any complexity (like a network graph), then you could directly model your data structure as an abstract model and use the network graph widget to display it.