Section 15.8 The Design of GUI Programs
For very simple GUI programs, no special program design is needed, as demonstrated in the previous “Hello World” example programs. However, any non-trivial GUI program will require extensive use of global variables if the structure of the code does not use a Python
class. You have learned in previous lessons that global variables are bad because they make debugging programs more difficult. Therefore we want a design for GUI programs that avoids global variables as much as possible.
To demonstrate this, let's look at two versions of a simple program that increments a counter each time a user clicks a button. The first version of this code does not use a
class definition and requires that a global variable called my_counter be used. This is because the label that represents the counter is created in the
create_user_interface function but it must be accessed in the event handler function
increment_counter. In fact, the event handlers of a GUI program almost always need access to multiple widgets in the program's interface and the values can't be passed as parameters because an
event handler function receives no parameters and a
event handler function receives exactly one parameter – an
event object. Study the following example and pay close attention to where the
my_counter global variable is used.
import tkinter as tk from tkinter import ttk global my_counter def create_user_interface(application_window): global my_counter my_counter = ttk.Label(application_window, text="0") my_counter.grid(row=0, column=0) increment_button = ttk.Button(application_window, text="Add 1 to counter") increment_button.grid(row=1, column=0) increment_button['command'] = increment_counter quit_button = ttk.Button(application_window, text="Quit") quit_button.grid(row=2, column=0) quit_button['command'] = window.destroy def increment_counter(): global my_counter my_counter['text'] = str(int(my_counter['text']) + 1) # Create the application window window = tk.Tk() create_user_interface(window) # Start the GUI event loop window.mainloop()
Let's compare the above program to an identical application that is designed as a Python
class encapsulates all of the values needed for the GUI interface and the
event handlers and we don't need global variables!
import tkinter as tk from tkinter import ttk def main(): # Create the entire GUI program program = CounterProgram() # Start the GUI event loop program.window.mainloop() class CounterProgram: def __init__(self): self.window = tk.Tk() self.my_counter = None # All attributes should be initialize in init self.create_widgets() def create_widgets(self): self.my_counter = ttk.Label(self.window, text="0") self.my_counter.grid(row=0, column=0) increment_button = ttk.Button(self.window, text="Add 1 to counter") increment_button.grid(row=1, column=0) increment_button['command'] = self.increment_counter quit_button = ttk.Button(self.window, text="Quit") quit_button.grid(row=2, column=0) quit_button['command'] = self.window.destroy def increment_counter(self): self.my_counter['text'] = str(int(self.my_counter['text']) + 1) if __name__ == "__main__": main()
Notice the following about this design:
The application's window is created in the constructor (
__init__) of the
CounterProgramclass and then the interface widgets are created by a call to
The event handler,
increment_countercan access the label
self.my_counterusing the object's attributes.
The code creates an instance of the class
CounterProgramand starts the GUI event-loop.
It is recommended that you develop all of your GUI programs as Python Classes. For complex designs, a Python
Class can help manage the complexity of the code and the scoping of variables.