Search code examples
pythonpython-3.xpyside2

Set SingleSelection in QListView


I am writing an application in PySide2 and I have developed a class inheriting from Qdialog to display a list with checkboxes:

The code of the class:

class ListDialog(QDialog):
    def __init__(self, items, all_checked = False, parent=None):
        super(ListDialog, self).__init__(parent=parent)
        self.setWindowTitle(title)
        form = QFormLayout(self)

        self.listView = QListView(self)
        self.listView.setSelectionMode(QTableView.NoSelection)

        form.addRow(self.listView)
        self.model = QStandardItemModel(self.listView)
        for item in items:
            # create an item with a caption
            standardItem = QStandardItem(item)
            standardItem.setCheckable(True)
            standardItem.setEditable(False)
            if all_checked:
                standardItem.setCheckState(Qt.Checked)
            self.model.appendRow(standardItem)
        self.listView.setModel(self.model)

The result (plus some extra code):

list image example

As it is, you can check multiple checkboxes, but I need to make it a single selection.

Note the line:

self.listView.setSelectionMode(QTableView.NoSelection)

At first, I thought setSelectionMode was responsible for this behaviour but this controls only the highlighting on the items of the list and not its checkboxes. Therefore I set it to NoSelection for not highlighting the text part, the checkboxes are working!

Is there an easy way to set the selection mode to single? Or should I overload the signal that controls the box checking to unselect all the boxes and then select the one I clicked?


Solution

  • An easy way to do that it to use a proxy model which will handle the single selection and the signal QStandardItemModel::itemChanged to know when user clicks on an item.

    For example:

    class SingleCheckProxyModel(QIdentityProxyModel):
        def __init__(self, model, parent=None):
            super().__init__(parent)
            model.itemChanged.connect(self.checkSingleCheck)
            self.setSourceModel(model)
            self.currentItemChecked = None
    
        def checkSingleCheck(self, item):
            if self.currentItemChecked:
                self.currentItemChecked.setCheckState(Qt.Unchecked)
            if item.checkState(): # Allows the user to uncheck then check the same item
                self.currentItemChecked = item
            else:
                self.currentItemChecked = None
    
    
    class ListDialog(QDialog):
        def __init__(self, items, all_checked = False, parent=None):
            super(ListDialog, self).__init__(parent=parent)
            self.setWindowTitle("kjnve")
            form = QFormLayout(self)
    
            self.listView = QListView(self)
            self.listView.setSelectionMode(QTableView.NoSelection)
    
            form.addRow(self.listView)
            self.model = QStandardItemModel(self.listView)
            for item in items:
                # create an item with a caption
                standardItem = QStandardItem(item)
                standardItem.setCheckable(True)
                standardItem.setEditable(False)
                if all_checked:
                    standardItem.setCheckState(Qt.Checked)
                self.model.appendRow(standardItem)
            self.listView.setModel(SingleCheckProxyModel(self.model)) # Use proxy
    

    The checkSingleCheck method will be called when the user clicks on an item. But, if you want to be able to edit the items, you have to adapt this function.