views:

416

answers:

3

After a bind a method to an event of a Tkinter element is there a way to get the method back?

>>> root = Tkinter.Tk()
>>> frame = Tkinter.Frame(root, width=100, height=100)
>>> frame.bind('<Button-1>', lambda e: pprint('Click')) # function needed
>>> frame.pack()
>>> bound_event_method = frame.???
A: 

Doesn't appear to be... why not just save it yourself if you're going to need it, or use a non-anonymous function?

Also, your code doesn't work as written: lambda functions can only contain expressions, not statements, so print is a no-go (this will change in Python 3.0 when print() becomes a function).

Dan
Cristian used pprint, which is okay for a lambda.
nosklo
@nosklo: That was changed in an edit.
skymt
+1  A: 

The associated call to do that for the tk C API would be Get_GetCommandInfo which

places information about the command in the Tcl_CmdInfo structure pointed to by infoPtr

However this function is not used anywhere in _tkinter.c which is the binding for tk used by python trough Tkinter.py.

Therefore it is impossible to get the bound function out of tkinter. You need to remember that function yourself.

Florian Bösch
+1  A: 

The standard way to do this in Tcl/Tk is trivial: you use the same bind command but without the final argument.

bind .b <Button-1> doSomething
puts "the function is [bind .b <Button-1>]"
=> the function is doSomething

You can do something similar with Tkinter but the results are, unfortunately, not quite as usable:

e1.bind("<Button-1>",doSomething)
e1.bind("<Button-1>")
=> 'if {"[-1208974516doSomething %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y %D]" == "break"} break\n'

Obviously, Tkinter is doing a lot of juggling below the covers. One solution would be to write a little helper procedure that remembers this for you:

def bindWidget(widget,event,func=None):
    '''Set or retrieve the binding for an event on a widget'''

    if not widget.__dict__.has_key("bindings"): widget.bindings=dict()

    if func:
        widget.bind(event,func)
        widget.bindings[event] = func
    else:
        return(widget.bindings.setdefault(event,None))

You would use it like this:

e1=Entry()
print "before, binding for <Button-1>: %s" % bindWidget(e1,"<Button-1>")
bindWidget(e1,"<Button-1>",doSomething)
print " after, binding for <Button-1>: %s" % bindWidget(e1,"<Button-1>")

When I run the above code I get:

before, binding for <Button-1>: None
 after, binding for <Button-1>: <function doSomething at 0xb7f2e79c>

As a final caveat, I don't use Tkinter much so I'm not sure what the ramifications are of dynamically adding an attribute to a widget instance. It seems to be harmless, but if not you can always create a global dictionary to keep track of the bindings.

Bryan Oakley