Drise
Drise

Reputation: 4388

QTreeView Checkboxes

I know this has been asked a bunch of times, but I cant seem to find anything relevant.

Using the simpletreemodel tutorial that comes packaged with Qt, how would I add checkboxes?

Upvotes: 23

Views: 27650

Answers (4)

keineahnung2345
keineahnung2345

Reputation: 2701

I just want to complete @JediLlama's answer by adding the setData method, translated from @taynaron's Python version:

bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
    TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

    if (index.column() == 0) {
        if (role == Qt::EditRole) {
            return false;
        }
        if (role == Qt::CheckStateRole) {
            item->setCheck(value.toBool());
            emit dataChanged(index, index);
            return true;
        }
    }

    return QAbstractItemModel::setData(index, value, role);
}

Upvotes: 1

Sebastian Wagner
Sebastian Wagner

Reputation: 2516

Here is another PyQt complete woking example using QStandardItemModel:

model = QStandardItemModel()
parent_item = model.invisibleRootItem()  # type: QStandardItem
for row in [
    (Qt.Unchecked, 'unchecked'),
    (Qt.PartiallyChecked, 'partially'),
    (Qt.Checked, 'checked')
]:
    checked, text = row
    check_item = QStandardItem('')
    check_item.setCheckable(True)
    check_item.setCheckState(checked)
    parent_item.appendRow([check_item, QStandardItem(text)])
treeview.setModel(model)

Btw, this should also work for any C++ applications.

Upvotes: 2

JediLlama
JediLlama

Reputation: 1173

Firstly, you'll need to modify TreeItem to keep track of the checked state:

private:
    ...
    bool checked;

and a setter and getter:

bool isChecked() const { return checked; }
void setChecked( bool set ) { checked = set; }

Now the model will need to be modified so that the view knows about the check state:

QVariant TreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

    if ( role == Qt::CheckStateRole && index.column() == 0 )
        return static_cast< int >( item->isChecked() ? Qt::Checked : Qt::Unchecked );

    if (role != Qt::DisplayRole)
        return QVariant();

    return item->data(index.column());
}

and modify the model's flags method to let views know that the model contains checkable items:

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

    Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;

    if ( index.column() == 0 )
        flags |= Qt::ItemIsUserCheckable;

    return flags;
}

I think this should do it. If you want to be able to update the TreeItem check state when the user ticks and unpicks the items, then you'll need to provide the QAbstractItemModel::setData method in your TreeModel.

Upvotes: 25

taynaron
taynaron

Reputation: 730

I converted the above to PyQt for my own purposes and figured I'd share.

def data(self, index, role):
    if not index.isValid():
        return None

    item = index.internalPointer();

    if role == Qt.CheckStateRole and index.column() == self.check_col:
        return int( Qt.Checked if item.isChecked() else Qt.Unchecked )

    return super(TreeModel, self).data(index, role)


def flags(self, index):
    if not index.isValid():
        return None

    if index.column() == self.check_col:
        flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable
    else:
        flags = super(TreeModel, self).flags(index)

    return flags


def setData(self, index, value, role=Qt.EditRole):
    if index.column() == self.check_col:
        if role == Qt.EditRole:
            return False
        if role == Qt.CheckStateRole:
            item = self.getItem(index)
            item.setChecked(value)
            self.dataChanged.emit(index, index)
            return True

    return super(TreeModel, self).setData(index, value, role)

Upvotes: 23

Related Questions