faqts : Computers : Programming : Languages : Python : Snippets : Tkinter

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

6 of 6 people (100%) answered Yes
Recently 5 of 5 people (100%) answered Yes

Entry

Identifying callback button?

Jul 5th, 2000 10:00
Nathan Wallace, unknown unknown, Hans Nowak, Snippet 158, Randall Hopper


"""
Packages: gui.tkinter
"""
#!/usr/bin/env python
#
#  closure.py  -  Demonstrate various ways to generate and use 
#                 parameterized callback functions.
#
#                 Examples are shown for both total and partial resolution
#                 of callback arguments at registration time.
#
#                 Techniques:  lambdas, callable objects, deferred execution.
#
class Call:
  """Instances of this class store a function as well as a list of arguments.
     When they are called, the function will be called together with the
     arguments used for creating the instance.
     Slightly different than lambda, but nicer syntax."""
  def __init__ (self, func, *args):
    self.func = func             # save the function (or bound method, or...)
    self.args = args             # save the arguments to use 
  def __call__ (self):
    apply (self.func, self.args) # call function, using args as arguments.
class CallbackHookFull:
  """
  Demonstrates various ways to do full resolution of callback arguments
  at registration time, and return/invoke the callback.
  """
  def ExecCommand(self,command):
    exec command
  def DoIt(self,color):
    """ The callback we eventually want to end up calling, but with no args. """
    print color
  def Invoke(self):
    self.doit_cb()
  #---------------------------------------------------------------------
  def Register1( self, color ):
    """ Generate a callable class that'll invoke the callback w/ args """
    self.doit_cb = Call( self.DoIt, color )
  #---------------------------------------------------------------------
  class ColorCallback:
    def __init__( self, obj, color ):
        self.obj   = obj
        self.color = color
    def callback( self ):
        self.obj.DoIt( self.color )
  def Register2( self, color ):
    """ Wrap the args in a class, and have a class method with the appropriate
        signature do the calling.  """
    self.doit_cb = CallbackHookFull.ColorCallback( self, color).callback
  #---------------------------------------------------------------------
  def Register3a( self, color ):
    """ Generate a closure which is a wrapper around the invocation w/ args """
    self.doit_cb = lambda callback=self.DoIt, color=color: \
      callback(color)
  #---------------------------------------------------------------------
  def Register4a( self, color ):
    self.doit_cb = lambda callback=self.DoIt, color=color: \
      callback(color)
  #---------------------------------------------------------------------
  def Register5a( self, color ):
    """ Build a string with the invocation we want, and then build a closure
        that will interpret that string when invoked. """
    self.doit_cb = lambda self=self, command="self.DoIt('"+color+"')": \
      self.ExecCommand(command)
  #---------------------------------------------------------------------
  def BuildCallback3b( self, callback, color ):
    return lambda callback=callback, color=color: callback(color)
  def Register3b(self,color):
    """ Nicer-looking 3a with lambda generated by a function """
    self.doit_cb = self.BuildCallback3b( self.DoIt, color );
  #---------------------------------------------------------------------
  def BuildCallback4b( self, callback, color ):
    def _( callback=callback, color=color ): callback(color)
    return _
  def Register4b(self,color):
    """ Same as the 3b, but use def instead of lambda; exactly the same
        because functions are just named lambdas. """
    self.doit_cb = self.BuildCallback3b( self.DoIt, color );
  #---------------------------------------------------------------------
  def BuildCallback5b( self, callback, color ):
    return lambda self=self, command=callback+"('"+color+"')" : \
      self.ExecCommand(command)
  def Register5b(self,color):
    """ Nicer-looking 5a with lambda generated by a function """
    self.doit_cb = self.BuildCallback5b( "self.DoIt", color )
#=============================================================================
class CallbackHookPartial:
  """
  Demonstrates various ways to do partial resolution of callback arguments
  at registration time (i.e. for cases where the callback takes argument(s) at
  invocation time as well as registration time), and return/invoke the callback.
  """
  def ExecCommand(self,command):
    exec command
  def DoIt(self, color, what_to_paint):
    """ The callback we eventually want to end up calling, but with one arg
        (what_to_paint) instead of two. """
    print what_to_paint + " = " + color
  def Invoke(self):
    self.doit_cb( "walls" )
  #---------------------------------------------------------------------
  class ColorCallback:
    def __init__( self, callback, color ):
        self.cb    = callback
        self.color = color
    def callback( self, what_to_paint ):
        self.cb( self.color, what_to_paint )
  def Register2( self, color ):
    """ Wrap the args in a class, and have a class method with the appropriate
        signature be the callback.  """
    self.doit_cb = CallbackHookPartial.ColorCallback(self.DoIt,color).callback
  #---------------------------------------------------------------------
  def Register3a( self, color ):
    """ Generate a closure which is a wrapper around the invocation w/ args """
    self.doit_cb = lambda what_to_paint, callback=self.DoIt, color=color:\
      callback(color, what_to_paint)
  #---------------------------------------------------------------------
  def BuildCallback3b( self, callback, color ):
    return lambda what_to_paint, callback=self.DoIt, color=color: \
      callback(color, what_to_paint)
  def Register3b(self,color):
    """ Nicer-looking 3a with lambda generated by a function """
    self.doit_cb = self.BuildCallback3b( self.DoIt, color );
  #---------------------------------------------------------------------
  def BuildCallback4b( self, callback, color ):
    def _( what_to_paint, callback=self.DoIt, color=color ): \
      callback(color, what_to_paint)
    return _
  def Register4b(self,color):
    """ Same as the 3b, but use def instead of lambda; exactly the same
        because functions are just named lambdas. """
    self.doit_cb = self.BuildCallback3b( self.DoIt, color );
if __name__ == "__main__":
  cb = CallbackHookFull()
  cb.Register1('orange')
  cb.Invoke()
  cb.Register2('white')
  cb.Invoke()
  cb.Register3a('blue')
  cb.Invoke()
  cb.Register5a('red')
  cb.Invoke()
  cb.Register3b('green')
  cb.Invoke()
  cb.Register4b('purple')
  cb.Invoke()
  cb.Register5b('yellow')
  cb.Invoke()
  print "--------"
  cb = CallbackHookPartial()
  cb.Register2('white')
  cb.Invoke()
  cb.Register3a('blue')
  cb.Invoke()
  cb.Register3b('green')
  cb.Invoke()
  cb.Register4b('purple')
  cb.Invoke()