使用存储在python dict中的信号

问题描述 投票:1回答:2

我想动态创建然后操纵大量的小部件。我的想法是将小部件存储在dict(mywidgets)中,并用另一个dict(mysignals)中存储的信号触发它们。两个dict共享列表(名称)中定义的相同键,dicts用for循环初始化。

当我将信号连接到插槽时,我正面临一个AttributeError:'PyQt5.QtCore.pyqtSignal'对象没有属性'connect'。

我试图禁用信号/插槽连接:GUI看起来不错,QLineEdit很好地存储在mywidgets中。 mysignals项的类型是正确的:类'PyQt5.QtCore.pyqtSignal'。

请问您能解释一下问题的来源吗?谢谢。

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout
from PyQt5.QtCore import pyqtSlot, pyqtSignal

class App(QWidget):

    names = ["foo","bar"]
    mysignals = {}    # Store several signals in a dict
    for name in names:
        mysignals[name] = pyqtSignal(str)

    def __init__(self):
        super().__init__()

        # Create Widgets
        self.btn_go = QPushButton("Go")     #Simple push button
        self.mywidgets = {}                   #Store several QLineEdit in a dict
        for name in self.names:
            self.mywidgets[name] = QLineEdit()

        # Connect signals
        self.btn_go.clicked.connect(self.on_click)                  #Connect push button
        for name in self.names:
            print(type(self.mysignals[name]))
            self.mysignals[name].connect(self.mywidgets[name].setText)  #Connect several signals

        # Configure layout
        layout = QVBoxLayout()
        layout.addWidget(self.btn_go)
        for name in self.names:
            layout.addWidget(self.mywidgets[name])
        self.setLayout(layout) 

        # Show widget
        self.show()


    @pyqtSlot()
    def on_click(self):
        data = {"foo":"Python3","bar":"PyQt5"}
        for key,value in data.items():
            self.mysignals[key].emit(value)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

预期的结果是在单击按钮时在我的窗口小部件[“foo”]和mywidgets [“bar”] QLineEdit窗口小部件中分别显示Python和PyQt5。

python pyqt pyqt5 qt-signals
2个回答
0
投票

正如docs指出:

信号(特别是未绑定信号)是类属性。当信号被引用为类的实例的属性时,PyQt5自动将实例绑定到信号以便创建绑定信号。这与Python本身用于从类函数创建绑定方法的机制相同。

信号被声明为类的属性,但是当通过self引用时,与对象进行绑定,即声明的信号与实例化的信号不同:

from PyQt5 import QtCore

class Foo(QtCore.QObject):
    fooSignal = QtCore.pyqtSignal()
    print("declared:", fooSignal)

    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        print("instantiated:", self.fooSignal)

if __name__ == '__main__':
    import sys
    app = QtCore.QCoreApplication(sys.argv)
    obj = Foo()

输出:

declared: <unbound PYQT_SIGNAL )>
instantiated: <bound PYQT_SIGNAL fooSignal of Foo object at 0x7f4beb998288>

这就是你得到错误的原因,所以如果你想使用信号,你必须使用对象获取它,这样我们就可以检查属性并获取信号:

from PyQt5 import QtCore, QtGui, QtWidgets

class Widget(QtWidgets.QWidget):
    foo = QtCore.pyqtSignal(str)
    bar = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.fill_signals()
        self.names = ["foo", "bar"]

        self.btn_go = QtWidgets.QPushButton("Go")
        self.mywidgets = {}
        for name in self.names:
            self.mywidgets[name] = QtWidgets.QLineEdit()
            signal = self.mysignals.get(name)
            if signal is not None:
                signal.connect(self.mywidgets[name].setText)

        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.btn_go)
        for name in self.names:
            layout.addWidget(self.mywidgets[name])

        self.btn_go.clicked.connect(self.testing)

    def fill_signals(self):
        self.mysignals = dict()
        for p in dir(self):
            attr = getattr(self, p)
            if isinstance(attr, QtCore.pyqtBoundSignal):
                self.mysignals[p] = attr

    def testing(self):
        self.foo.emit("foo")
        self.bar.emit("bar")

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

0
投票

对不起,我认为你已经使算法复杂化以获得预期的结果。试试吧:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout
from PyQt5.QtCore    import pyqtSlot, pyqtSignal

class App(QWidget):
    def __init__(self, names):
        super().__init__()

        self.names = names
        layout = QVBoxLayout()

        # Create Widgets
        self.btn_go = QPushButton("Go")            # Simple push button
        self.btn_go.clicked.connect(self.on_click) # Connect push button
        layout.addWidget(self.btn_go)

        self.mywidgets = {}                        # Store several QLineEdit in a dict
        for name in self.names:
            self.mywidgets[name] = QLineEdit()    
            layout.addWidget(self.mywidgets[name])

        self.setLayout(layout) 

    @pyqtSlot()
    def on_click(self):
        data = {"foo":"Python3", "bar":"PyQt5"}
        for key, value in data.items():
            self.mywidgets[key].setText(value)

if __name__ == '__main__':
    app = QApplication(sys.argv)

    names = ["foo", "bar"]
    ex = App(names)

    ex.show()
    sys.exit(app.exec_())

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.