2

I wonder if it's possible to create a list of "def", to connect a list of 20 buttons, and to have a different callback for each button.

Thanks

Guillaume
  • 2,752
  • 5
  • 27
  • 42
  • do you mean find the list of functions as defined in a module, or a class ? You can do this in different ways - but the best way is to use the inspect module. – Tony Suffolk 66 Nov 22 '14 at 10:47
  • yes, I would like to connect each button (construct by `self.buttons = [gtk.Button() for n in xrange(20)]` ) in 20 differents callbacks. I will study for inspect module to do this. Thanks – Guillaume Nov 22 '14 at 10:52
  • 1
    I think one of the things to ask yourself is why do you want the code to be so dynamic (i.e. determined at run-time). – Tony Suffolk 66 Nov 22 '14 at 11:02
  • I fact, each button as an own image and label (different each time), and the user choose a result by clicking on the button. – Guillaume Nov 22 '14 at 11:16
  • Do you _really_ need 20 different callbacks, though? When you register a widget callback using the widget's `connect()` method you can supply extra arguments that the callback will be called with, which gives you a lot of flexibility. – PM 2Ring Nov 22 '14 at 11:18
  • I have a maximum of 20 button. However, it can be less. So, the `connect()` method with the same def can't reconize what is the button clicked. But it's possible too that I have not found the good argument ! (I'm beginner in python) – Guillaume Nov 22 '14 at 11:24
  • Well, there are several ways for the callback to identify which button is clicked. I'll post a demo program that shows some of them. – PM 2Ring Nov 22 '14 at 11:41

2 Answers2

2

def in Python is just a regular statement that binds a name to a function/closure. For example you can write

flist = []
for i in range(30):
    def func(x, i=i):
        print(x * i)
    flist.append(func)

after that flist[7](6) will return 42.

The tricky part is only i=i in the above declaration. This is needed because a closure caputures the variable, not the current variable value. Without that i=i all of the functions would be using the same variable i used for looping.

Python also has some support for anonymous functions so in the above simple case the code could be shortened to

flist = []
for i in range(30):
    flist.append(lambda x, i=i: print(x * i))

but lambdas are very limited (just a single expression, no statements)

6502
  • 112,025
  • 15
  • 165
  • 265
  • Thanks, i will try to adapt this in my code. I will tell you if it works. – Guillaume Nov 22 '14 at 11:00
  • The default argument hack is cleaner in Python 3 if you use a keyword-only argument, e.g. `lambda x, *, i=i: print(x * i)`. I prefer to create a new closure instead: `(lambda j: lambda x: print(x * j))(i)`. – Eryk Sun Nov 22 '14 at 13:23
  • @eryksun: the double-lambda is idiomatic in Javascript (where it's the only solution to the problem), the default argument `i=i` is instead idiomatic in Python. – 6502 Nov 22 '14 at 13:25
  • It isn't clean since it changes the function's signature, opening the possibility for bugs. At least using a keyword-only argument curtails that. As to idiom, the default argument hack is a legacy that predates the introduction of lexical closures for function scopes. It was always a hack. – Eryk Sun Nov 22 '14 at 13:31
  • @eryksun: Interesting bit of history, I somehow thought that lexical closures where in from first versions of Python. Indeed with python 1.5 the default argument trick works and the double-lambda doesn't. – 6502 Nov 22 '14 at 13:57
1

Rather than cluttering up your program with a multitude of callback functions you can do what you want with a single callback function.

The program below uses a helper function to create each button. This function passes two items of user data to each button's connect method: the button's label text and the button's index number. When the callback is called, it receives these items of data, as well as the button widget itself. The callback can use the button widget's get_label() method to identify the button, but passing the index number is often convenient. Passing the label text as user data is a bit redundant here, but I only did it to demonstrate the technique. (What do you expect from a demo program written in 20 minutes? :) )

#!/usr/bin/env python

''' A simple gtk2+ button demo

    Written by PM 2Ring 2014.11.22
'''

import pygtk
pygtk.require('2.0')
import gtk


class ButtonDemo(object):
    def button_cb(self, widget, text, data):
        print "Button '%s' clicked. Data=%s. Label '%s'" % (text, data, widget.get_label())
        return True

    def quit(self, widget): gtk.main_quit()

    def __init__(self):
        win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        width = gtk.gdk.screen_width() // 2
        height = gtk.gdk.screen_height() // 8
        win.set_size_request(width, height)
        win.set_title("GTK Button demo")
        win.set_border_width(10)

        win.connect("destroy", self.quit)

        box = gtk.HBox()
        box.show()
        win.add(box)

        def make_button(text, data):
            button = gtk.Button(label=text)
            button.connect('clicked', self.button_cb, text, data)
            button.show()
            return button

        base = ord('A')
        for i in xrange(5):
            button = make_button('Button_%s' % chr(base + i), i)
            box.pack_start(button, True)

        button = gtk.Button('_Quit')
        button.show()
        box.pack_start(button, False)
        button.connect("clicked", self.quit)

        win.show()


def main():
    ButtonDemo()
    gtk.main()


if __name__ == "__main__":
    main()
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Thanks for your job ! I have successfuly adapted the 6502's code, but I don't know what is the best solution... Thanks again – Guillaume Nov 22 '14 at 12:00
  • 1
    My pleasure. 6502 answered the question you asked, so it's only fair that you accept his answer. I answered the question that you didn't know to ask. :) See [What is the XY problem?](http://meta.stackexchange.com/a/66378) – PM 2Ring Nov 22 '14 at 12:05
  • Exact ! There's so many way to find a solution... – Guillaume Nov 22 '14 at 12:08