3
votes

I'm writing an application that has a QTreeWidget that is populated by parsing an XML file containing the tree levels. If I select a top level checkbox, I need all of the sub-level checkboxes to be checked also.

I already have the XML parser working and populating the QTreeWidget with QTreeWidgetItems that have checkboxes but they can only be individually checked.

3
That's two different things you're talking about there, rabid: checking the checkboxes (in paragraph 1) and selecting the items (in paragraph 2). Which do you mean?TonyK

3 Answers

6
votes

To do this, keep the code you have to generate the tree with your XML. Then connect to the itemChanged() signal and update the check states in a slot. It should look something like:

connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
        this,       SLOT(updateChecks(QTreeWidgetItem*, int)));

void ClassName::updateChecks(QTreewidgetItem* item, int column)
{
    // Checkstate is stored on column 0
    if(column != 0)
        return;

    recursiveChecks(item);
}

void ClassName::recursiveChecks(QTreeWidgetItem* parent)
{
    Qt::CheckState checkState = parent->checkState(0);
    for(int i = 0; i < parent->childCount(); ++i)
    { 
       parent->child(i)->setCheckState(0, checkState);
       recursiveChecks(parent->child(i));
    }   
}

A few notes to consider:

  1. You may be tempted to use the itemClicked signal instead of the itemChanged signal. This usually works, but will not work when the user uses the arrow keys and the space bar to change checkstates.
  2. You will need to think about what will happen when you uncheck one of the sub-items that have been checked by clicking on its parent. Usually this means you need to make all ancestors either uncheck or partially-checked. This may not be true for your case.
  3. itemUpdated will also get fired for other changes to the item (like the text changing), so be aware that this is not a super efficient way of doing this.
3
votes

I just worked on this a little and got nice results based on Rick's answer. Maybe it can help other out there. It updates the state of parents and children with a tristate status for parents only (checked, unchecked, partially-checked).

void ClassName::updateChecks(QTreeWidgetItem *item, int column)
{
    bool diff = false;
    if(column != 0 && column!=-1)
        return;
    if(item->childCount()!=0 && item->checkState(0)!=Qt::PartiallyChecked && column!=-1){
        Qt::CheckState checkState = item->checkState(0);
        for (int i = 0; i < item->childCount(); ++i) {
           item->child(i)->setCheckState(0, checkState);
        }
    } else if (item->childCount()==0 || column==-1) {
        if(item->parent()==0)
            return;
        for (int j = 0; j < item->parent()->childCount(); ++j) {
            if(j != item->parent()->indexOfChild(item) && item->checkState(0)!=item->parent()->child(j)->checkState(0)){
                diff = true;
            }
        }
        if(diff)
            item->parent()->setCheckState(0,Qt::PartiallyChecked);
        else
            item->parent()->setCheckState(0,item->checkState(0));
        if(item->parent()!=0)
            updateChecks(item->parent(),-1);
    }
}

Doesn't need recursiveChecks() anymore. Connect between the treeWidget and updateChecks still active.

3
votes

This appears still quite high in search engines, and is outdated. Just set the flag Qt::ItemIsAutoTristate on your top-level item.