Search code examples
pyqt5popupqcomboboxqlineedit

Multi-QComboBox Hide/Show Popup on LineEdit Press (PyQT5)


I have created a checkable QComboBox where all the options are checkboxes. Everything works fine, except I can no longer click on the QLineEdit to open/close the combobox pop-up, the way a regular QComboBox would work.

I have tried to apply an event filter to the QLineEdit, as shown below, that should ideally close the combobox pop-up if it is currently open, and open it if it is currently closed. But instead, clicking on QLineEdit only opens the pop-up everytime.

I believe this is because the mouse button press (QEvent.MouseButtonPress) closes the pop-up (hence setting the self.isPopup boolean to False), so the mouse button release (QEvent.MouseButtonRelease) will always open the pop-up. I've tried to get the QCombobox to ignore the MouseButtonPress event, but to no avail. I'm not sure where I've gone wrong here - if anyone has any suggestions, it would be much appreciated.

(Here's the relevant parts of the code)

class CustomComboBox(QtWidgets.QComboBox):
    def __init__(self):
        super(CustomComboBox, self).__init__()
        self.setModel(QtGui.QStandardItemModel(self)) # setting up widget to make it checkable
        self.setEditable(True)
        self.lineEdit().setReadOnly(True)
        self.lineEdit().setPlaceholderText("--Select Option--")

        self.isPopup = False  # bool to close or open pop up 

        self.lineEdit().installEventFilter(self)  # event filter for lineedit presses

    def hidePopup(self):
        super().hidePopup()
        self.isPopup = False

    def showPopup(self):
        super().shwoPopup()
        self.isPopup = True

    def eventFilter(self, widget, event):
        if widget == self.lineEdit():
            if event.type() == QtCore.QEvent.MouseButtonRelease:
                if self.isPopup:
                    self.hidePopup()
                else:
                    self.showPopup()
                return True
            elif event.type() == QtCore.QEvent.MouseButtonPress:
                event.ignore()
                return True
        return super(CustomComboBox, self).eventFilter(widget, event)

Solution

  • The problem is caused by the fact that making the combo editable, you actually have two widgets that can handle mouse events, and since QComboBox handles mouse buttons in a specific way (to allow proper popup management) that makes things difficult, because the popup normally closes after the button press.

    Since your requirement for the editable line edit is just to write custom text, then just override the paintEvent by slightly changing the default behavior:

    class CustomComboBox(QtWidgets.QComboBox):
        customText = ''
        def setCustomText(self, text):
            if self.customText != text:
                self.customText = text
                self.update()
    
        def paintEvent(self, event):
            if not self.customText:
                super().paintEvent(event)
                return
            painter = QStylePainter(self)
            painter.setPen(self.palette().color(QPalette.Text))
            opt = QStyleOptionComboBox()
            self.initStyleOption(opt)
            painter.drawComplexControl(QStyle.CC_ComboBox, opt)
            opt.text = self.customText
            painter.drawControl(QStyle.CE_ComboBoxaLabel, opt)
    

    With the code above, you don't need to make the combo editable, and therefore there is no event filtering.