如何在PyQt中向事件循环发出自定义事件

4
我将尝试在PyQt中触发自定义事件。一个小部件会触发事件,另一个小部件将监听事件,但是这两个小部件之间不需要关联。
在JavaScript中,我可以通过以下方式实现:
// Component 1
document.addEventListener('Hello', () => console.log('Got it'))

// Component 2
document.dispatchEvent(new Event("Hello"))

编辑:我知道信号和槽机制,但只知道在父子控件之间如何使用。那么我该如何在任意不相关的小部件之间使用此机制(或其他机制)?


我知道信号和槽,但只知道如何在父子之间使用它们。我该如何在任意的、不相关的小部件之间使用它们? - Andrei Cioara
1
你实际上想通过这样做达到什么目的?你试图解决什么具体问题?在Qt中发送自定义事件是可能的,但很少需要这样的功能。也很难理解你怎么可能只知道如何在父子之间使用信号和槽。 - ekhumoro
我有一个包含按钮的小部件A。当我按下该按钮时,我希望灯变成绿色。该灯与按钮相距很远(在层次结构上)。我需要依次经过按钮->小部件A->父级->父级->子级->子级->子级->子级->灯才能到达它。 - Andrei Cioara
也许我试图将已有的知识应用到错误的地方了。在React中,我会从按钮更新Redux Store,并从light订阅状态更改。在Qt中,我该如何思考呢? - Andrei Cioara
只需将按钮的点击信号连接到更改灯光颜色的插槽即可。小部件的层次结构应该是无关紧要的。如果不是这样,您可能需要重新构建您的类。除非看到一些实际的代码,否则无法说更多。 - ekhumoro
显示剩余2条评论
1个回答

5
在PyQt中,以下指令:
document.addEventListener('Hello', () => console.log('Got it'))

等同于

document.hello_signal.connect(lambda: print('Got it'))

以类似的方式:
document.dispatchEvent(new Event("Hello"))

等价于

document.hello_signal.emit()

不同之处在于"document"对象的范围,因为连接是全局元素之间的。但在PyQt中,该元素不存在。

模拟您指出的行为的一种方法是创建一个全局对象:

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] = [func]
        else:
            self._events[name].append(func)

    def dispatchEvent(self, name):
        functions = self._events.get(name, [])
        for func in functions:
            QtCore.QTimer.singleShot(0, func)

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)
        self._label = QtWidgets.QLabel()
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self._label)

    @QtCore.pyqtSlot()
    def foo(self):
        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_())

工作得像魔法一样。点赞不够!此外,解决方案清晰明了,我完全理解了,谢谢! - Andrei Cioara
@eyllanesc,我们如何使用这种方法将参数传递给函数? - Voldemort
使用QApplication作为全局对象怎么样?您可以通过QApplication.instance()在任何地方访问它... - mike rodent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接