2012-01-11 6 views
7

Ho circa 10 QAction (questo numero varierà in runtime) in una barra degli strumenti, che farà tutti la stessa cosa, ma utilizzando parametri diversi. Sto pensando di aggiungere un parametro come attributo all'oggetto QAction, e quindi il segnale innescato di QAction invierà anche l'oggetto stesso alla funzione di callback, in modo da ottenere i parametri richiesti per la funzione. In realtà ho 2 domande su questo:Come passare argomenti alle funzioni di callback in PyQt

  • Si può fare?
  • C'è un modo migliore per farlo?

risposta

4

È possibile inviare l'oggetto azione stessa utilizzando un signal mapper. Tuttavia, potrebbe essere meglio inviare semplicemente un identificatore e fare tutto il lavoro all'interno del gestore del segnale.

Ecco uno script demo semplice:

from PyQt4 import QtGui, QtCore 

class Window(QtGui.QMainWindow): 
    def __init__(self): 
     QtGui.QMainWindow.__init__(self) 
     self.mapper = QtCore.QSignalMapper(self) 
     self.toolbar = self.addToolBar('Foo') 
     self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) 
     for text in 'One Two Three'.split(): 
      action = QtGui.QAction(text, self) 
      self.mapper.setMapping(action, text) 
      action.triggered.connect(self.mapper.map) 
      self.toolbar.addAction(action) 
     self.mapper.mapped['QString'].connect(self.handleButton) 
     self.edit = QtGui.QLineEdit(self) 
     self.setCentralWidget(self.edit) 

    def handleButton(self, identifier): 
     if identifier == 'One': 
      text = 'Do This' 
     elif identifier == 'Two': 
      text = 'Do That' 
     elif identifier == 'Three': 
      text = 'Do Other' 
     self.edit.setText(text) 

if __name__ == '__main__': 

    import sys 
    app = QtGui.QApplication(sys.argv) 
    window = Window() 
    window.resize(300, 60) 
    window.show() 
    sys.exit(app.exec_()) 
+0

Questa risposta non sembra molto popolare. 'QSignalMapper' non è ampiamente utilizzato? – 101

+0

@figs. Non so perché il PO abbia accettato questa risposta, dato che la soluzione più convenzionale 'lambda/parziale' era già stata data. C'è stato un tempo in cui 'QSignalMapper' è stato più utile, perché PyQt non ha sempre supportato l'uso di' lambda/partial' come slot - ma è stato tanto tempo fa.Al momento, però, non riesco a pensare a uno scenario in cui potrebbe essere più efficace (tranne che in C++, ovviamente). – ekhumoro

35

Come passare argomenti di funzioni di callback in PyQt

È possibile utilizzare functools.partial dalla libreria standart Python. Esempio con QAction:

some_action.triggered.connect(functools.partial(some_callback, param1, param2)) 
+1

molto semplice ed efficace. IMHO, questa dovrebbe essere la risposta accettata. – reima

+0

Uso semplicemente questa soluzione. Funziona bene! – swdev

1

C'è ancora un bug nel modo in PyQt utilizza QSignalMapper. Ho scoperto un work-around qui:

http://www.riverbankcomputing.com/pipermail/pyqt/2010-March/026113.html

Per fissare la domanda ha risposto apportare la seguente modifica:

 action.triggered[()].connect(self.mapper.map) 

Ciò è stato necessario per la seguente versione di Python:

Python 2.6.6 (r266:84292, Feb 21 2013, 19:26:11) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2 
9

È possibile utilizzare una funzione lambda associata allo slot del controllo della GUI per passare argomenti aggiuntivi al metodo che si desidera eseguire.

# Create the build button with its caption 
self.build_button = QPushButton('&Build Greeting', self) 
# Connect the button's clicked signal to AddControl 
self.build_button.clicked.connect(lambda: self.AddControl('fooData')) 
def AddControl(self, name): 
    print name 

Fonte: snip2code - Using Lambda Function To Pass Extra Argument in PyQt4

+1

Grazie! Esattamente quello che stavo cercando! –

3

Il modo migliore per passare gli argomenti è quello di non passare affatto. È possibile utilizzare la natura dinamica di Python e impostare qualsiasi dato necessario come proprietà proprie sui widget stessi, ottenere il widget di destinazione nel gestore utilizzando self.sender() e quindi ottenere le proprietà necessarie direttamente dal widget.

In questo esempio cinque pulsanti vengono creati e lo stato richiesto è impostato sul widget tasto come my_own_data proprietà:

import sys 
from PyQt4 import QtGui, QtCore 

class Main(QtGui.QMainWindow): 

    def __init__(self): 
     QtGui.QMainWindow.__init__(self) 
     self.centralwidget = QtGui.QWidget() 
     self.vboxlayout = QtGui.QVBoxLayout() 

     for idx in range(5): 
      button = QtGui.QPushButton('button ' + str(idx), None) 
      button.my_own_data = str(idx) # <<< set your own property 
      button.clicked.connect(self.click_handler) # <<< no args needed 
      self.vboxlayout.addWidget(button) 

     self.centralwidget.setLayout(self.vboxlayout) 
     self.setCentralWidget(self.centralwidget) 
     self.show() 

    def click_handler(self, data=None): 
     if not data: 
      target = self.sender() # <<< get the event target, i.e. the button widget 
      data = target.my_own_data # <<< get your own property 
     QtGui.QMessageBox.information(self, 
             "you clicked me!", 
             "my index is {0}".format(data)) 

app = QtGui.QApplication(sys.argv) 
main = Main() 
main.show() 

sys.exit(app.exec_()) 
+0

Ci sono alcuni aspetti negativi di questo approccio, che sono definiti in [documenti per il mittente] (http://doc.qt.io/qt-4.8/qobject.html#sender). Come hai scritto, 'click_handler' non può essere chiamato con sicurezza da qualcosa di diverso dai pulsanti specifici che sono collegati ad esso. – ekhumoro

+0

@ekhumoro: concordato; aggiunto un parametro opzionale per i dati, in modo che 'click_handler()' possa essere usato in un modo più generico. – ccpizza