[Tutor] How to shorten this code using classes?

Peter Otten __peter__ at web.de
Thu Nov 24 12:14:41 CET 2011

Mic wrote:

>>I chose to ignore the "using classes" part. If you like you can turn the
>>button_clicked() function into a method of a subclass of Button. You can
>>also move the Button configuration done in create_widgets() into the
>>__init__() method of that subclass.
> import tkinter as tk
> from functools import partial
> def button_clicked(button):
>     if button["bg"] == "green":
>         button.configure(bg="red", text="Hi 2")
>     else:
>         button.configure(bg="green", text="Hi 1")
> class Window(tk.Frame):
>     def __init__(self, master):
>         super (Window, self).__init__(master)
>         self.grid()
>         self.create_widgets()
>     def create_widgets(self):
>         for _ in range(2):
>             button = tk.Button(self)
>             command = partial(button_clicked, button)
>             button["command"] = command
>             button.grid()
>             command()
> root = tk.Tk()
> root.title("Test")
> root.geometry("200x200")
> app = Window(root)
> root.mainloop()

> A very elegant solution. Much better than my previous one. However, I am a
> bit unfamiliar with your way of
> coding, I assume you are used to a version other than Python 3.2? 

Yes, though I don't think the difference between 2.x and 3.x matters here.

> Because, never before have I seen either of those

Most tkinter tutorials seem to use

from tkinter import *

which I don't like because it doesn't make explict which names are put into 
the current module's namespace. The alternative

import tkinter

leads to long qualified names.

> import tkinter as tk

is a popular compromise.

> from functools import partial

I use this  kind of explicit import for a few names that I use frequently, 
namely defaultdict, contextmanager, everything from itertools...
I think of these as my personal extended set of builtins ;)

As to the actual partial() function, you probably don't see it a lot because 
it has been in the standard library for only three years. The older idiom 
for making a function that calls another function with a fixed argument is

command = lambda button=button: button_clicked(button)

> I also wonder, if I implement your solution, is there anyway I can place
> the buttons in the program as I would like, or will they be played in a
> straight, vertical row
> always?

You can iterate over (row, column) pairs instead of the dummy _ variable:

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

> Also, assume that I have a already have a window with a button in it. If
> you press this button, this window is supposed to open.
> So, if I press the button, will this window open? Because I created the
> previous window using
> from tkinter import* and not import tkinter as tk.

You can make the decision what style you want to use on a per-module basis. 
In that module you can then access (for example) a tkinter button with 
either tkinter.Button, tk.Button or just Button.
You can even mix styles if you put the respective imports at the beginning 
of the module (not recommended).
What approach you take has no consequences on the execution of the program.

> I hope my English is understandable, because it is not my primary
> language. Thanks for your help, it is greatly appreciated!

Many posters aren't native speakers, so you can never be sure that what you 
pick up here is actually English ;) I didn't have any problems with your 
command of the language, but I'm not a native speaker either.

More information about the Tutor mailing list