[Tutor] Shortening the code

Mic o0MB0o at hotmail.se
Sat Nov 26 12:36:35 CET 2011


>> Alright! What is a fixed argument?


>Its onre that is the same every time the function is called.

>The lambda  construct above is equivalent to the following which may
>make it clearer:

>def button_clicked(aButton):
      # do something with aButton here
      # that  uses a lot of code and
      # we want to reuse for any button

>Now we want to use that function as an event handler, but the function
>passed as the command in tkinter is not allowed to take an argument.

>So we create a very short function that takes no arguments but calls
>button_clicked with a fixed argument. And we do this for two buttons
>so that they can both use the button_clicked

>def button1_clicked():
    > button_clicked(buttonOne)

>def button2_clicked():
    > button_clicked(buttonTwo)

>and we can use these whehn creating our buttons:

>buttonOne = Button(parent, command=button1_clicked, ....)
>buttonTwo = Button(parent, command=button2_clicked, ....)

>Now a shorter way of doing that, which avoids writing lots of these very
>small functions is to use lambda, which is an operator that returns a
>function as a value.

>Thus

>def add2(x): return x+2

>can be written

>add2 = lambda x: x+2

>And if we don't really care about the name - which is
>true for the button commands we can put lambda directly
>into the widget creation:

>buttonOne = Button(parent,
                    command=lambda b=buttonOne: button_clicked(b), ....)

>buttonTwo = Button(parent,
                    command=lambda b=buttonTwo: button_clicked(b), ....)

>Note that this works because the lambda defines a default argument for
>the command. Thus Tkinter can call the function with no arguments and
>Python will insert the default value at runtime.


This was a lot of new information for me so it might take some time for it 
to sink in.
Thanks for the information!


>>> While its perfectly legal Python to create a class inside a method its
>>> very unusual in practice and very restricting in the use of the class.
>>
>> Why is it restricting?

>Because the class is only available inside the function. You cannot
>create objects of that class anywhere outside the class.

And that is not good because you want to be able to create objects of a 
class
outside the class mostly?


>Note you are setting text to '01' in every case. Thats probably not what
>you want?

No, that is not what I want.  The text should be 1 for button one, 2 for 
button two and so
on. I managed to solve that, but your solution above seems like a better 
alternative to
solving that problem.

> def create_widgets(self):
>     list_chair=[(0, 0), (0, 1), (0, 3), (0, 4), (1,0)]
>     for row, column in list_chair:
>        button = tk.Button(self)
>        command = partial(button_clicked, button)
>        button["command"] = command
>        button.grid(row=row, column=column)
>        command()

>As stated above this will result in every chair being green
>and having the text '01' It would be better to put the initial colour
>and text in your data list and configure each button directly:

Is there any difference between a list and a data list?

>def create_widgets(self):
      >list_chair=[(0, 0, '01'), (0, 1, '02'),
                  (0, 3, '03'), (0, 4, '04'),
                  (1, 0, '05')]
      >for row, column, name in list_chair:
         >command = partial(button_clicked, button)
         >button = tk.Button(self, color='green',
                            >command=command, text=name)
         >button.grid(row=row, column=column)


This solution was better than mine since it is possible
to place the widgets where you want, which I couldn't do.


So, using your suggestions I get this:

import tkinter as tk
from functools import partial

def button_clicked(button):
    if button["bg"] == "green":
        button.configure(bg="red")
    else:
        button.configure(bg="green")

class Window(tk.Frame):
    def __init__(self, master):
        super (Window, self).__init__(master)
        self.grid()
        self.create_widgets()

    def create_widgets(self):
        list_chair=[(0, 0, '01'), (0, 1, '02'),
                  (0, 3, '03'), (0, 4, '04'),
                  (1, 0, '05')]
        for row, column, name in list_chair:
            command = partial(button_clicked, button)
            button = tk.Button(self, color='green',
                            command=command, text=name)
            button.grid(row=row, column=column)




root = tk.Tk()
root.title("Test")
root.geometry("200x200")
app = Window(root)
root.mainloop()

However, nothing happens when I run the program.









>This is one of the good habits in programming. Do it step by step. Get
>one thing working first before trying to add more features. Its a lot
>easier to debug code when you know what you changed to stop it working.

Thanks for the suggestions.



>Yes, but lets get the UI all done first, then we can add the button
>features.

UI means user interface, right?

>>> You should only ever have one Tk() object in a Tkinter program.

>> Why is that?

>Because thats how Tkinter expects to work! It builds a tree of all the
>windows and widgets in your program. If you have two trees in the same
>program it can get very confused about which widget is doing what to
>which other widgets, especially if they wind up in different trees!.

Alright, I understand. Thanks a lot for the help.


Mic




More information about the Tutor mailing list