0

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?

Voldemort
  • 175
  • 5
  • 20
  • 2
    Please provide a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of what you've got so far. – musicamante Jul 28 '20 at 16:32
  • @musicamante, please refer to the link in the question. I just want to know how to pass a parameter to the called function. – Voldemort Jul 28 '20 at 16:35

1 Answers1

2

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_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 1
    actually sir I want to pass a single parameter which will be a variable int value through ```dispatchEvent()``` in ```on_clicked``` function and want to catch that parameter in ```addEventListener()```. In your example, I can print only a fixed parameter ```foo baz``` in this case. Can you please edit the code. – Voldemort Jul 28 '20 at 17:35
  • @deepanshu I am not going to edit my answer since it is a generic example that you don't seem to understand, but I am going to point out that you must modify to obtain what you want: 1) use `GlobalObject().addEventListener("hello", self.foo, args=(5,))` and 2) `def foo(self, x): print(x)` – eyllanesc Jul 28 '20 at 17:39
  • Sir I just wanted to say that in this example everytime when I click the button, the same value is printed. But I want to print different values. If you don't want to edit then it's completely fine. – Voldemort Jul 28 '20 at 17:50
  • 1
    @deepanshu I think I understand what you want, you want dispatchEvent to accept arguments, am I correct? – eyllanesc Jul 28 '20 at 17:54
  • 1
    yes sir that's exactly what I want. I want the dispatchEvent to capture the arguments and pass it to addEventListener. Some sort of a message passing behaviour. – Voldemort Jul 28 '20 at 17:57
  • exactly what I wanted. Thank you! – Voldemort Jul 28 '20 at 18:11