Используйте сигналы, хранящиеся в диктоне Python - PullRequest
0 голосов
/ 18 февраля 2019

Я хотел бы динамически создавать, а затем манипулировать множеством виджетов.Моя идея состоит в том, чтобы хранить виджеты в dict (mywidgets) и запускать их с помощью сигналов, хранящихся в другом dict (mysignals).Оба ключа используют одни и те же ключи, определенные в списке (именах), с которыми они инициализируются для циклов for.

Когда я подключаю сигналы к слотам, в настоящее время я сталкиваюсь с AttributeError: объект PyQt5.QtCore.pyqtSignal имеетнет атрибута «соединиться».

Я попытался отключить соединения сигнал / слот: графический интерфейс выглядит хорошо, 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_())

Ожидаемый результат заключается в отображении соответственно только Python3 и PyQt5 в виджетах mywidgets ["foo"] и mywidgets ["bar"] QLineEdit при нажатии кнопки.

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Как указывают документы :

Сигнал (в частности, несвязанный сигнал) является атрибутом класса.Когда сигнал упоминается как атрибут экземпляра класса, тогда 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 голосов
/ 18 февраля 2019

Извините, я думаю, вы усложнили алгоритм для получения ожидаемого результата.Попробуйте:

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

...