Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to connect multiple PyQt Signals w/ variable arguments to single slot/signal repeater

Example:

class MyClass(QObject):
    signal_1 = pyqtSignal(str, int)
    signal_2 = pyqtSignal(int, int)
    signal_3 = pyqtSignal(str, int, int)

Let's say each of these signals is connected elsewhere to perform various functions, however, there's a specific function I want to be performed as well when any of the signals are emitted. What this indiscriminate function does cares only about, say, the last int argument emitted with the signal. The slot would ostensibly look like:

class OtherClass(QObject):

    ...

    @pyqtSlot(int)
    def unifiedResponse(self, index):
        # Here I would do something with that index

Is there a way to connect some arbitrary number of signals with arbitrary arguments either directly to a slot or to a repeater signal?

If there is a way to define a slot like so:

@pyqtSlot(???)
def unifiedResponse(self, *args):
    important_var = args[-1]

Then I could simply capture the last argument as such. However, I have been unsuccessful in determining how to form the slot signature.

Update:

I may have answered my own question, using lambdas:

signal_1.connect(lambda _, source: OtherClass.unifiedResponse(source))
signal_2.connect(lambda _, source: OtherClass.unifiedResponse(source))
signal_3.connect(lambda _, _, source: OtherClass.unifiedResponse(source))

The solution from @eyllanesc below however is preferred, as it allows for more flexibility the larger the signal count.

like image 828
Connor Spangler Avatar asked Oct 26 '25 10:10

Connor Spangler


1 Answers

You have to set all the signatures of the signals through several pyqtSlot:

from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QTimer


class MyClass(QObject):
    signal_1 = pyqtSignal(str, int)
    signal_2 = pyqtSignal(int, int)
    signal_3 = pyqtSignal(str, int, int)


class OtherClass(QObject):
    @pyqtSlot(str, int)
    @pyqtSlot(int, int)
    @pyqtSlot(str, int, int)
    def unifiedResponse(self, *args):
        print(args)


def main():
    import sys

    app = QCoreApplication(sys.argv)

    sender = MyClass()
    receiver = OtherClass()

    sender.signal_1.connect(receiver.unifiedResponse)
    sender.signal_2.connect(receiver.unifiedResponse)
    sender.signal_3.connect(receiver.unifiedResponse)

    def on_timeout():
        sender.signal_1.emit("StackOverflow", 1)
        sender.signal_2.emit(1, 2)
        sender.signal_3.emit("StackOverflow", 1, 2)

        QTimer.singleShot(1000, QCoreApplication.quit)

    QTimer.singleShot(1000, on_timeout)

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

If in general you want to send several types of data then it is best to use a more generic data type such as a list (or an object):

class MyClass(QObject):
    signal_1 = pyqtSignal(list)
    signal_2 = pyqtSignal(list)
    signal_3 = pyqtSignal(list)


class OtherClass(QObject):
    @pyqtSlot(list)
    def unifiedResponse(self, args):
        print(args)


def main():
    import sys

    app = QCoreApplication(sys.argv)

    sender = MyClass()
    receiver = OtherClass()

    sender.signal_1.connect(receiver.unifiedResponse)
    sender.signal_2.connect(receiver.unifiedResponse)
    sender.signal_3.connect(receiver.unifiedResponse)

    def on_timeout():
        sender.signal_1.emit(["StackOverflow", 1])
        sender.signal_2.emit([1, 2])
        sender.signal_3.emit(["StackOverflow", 1, 2])

        QTimer.singleShot(1000, QCoreApplication.quit)

    QTimer.singleShot(1000, on_timeout)

    sys.exit(app.exec_())

Update:

There is no elegant way to access the last element, but the first ones since the slot signature must be a subset of the signal signature, for example the slot with signature "int" can accept signals with a signature that has the first type to "int", the other arguments are discarded:

from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QTimer


class MyClass(QObject):
    signal_1 = pyqtSignal(int, str)
    signal_2 = pyqtSignal(int, int)
    signal_3 = pyqtSignal(int, str, int)


class OtherClass(QObject):
    @pyqtSlot(int)
    def unifiedResponse(self, index):
        print(index)


def main():
    import sys

    app = QCoreApplication(sys.argv)

    sender = MyClass()
    receiver = OtherClass()

    sender.signal_1.connect(receiver.unifiedResponse)
    sender.signal_2.connect(receiver.unifiedResponse)
    sender.signal_3.connect(receiver.unifiedResponse)

    def on_timeout():
        sender.signal_1.emit(1, "StackOverflow")
        sender.signal_2.emit(2, 2)
        sender.signal_3.emit(3, "StackOverflow", 1)

        QTimer.singleShot(1000, QCoreApplication.quit)

    QTimer.singleShot(1000, on_timeout)

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
like image 109
eyllanesc Avatar answered Oct 28 '25 00:10

eyllanesc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!