2014-02-07 3 views
1

Я новичок в PySide и пытаюсь выяснить, как изящно создавать то, что эффективно является сигнальным раздутием; то есть doodad, который выглядит как слот для классов контейнеров и, таким образом, может быть подключен() ed к и просто пересылает этот сигнал к содержащимся классам, предпочтительно без добавления слишком большого количества служебных данных в синтаксисе или производительности.Релейные сигналы к содержащимся виджетам

Давайте совершенно надуманный пример:

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) 

Таким образом, это делает большинство из того, что я ищу. Сигнал stateChanged от флажка Alpha попадает в порт сигнала на TripleCheckBox, который затем ретранслирует в One, Two и Three, и (я полагаю) делает это полностью внутри библиотеки Qt, не требуя отскока назад и вперед между библиотечным кодом и Python ,

Но MainWindow.ClearChecks() не работает. Для реального QCheckBox, setCheckState является слотом и, следовательно, функцией и может быть вызван с помощью обычного функционального синтаксиса. Но setCheckState на TripleCheckBox является сигналом и поэтому должен быть «вызван» как self.tri.setCheckState.emit(0)

Это синтаксически уродливое, но также имеет некоторые уродливые последствия для мантирования. Если у меня есть QCheckBox, я могу рассматривать setCheckState как функцию. Если у меня есть TripleCheckBox, я должен рассматривать его как сигнал вместо этого, хотя все, что он делает, завершает 3 QCheckBoxes.

В идеале, TripleCheckBox.setCheckState будет похож на сигнал, который просто имеет метод __call__, который вызывает/is emit. Но вы не можете наследовать от Signal.

Я могу придумать несколько неэлегантных способов сделать это, все из которых связаны с множеством репликации кода и adhocery. Но это довольно простой базовый уровень, конечно, есть элегантный ответ. Правильно?

ответ

1

Использование пользовательского сигнала в вашем примере является избыточным и делает код излишне сложным.

Учитывая его название и предполагаемое использование, TripleCheckBox.setCheckState, должен быть слот, а не сигнал. Сигнал не должен сам по себе do ничего: это просто уведомление о том, что произошло какое-то событие (или должно произойти). Сигнал не должен иметь прямых побочных эффектов, и любой объект, передающий его, не должен заботиться о том, какие последствия (если они есть) могут возникнуть после его выхода.

По этой причине линия 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_()) 
Смежные вопросы