Book Image

Tkinter GUI Application Development Cookbook

By : Alejandro Rodas de Paz
Book Image

Tkinter GUI Application Development Cookbook

By: Alejandro Rodas de Paz

Overview of this book

As one of the more versatile programming languages, Python is well-known for its batteries-included philosophy, which includes a rich set of modules in its standard library; Tkinter is the library included for building desktop applications. Due to this, Tkinter is a common choice for rapid GUI development, and more complex applications can benefit from the full capabilities of this library. This book covers all of your Tkinter and Python GUI development problems and solutions. Tkinter GUI Application Development Cookbook starts with an overview of Tkinter classes and at the same time provides recipes for basic topics, such as layout patterns and event handling. Next, we cover how to develop common GUI patterns, such as entering and saving data, navigating through menus and dialogs, and performing long-running actions in the background.You can then make your apps leverage network resources effectively and perform graphical operations on a canvas and related tasks such as detecting collisions between items. Finally, this book covers using themed widgets, an extension of Tk widgets that have a more native look and feel. Finally, this book covers using the canvas and themed widgets. By the end of the book, you will have an in-depth knowledge of Tkinter classes, and will know how to use them to build efficient and rich GUI applications.
Table of Contents (10 chapters)

Structuring a Tkinter application

One of the main advantages of making applications with Tkinter is that it is very easy to set up a basic GUI with a script of a few lines. As the programs get more complex, it becomes more difficult to separate logically each part, so an organized structure will help us to keep our code clean.

Getting ready

We will take the following program as an example:

from tkinter import * 
 
root = Tk() 
btn = Button(root, text="Click me!") 
btn.config(command=lambda: print("Hello, Tkinter!"))
btn.pack(padx=120, pady=30)
root.title("My Tkinter app")
root.mainloop()

It creates a main window with a button that prints Hello, Tkinter! in the console each time it is clicked. The button is placed with a padding of 120px in the horizontal axis and 30px in the vertical axis. The last statement starts the main loop, which processes user events and updates the GUI until the main window is destroyed:

You can execute the program and verify that it is working as expected. However, all our variables are defined in the global namespace, and the more widgets you add, the more difficult it becomes to reason about the parts where they are used.

Wildcard imports (from ... import *) are strongly discouraged in production code because they pollute your global namespace—we only used them here to illustrate an anti-pattern that can be commonly seen in online examples.

These maintainability issues can be addressed with basic OOP techniques, which are considered good practice in all types of Python programs.

How to do it...

To improve the modularity of our simple program, we will define a class that wraps our global variables:

import tkinter as tk 
 
class App(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        self.btn = tk.Button(self, text="Click me!", 
                             command=self.say_hello) 
        self.btn.pack(padx=120, pady=30) 
 
    def say_hello(self): 
        print("Hello, Tkinter!") 
 
if __name__ == "__main__": 
    app = App() 
    app.title("My Tkinter app") 
    app.mainloop()

Now, each variable is enclosed in a specific scope, including the command function, which is moved as a separate method.

How it works...

First, we replaced the wildcard import with the import ... as syntax to have better control over our global namespace.

Then, we defined our App class as a Tk subclass, which now is referenced via the tk namespace. To properly initialize the base class, we will call the __init__ method of the Tk class with the built-in super() function. This corresponds to the following lines:

class App(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        # ... 

Now, we have a reference to the App instance with the self variable, so we will add all the Button widget as an attribute of our class.

Although it may look overkill for such a simple program, this refactoring will help us to reason about each part, the button instantiation is separated from the callback that gets executed when it is clicked, and the application bootstrapping is moved to the if __name__ == "__main__" block, which is a common practice in executable Python scripts.

We will follow this convention through all the code samples, so you can take this template as the starting point of any larger application.

There's more...

We subclassed the Tk class in our example, but it is also common to subclass other widget classes. We did this to reproduce the same statements that we had before we refactored the code.

However, it may be more convenient to subclass Frame or Toplevel in larger programs, such as those with multiple windows. This is because a Tkinter application should have only one Tk instance, and the system creates one automatically if you instantiate a widget before you create the Tk instance.

Keep in mind that this decision does not affect the structure of our App class since all widget classes have a mainloop method that internally starts the Tk main loop.