Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QComboBox with checkboxes
Forum Updated to NodeBB v4.3 + New Features

QComboBox with checkboxes

Scheduled Pinned Locked Moved Unsolved General and Desktop
7 Posts 5 Posters 5.1k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    voltron
    wrote on last edited by
    #1

    I need a combobox which allows user to select multiple items. I created custom model and QComboBox subclass which uses that model. Here is my code

    class CheckableItemsModel(QStandardItemModel):
    
        checkStateChanged = pyqtSignal()
    
        def __init__(self, parent=None):
            super(CheckableItemsModel, self).__init__(parent)
    
        def flags(self, index):
            return super(CheckableItemsModel, self).flags(index) | Qt.ItemIsUserCheckable
    
        def data(self, index, role=Qt.DisplayRole):
            value = super(CheckableItemsModel, self).data(index, role)
            if index.isValid() and role == Qt.CheckStateRole and value is None:
                value = Qt.Unchecked
            return value
    
        def setData(self, index, value, role=Qt.EditRole):
            ok = super(CheckableItemsModel, self).setData(index, value, role)
            if ok and role == Qt.CheckStateRole:
                self.checkStateChanged.emit()
    
            return ok
    
    
    class CheckComboBox(QComboBox):
    
        checkedItemsChanged = pyqtSignal(list)
    
        def __init__(self, parent=None):
            super(CheckComboBox, self).__init__(parent)
    
            # workaround for Mac and GTK to show checkboxes
            self.setStyleSheet('QComboBox { combobox-popup: 1px }')
    
            self.defaultText = ''
            self.separator = ','
            self.containerPressed = False
    
            self.checkableModel = CheckableItemsModel(self)
            self.setModel(self.checkableModel)
    
            self.model().checkStateChanged.connect(self.updateCheckedItems)
            self.model().rowsInserted.connect(self.updateCheckedItems)
            self.model().rowsRemoved.connect(self.updateCheckedItems)
    
            self.activated.connect(self.toggleCheckState)
    
        def itemCheckState(self, index):
            return self.itemData(index, Qt.CheckStateRole)
    
        def setItemCheckState(self, index, state):
            self.setItemData(index, state, Qt.CheckStateRole)
    
        def checkedItems(self):
            items = list()
            if self.model():
                index = self.model().index(0, self.modelColumn(), self.rootModelIndex())
                indexes = self.model().match(index, Qt.CheckStateRole, Qt.Checked, -1, Qt.MatchExactly)
                for i in indexes:
                    items.append(index.data())
    
            return items
    
        def setCheckedItems(self, items):
            for i in items:
                index = self.findText(i)
                self.setItemCheckState(index, Qt.Checked if index != -1 else Qt.Unchecked)
    
        def updateCheckedItems(self):
            items = self.checkedItems()
            if len(items) == 0:
                self.setEditText(self.defaultText)
            else:
                self.setEditText(self.separator.join(items))
    
            self.checkedItemsChanged.emit(items)
    
        def toggleCheckState(self, index):
            value = self.itemData(index, Qt.CheckStateRole)
            if value is not None:
                self.setItemData(index, Qt.Checked if value == Qt.Unchecked else Qt.Unchecked, Qt.CheckStateRole)
    

    With this code I have combobox with checkable items. But there are two problems:

    • I can't select multiple items. When I click (check) one item, dropdown list closed. I suspect that some event filtering should help.
    • in Mac and GTK environments checkboxes are not visible

    Any ideas how to fix these two issues? Thanks in advance.

    jsulmJ 1 Reply Last reply
    0
    • V voltron

      I need a combobox which allows user to select multiple items. I created custom model and QComboBox subclass which uses that model. Here is my code

      class CheckableItemsModel(QStandardItemModel):
      
          checkStateChanged = pyqtSignal()
      
          def __init__(self, parent=None):
              super(CheckableItemsModel, self).__init__(parent)
      
          def flags(self, index):
              return super(CheckableItemsModel, self).flags(index) | Qt.ItemIsUserCheckable
      
          def data(self, index, role=Qt.DisplayRole):
              value = super(CheckableItemsModel, self).data(index, role)
              if index.isValid() and role == Qt.CheckStateRole and value is None:
                  value = Qt.Unchecked
              return value
      
          def setData(self, index, value, role=Qt.EditRole):
              ok = super(CheckableItemsModel, self).setData(index, value, role)
              if ok and role == Qt.CheckStateRole:
                  self.checkStateChanged.emit()
      
              return ok
      
      
      class CheckComboBox(QComboBox):
      
          checkedItemsChanged = pyqtSignal(list)
      
          def __init__(self, parent=None):
              super(CheckComboBox, self).__init__(parent)
      
              # workaround for Mac and GTK to show checkboxes
              self.setStyleSheet('QComboBox { combobox-popup: 1px }')
      
              self.defaultText = ''
              self.separator = ','
              self.containerPressed = False
      
              self.checkableModel = CheckableItemsModel(self)
              self.setModel(self.checkableModel)
      
              self.model().checkStateChanged.connect(self.updateCheckedItems)
              self.model().rowsInserted.connect(self.updateCheckedItems)
              self.model().rowsRemoved.connect(self.updateCheckedItems)
      
              self.activated.connect(self.toggleCheckState)
      
          def itemCheckState(self, index):
              return self.itemData(index, Qt.CheckStateRole)
      
          def setItemCheckState(self, index, state):
              self.setItemData(index, state, Qt.CheckStateRole)
      
          def checkedItems(self):
              items = list()
              if self.model():
                  index = self.model().index(0, self.modelColumn(), self.rootModelIndex())
                  indexes = self.model().match(index, Qt.CheckStateRole, Qt.Checked, -1, Qt.MatchExactly)
                  for i in indexes:
                      items.append(index.data())
      
              return items
      
          def setCheckedItems(self, items):
              for i in items:
                  index = self.findText(i)
                  self.setItemCheckState(index, Qt.Checked if index != -1 else Qt.Unchecked)
      
          def updateCheckedItems(self):
              items = self.checkedItems()
              if len(items) == 0:
                  self.setEditText(self.defaultText)
              else:
                  self.setEditText(self.separator.join(items))
      
              self.checkedItemsChanged.emit(items)
      
          def toggleCheckState(self, index):
              value = self.itemData(index, Qt.CheckStateRole)
              if value is not None:
                  self.setItemData(index, Qt.Checked if value == Qt.Unchecked else Qt.Unchecked, Qt.CheckStateRole)
      

      With this code I have combobox with checkable items. But there are two problems:

      • I can't select multiple items. When I click (check) one item, dropdown list closed. I suspect that some event filtering should help.
      • in Mac and GTK environments checkboxes are not visible

      Any ideas how to fix these two issues? Thanks in advance.

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @voltron For such a use case it would be better to use a QListBox

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      2
      • V Offline
        V Offline
        voltron
        wrote on last edited by
        #3

        @jsulm I know about QListWidget/QListView, but I need combobox with multiple selection not QListWidget/QListView. If I could use them, I definitely won't bother with developing custom widget.

        J.HilkJ E 2 Replies Last reply
        0
        • V voltron

          @jsulm I know about QListWidget/QListView, but I need combobox with multiple selection not QListWidget/QListView. If I could use them, I definitely won't bother with developing custom widget.

          J.HilkJ Offline
          J.HilkJ Offline
          J.Hilk
          Moderators
          wrote on last edited by
          #4

          @voltron

          the problem is the Activated Signal that is emited as soon as you select a new entry from the Combobox. You would have to overwrite the functions that emit the signal and I believe most of those are private.

          The simples solution is to block the Signals and somehow define your own "I'm done with selection"-condition.


          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


          Q: What's that?
          A: It's blue light.
          Q: What does it do?
          A: It turns blue.

          V 1 Reply Last reply
          0
          • V voltron

            @jsulm I know about QListWidget/QListView, but I need combobox with multiple selection not QListWidget/QListView. If I could use them, I definitely won't bother with developing custom widget.

            E Offline
            E Offline
            Eeli K
            wrote on last edited by
            #5

            @voltron You decide, of course, but the purpose of combobox is to let the user select one option out of many. User interface controls should not be used against their intended meaning to ensure consistent user experience and intuitiveness. Such selection should be done with a button and popup menu or something like that. In a combobox the selected item is visible in the basic state and it's not possible with multiple selection.

            1 Reply Last reply
            3
            • J.HilkJ J.Hilk

              @voltron

              the problem is the Activated Signal that is emited as soon as you select a new entry from the Combobox. You would have to overwrite the functions that emit the signal and I believe most of those are private.

              The simples solution is to block the Signals and somehow define your own "I'm done with selection"-condition.

              V Offline
              V Offline
              voltron
              wrote on last edited by
              #6

              @J.Hilk thanks for the hint. I implemented event filter, so popup does not closed after selecting single item, updated code below. But still no luck with multiple selection, seems something wrong with model too.

              class CheckableItemsModel(QStandardItemModel):
              
                  checkStateChanged = pyqtSignal()
              
                  def __init__(self, parent=None):
                      super(CheckableItemsModel, self).__init__(parent)
              
                  def flags(self, index):
                      return super(CheckableItemsModel, self).flags(index) | Qt.ItemIsUserCheckable
              
                  def data(self, index, role=Qt.DisplayRole):
                      value = super(CheckableItemsModel, self).data(index, role)
                      if index.isValid() and role == Qt.CheckStateRole and value is None:
                          value = Qt.Unchecked
                      return value
              
                  def setData(self, index, value, role=Qt.EditRole):
                      ok = super(CheckableItemsModel, self).setData(index, value, role)
                      if ok and role == Qt.CheckStateRole:
                          self.checkStateChanged.emit()
              
                      return ok
              
              
              class CheckComboBox(QComboBox):
              
                  checkedItemsChanged = pyqtSignal(list)
              
                  def __init__(self, parent=None):
                      super(CheckComboBox, self).__init__(parent)
              
                      self.defaultText = ''
                      self.separator = ','
                      self.containerMousePress = False
              
                      self.checkableModel = CheckableItemsModel(self)
                      self.setModel(self.checkableModel)
              
                      self.model().checkStateChanged.connect(self.updateCheckedItems)
                      self.model().rowsInserted.connect(self.updateCheckedItems)
                      self.model().rowsRemoved.connect(self.updateCheckedItems)
              
                      self.activated.connect(self.toggleCheckState)
              
                  def itemCheckState(self, index):
                      return self.itemData(index, Qt.CheckStateRole)
              
                  def setItemCheckState(self, index, state):
                      self.setItemData(index, state, Qt.CheckStateRole)
              
                  def checkedItems(self):
                      items = list()
                      if self.model():
                          index = self.model().index(0, self.modelColumn(), self.rootModelIndex())
                          indexes = self.model().match(index, Qt.CheckStateRole, Qt.Checked, -1, Qt.MatchExactly)
                          for i in indexes:
                              items.append(index.data())
              
                      return items
              
                  def setCheckedItems(self, items):
                      for i in items:
                          index = self.findText(i)
                          self.setItemCheckState(index, Qt.Checked if index != -1 else Qt.Unchecked)
              
                  def updateCheckedItems(self):
                      items = self.checkedItems()
                      if len(items) == 0:
                          self.setEditText(self.defaultText)
                      else:
                          self.setEditText(self.separator.join(items))
              
                      self.checkedItemsChanged.emit(items)
              
                  def toggleCheckState(self, index):
                      value = self.itemData(index, Qt.CheckStateRole)
                      if value is not None:
                          self.setItemData(index, Qt.Checked if value == Qt.Unchecked else Qt.Unchecked, Qt.CheckStateRole)
              
                  def eventFilter(self, receiver, event):
                      eventType = event.type()
                      if eventType in [QEvent.KeyPress, QEvent.KeyRelease]:
                          if receiver == self and event.key() in [Qt.Key_Up, Qt.Key_Down]:
                              self.showPopup()
                              return True
                          elif event.key() in [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape]:
                              self.hidePopup()
                              if event.key() != Qt.Key_Escape:
                                  return True
                      elif eventType == QEvent.MouseButtonPress:
                          self.containerMousePress = receiver == self.view().window()
                      elif eventType == QEvent.MouseButtonRelease:
                          self.containerMousePress = False
              
                      return False
              
                  def hidePopup(self):
                      if self.containerMousePress:
                          super(CheckComboBox, self).hidePopup()
              
              
              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by
                #7

                Hi,

                QComboBox uses a QAbstractItemView based widget for its view so you can modify the selection mode property of it.

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                1 Reply Last reply
                1

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved