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