Создание pyqtSignals из словаря

#python #pyqt #pyqt5 #qt-signals #qobject

#python #pyqt #pyqt5 #qt-сигналы #qobject

Вопрос:

Ну, я писал сценарий программного обеспечения, разработанного на Python, в котором я буду слишком часто использовать сигналы и слоты в приложении PyQt5. У меня появилась идея создать словарь, куда поступают все сигналы, и у каждого сигнала будет свой собственный ключ для доступа (или, в основном, для подключения его к функции). Проблема в том, что я получаю эту ошибку ‘AttributeError: ‘Объект PyQt5.QtCore.pyqtSignal’ по какой-то причине не имеет атрибута ‘connect’. Я прочитал об этой ошибке и обнаружил, что мне нужно объявлять сигналы вне конструктора, чтобы заставить его работать, но, к сожалению, это нарушит мою идею, поэтому я пришел сюда, чтобы кто-нибудь мог решить мою проблему.

Вот код, если вы все еще не понимаете:

 from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QRunnable, pyqtSlot, QThreadPool, QObject, pyqtSignal

class WorkerSignals(QObject):
    signals = {}

    def __init__(self, **kwargs):
        QObject.__init__(self)
        if (kwargs is not None):
            for key, value in kwargs.items():
                self.signals[key] = value

class Worker(QRunnable):
    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()
        self.fn = fn
        self.args = args
        self.kwargs = kwargs

    @pyqtSlot()
    def run(self):
        self.fn(*self.args, **self.kwargs)
  

И пример создания сигналов:

 worker_signals = WorkerSignals(result=pyqtSignal(str), error=pyqtSignal(str))
worker_signals.signals['result'].connect(self.on_receive_result)
worker_signals.signals['error'].connect(self.on_receive_error)
  

Ответ №1:

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

Сигнал (в частности, несвязанный сигнал) является атрибутом класса. Когда на сигнал ссылаются как на атрибут экземпляра класса, PyQt5 автоматически привязывает экземпляр к сигналу, чтобы создать связанный сигнал. […]

Таким образом, это не только необходимо, чтобы это было объявлено вне конструктора, но это должен быть статический атрибут, поскольку он служит прототипом для создания сигналов, которые принадлежат экземпляру. Возможным решением является использование type для создания динамических классов:

 from PyQt5 import QtCore

d = {
    "result": QtCore.pyqtSignal(str),
    "error": QtCore.pyqtSignal(str)
}

WorkerSignals = type("WorkerSignals", (QtCore.QObject,), d)

if __name__ == '__main__':
    import sys
    app = QtCore.QCoreApplication(sys.argv)
    worker_signals = WorkerSignals()

    def on_result(text):
        print("result:", text)

    def on_error(text):
        print("error:", text)

    worker_signals.result.connect(on_result)
    worker_signals.error.connect(on_error)

    def emit_result():
        worker_signals.result.emit(" 1 1=2 ")
    def emit_error():
        worker_signals.error.emit(" :( ")

    QtCore.QTimer.singleShot(1000, emit_result)
    QtCore.QTimer.singleShot(2000, emit_error)

    QtCore.QTimer.singleShot(3000, app.quit)
    sys.exit(app.exec_())
  

Комментарии:

1. На самом деле это не то, что я искал. Я страдаю от создания нескольких классов только для инициализации сигналов. Мне нужен только один класс, который я могу просто использовать, чтобы объявить, какие сигналы необходимы для определенного потока или в основном динамических сигналов, точно так же, как я сделал выше. Кстати, спасибо за оперативный ответ.

2. @Mqtyx ммм, вы хотите, чтобы объекты одного и того же класса имели разные сигналы, потому что в настоящее время это невозможно (в PyQt4 вы могли бы создавать сигналы, как если бы они были атрибутами объекта), поскольку PyQt5 требует, чтобы они были объявлены как атрибуты класса (несвязанный сигнал), поскольку внутренний SIP преобразует его в атрибуты объекта (связанный сигнал). Так что вам это точно не нужно, вы не можете.

3. Понял. Я просто продолжу обычную процедуру создания сигналов. Спасибо за ваше время.