Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QComboBox editor showPopup incorrect placment of listview



  • 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_())
    

  • Banned

    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
    


  • @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


  • Banned

    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)



  • 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_())
    

Log in to reply