TK program problem

Discussion in 'Python' started by bvdp, May 20, 2011.

  1. bvdp

    bvdp Guest

    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!
    bvdp, May 20, 2011
    #1
    1. Advertising

  2. bvdp

    Ian Kelly Guest

    On Fri, May 20, 2011 at 12:03 PM, bvdp <> wrote:
    > 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.


    I'm not a tk user, but it sounds like it has regressed from accepting
    arbitrary callables as callbacks to accepting functions specifically.

    What happens if you replace:

    ("Favorites", selectFav),

    with:

    ("Favorites", lambda: selectFav()),
    Ian Kelly, May 20, 2011
    #2
    1. Advertising

  3. bvdp

    bvdp Guest


    > I'm not a tk user, but it sounds like it has regressed from accepting
    > arbitrary callables as callbacks to accepting functions specifically.
    >
    > What happens if you replace:
    >
    > ("Favorites", selectFav),
    >
    > with:
    >
    > ("Favorites", lambda: selectFav()),


    Okay, this works. Great and thanks! Seems to me that the way I was
    doing it should be alright ... and I've got some other programs
    exhibiting the same problem.

    Before I go "fixing" the issue ... is this known or even a bug?
    bvdp, May 20, 2011
    #3
  4. bvdp

    Ian Kelly Guest

    On Fri, May 20, 2011 at 4:12 PM, bvdp <> wrote:
    > Okay, this works. Great and thanks! Seems to me that the way I was
    > doing it should be alright ... and I've got some other programs
    > exhibiting the same problem.
    >
    > Before I go "fixing" the issue ... is this known or even a bug?


    The docs [1] say that a callback is a function, so I guess that if it
    worked before it was just luck. You should bring it up on the tkinter
    list and see what they have to say about it, though.

    I'm a bit confused about why you would want to use a class as a
    callback anyway. It looks like when the button is clicked it
    instantiates the class and then throws it away?

    [1] http://docs.python.org/library/tkinter.html#tk-option-data-types
    Ian Kelly, May 20, 2011
    #4
  5. bvdp

    bvdp Guest

    Probably the fix is to use a function :)

    > The docs [1] say that a callback is a function, so I guess that if it
    > worked before it was just luck.  You should bring it up on the tkinter
    > list and see what they have to say about it, though.
    >
    > I'm a bit confused about why you would want to use a class as a
    > callback anyway.  It looks like when the button is clicked it
    > instantiates the class and then throws it away?


    I have no idea why I used a class like this, expect that it seemed to
    work at the time. Yes, the class is instantiated when the button is
    clicked. Then, the user stays in the class and uses its methods until
    he hits <close> in the class.

    So, I guess what's happening is that I'm treating the button click
    much like a call to a new program/window which sets some options, etc.
    in the main program.

    You mention the tkinter group. Ummm, what group is that???

    Best,
    bvdp, May 21, 2011
    #5
  6. bvdp

    Ian Kelly Guest

    Ian Kelly, May 21, 2011
    #6
  7. bvdp

    bvdp Guest

    On May 20, 4:29 pm, Ian Kelly <> wrote:
    > On Fri, May 20, 2011 at 5:07 PM, bvdp <> wrote:
    > > You mention the tkinter group. Ummm, what group is that???

    >
    > http://tkinter.unpythonic.net/wiki/TkinterDiscuss


    Thanks. New one for me. I'll subscribe and see if they know about
    this.

    Best,
    bvdp, May 21, 2011
    #7
  8. bvdp

    rantingrick Guest

    On May 20, 6:07 pm, bvdp <> wrote:
    > Probably the fix is to use a function :)
    >
    > > The docs [1] say that a callback is a function, so I guess that if it
    > > worked before it was just luck.  You should bring it up on the tkinter
    > > list and see what they have to say about it, though.

    >
    > > I'm a bit confused about why you would want to use a class as a
    > > callback anyway.  It looks like when the button is clicked it
    > > instantiates the class and then throws it away?

    >
    > I have no idea why I used a class like this, expect that it seemed to
    > work at the time. Yes, the class is instantiated when the button is
    > clicked. Then, the user stays in the class and uses its methods until
    > he hits <close> in the class.


    Thats sounds to me a lot like hammering square pegs though round
    holes... Perhaps you should explain first in "plain english" what
    problem your application is intended to solve, then how it is expected
    to interact with the user, and finally, what exactly is NOT working
    correctly. I would suffix such a documentary with the current source
    code, verbatim.
    rantingrick, May 21, 2011
    #8
  9. bvdp

    Peter Otten Guest

    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.
    Peter Otten, May 21, 2011
    #9
  10. bvdp

    bvdp Guest

    Thanks, Peter, for the detailed explanation. I was going to write a
    bit of sample/minimal code to demo this, but you nicely beat me to
    it!

    > 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"
    > $


    Any idea why I'm not getting any traceback in my program? It just runs
    and appears to ignore the callback.

    > 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.


    Yes, I can confirm that both the lambda and setting the class to:

    class selectFav(object):

    works.

    > 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.


    IIRC, I used the class method since it nicely encapsulates a set of
    operations:

    - create/raise a window
    - list a set of configurable options
    - have <cancel> and <save> buttons, both of which destroy the
    window.

    Having a function as the callback certainly works as well. Not sure
    which is the best method in the long run ... I'm trying to use classes
    more in my programming since it's nice to wrap a bunch of functions up
    like this.

    Thanks again.
    bvdp, May 22, 2011
    #10
  11. bvdp

    bvdp Guest

    On May 20, 4:37 pm, rantingrick <> wrote:

    > Thats sounds to me a lot like hammering square pegs though round
    > holes... Perhaps you should explain first in "plain english" what


    Ahh, but what fun would the Internet, Usenet and programming be
    without round holes and square pegs.

    I thought my English was pretty good.

    > problem your application is intended to solve, then how it is expected


    I'm trying very much to focus on the problem I'm having with a
    particular bit of python code. I'm not trying to have the community
    solve my coding problems.

    > to interact with the user, and finally, what exactly is NOT working
    > correctly. I would suffix such a documentary with the current source
    > code, verbatim.


    You want me to attach several hundred/thousand lines of source code to
    demonstrate that a particular bit of python has changed behavior
    between versions?
    bvdp, May 22, 2011
    #11
  12. bvdp

    Terry Reedy Guest

    On 5/21/2011 8:03 PM, bvdp wrote:

    > Yes, I can confirm that both the lambda and setting the class to:
    >
    > class selectFav(object):


    One of the purposes and advantages of Python 3 is having only one class
    system. Best to always use new-style classes in Python 2.2+ unless you
    understand and need old-style classes (and need should be never for most
    people).

    --
    Terry Jan Reedy.
    Terry Reedy, May 22, 2011
    #12
  13. bvdp

    bvdp Guest


    > One of the purposes and advantages of Python 3 is having only one class
    > system. Best to always use new-style classes in Python 2.2+ unless you
    > understand and need old-style classes (and need should be never for most
    > people).
    >


    Thanks for this. I'll keep it in mind!

    One thing I really don't understand ... is there a difference between
    the old/new forms:

    class foo:
    class foo():

    In cases where I've played with them, they _appear_ to work the same?
    Also, where does one find the magic that says that for a tkinter class
    you should use:

    class foo(object):

    Not really sure where "object" comes from.

    Thanks and best,
    bvdp, May 22, 2011
    #13
  14. bvdp

    rantingrick Guest

    On May 21, 7:03 pm, bvdp <> wrote:
    > IIRC, I used the class method since it nicely encapsulates a set of
    > operations:
    >
    >    - create/raise a window
    >    - list a set of configurable options
    >    - have <cancel> and <save> buttons, both of which destroy the
    > window.


    Ok NOW we are getting somewhere! It is amazing how helpful people can
    be when you explain your problem in "plain English". What you are
    describing here is a dialog (be it modal or not). However your earlier
    description of...

    """Yes, the class is instantiated when the button is clicked. Then,
    the user stays in the class and uses its methods until he hits <close>
    in the class. """

    .... was about as effective as the "orb of confusion" on Patrick...
    *drool*

    > Having a function as the callback certainly works as well. Not sure
    > which is the best method in the long run ... I'm trying to use classes
    > more in my programming since it's nice to wrap a bunch of functions up
    > like this.


    Yes you ABSOLUTELY SHOULD encapsulate the dialog code in a class! THEN
    create an instance of the class inside a "easy to call" function.

    class MyDialog(blah):
    blah,blah,blah

    def show_dialog(*args):
    d = MyDialog(*args)
    return d.result

    result = show_dialog()

    ....and they all lived happily ever after ;-)
    rantingrick, May 22, 2011
    #14
  15. On Mon, May 23, 2011 at 12:03 AM, rantingrick <> wrote:
    > ... was about as effective as the "orb of confusion" on Patrick...
    > *drool*
    >


    That sounds like a Dungeons and Dragons artifact item... invoking the
    orb of confusion is a standard action that does not provoke an Attack
    of Opportunity.

    > class MyDialog(blah):
    > blah,blah,blah
    >
    > def show_dialog(*args):
    > d = MyDialog(*args)
    > return d.result
    >
    > result = show_dialog()


    I don't really see why it shouldn't be valid to use the class itself
    in this way. Once the constructor returns, the object IS the return
    value.

    But then, my opinion may not be valid. I've done weird things in C++
    where, for instance, the invocation of a modeless dialog is:

    new FoobarDialog(params);

    And somewhere it has an event (eg when the window is destroyed) that executes:

    delete this;

    I'm a little mad and generally silly, so my opinion doesn't matter
    (matter matter matter matter). But it did work, and the code was nice
    and clean!

    Chris Angelico
    Chris Angelico, May 22, 2011
    #15
  16. bvdp

    Terry Reedy Guest

    On 5/21/2011 10:20 PM, bvdp wrote:
    >
    >> One of the purposes and advantages of Python 3 is having only one class
    >> system. Best to always use new-style classes in Python 2.2+ unless you
    >> understand and need old-style classes (and need should be never for most
    >> people).
    >>

    >
    > Thanks for this. I'll keep it in mind!
    >
    > One thing I really don't understand ... is there a difference between
    > the old/new forms:
    >
    > class foo:
    > class foo():
    >
    > In cases where I've played with them, they _appear_ to work the same?


    I believe they are. Same is true in 3.x except that the result in a
    new-style class.

    > Also, where does one find the magic that says that for a tkinter class
    > you should use:
    >
    > class foo(object):


    Perhaps nowhere. It may have been an unintended side-effect of the
    change in callable check, or intentional but not documented.

    > Not really sure where "object" comes from.


    It is the base class of all (new-style) classes.
    >>> object()

    <object object at 0x00EB6668>

    --
    Terry Jan Reedy
    Terry Reedy, May 22, 2011
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. pipi
    Replies:
    1
    Views:
    612
    Joseph Millar
    Jul 21, 2003
  2. Rey
    Replies:
    4
    Views:
    10,913
    Roedy Green
    Dec 12, 2003
  3. ola
    Replies:
    3
    Views:
    702
    Marco Schmidt
    Feb 16, 2004
  4. Parvsandhu
    Replies:
    2
    Views:
    406
    Thomas Weidenfeller
    Jul 11, 2005
  5. sylsau
    Replies:
    1
    Views:
    347
    Steve Sobol
    Apr 29, 2006
Loading...

Share This Page