Tkinter button command curiousity

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • mksql@yahoo.com

    Tkinter button command curiousity

    New to Tkinter. Initially, I had some code that was executing button commands at
    creation, rather than waiting for user action. Some research here gave me a
    solution, but I am not sure why the extra step is necessary.

    This causes the "graph" function to execute when the button is created:
    Button(root, text='OK', command=graph(c anvas)))

    However, this waits until the button is pressed (the desired behavior):
    def doit():
    graph(canvas)
    Button(root, text='OK', command=doit))


    Functionally, what is the difference? Why do I need to create a function, to
    call a function, simply to make a button command wait until pressed? Is there a
    better method?

  • Hans-Joachim Widmaier

    #2
    Re: Tkinter button command curiousity

    Am Thu, 08 Jan 2004 15:08:56 -0500 schrieb mksql:
    [color=blue]
    > New to Tkinter. Initially, I had some code that was executing button commands at
    > creation, rather than waiting for user action. Some research here gave me a
    > solution, but I am not sure why the extra step is necessary.[/color]

    There is no "extra step," as you will see.
    [color=blue]
    > This causes the "graph" function to execute when the button is created:
    > Button(root, text='OK', command=graph(c anvas)))[/color]

    Here you are calling graph() immediatly and bind the result to the
    parameter 'command' - most likely not the intended effect (as you
    observed).
    [color=blue]
    > However, this waits until the button is pressed (the desired behavior):
    > def doit():
    > graph(canvas)
    > Button(root, text='OK', command=doit))[/color]

    Here you bind the function object 'doit' to the command parameter, which
    will then be called when the button is clicked on. You could also bind
    the 'graph' function object to command, but then you cannot give it your
    argument 'canvas'. Lambdas are often used to overcome this problem, but
    your 'doit' local function essentially amounts to the same.
    [color=blue]
    > Functionally, what is the difference? Why do I need to create a
    > function, to call a function, simply to make a button command wait until
    > pressed? Is there a better method?[/color]

    The difference is, again:

    def function(blah)
    pass

    Here, 'function(x)' calls the function, whereas 'function' (without the
    parenthethis) is just the function object (which can be bound to a name
    which then later can be used to call it).

    A better method? Would you call this better?

    Button(root, text='OK', command=lambda event, cvs=canvas: graph(cvs))

    Usually, Tkinter programs employ a class that implements the GUI. Here you
    can keep your canvas in a class attribute and use it in the callback
    method:

    class myGUI(Frame):
    # ....
    self.canvas = Canvas(root, ...)
    Button(root, text='OK', command=self.gr aph)

    def graph(self, event=None):
    # Use self.canvas

    Hope this helps,
    Hans-Joachim


    Comment

    • Russell E. Owen

      #3
      Re: Tkinter button command curiousity

      In article <srdrvv47qvrrb6 r26bkuh925phb1l pqkno@4ax.com>,
      mksql@yahoo.com wrote:
      [color=blue]
      >New to Tkinter. Initially, I had some code that was executing button commands
      >at
      >creation, rather than waiting for user action. Some research here gave me a
      >solution, but I am not sure why the extra step is necessary.
      >
      >This causes the "graph" function to execute when the button is created:
      > Button(root, text='OK', command=graph(c anvas)))
      >
      >However, this waits until the button is pressed (the desired behavior):
      > def doit():
      > graph(canvas)
      > Button(root, text='OK', command=doit))
      >
      >
      >Functionally , what is the difference? Why do I need to create a function, to
      >call a function, simply to make a button command wait until pressed? Is there
      >a
      >better method?[/color]

      This is a standard issue with callback functions.

      Suppose you have a trivial function foo:
      def foo(arg1, arg2):
      print "foo(%r, %r)" % (arg1, arg2)

      To use foo as a callback function you need to pass it *AS* a function:
      anobj(callback= foo)
      and that callback had better include the necessary args. If you try to
      specify args when specifying the callback, you end up passing the RESULT
      of the function (the mistake):
      anobj(callback= foo("bar", "baz"))
      foo gets called just once, when creating an obj, and callback gets set
      to None (the result of calling foo with args "bar" and "baz"). Later
      when anobj wants to call the callback, it has nothing to call!

      There are various solutions. The one you chose is excellent. Others
      include:

      -Use lambda to avoid creating a named function, but I find this much
      less readable.

      - Use a "currying" class; this takes an existing function and
      pre-defined arguments and returns a new function that you use as your
      callback. A good example of such a class is:
      <http://aspn.activestat e.com/ASPN/Cookbook/Python/Recipe/52549>. It's a
      nice thing to have around if you do a lot of callbacks, but otherwise I
      find your solution the most readable.

      - If your widget is a class, you may be able to pass your data as class
      instances. This works if there is only one obvious canvas to graph. For
      example:
      class mywdg(Tkinter.F rame):
      def __init__(self, master, etc.)
      Tkinter.Frame._ _init__(self, master)
      self.canvas = Tkinter.Canvas( self,...)
      self.button = Tkinter.Button( self, command=self.gr aph)
      ...
      def graph(self):
      # do stuff to self.canvas

      - As a more sophisticated variant, if you several canvases to graph, and
      want one button for each, you could make the canvases into objects
      (similar to the example above) and pass the right one to the button,
      e.g.:
      for cnvsobj in listofobj:
      abutton = Tkinter.Button( self, command= cnvsobj.graph)

      - Also note that a very few Tkinter functions (such as after) allow you
      to specify a function and additional arguments.

      -- Russell

      Comment

      Working...