[Qt-interest] Qt::CheckStateRole

Sean Harmer sean.harmer at maps-technology.com
Thu Sep 17 10:22:47 CEST 2009


Hi,

On Thursday 17 Sep 2009 02:06:56 Willy P wrote:
> I've implemented my own model by inheriting from QAbstractItemModel
> and my own view by inheriting from QTreeView.  I've got all the
> parents and children showing up and I've got checkboxes showing up
> next to them in their proper state.  Checking and unchecking items is
> properly setting the values in the model, but they're not cascading up
> the chain to the parents.  In other words, in the view, a parent's
> check state is not Qt::PartiallyChecked when only some of it's
> children are checked.  How do I get the automatic updating of parent's
> and children's checkboxes?  Or do I have to do that myself?
>
> Thanks in advance...
I think you have to do this yourself. Here is some snippets of how I did it a 
couple of years ago. (Bear in mind I was still learning the model/view API 
then so there may be a better way of doing this).

In your model's setData function have a section like this:

bool MyModel::setData( const QModelIndex& index, const QVariant& value, int 
role )
{
    if ( role == Qt::CheckStateRole )
    {
        Node* item = static_cast<Node*>( index.internalPointer() );
        Qt::CheckState state = Qt::Checked;
        if ( item->checkState() == Qt::Checked )
            state = Qt::Unchecked;
        item->setCheckState( state );
        if ( item->itemType() == Node::File )
        {
            QModelIndex highestChanged = propagateCheckStateToRoot( index, 
state );
            emit dataChanged( highestChanged, index );
            emit layoutChanged();
        }
        else
        {
            // For (un)checking a group we must recurse down and apply the 	
            // change to all children too
            QModelIndex lowestChanged;
            QModelIndex highestChanged = 
                propagateCheckStateToLeaves( index, lowestChanged, 
                                                                    state,true);
            emit dataChanged( highestChanged, lowestChanged );
            emit layoutChanged();
        }
        return true;
    }

    return false;
}

This assumes that we have two types of node in our model leaf nodes and 
grouping nodes and takes care of calling either propagateCheckStateToLeaves or 
propagateCheckStateToRoot as needed. The implementation of these functions 
are:

QModelIndex MyModel::propagateCheckStateToRoot( const QModelIndex& index, 
Qt::CheckState state )
{
    QModelIndex parent = index.parent();
    if ( parent.isValid() )
    {
        Node* parentItem = static_cast<Node*>( parent.internalPointer() );
        Qt::CheckState parentState = parentItem->checkState();

        if ( !childrenAllSameSelectionState( parent ) )
            state = Qt::PartiallyChecked;

        if ( parentState != state )
        {
            setCheckState( parent, state );
            return propagateCheckStateToRoot( parent, state );
        }
        return index;
    }

    return parent;
}


QModelIndex MyModel::propagateCheckStateToLeaves( const QModelIndex& index,
    QModelIndex& lowest, Qt::CheckState state, bool initiator )
{
    if ( hasChildren( index ) )
    {
        // Recurse this call into the children
        for ( int i = 0; i < rowCount( index ); i++ )
        {
            QModelIndex child = MyModel::index( i, 0, index );
            propagateCheckStateToLeaves( child, lowest, state );
        }
    }
    else
    {
        // The item referenced by index has no children. 
        // Set the state and record the index
        setCheckState( index, state );
        lowest = index;
    }

    if ( initiator )
        return propagateCheckStateToRoot( index, state );

    return index;
}


And finally these function use a couple of small helpers:

bool MyModel::childrenAllSameSelectionState( const QModelIndex& parent ) const
{
    if ( !parent.isValid() )
        return false;

    Node* parentItem = static_cast<Node*>( parent.internalPointer() );
    if ( parentItem->itemType() != Node::Group )
        return false;

    GroupItem* group = static_cast<GroupItem*>( parentItem );
    if ( group->childCount() == 0 || group->childCount() == 1 )
        return true;

    Qt::CheckState state = group->child( 0 )->checkState();
    for ( int i = 1; i < group->childCount(); i++ )
    {
        if ( group->child( i )->checkState() != state )
            return false;
    }

    return true;
}

void MyModel::setCheckState( const QModelIndex& index, Qt::CheckState state )
{
    if ( !index.isValid() )
        return;

    Node* item = static_cast<Node*>( index.internalPointer() );
    item->setCheckState( state );
}

Anyway, you should be able to get the idea from this and port it to your 
specific case.

HTH,

Sean



More information about the Qt-interest-old mailing list