GingerBadger
GingerBadger

Reputation: 392

Connect a slot to signals from all objects derived from a class

I'm just starting out with PyQt5 and would appreciate some help. I have a widget W that responds to information from all object derived from some class C. Widget W can only ever be updated by objects of class C and, during an update, it must know, which particular object triggered it.

The problem is that objects of class C may continue to be created throughout the course of the program. Is it possible to apply the signal/slot paradigm to this problem?

My thoughts so far:

  1. Ideally, I would connect the slot on W to a class variable on C. However, Qt doesn't allow signals as class variables.
  2. I could use a collection object CO, such that any new objects C must be created through this object's interface. Then I define a signal on this object, which would be connected to a slot on W. However, this is ugly because CO would only exist for the purpose of solving this problem.
  3. I could drop the signal/slot paradigm all together and instead simply add W's callbacks (slots) to a class variable on C. Then, whenever a new object C is created, it already has a reference to the callback.

Upvotes: 0

Views: 769

Answers (1)

eyllanesc
eyllanesc

Reputation: 243983

One possible solution is to use metaclasses:

import sys

from PyQt5 import QtCore, QtWidgets


class MetaC(type(QtCore.QObject), type):
    def __call__(cls, *args, **kw):
        obj = super().__call__(*args, **kw)
        for receiver in cls.receivers:
            obj.signal.connect(receiver)
        return obj


class C(QtCore.QObject, metaclass=MetaC):
    signal = QtCore.pyqtSignal()
    receivers = []

    @classmethod
    def register(cls, slot):
        cls.receivers.append(slot)


class C1(C):
    def test(self):
        self.signal.emit()


class C2(C1):
    pass


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        C.register(self.foo)

    def foo(self):
        print("foo")


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()

    c1, c2 = C1(), C2()

    QtCore.QTimer.singleShot(1000, c1.test)
    QtCore.QTimer.singleShot(5 * 1000, c2.test)

    QtCore.QTimer.singleShot(6 * 1000, QtCore.QCoreApplication.quit)

    sys.exit(app.exec_())

Upvotes: 2

Related Questions