70

I want to pass the arguments to a function when I click the button. What should I add to this line button.connect(button, QtCore.SIGNAL('clicked()'), calluser(name)) so it will pass the value to the function:

def calluser(name):
    print name

def Qbutton():
    button = QtGui.QPushButton("button",widget)
    name = "user"
    button.setGeometry(100,100, 60, 35)
    button.connect(button, QtCore.SIGNAL('clicked()'), calluser(name))

One more thing, buttons will be generated using for loop; so name value will vary. So I want to attach each name with the button. I have done same thing in Pytk by using for loop and calling the argument base function when clicked.

2
  • 1
    you need to set the variable "name" somewhere.. where is that being set? Commented Jul 21, 2011 at 23:41
  • 1
    The lambda method works for me and maintains the Qt program structure: stackoverflow.com/a/53928495/7944058 Commented Jun 20, 2019 at 20:50

7 Answers 7

130

You can simply write

name = "user"
button.clicked.connect(lambda: calluser(name))
Sign up to request clarification or add additional context in comments.

6 Comments

if the above does not work (for example inside a loop), do this: button.clicked.connect(lambda state, x=name: calluser(x))
@magamig why does the above not work inside a loop?
each time signal will be assigned to same function, at the end of loop last item will be assigned to method. thats why it doesnt work inside loop, you should think different logic
I tried lambda x=id: my_func(x) but it didn't work and I don't understand why because lambda _, x=id: my_func(x) works perfectly. Can someone explain?
@Leroy sorry to necrobump, but I believe the behaviour you are seeing it because the lambda needs to take event as a argument since this is passed into it by PyQt. A lambda should be defined as: button.clicked.connect(lambda event, var='value': func(var)). Hope this helps anyone else coming across this thread
|
44

I tried an efficient way of doing it and it worked out well for me. You can use the code:

from functools import partial

def calluser(name):
    print name

def Qbutton():
    button = QtGui.QPushButton("button",widget)
    name = "user"
    button.setGeometry(100,100, 60, 35)
    button.clicked.connect(partial(calluser,name))

3 Comments

very elegant :D
Has anyone actually tried this with multiple names before upvoting? Partial will freeze the name. I.e. once it is set to "user", there is no way you can change it via the GUI.
partial is very helpful when you connect inside forloops where your variables overwritten in loops.
42

Usually GUIs are built using classes. By using bound methods as callbacks (see self.calluser below) you can "pass" information to the callback through self's attributes (e.g. self.name):

For example, using slightly modified code from this tutorial:

import sys
import PyQt4.QtCore as QtCore
import PyQt4.QtGui as QtGui

class QButton(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.button = QtGui.QPushButton('Button', self)
        self.name='me'
        self.button.clicked.connect(self.calluser)
    def calluser(self):
        print(self.name)

def demo_QButton():
    app = QtGui.QApplication(sys.argv)
    tb = QButton()
    tb.show()
    app.exec_()

if __name__=='__main__':
    demo_QButton()

Since the callback per se is always called with no additional arguments, when you need to pass distinct additional information to many callbacks, you need to make different callbacks for each button.

Since that can be laborious (if done manually), use a function factory instead. See below for an example. The function factory is a closure. It can be passed additional arguments, which the inner function can access when called:

class ButtonBlock(QtGui.QWidget):

    def __init__(self, *args):
        super(QtGui.QWidget, self).__init__()
        grid = QtGui.QGridLayout()
        names = ('One', 'Two', 'Three', 'Four', 'Five',
                 'Six', 'Seven', 'Eight', 'Nine', 'Ten')
        for i, name in enumerate(names):
            button = QtGui.QPushButton(name, self)
            button.clicked.connect(self.make_calluser(name))
            row, col = divmod(i, 5)
            grid.addWidget(button, row, col)
        self.setLayout(grid)

    def make_calluser(self, name):
        def calluser():
            print(name)
        return calluser

app = QtGui.QApplication(sys.argv)
tb = ButtonBlock()
tb.show()
app.exec_()

4 Comments

Thanks for the reply, but in my case the value of the name will change as the buttons will be generated by a for loop. So can I pass the values as arguments to the function?
+1; the calluser-Function can also access any methods or attributes via self.* This works, where lambda functions fail!
@LuWi Do you know why the lambda functions fail?
I guess it has something to do with scopes, but I'm not entierly sure. I was glad to get it working ;)
6

In Python, class instances are callable. You can just use an instance of a class as a function. That class can contain whatever you want. (In some languages or frameworks, a callable object is called a functor or a function object.)

class CallUser:
    def __init__(self, name):
        self.name = name
    def __call__(self):
        print(self.name)

def Qbutton():
    button = QtGui.QPushButton("button",widget)
    name = "user"
    button.setGeometry(100,100, 60, 35)
    button.clicked.connect(CallUser(name))
    # Object of type CallUser will work as a function!

Comments

5

Here is another way. --- PARTIAL -- I find this one most simple:

    widget = QWidget()
    widgetLayout = QVBoxLayout()

    for action in list:

        button = QPushButton("{action}".format(action=action['name']),self)
        button.clicked.connect(partial(self.action_selected,action=action['name']))
        widgetLayout.addWidget(button)

    widget.setLayout(widgetLayout)

def action_selected(self,action):
    print action

found on: http://tech-artists.org/forum/showthread.php?3118-PyQt-Maya-How-to-pass-arguments-to-a-function-when-connecting-it-to-PyQt-button

1 Comment

It doesn't seem to work if the input argument changes. E.g. button.clicked.connect(partial(self.function, self.currentSelection)) if self.currentSelection is changed. Then you should set the argument inside the function via self, or create a nested function.
0

The code shown below illustrates a method of associating data with generated buttons. For example, you could change the statement self.keydata[b] = key to store a tuple of data into self.keydata[b] for use when processing a button event later.

Note, in the following code, assets is a previously-defined dictionary containing titles for buttons. In the processButton(self) routine, self.sender() is equal to an entry in the class variable buttons[].

class Tab5(QtGui.QWidget):
    buttons, keydata = {}, {}
    def __init__(self, fileInfo, parent=None):
        super(Tab5, self).__init__(parent)
        layout = QtGui.QVBoxLayout()

        for key in sorted(assets):
            b = self.buttons[key] = QtGui.QPushButton(assets[key], self)
            b.clicked.connect(self.processButton)
            layout.addWidget(b)
            print 'b[key]=',b, ' b-text=',assets[key]
            self.keydata[b] = key

        layout.addStretch(1)
        self.setLayout(layout)

    def processButton(self):
         print 'sender=',self.sender(), ' s-text=',self.sender().text(), ' data[.]=', self.keydata[self.sender()] 
         pass

Output was like the following, where the first four lines printed during the for loop, and the last four printed when the four buttons were pressed in order.

b[key]= <PySide.QtGui.QPushButton object at 0x7f382f2ca830>  b-text= K1
b[key]= <PySide.QtGui.QPushButton object at 0x7f382f2ca908>  b-text= K2
b[key]= <PySide.QtGui.QPushButton object at 0x7f382f2ca950>  b-text= K3
b[key]= <PySide.QtGui.QPushButton object at 0x7f382f2ca998>  b-text= K4
sender= <PySide.QtGui.QPushButton object at 0x7f382f2ca830>  s-text= K1  data[.]= L1
sender= <PySide.QtGui.QPushButton object at 0x7f382f2ca908>  s-text= K2  data[.]= L2
sender= <PySide.QtGui.QPushButton object at 0x7f382f2ca950>  s-text= K3  data[.]= L3
sender= <PySide.QtGui.QPushButton object at 0x7f382f2ca998>  s-text= K4  data[.]= L4

Comments

0

You can do with parameters as well,

button.cliked.connect(lambda self.change_value( "extra"))

the function

def change_value( extraVariable):
     print( extraVariable)

1 Comment

Isn't there a c and a : missing? Shouldn't it be: button.clicked.connect(lambda: self.change_value( "extra")) And I don't see anything new compared to @ozcanyarimdunya's answer from 2019.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.