Jeka
Jeka

Reputation: 1384

QTreeView disable selection on some rows

I Have a JSON model, and I populate it with QTreeView:

*-group1
| |  
| *-item1     value1
| |
| *-item2     value2
|
*-group2
  |
  *-item4     value3

Now I want to disable selection for groups, so that user can select only rows with items. And I want to achive it without modification of model.

Upvotes: 6

Views: 3062

Answers (3)

Edward Spencer
Edward Spencer

Reputation: 649

I wanted a simple solution that used the QTreeWidget out of the box, and I didn't have much luck with @Evgeny 's solution but I had more success with the itemSelectionChanged signal. Below is a minimum working example of what I ended up doing - apologies it's not in C++, I am a PySide Pleb :)

class MyWidget(QWidget):

    def setup_tree(self):

        # a basic tree widget, no further jiggery pokery required.
        self.tree = QTreeWidget()

        # allows multiple selection with ctrl+click, shift+click etc.
        self.tree.setSelectionMode(QTreeWidget.ExtendedSelection)  

        # this is where the magic happens
        self.tree.itemSelectionChanged.connect(self.on_item_selection_changed)

        # add the tree to layouts etc. as normal

    def on_item_selection_changed(self)

        selected = self.tree.selectedItems()

        # filter out nodes we consider un-selectable, in my case anything with data 
        # on the user role, but this could be changed to any criteria
        valid = list(filter(lambda item: item.data(0, Qt.UserRole), selected))

        # if there's no invalid selections, then we're done
        if len(valid) == len(selected):
            return

        # block signals while updating selection 
        # so we don't trigger itemSelectionChanged again before we're done
        self.tree.blockSignals(True)

        self.tree.clearSelection()
        for item in valid:
            item.setSelected(True)

        self.tree.blockSignals(False)

Upvotes: 0

Frank Osterfeld
Frank Osterfeld

Reputation: 25155

Use a proxy model such as QIdentityProxyModel and reimplement QAbstractItemModel::flags(), removing the Qt::ItemIsSelectable flag for the group items:

Qt::ItemFlags DisableGroupProxyModel::flags(const QModelIndex& index) const {
   const auto flags = QIdentityProxyModel::flags(index);
   if (index is group) {
       return flags & ~Qt::ItemIsSelectable;
   }

   return flags;
}

Then set the original (unmodified) model as source model of this proxy model and the proxy model instance as the tree view’s model:

DisableGroupProxyModel* proxy = new DisableGroupProxyModel(this);
proxy->setSourceModel(originalModel);
treeView->setModel(proxy);

Upvotes: 3

Evgeny
Evgeny

Reputation: 4010

This can be done with QItemSelectionModel. You can get selection model with

treeView->selectionModel();

Then connect to model's signal

void currentRowChanged(const QModelIndex &current, const QModelIndex &previous)

and inside the connected slot check if new index is group or not and if group just select previous model index.

Upvotes: 1

Related Questions