Search code examples
pythonpython-3.xpyqtpyqt5qcombobox

How to intercept a mouse signal in a QComboBox


I have a custom combo box placed on a QDialog widget, and I can not catch any of the mouse signals. I sub classed QComboBox to intercept two signals not provided by QComboBox: LostFocusEvent and mouseDobleClickEvent. LostFocusEvent works well but the combo is not firing the mouse events. I need three signals on the combo box and only one suitable is provided.

I tried to set combo.grabMouse(), disregarding the documentation warnings, and the combo.doubleClicked start working, but all other widgets connected through signals start behaving erratically. Also tried combo.view().doubleClick.connect with similar results. Also I tried other mouse events with similar results (Press- Release- etc) Finally, I tried to to use event instead of QMouseEvent in the comboBox sub class, but it's intercepted by the focusOutEvent slot. Mouse event work on QPushButtons including double click on QTableView widgets Using Windows 8 Python 3.7 PyQt5.

`class Agreement(QDialog):
    def __init__(self,db, address, parent=None):
        super().__init__(parent= None)
        self.parent = parent
.......................................

    def setUi(self):
    .....................................
        self.comboSupplier = ComboFocus.FocusCombo(self)
        self.comboSupplier.setMaximumSize(220,30)
        self.comboSupplier.setEditable(True)
        #self.comboSupplier.grabMouse()
        self.comboSupplier.activated.connect(self.supplierChange)
        self.comboSupplier.focusLost.connect(self.supplierFocusLost)
        self.comboSupplier.doubleClicked.connect(self.editContact)
    ...........................................

     def supplierChange(self):
        try:
            row = self.comboSupplier.currentIndex()
            idx = self.comboSupplier.model().index(row,0)
            self.supplierId = self.comboSupplier.model().data(idx)
            self.agreementTitle[0] = self.comboSupplier.currentText()
            self.setAgreementTitle()
            self.okToSave[2] = int(self.supplierId)
            self.okSaving()
        except TypeError as err:
            print('supplierChange' + type(err).__name__ + ' ' + err.args[0])

    @pyqtSlot()
    def editContact(self):
        try:
            c = Contacts(self.db,self.comboSupplier.currentText(), 
                APM.OPEN_EDIT_ONE, self.supplierId,parent=self)
            c.show()
            c.exec()
        except Exception as err:
            print(type(err).__name__, err-args)

    @pyqtSlot(ComboFocus.FocusCombo)
    def supplierFocusLost(self, combo):
        try:
            self.setFocusPolicy(Qt.NoFocus)
            name = combo.currentText()
            if combo.findText(name) > -1:
                return
       ........................................

class FocusCombo(QComboBox):
    focusLost = pyqtSignal(QComboBox)
    focusGot = pyqtSignal(QComboBox)
    doubleClicked = pyqtSignal(QComboBox)

    def __init__(self, parent = None):
        super().__init__(parent)
        self.parent = parent

    def mouseDoubleClickEvent(self,event=QMouseEvent.MouseButtonDblClick):
        print("double click detected")

        self.doubleClicked.emit(self)

    def focusOutEvent(self, event):
        if event.gotFocus():
            self.focusGot.emit(self)

        elif event.lostFocus():
            self.focusLost.emit(self)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    cb = FocusCombo()
    cb.show()
    app.exec_()
    sys.exit(app.exec_())

I'd like to double click on the comboBox to open a widget to edit the contact attributes on the fly.


Solution

  • When you set QLineEdit to editable, a QLineEdit is added, so to track your events you must use an eventFilter:

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class FocusCombo(QtWidgets.QComboBox):
        focusLost = QtCore.pyqtSignal(QtWidgets.QComboBox)
        focusGot = QtCore.pyqtSignal(QtWidgets.QComboBox)
        doubleClicked = QtCore.pyqtSignal(QtWidgets.QComboBox)
    
        def setEditable(self, editable):
            super(FocusCombo, self).setEditable(editable)
            if self.lineEdit() is not None:
                self.lineEdit().installEventFilter(self)
    
        def eventFilter(self, obj, event):
            if obj is self.lineEdit():
                if event.type() == QtCore.QEvent.MouseButtonDblClick:
                    self.doubleClicked.emit(self)
                """elif event.type() == QtCore.QEvent.MouseButtonPress:
                    print("press")
                elif event.type() == QtCore.QEvent.MouseButtonRelease:
                    print("release")"""
            return super(FocusCombo, self).eventFilter(obj, event)
    
        def mouseDoubleClickEvent(self,event):
            print("double click detected")
            self.doubleClicked.emit(self)
            super(FocusCombo, self).mouseDoubleClickEvent(event)
    
        def focusOutEvent(self, event):
            if event.gotFocus():
                self.focusGot.emit(self)
    
            elif event.lostFocus():
                self.focusLost.emit(self)
            super(FocusCombo, self).focusOutEvent(event)
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        cb = FocusCombo()
        cb.addItems(list("abcdef"))
        cb.setEditable(True)
        cb.doubleClicked.connect(print)
        cb.show()
        sys.exit(app.exec_())