Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Forum Updated on Feb 6th

    Unsolved QComboBox editor showPopup incorrect placment of listview

    Qt for Python
    2
    5
    503
    Loading More Posts
    • 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.
    • alom
      alom last edited by

      I have a custom delegate that has a QComboBox as an editor for a tableview. I would like the ComboBox to auto expand and show the list view when the user double clicks the cell. Currently its double click to initiate the editor and then another click to expand to show the pop up.

      I found the ComboBox has a .showPopup(), but when I use this, it provides the behavior of only using two clicks to expand the editor BUT always places the popup under the first cell upon the first edit event of the cell.

      Is the creatEditor() the wrong place to call the showPopup? Or should I use some signal or editHint instead? Feels like Qt doesn't have all the information it needs and places the popup under the first cell

      On windows 10, Qt 5.13

      from PySide2 import QtGui, QtCore, QtWidgets
      import sys
      
      
      class CustomTableDelegate(QtWidgets.QStyledItemDelegate):
      
          def __init__(self, parent=None):
              super(CustomTableDelegate, self).__init__(parent)
      
          def createEditor(self, parent, option, index):
              """Create the ComboBox editor view."""
              if index.column() == 0:
                  editor = QtWidgets.QComboBox(parent)
                  editor.addItems(['v001', 'v002', 'v003'])
                  # !! this causes the pop up in incorrect place
                  editor.showPopup()
                  editor.activated.connect(self.commitAndCloseEditor)
                  return editor
      
          def setEditorData(self, editor, index):
              """Set the ComboBox's current index."""
              value = index.data(QtCore.Qt.DisplayRole)
              i = editor.findText(value)
              if i == -1:
                  i = 0
              editor.setCurrentIndex(i)
      
          def commitAndCloseEditor(self):
              editor = self.sender()
              self.commitData.emit(editor)
              self.closeEditor.emit(editor, QtWidgets.QStyledItemDelegate.NoHint)
      
          def setModelData(self, editor, model, index):
              """Set the table's model's data when finished editing."""
              value = editor.currentText()
              model.setData(index, value)
      
      if __name__ == '__main__':
          app = QtWidgets.QApplication([])
          data = [["v001", "stuff", "fixing source", '2020-01-14', ''],
                  ["v002", "more stuff", "currently broken", '2020-06-30', ''],
                  ["v003", "too much stuff", "scaling issues", '2020-02-08', ''],
                  ["v088", "still missing stuff", "not coment", '2020-11-13', ''], ]
      
          table_view = QtWidgets.QTableView()
          table_view.setItemDelegate(CustomTableDelegate(table_view))
          model = QtGui.QStandardItemModel()
          model.setHorizontalHeaderLabels(['version', 'file', 'comment', 'date', ''])
          table_view.setModel(model)
          for r in range(len(data)):
              for c in range(5):
                  model.setItem(r, c, QtGui.QStandardItem(data[r][c]))
          table_view.show()
          sys.exit(app.exec_())
      
      1 Reply Last reply Reply Quote 0
      • Denni 0
        Denni 0 Banned last edited by Denni 0

        Okay first I adjusted your program to be a bit more python-pyqt-ish -- and after doing that I found that the "ComboBox" gets placed correctly it is just the .showPopup() that was miss placing it -- this says to me that you should not be using .showPopup() event but the event that gets triggered when you click that down arrow for the ComboBox -- here is the adjusted code

        from PySide2.QtCore    import *
        from PySide2.QtGui     import *
        from PySide2.QtWidgets import *
        
        class CustomTableDelegate(QStyledItemDelegate):
            def __init__(self):
                QStyledItemDelegate.__init__(self)
                pass
        
            def createEditor(self, parent, option, index):
                """Create the ComboBox editor view."""
                if index.column() == 0:
                    editor = QComboBox(parent)
                    editor.addItems(['v001', 'v002', 'v003'])
                    # !! this causes the pop up in incorrect place
        #            editor.showPopup()
                    editor.activated.connect(self.commitAndCloseEditor)
                    return editor
        
            def setEditorData(self, editor, index):
                """Set the ComboBox's current index."""
                value = index.data(Qt.DisplayRole)
                i = editor.findText(value)
                if i == -1:
                    i = 0
                editor.setCurrentIndex(i)
        
            @Slot()
            def commitAndCloseEditor(self):
                editor = self.sender()
                self.commitData.emit(editor)
                self.closeEditor.emit(editor, QStyledItemDelegate.NoHint)
        
            def setModelData(self, editor, model, index):
                """Set the table's model's data when finished editing."""
                value = editor.currentText()
                model.setData(index, value)
        
        class CstmTableView(QTableView):
            def __init__(self):
                QTableView.__init__(self)
                self.resize(450, 200)
        
                data = [["v001", "stuff", "fixing source", '2020-01-14'],
                        ["v002", "more stuff", "currently broken", '2020-06-30'],
                        ["v003", "too much stuff", "scaling issues", '2020-02-08'],
                        ["v088", "still missing stuff", "not comment", '2020-11-13'], ]
        
                self.CstmTblDel = CustomTableDelegate()
                self.setItemDelegate(self.CstmTblDel)
        
                model = QStandardItemModel()
                model.setHorizontalHeaderLabels(['Version', 'File', 'Comment', 'Date'])
                self.setModel(model)
        
                for row in range(len(data)):
                    for col in range(4):
                        model.setItem(row, col, QStandardItem(data[row][col]))
        
        if __name__ == '__main__':
            MainEvntHndlr = QApplication([])
        
            MainApp = CstmTableView()
            MainApp.show()
        
            MainEvntHndlr.exec()
        
          # If anyone wants more extensive free help I run an online lab-like classroom-like 
          # message server feel free and drop by you will not be able to post until I clear 
          # you as a student as this prevents spammers so if interested here is the invite
          # https://discord.gg/3D8huKC
        
        1 Reply Last reply Reply Quote 0
        • alom
          alom last edited by alom

          @Denni-0
          Sorry does your code have the event to trigger the dropdow instead of the .showPopup?

          Also you decorated the commitAndCloseEditor(), what is the purpose of that? and did you mean to use "@Slot" instead? I thought @pyqtslot is a pyqt5 decorator.
          https://www.learnpyqt.com/blog/pyqt5-vs-pyside2/

          Cheers

          1 Reply Last reply Reply Quote 0
          • Denni 0
            Denni 0 Banned last edited by Denni 0

            oh sorry yeah I do pyqt5 forgot about the pyqtSlot being only Slot for PySide2 fixed that in the above code

            Next no I did not supply the code to auto-drop-down your combobox list as I figured since I fixed your problem and I pointed it out you would be able to implement that aspect if you wanted it

            The decorator is something you should use in the case of all function/methods that are associated with a .connection( ) as the .connect( ) is a Signal and while you can get by without using Slots doing so can create hidden issues within your code later on so it always best to be explicit -- it is not like that little one-liner is all that difficult to include so do yourself a favor and start including it always (aka do not develop lazy coder habits as those are only problematic)

            1 Reply Last reply Reply Quote 0
            • alom
              alom last edited by

              no worries,

              So I was able to try an event filter and test if a HoverEnter event happened, Then trigger the show pop up. Seems to solve the placement issue, not sure if this is the proper way of handling this,but gets the job done so far.

              from PySide2 import QtGui, QtCore, QtWidgets
              import sys
              
              
              class CustomTableDelegate(QtWidgets.QStyledItemDelegate):
              
                  def __init__(self, parent=None):
                      super(CustomTableDelegate, self).__init__(parent)
              
                  def createEditor(self, parent, option, index):
                      """Create the ComboBox editor view."""
                      if index.column() == 0:
                          editor = QtWidgets.QComboBox(parent)
                          editor.addItems(['v001', 'v002', 'v003'])
                          editor.installEventFilter(self)
                          editor.activated.connect(self.commitAndCloseEditor)
                          return editor
              
                  def setEditorData(self, editor, index):
                      """Set the ComboBox's current index."""
                      value = index.data(QtCore.Qt.DisplayRole)
                      i = editor.findText(value)
                      if i == -1:
                          i = 0
                      editor.setCurrentIndex(i)
              
                  def commitAndCloseEditor(self):
                      editor = self.sender()
                      self.commitData.emit(editor)
                      self.closeEditor.emit(editor, QtWidgets.QStyledItemDelegate.NoHint)
              
                  def setModelData(self, editor, model, index):
                      """Set the table's model's data when finished editing."""
                      value = editor.currentText()
                      model.setData(index, value)
              
                  def eventFilter(self, p_object, event):
                      if event.type() == QtCore.QEvent.Type.HoverEnter:
                          p_object.showPopup()
                          return True
                      else:
                          return False
              if __name__ == '__main__':
                  app = QtWidgets.QApplication([])
                  data = [["v001", "stuff", "fixing source", '2020-01-14', ''],
                          ["v002", "more stuff", "currently broken", '2020-06-30', ''],
                          ["v003", "too much stuff", "scaling issues", '2020-02-08', ''],
                          ["v088", "still missing stuff", "not coment", '2020-11-13', ''], ]
              
                  table_view = QtWidgets.QTableView()
                  table_view.setItemDelegate(CustomTableDelegate(table_view))
                  model = QtGui.QStandardItemModel()
                  model.setHorizontalHeaderLabels(['version', 'file', 'comment', 'date', ''])
                  table_view.setModel(model)
                  for r in range(len(data)):
                      for c in range(5):
                          model.setItem(r, c, QtGui.QStandardItem(data[r][c]))
                  table_view.show()
                  sys.exit(app.exec_())
              
              1 Reply Last reply Reply Quote 0
              • First post
                Last post