Calling a function upon button press

user2109621 picture user2109621 · Feb 26, 2013 · Viewed 70.8k times · Source

I am trying to call function funcion when I click btn_brow_3. How could I accomplish this?

When running the following code, I receive this error message: TypeError: connect() slot argument should be a callable or a signal not "nonetype"

import atexit
import sys
from PyQt4 import QtGui
import os                           # Libreria para manejar directorios del sistema operativo

def funcion(a):
    print "Hola mundo" + str(a)

class Example(QtGui.QMainWindow):

    def __init__(self):
        super(Example, self).__init__()
        
        self.initUI()
        
    def initUI(self):               
        
        exitAction = QtGui.QAction(QtGui.QIcon('c:/prueba gui/resource/logo.png'), 'Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.triggered.connect(QtGui.qApp.quit)
        
        btn_brow_1 = QtGui.QPushButton('Browser...', self)
        btn_brow_1.resize(btn_brow_1.sizeHint())
        btn_brow_1.move(300, 50)
        btn_brow_1.clicked.connect(self.showDialog_points)
        
        btn_brow_2 = QtGui.QPushButton('Dir browser', self)
        btn_brow_2.resize(btn_brow_2.sizeHint())
        btn_brow_2.move(300, 80)
        btn_brow_2.clicked.connect(self.showDialog_indir_stl)
        
        btn_brow_3 = QtGui.QPushButton('Dir browser', self)
        btn_brow_3.resize(btn_brow_3.sizeHint())
        btn_brow_3.move(300, 110)
        btn_brow_3.clicked.connect(self.showDialog_outdir_stl)
        
        
        
        
        btn_brow_4 = QtGui.QPushButton('Crear soportes', self)
        btn_brow_4.setGeometry(20, 145, 250, 25)
        #btn_brow_4.clicked.connect(support.main(fname_points, self.fname_stl_indir, self.fname_stl_outdir))
        btn_brow_4.clicked.connect(funcion(12))  # HERE IS THE PROBLEM!
        
        self.le1 = QtGui.QLineEdit(self)
        self.le1.setGeometry(20, 50, 250,21)
        
        self.le2 = QtGui.QLineEdit(self)
        self.le2.setGeometry(20, 80, 250,21)
        
        self.le3 = QtGui.QLineEdit(self)
        self.le3.setGeometry(20, 110, 250,21)
        
        self.statusBar().showMessage("Ready")
        
        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(exitAction)
        
        self.setGeometry(300, 300, 400, 200)
        self.setWindowTitle('Support from points generator')    
        self.show()
        
        
    
    def showDialog_points(self):
        
        self.fname_points = QtGui.QFileDialog.getOpenFileName(self, 'Open points file', '/home')
        self.statusBar().showMessage(str(self.fname_points))
        self.le1.setText(str(self.fname_points))
        self.fname_points = str(self.le1.text())
        print fname_points

    
    def showDialog_indir_stl(self):
        
        self.fname_stl_indir = QtGui.QFileDialog.getExistingDirectory(self, 'Select STL INPUT directory', '/home')
        self.statusBar().showMessage(str(self.fname_stl_indir))
        self.le2.setText(str(self.fname_stl_indir))
        self.fname_stl_indir = str(self.le2.text())
        print fname_stl_indir 

    def showDialog_outdir_stl(self):
        
        self.fname_stl_outdir = QtGui.QFileDialog.getExistingDirectory(self, 'Select STL OUTPUT directory', '/home')
        self.statusBar().showMessage(str(self.fname_stl_outdir))
        self.le3.setText(str(self.fname_stl_outdir))
        self.fname_stl_outdir = str(self.le3.text())
        print fname_stl_outdir 
  
def main():
    
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    
    
    sys.exit(app.exec_())
    
if __name__ == '__main__':
    main()

Answer

rainer picture rainer · Feb 26, 2013

Qt signals can have arguments that will be passed to the slots they are connected to; an example would be the new value as an argument in a signal changed. Therefore, while you can have a slot with arguments, you cannot define the actual values of those when connecting the signal to the slot, as they will be defined when emitting signal.

For defining an argument at connect time, you can use an additional function which does nothing but calling the original function with the defined argument:

def wrapper():
    funcion(12)

def funcion(a):
    print "Hola mundo" + str(a)

[...]
btn_brow_4.clicked.connect(wrapper)

As a side note: wrapper does not use braces here: the function is not called, but simply passed as an argument to the function connect. In your code, you called your function funcion, which returns nothing (=None), which was passed to connect in your original code, resulting in the error message you received.

To make that a bit cleaner, you can also use an anonymous function:

btn_brow_4.clicked.connect(lambda: funcion(12))

Note that Qt also provides ways of doing this, but (at least for me) the Python variants are easier to read.

Edit: Some more information: http://eli.thegreenplace.net/2011/04/25/passing-extra-arguments-to-pyqt-slot/