How to pass arguments to callback functions in PyQt
Solution 1
You can send the action object itself using a signal mapper. However, it may be better to simply send an identifier and do all the work within the signal handler.
Here's a simple demo script:
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_())
Solution 2
How to pass arguments to callback functions in PyQt
You can use functools.partial
from standart Python library. Example with QAction
:
some_action.triggered.connect(functools.partial(some_callback, param1, param2))
Solution 3
You could use a lambda function associated to the GUI control's slot to pass extra arguments to the method you want to execute.
# 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
Source: snip2code - Using Lambda Function To Pass Extra Argument in PyQt4
Solution 4
The best way to pass the arguments is to not pass them at all. You can use the dynamic nature of python, and set whatever data you need as your own properties on the widgets themselves, get the target widget in the handler using self.sender()
, and then get whatever properties you need directly from the widget.
In this example five buttons are created and the required state is set on the button widget as my_own_data
property:
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_())
Solution 5
There is still a bug in the way PyQt uses QSignalMapper. I discovered a work-around here:
http://www.riverbankcomputing.com/pipermail/pyqt/2010-March/026113.html
To fix the answered question make the following change:
action.triggered[()].connect(self.mapper.map)
This was needed for the following version of 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
Comments
-
yasar almost 2 years
I have around 10 QAction (this number will vary in runtime) in a toolbar, which all will do same thing, but using different parameters. I am thinking to add parameter as an attribute to QAction object, and then, QAction's triggered signal will also send object's itself to the callback function, so that I could get required parameters for the function. I have actually 2 questions about this:
- Can it be done?
- Is there a better way of doing this?
-
101 almost 9 yearsThis answer doesn't seem very popular. Is
QSignalMapper
not widely used? -
ekhumoro almost 9 years@figs. I don't know why the OP accepted this answer, given that the more conventional
lambda/partial
solution was already given. There was once a time whenQSignalMapper
was more useful, because PyQt hasn't always supported usinglambda/partial
as a slot - but that was a long time ago. At the moment, though, I can't think of a scenario where it might be more effective (other than in C++, of course). -
ekhumoro over 8 yearsThere are some downsides to this approach, which are set out in the docs for sender. As you've written it,
click_handler
cannot be safely called by anything other than the specific buttons that are connected to it. -
ccpizza over 8 years@ekhumoro: Agreed; added optional data parameter, so that
click_handler()
can be used in a more generic way.