如何将多个具有可变参数的PyQt信号连接到单个槽函数/信号转发器

4

例子:

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

假设每个信号都已连接到其他地方以执行各种功能,但当任何一个信号被发出时,我也希望执行特定的功能。 这个无差别的函数只关心与信号一起发出的最后一个int参数。 插槽看起来应该如下所示:
class OtherClass(QObject):

    ...

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

有没有一种方法可以将任意数量和任意参数的信号直接连接到一个槽或中继器信号?
如果有一种定义槽的方式:
@pyqtSlot(???)
def unifiedResponse(self, *args):
    important_var = args[-1]

那么我可以简单地捕获最后一个参数,如下所示。然而,我一直未能确定如何形成插槽签名。

更新:

我可能已经回答了自己的问题,使用 lambda

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

下面@eyllanesc的解决方案更受欢迎,因为它允许在信号计数越大时拥有更多的灵活性。

这个回答解决了你的问题吗?在PyQt4中循环连接插槽和信号 - Matheus Torquato
2个回答

3

您需要通过几个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()

如果您想发送多种类型的数据,一般最好使用更通用的数据类型,如列表(或对象):
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_())

更新:

没有一种优雅的方法可以访问最后一个元素,但可以访问前面的元素,因为插槽签名必须是信号签名的子集。例如,具有“int”签名的插槽可以接受带有从第一个类型到“int”的签名的信号,其他参数将被丢弃:

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()

哦,这真的不太理想。但如果这是唯一的方法,那我想我应该尝试从不同的角度来解决问题。 - Connor Spangler
@ConnorSpangler 你期望得到什么样的答案?也许你遇到了一个XY问题 - eyllanesc
我不这么认为。我有多个信号,用于指示绘画应用程序何时更新,例如像素或颜色等。在撤消和重做时,我希望应用程序切换到适当的选项卡,该选项卡由已传递到这些信号的变量标识。每当发出这些信号时,一个连接到它们所有的插槽可以抓取该索引并切换到适当的选项卡。我真的希望有一种方法可以将信号连接到插槽,但仅传递一些信号参数。 - Connor Spangler
@ConnorSpangler 为什么索引在最后一个位置?如果它在第一个位置,那么就有解决方案了。 - eyllanesc
它不必是一个人为制定的标准,而可以是自然形成的标准。我当然愿意并且能够改变参数顺序。 - Connor Spangler
啊,我之前甚至偶然遇到过那种行为。没想到可以把它当作一个特性来使用!我自己找到了一种解决方案,使用了lambda表达式,在我的更新中进行了概述。 - Connor Spangler

0

我一直在尝试将带有可变参数的多个PyQt信号连接到单个槽上,但遇到了问题。

这是我找到的解决方案,用于将所有来自input_lineEdit_widgets的小部件连接到validate_values

class View_Controller():

    def connect_signals(self,ui):

        #This is a list of lineEdit widgets from a QtWidgets.QMainWindow (ui)
        input_lineEdit_widgets = [ui.prim_press_lineEdit, ui.gas_vol_lineEdit, ui.melt_1_temp_lineEdit,
                                 ui.melt_1_time_lineEdit, ui.melt_1_flow_lineEdit, ui.melt_2_temp_lineEdit,
                                 ui.melt_2_time_lineEdit, ui.melt_2_flow_lineEdit]

        for widgets in input_lineEdit_widgets:
            widgets.textChanged.connect(lambda *args, widgets = widgets: self.validate_values(widgets))


    def validate_values(self, widget):
        print(widget.objectName())

更多细节在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接