将信号转发到包含的小部件

时间:2014-02-07 17:54:47

标签: python pyside signals-slots

我是PySide的新手,并试图弄清楚如何优雅地创造出有效的信号扇出;即一个看起来像Slot to容器类的doodad,因此可以connect()编辑,并简单地将该信号转发到包含的类,最好不会在语法或性能上增加太多开销。

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

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上的信号端口,然后重新转发为一,二和三,并且(我相信)完全在Qt库中完成,而不必在库代码和Python之间来回反弹

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

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

理想情况下,TripleCheckBox.setCheckState就像一个信号,它只有__call__方法,调用/是emit。但是你不能从Signal继承。

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

1 个答案:

答案 0 :(得分: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_())