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?

2
  • 2
    Please provide a minimal, reproducible example of what you've got so far. Commented Jul 28, 2020 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. Commented Jul 28, 2020 at 16:35

1 Answer 1

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_())
Sign up to request clarification or add additional context in comments.

6 Comments

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.
@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)
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.
@deepanshu I think I understand what you want, you want dispatchEvent to accept arguments, am I correct?
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.