Voldemort
Voldemort

Reputation: 225

Call function of a different class with parameter PyQt

I want to trigger a function of a different class on press of a button. Something like this example How to emit custom Events to the Event Loop in PyQt.

But I also want to pass a parameter to that function everytime the button is clicked. How do I achieve that?

Upvotes: 0

Views: 584

Answers (1)

eyllanesc
eyllanesc

Reputation: 244202

If you want to add additional arguments you can use functools.partial:

main.py

from PyQt5 import QtCore, QtWidgets
from globalobject import GlobalObject

import functools


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        button = QtWidgets.QPushButton(text="Press me", clicked=self.on_clicked)
        self.setCentralWidget(button)

    @QtCore.pyqtSlot()
    def on_clicked(self):
        GlobalObject().dispatchEvent("hello")


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        wrapper = functools.partial(self.foo, "foo", bar="baz")
        GlobalObject().addEventListener("hello", wrapper)
        self._label = QtWidgets.QLabel()
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self._label)

    def foo(self, foo, bar=None):
        print(foo, bar)
        self._label.setText(foo)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w1 = MainWindow()
    w2 = Widget()
    w1.show()
    w2.show()
    sys.exit(app.exec_())

That logic can also be implemented in the library:

globalobject.py

from PyQt5 import QtCore
import functools


@functools.lru_cache()
class GlobalObject(QtCore.QObject):
    def __init__(self):
        super().__init__()
        self._events = {}

    def addEventListener(self, name, func, *, args=(), kwargs=None):
        kwargs = kwargs or {}
        if name not in self._events:
            self._events[name] = []
        self._events[name].append((func, args, kwargs))

    def dispatchEvent(self, name):
        functions = self._events.get(name, [])
        for func, args, kwargs in functions:
            wrapper = func
            wrapper = functools.partial(func, *args, **kwargs)
            QtCore.QTimer.singleShot(0, wrapper)

main.py

from PyQt5 import QtCore, QtWidgets
from globalobject import GlobalObject


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        button = QtWidgets.QPushButton(text="Press me", clicked=self.on_clicked)
        self.setCentralWidget(button)

    @QtCore.pyqtSlot()
    def on_clicked(self):
        GlobalObject().dispatchEvent("hello")


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        GlobalObject().addEventListener(
            "hello", self.foo, args=("foo",), kwargs={"bar": "baz"}
        )
        self._label = QtWidgets.QLabel()
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self._label)

    def foo(self, foo, bar=None):
        print(foo, bar)
        self._label.setText(foo)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w1 = MainWindow()
    w2 = Widget()
    w1.show()
    w2.show()
    sys.exit(app.exec_())

Update:

If you want to send arguments through the dispatchEvent method then you should use the following:

globalobject.py

from PyQt5 import QtCore
import functools


@functools.lru_cache()
class GlobalObject(QtCore.QObject):
    def __init__(self):
        super().__init__()
        self._events = {}

    def addEventListener(self, name, func):
        if name not in self._events:
            self._events[name] = []
        self._events[name].append(func)

    def dispatchEvent(self, name, *, args=(), kwargs=None):
        kwargs = kwargs or {}
        functions = self._events.get(name, [])
        for func in functions:
            wrapper = func
            wrapper = functools.partial(func, *args, **kwargs)
            QtCore.QTimer.singleShot(0, wrapper)

main.py

from PyQt5 import QtCore, QtWidgets
from globalobject import GlobalObject


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        button = QtWidgets.QPushButton(text="Press me", clicked=self.on_clicked)
        self.setCentralWidget(button)
        self.counter = 0

    @QtCore.pyqtSlot()
    def on_clicked(self):
        self.counter += 1
        GlobalObject().dispatchEvent("hello", args=(self.counter,))


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        GlobalObject().addEventListener("hello", self.foo)
        self._label = QtWidgets.QLabel()
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self._label)

    def foo(self, x):
        print(x)
        self._label.setNum(x)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w1 = MainWindow()
    w2 = Widget()
    w1.show()
    w2.show()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions