Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding event handlers in PyQT

Using PyQt5, there are certain event handlers which I can bind to my object, and others that only work if I implement them as methods. changeEvent and event are examples of the later type.

You can see in my example below that I can add a keyPressEvent handler to my widget programmatically, but I can not do the same thing for changeEvent.

from PyQt5 import QtGui, QtWidgets, QtCore

import types

def keyPressEvent(self, key: QtGui.QKeyEvent) -> None:
    #works
    print(key.isAutoRepeat())

def changeEvent(self, a0: QtCore.QEvent) -> None:
    #doesn't work
    print("bound change event", a0.type())


bindable = [keyPressEvent, changeEvent]

def bind_key_functions(target):
    for bound in bindable:
        setattr(target, bound.__name__, types.MethodType(bound, target))

class my_widget(QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("w1")
        bind_key_functions(self)


class my_widget2(QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("w2")

    def changeEvent(self, a0: QtCore.QEvent) -> None:
        #this does work
        print("derived change event", a0.type())

app = QtWidgets.QApplication([])
mw1 = my_widget()
mw1.show()
mw2 = my_widget2()
mw2.show()
app.exec_()

What makes the changeEvent different? how can I force it to behave as I want?

like image 339
Techniquab Avatar asked Oct 29 '25 02:10

Techniquab


1 Answers

Using setattr to override methods is a bad choice as it is not very elegant and if you want to listen to the events of another QWidget then it is better to use an event filter.

from PyQt5 import QtGui, QtWidgets, QtCore


class Binder(QtCore.QObject):
    def __init__(self, qobject):
        super().__init__(qobject)
        self._qobject = qobject
        self.qobject.installEventFilter(self)

    @property
    def qobject(self):
        return self._qobject

    def eventFilter(self, obj, event):
        if self.qobject is obj:
            print(event.type(), event)

        return super().eventFilter(obj, event)


class My_Widget(QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("w1")
        Binder(self)


class My_Widget2(QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("w2")


app = QtWidgets.QApplication([])
mw1 = My_Widget()
mw1.show()
mw2 = My_Widget2()
mw2.show()
app.exec_()

On the other hand, it is not documented which methods can be assigned or not, so if you want to find the reason you must analyze the source code of sip and pyqt5. What little is pointed out is that PyQt5 creates a cache of the methods (it is not known when or what methods are stored in the cache).

like image 183
eyllanesc Avatar answered Oct 30 '25 17:10

eyllanesc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!