[Tutor] Question on tkinter event binding

Steven D'Aprano steve at pearwood.info
Sat Dec 4 15:49:26 CET 2010


Albert-Jan Roskam wrote:

> Meanwhile, I tinkered a bit more with the code. I used exec() to isolate the 
> event handler function. It works and it's better, but I think it could be still 
> better. I'm not so fond of eval() and exec().

They have their uses, but yes, it's best to avoid them unless you need 
them. Let's see if we can avoid them :)


> from Tkinter import *
> 
> def createWidgets(veldnamen):
>     root=Tk()
>     termenlijst = {"Naam": set(["Bill Gates", "Elvis Presley"]),
>                    "*Postcode": set(["2600AA", "8000BB"]),
>                    "Adres": set(["Street", "Avenue"])}
>     handleDeletions = {}
>     for veldnaam in veldnamen:
>         labelWidget=Label(root, text=veldnaam, takefocus=False)
>         labelWidget.grid()
>         # tcl names must start with a lowercase letter
>         tclName = veldnaam[0].lower() + veldnaam[1:]
>         content = StringVar()
>         entryWidget=Entry(root, name=tclName, textvariable=content)
>         entryWidget.grid()
> 
>         exec(doHandleDeletion())
>         handleDeletions[entryWidget] = handleDeletion

The classic solution for callbacks is to use lambda, but of course 
lambda is limited to a single expression and won't do the job here. So 
what you need is a factory function that returns a new function:

         handleDeletions[entryWidget] = make_deletion_handler()


and the factory itself is defined something like this:


def make_deletion_handler():
     # Create a function.
     def handleDeletion(event, widget=entryWidget, root=root,
                        termenlijst=termenlijst, content=content):
         actieveVenster = root.focus_get()
         actieveVensternaam = str(actieveVenster)[1:].capitalize()
         if actieveVensternaam.startswith("*"):
             actieveVensternaam = "*"+actieveVensternaam[1:].capitalize()
             vensterinhoud = content.get().strip()
             print "Name: %s -- Contents: %s" \
                   % (actieveVensternaam, vensterinhoud)
             try:
                 termenlijst[actieveVensternaam].remove(vensterinhoud)
                 actieveVenster.delete(0, END)
                 print "Deleted term '%s'" % vensterinhoud
             except KeyError:
                 print "No such term '%s'" % vensterinhoud
     # And return it.
     return handleDeletion


If you move the factory outside of your createWidgets function, you will 
need to explicitly pass arguments entryWidget, content, etc. to the 
factory. Otherwise you can nest the factory inside createWidgets, at it 
will pick the variables up automatically.


Hope this helps,



-- 
Steven


More information about the Tutor mailing list