How to pass arguments to callback functions in PyQt
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?
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))
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
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_())
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_())