Tkinter callback arguments

Alf P. Steinbach alfps at start.no
Mon Nov 2 05:37:43 EST 2009


* Peter Otten:
> Alf P. Steinbach wrote:
> 
>>>     for x in range(0,3):
>>>         Button(......, command=lambda x=x: function(x))
>> An alternative reusable alternative is to create a button-with-id class.
>>
>> This is my very first Python class so I'm guessing that there are all
>> sorts of issues, in particular naming conventions.
> 
> Pseudo-private attributes

That means there is some way of making attributes private?

Probably that comes across as an inane question but I ask it anyway. I haven't 
really started to look at Python classes. I'm guessing that by asking here I may 
learn things that are not obvious from the documentation.


>, javaesque getter methods,

What do you mean by that?

What I associate with Java getter method is mainly the "get" prefix, for Java 
introspection.


> unidiomatic None-checks

What's the idiomatic Python way for an optional thing?

In this case one alternative I see could be to get rid of the __on_tc_command 
method and more directly tell tkinter.Button to call the relevant function, 
doing the if-else choice once only in the IdButton constructor.

Is that what you mean?

I'm thinking more in terms of customization points when I write code.

So I tend to avoid hardcoding things internally in methods, instead designing 
the choices out to where they're accessible to client code.


>, broken naming conventions (**args),

How to do this argument forwarding in modern style?

Or is there an alternative to argument forwarding for this example?


> spaces in funny places...

Bah. ;-)


>> And the idea of creating a reusable solution for such a small issue may be
>> un-pythonic?
> 
> Screw pythonic, the signal/noise ratio is awful in any language.
>  
>> But just as an example, in Python 3.x,
> 
> ...for achieving less in more lines?

Now, that's a good Python-independent question! :-)

Re your question's the number of lines: /if/ the code is heavily reused then the 
number of lines doesn't matter since they're only written /once/; the net effect 
can even be to reduce the total number of lines, or at least the number of 
apparent function points (or whatever size metric). That's part of what 
"reusable" means. For example, if the OP used the code then he or she didn't 
type them lines, but just copy/paste'd them, less work than typing in a lambda 
definition in every button creation, and more clear code at every creation.

Re your question's what (the) reusability achieves.

First, when you and others have used such a thing in a number of places then you 
gain confidence in correctness. For example, you don't wonder whether Python's 
str type is correct, and when you test your program you don't test the str type 
implementation. Since it's been used so much you know that it (mainly) is 
correct, and that any remaining bug in there can't be all that serious, because 
if it was then it would've surfaced in earlier use of the type.

This advantage of confidence in correctness can be realized even without heavy 
reuse, because the encapsulation that's necessary for reuse, here having the 
code in a class, also makes it possible with more centralized testing.

A centralized, encapsulated piece of code can be tested to death and deemed 
correct (enough) and frozen, while the application of a code pattern in umpteen 
places in umpteen programs, generally can't.

Second, when you do find a bug, or need more functionality, or whatever, there's 
just /one place/ to fix/extend, whatever, instead of updating umpteen places in 
umpteen programs, and trying to be sure that you've covered all instances and 
done the right thing every place regardless of local variations (which is pretty 
hopeless in general, but my experience with that kind of badness has mostly been 
with C, not Python). More technically, it's reduced redundancy, in this case 
avoiding redundant application of a code pattern everywhere one needs a button 
with id (if that happens often). Reduced redundancy = good.

And third, as I mentioned, at every button creation, or in general every place 
you'd use inline code rather than some reusable thing, you can get more clear 
code, which can (but will not automatically :-) ) reduce maintainance time.

But, the big drawback. It's easy to become enamoured by reusability and invest a 
lot of work in encapsulation and general reusability, since it has those three 
big desirable traits. But when what one creates is not actually reused then most 
of that work can be /wasted/... :-) For example, if the code only is used in 1 
place, then the advantage of centralized testing is pretty moot unless that code 
is changing for other reasons, for it doesn't matter much if you test it here or 
there. It can be simpler to test it here, inline, than over there.

And another big drawback, but I believe it's less important in Python, that a 
reusable thing can be less efficient because it can't take advantage of locally 
available information and functionality each place where it's used, while inline 
code that achieves the same (applying a pattern) can take advantage.


>> <code>
>> import tkinter
>> # I guess for Python 2.x do "import Tkinter as tkinter" but haven't
>> # tested.
>>
>>
>> class IdButton( tkinter.Button ):
>>      def __init__( self, owner_widget, id = None, command = None, **args
>>      ):
>>          tkinter.Button.__init__(
>>              self, owner_widget, args, command = self.__on_tk_command
>>              )
>>          self.__id = id
>>          self.__specified_command = command
>>
>>      def __on_tk_command( self ):
>>          if self.__specified_command != None:
>>              self.__specified_command( self )
>>          else:
>>              self.on_clicked()
>>
>>      def on_clicked( self ):
>>          pass
>>      def id( self ):
>>          return self.__id
>>      def id_string( self ):
>>          return str( self.id() );
>>
>>
>> def on_button_click( aButton ):
>>      print( "Button " + aButton.id_string() + " clicked!" )
>>
>> window = tkinter.Tk()
>>
>> n_buttons = 3
>> for x in range( 1, n_buttons + 1 ):
>>      IdButton(
>>          window, id = x, text = "Button " + str( x ), command =
>>          on_button_click ).pack()
>>
>> window.mainloop()
>> </code>
> 
> I'm not grumpy, I just don't like your code ;) And I don't like the notion 
> that you are about to spread this style with your book...

He he. :-)  I've yet to acquire any Python style.


Cheers, & thanks,

- Alf



More information about the Python-list mailing list