TK program problem

Peter Otten __peter__ at web.de
Sat May 21 04:18:59 EDT 2011


bvdp wrote:

> I've just done an update to my system here to Ubuntu 11.04. Mostly no
> problems ... but I have an important (to me) python/TK program that's
> stopped working. Well, it works ... mostly.
> 
> The python version is 2.7.1+ (no idea what the + means!).
> 
> I _think_ I have traced the problem to certain menus which call a
> class. The calls appear to be ignored.
> 
> Basically, what I have is a line like:
> 
> bf = makeButtonBar(root, row=0, column=0, buttons=(
>              ("Quit", self.quitall ),
>              ("Stop", self.stopPmidi ),
>              ("New Dir", self.chd),
>              ("Load Playlist", self.playList),
>              ("Favorites", selectFav),
>              ("Options", setOptions) ) )
> 
> To create a menu bar. The function makeButtonBar() creates the buttons
> with:
> 
> for txt, cmd in buttons:
>         Button(bf, text=txt, height=1, command=cmd).grid(column=c,
> row=0, pady=5)
> 
> 
> All this is fine (and worked perfectly before my upgrade). The menu
> items which are ordinary functions continue to work. BUT the callbacks
> which are classes are just ignored when they are clicked.
> 
> A cut from one of the ignored classes:
> 
> 
> class selectFav:
> 
>     def __init__(self):
>     ...
> 
> And I've inserted some prints in the __init__() and nothing is
> printed. Also, converted the class to new-style () but no change there
> either.
> 
> Either python/tk has changed or my system is totally $*(#*#.
> Suggestions welcome!

Here's a minimal script to reproduces the problem:

$ cat tkcallclass.py
import Tkinter as tk

root = tk.Tk()
root.withdraw()

class Classic:
    def __init__(self):
        print "hello"

button = tk.Button(root, command=Classic)
button.invoke()
$ python2.6 tkcallclass.py
hello
$ python2.7 tkcallclass.py
Traceback (most recent call last):
  File "tkcallclass.py", line 11, in <module>
    button.invoke()
  File "/usr/local/lib/python2.7/lib-tk/Tkinter.py", line 2081, in invoke
    return self.tk.call(self._w, 'invoke')
_tkinter.TclError: invalid command name "__main__.Classic"
$

In 2.7 the Tkinter code was changed to use hasattr(obj, "__call__") instead 
of callable(obj) to recognize callbacks. This gives different results for 
oldstyle classes

>>> class A: pass
...
>>> callable(A)
True
>>> hasattr(A, "__call__")
False

...and they are no longer registered automatically with Tkinter. In theory 
you could register them explicitly yourself

$ cat tkcallclass2.py
import Tkinter as tk

root = tk.Tk()
root.withdraw()

class Classic:
    def __init__(self):
        print "hello"

button = tk.Button(root, command=root.register(Classic))
button.invoke()
$ python2.7 tkcallclass2.py
hello

but in practice changing them to newstyle (i. e. have them inherit from 
object) or wrapping them in a lambda appears convenient.

Personally, I would reconsider whether using a class as a callback is really 
necessary. Replacing the class with a function should be a straightforward 
process.




More information about the Python-list mailing list