2016-04-05 33 views
7

Sto leggendo attraverso alcuni documenti su PyQt5 per trovare un semplice meccanismo di slot del segnale. Mi sono fermato a causa di una considerazione di progettazione.PyQt uso corretto di emit() e pyqtSignal()

Si consideri il seguente codice:

import sys 
from PyQt5.QtCore import (Qt, pyqtSignal) 
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, 
    QVBoxLayout, QApplication) 


class Example(QWidget): 

    def __init__(self): 
     super().__init__() 

     self.initUI() 

    def printLabel(self, str): 
     print(str) 

    def logLabel(self, str): 
     '''log to a file''' 
     pass 

    def initUI(self): 

     lcd = QLCDNumber(self) 
     sld = QSlider(Qt.Horizontal, self) 

     vbox = QVBoxLayout() 
     vbox.addWidget(lcd) 
     vbox.addWidget(sld) 

     self.setLayout(vbox) 

     #redundant connections 
     sld.valueChanged.connect(lcd.display) 
     sld.valueChanged.connect(self.printLabel) 
     sld.valueChanged.connect(self.logLabel) 

     self.setGeometry(300, 300, 250, 150) 
     self.setWindowTitle('Signal & slot') 
     self.show() 


if __name__ == '__main__': 

    app = QApplication(sys.argv) 
    ex = Example() 
    sys.exit(app.exec_()) 

per tenere traccia delle modifiche apportate al dispositivo di scorrimento, ho semplicemente Print and log le modifiche apportate. Quello che non mi piace del codice è che sono obbligato a chiamare lo slot sld.valueChanged tre volte per inviare le stesse informazioni a 3 diversi slot.

È possibile creare il mio pyqtSignal che invia un numero intero a una singola funzione di alloggiamento. E a turno la funzione slot emette i cambiamenti che devono essere apportati?

  • Forse non capisco pienamente lo scopo di emit() perché non ci sono buoni esempi del suo scopo nel PyQt Signal-Slot docs. Tutto quello che ci viene dato è un esempio di come implementare uno emit senza parametri.

Quello che mi piacerebbe fare è creare una funzione che gestisca la funzione di emissione. Considera quanto segue:

import sys 
from PyQt5.QtCore import (Qt, pyqtSignal) 
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, 
    QVBoxLayout, QApplication) 


class Example(QWidget): 

    def __init__(self): 
     super().__init__() 

     #create signal 
     self.val_Changed = pyqtSignal(int, name='valChanged') 

     self.initUI() 

    def initUI(self): 

     lcd = QLCDNumber(self) 
     sld = QSlider(Qt.Horizontal, self) 

     vbox = QVBoxLayout() 
     vbox.addWidget(lcd) 
     vbox.addWidget(sld) 

     self.setLayout(vbox) 

     sld.val_Changed.connect(self.handle_LCD) 
     self.val_Changed.emit() 

     self.setGeometry(300, 300, 250, 150) 
     self.setWindowTitle('Signal & slot') 
     self.show() 

    def handle_LCD(self, text): 
     '''log''' 
     print(text) 
     '''connect val_Changed to lcd.display''' 

if __name__ == '__main__': 

    app = QApplication(sys.argv) 
    ex = Example() 
    sys.exit(app.exec_()) 

Ci sono ovviamente alcuni difetti di progettazione importanti qui. Non riesco a capovolgere l'ordine delle chiamate di funzione. E non sto implementando correttamente pyqtSignal. Tuttavia, ritengo che affermare correttamente i seguenti 3 punti mi aiuterà a produrre un'app corretta:

  1. Per un segnale predefinito: inviare il segnale alla funzione di slot. Lo slot può essere reimplementato per utilizzare i valori del segnale.
  2. Produce l'oggetto pyqtSignal con alcuni parametri. Non è ancora chiaro quale sia lo scopo di questi parametri e in che modo si differenziano dai parametri "emetti".
  3. emit può essere reimplementato per inviare valori di segnale specifici alla funzione di slot. Inoltre, non è ancora chiaro il motivo per cui avrei bisogno di inviare valori diversi dai metodi di segnale precedentemente esistenti.

Sentitevi liberi di modificare completamente il codice per quello che sto cercando di fare perché non ho ancora capito se è nel regno del buon stile.

risposta

11

È possibile definire il proprio slot (qualsiasi chiamabile in python) e collegarlo al segnale, quindi chiamare gli altri slot da quello slot.

class Example(QWidget): 

    def __init__(self): 
     super().__init__() 
     self.initUI() 

    def printLabel(self, str): 
     print(str) 

    def logLabel(self, str): 
     '''log to a file''' 
     pass 

    @QtCore.pyqtSlot(int) 
    def on_sld_valueChanged(self, value): 
     self.lcd.display(value) 
     self.printLabel(value) 
     self.logLabel(value) 

    def initUI(self): 

     self.lcd = QLCDNumber(self) 
     self.sld = QSlider(Qt.Horizontal, self) 

     vbox = QVBoxLayout() 
     vbox.addWidget(self.lcd) 
     vbox.addWidget(self.sld) 

     self.setLayout(vbox) 
     self.sld.valueChanged.connect(self.on_sld_valueChanged) 


     self.setGeometry(300, 300, 250, 150) 
     self.setWindowTitle('Signal & slot') 

Inoltre, se si vuole definire i propri segnali, essi devono essere definiti come variabili di classe

class Example(QWidget): 
    my_signal = pyqtSignal(int) 

gli argomenti da pyqtSignal definire i tipi di oggetti che saranno emit 'd su quel del segnale, quindi in questo caso, si potrebbe fare

self.my_signal.emit(1) 

Emit può essere reimplementata di inviare va segnale specifico lues alla funzione slot .Inoltre non è ancora chiaro il motivo per cui avrei bisogno di inviare diversi valori da metodi di segnale precedentemente esistenti.

Generalmente non si devono emettere i segnali integrati. Dovresti solo dover emettere i segnali che definisci. Quando si definisce un segnale, è possibile definire diverse firme con tipi diversi e gli slot possono scegliere la firma a cui si desidera connettersi. Per esempio, si potrebbe fare questo

my_signal = pyqtSignal([int], [str]) 

Questo definirà un segnale con due firme diverse, e uno slot potrebbe collegarsi ad uno dei due

@pyqtSlot(int) 
def on_my_signal_int(self, value): 
    assert isinstance(value, int) 

@pyqtSlot(str) 
def on_my_signal_str(self, value): 
    assert isinstance(value, str) 

In pratica, raramente sovraccaricare le firme di segnale. Normalmente creerei solo due segnali separati con diverse firme piuttosto che sovraccaricare lo stesso segnale. Ma esiste ed è supportato in PyQt perché Qt ha segnali che sono sovraccaricati in questo modo (ad esempio QComboBox.currentIndexChanged)

+0

Sembra che commentare "@pyqtSlot (int)" non influenzi l'output. Perché è così? – Max

+0

Sembra che il vero scopo del decoratore consenta troppo il sovraccarico. – Max

+0

Non è solo per sovraccaricare. Questa [risposta] (http://stackoverflow.com/a/14431607/1547004) lo spiega abbastanza bene. Se invii segnali attraverso i thread, richiede anche il decoratore di slot. –