【问题标题】:Relaying signals to contained widgets将信号中继到包含的小部件
【发布时间】:2014-03-05 06:25:25
【问题描述】:

我是 PySide 的新手,并试图弄清楚如何优雅地创建有效的信号扇出;即一个看起来像容器类的插槽的小玩意儿,因此可以被连接(),并简单地将信号转发到包含的类,最好不要在语法或性能上增加太多开销。

让我们举一个完全人为的例子:

class TripleCheckBox(QWidget):
    setCheckState = Signal(int)

    def __init__(self, parent):
        super().__init__(parent)
        self.checks = [QCheckBox(x, self) for x in ['One', 'Two', 'Three']]
        [self.setCheckState.connect(x) for x in self.checks]

class MainWindow(QWidget):
    def __init__(self):
        self.chk = QCheckBox('Alpha', self)
        self.btn = QPushButton('Push', self)
        self.tri = TripleCheckBox(self)
        self.chk.stateChanged.connect(self.tri.setCheckState)
        self.btn.clicked.connect(self.clearChecks)

    def clearChecks(self):
        self.tri.setCheckState(0)

所以,这可以满足我的大部分需求。来自 Alpha 复选框的 stateChanged 信号击中 TripleCheckBox 上的信号端口,然后重新广播到 One、2 和 Three,并且(我相信)完全在 Qt 库中这样做,而不必在库代码和 Python 之间来回切换.

但是MainWindow.ClearChecks() 不起作用。对于一个真正的 QCheckBox,setCheckState 是一个 Slot,因此是一个函数,可以用常规的函数语法调用。但是 TripleCheckBox 上的 setCheckState 是一个信号,因此必须“调用”为self.tri.setCheckState.emit(0)

这在语法上很丑陋,但也有一些丑陋的可维护性含义。如果我有一个 QCheckBox,我可以将 setCheckState 视为一个函数。如果我有一个 TripleCheckBox,我必须将其视为一个信号,即使它所做的只是包装 3 个 QCheckBox。

理想情况下,TripleCheckBox.setCheckState 应该是一个类似 Signal 的东西,它只是有一个 __call__ 方法,它调用/是 emit。但是你不能从 Signal 继承。

我可以想到几种不优雅的方法来做到这一点,所有这些都涉及大量的代码复制和临时性。但这是一些非常基本的东西,肯定有一个优雅的答案。对吧?

【问题讨论】:

    标签: python pyside signals-slots


    【解决方案1】:

    在您的示例中使用自定义信号是多余的,并且使代码变得不必要地复杂。

    鉴于其名称和预期用途,TripleCheckBox.setCheckState 应该是一个插槽而不是一个信号。信号本身不应该任何事情:它只是通知某个事件已经发生(或即将发生)。一个信号应该没有直接的副作用,并且任何广播它的对象都不应该关心它一旦发出后可能会产生什么后果(如果有的话)。

    出于这个原因,self.tri.setCheckState(0)作为信号没有意义(尽管它作为一个槽)。另一方面,像self.tri.stateChanged.emit(0) 这样的东西有意义(尽管不一定在那个特定的上下文中)。

    鉴于以上几点,这是重写示例的一种方法:

    from PySide import QtCore, QtGui
    
    class CheckBoxSet(QtGui.QWidget):
        def __init__(self, labels, parent=None):
            super(CheckBoxSet, self).__init__(parent)
            layout = QtGui.QVBoxLayout(self)
            self.checkboxes = []
            for label in labels:
                checkbox = QtGui.QCheckBox(label, self)
                layout.addWidget(checkbox)
                self.checkboxes.append(checkbox)
    
        def setCheckState(self, state=0):
            state = QtCore.Qt.CheckState(state)
            for checkbox in self.checkboxes:
                checkbox.setCheckState(state)
    
    class Window(QtGui.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            layout = QtGui.QVBoxLayout(self)
            self.chk = QtGui.QCheckBox('Alpha', self)
            self.btn = QtGui.QPushButton('Push', self)
            self.tri = CheckBoxSet('One Two Three'.split(), self)
            layout.addWidget(self.chk)
            layout.addWidget(self.btn)
            layout.addWidget(self.tri)
            self.chk.stateChanged.connect(self.tri.setCheckState)
            self.btn.clicked.connect(self.clearChecks)
    
        def clearChecks(self):
            self.tri.setCheckState(0)
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.setGeometry(500, 300, 300, 200)
        window.show()
        sys.exit(app.exec_())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-24
      • 1970-01-01
      • 2013-07-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多