Search code examples
pythonpyqtpyqt5pyside2

Override a Qt MouseEvent but only if class variable is True


I have QComboBox that I want the user to be able to lock the index of. To do this I subclassed QComboBox and overrode the mousePressEvent to be empty, but inside a method for QComboBox.

Two problems with what I tried:

  • I can't figure out how to re-define mousePressEvent inside another class method
  • I don't know how to restore the original mousePressEvent behavior of a QComboBox if a certain condition is not met. My code:
class MyComboBox(QtWidgets.QComboBox):
    def __init__(self, *args, **kwargs):
        super(self.__class__, self).__init__(*args, **kwargs)

    def lockSelection(self, bool=None):
        if bool:
            def mousePressEvent(self, *args, **kwargs):
                pass
        else:
            def mousePressEvent(self, *args, **kwargs):
                return QtWidgets.QComboBox.mousePressEvent(*args, **kwargs)

..... later

## I want this to get my overriden mousePressEvent 

MyComboBox.lockSelection(True)

## I want this to get the original mousePressEvent

MyComboBox.lockSelection(False)

Solution

  • If you want to block an inherited method you just have to override the method and avoid calling the method when necessary:

    from PyQt5 import QtWidgets
    
    
    class MyComboBox(QtWidgets.QComboBox):
        def lockSelection(self, lock):
            self._lock_selection = lock
    
        def mousePressEvent(self, event):
            lock = getattr(self, "_lock_selection", False)
            if not lock:
                super(MyComboBox, self).mousePressEvent(event)
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
    
        w = QtWidgets.QWidget()
    
        combo = MyComboBox()
        combo.addItems(list("ABCD"))
    
        radio = QtWidgets.QCheckBox("Lock")
        radio.toggled.connect(combo.lockSelection)
    
        lay = QtWidgets.QHBoxLayout(w)
        lay.addWidget(radio)
        lay.addWidget(combo)
        w.resize(320, 60)
        w.show()
        sys.exit(app.exec_())
    

    If you want to avoid inheritance you can implement a locker using an event filter so you can use it in any widget:

    from PyQt5 import QtCore, QtWidgets
    
    
    class MouseLocker(QtCore.QObject):
        def __init__(self, widget):
            super(MouseLocker, self).__init__(widget)
            widget.installEventFilter(self)
            self._lock_selection = False
    
        def lockSelection(self, lock):
            self._lock_selection = lock
    
        def eventFilter(self, obj, event):
            if obj is self.parent() and event.type() == QtCore.QEvent.MouseButtonPress:
                return self._lock_selection
            return super(MouseLocker, self).eventFilter(obj, event)
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
    
        w = QtWidgets.QWidget()
    
        combo = QtWidgets.QComboBox()
        combo.addItems(list("ABCD"))
        locker = MouseLocker(combo)
    
        radio = QtWidgets.QCheckBox("Lock")
        radio.toggled.connect(locker.lockSelection)
    
        lay = QtWidgets.QHBoxLayout(w)
        lay.addWidget(radio)
        lay.addWidget(combo)
        w.resize(320, 60)
        w.show()
        sys.exit(app.exec_())