Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Tkinter GUI Application Development Cookbook
Tkinter GUI Application Development Cookbook

Tkinter GUI Application Development Cookbook: A practical solution to your GUI development problems with Python and Tkinter

Arrow left icon
Profile Icon Alejandro Rodas de Paz
Arrow right icon
$19.99 per month
Full star icon Full star icon Full star icon Full star icon Half star icon 4.4 (7 Ratings)
Paperback Mar 2018 242 pages 1st Edition
eBook
$9.99 $35.99
Paperback
$43.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Alejandro Rodas de Paz
Arrow right icon
$19.99 per month
Full star icon Full star icon Full star icon Full star icon Half star icon 4.4 (7 Ratings)
Paperback Mar 2018 242 pages 1st Edition
eBook
$9.99 $35.99
Paperback
$43.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$9.99 $35.99
Paperback
$43.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing
Table of content icon View table of contents Preview book icon Preview Book

Tkinter GUI Application Development Cookbook

Getting Started with Tkinter

In this chapter, we will cover the following recipes:

  • Structuring a Tkinter application
  • Working with buttons
  • Creating text entries
  • Tracing text changes
  • Validating a text entry
  • Selecting numerical values
  • Creating selections with radio buttons
  • Implementing switches with checkboxes
  • Displaying a list of items
  • Handling mouse and keyboard events
  • Setting the main window's icon, title, and size

Introduction

Thanks to its clear syntax and the wide ecosystem of libraries and tools, Python has become a popular and general-purpose programming language. From web development to Natural Language Processing (NLP), you can easily find an open source library that fits the need of your application domain, and in the last instance, you can always use any of the modules included in the Python standard library.

The standard library follows the "batteries-included" philosophy, which means that it contains a large collection of utilities: regular expressions, mathematical functions, networking, and so on. The standard Graphical User Interface (GUI) package of this library is Tkinter, a thin object-oriented layer on top of Tcl/Tk.

Starting from Python 3, the Tkinter module was renamed to tkinter (with a lowercase t). It also affects to the tkinter.ttk and tkinter.tix extensions. We will dive into the tkinter.ttk module in the last chapter of this book, since the tkinter.tix module is officially deprecated.

In this chapter, we will explore several patterns for some basic classes of the tkinter module and some methods that are common to all widget subclasses.

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.

Working with buttons

Button widgets represent a clickable item of your GUI applications. They typically use a text or an image indicating the action that will be performed when clicked. Tkinter allows you to easily configure this functionality with some standard options of the Button widget class.

How to do it...

The following contains a button with an image that gets disabled when clicked and a list of buttons with the different types of available reliefs:

import tkinter as tk 
 
RELIEFS = [tk.SUNKEN, tk.RAISED, tk.GROOVE, tk.RIDGE, tk.FLAT] 
 
class ButtonsApp(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        self.img = tk.PhotoImage(file="python.gif") 
        self.btn = tk.Button(self, text="Button with image", 
                             image=self.img, compound=tk.LEFT, 
                             command=self.disable_btn) 
        self.btns = [self.create_btn(r) for r in RELIEFS]         
        self.btn.pack() 
        for btn in self.btns: 
            btn.pack(padx=10, pady=10, side=tk.LEFT) 
 
    def create_btn(self, relief): 
        return tk.Button(self, text=relief, relief=relief) 
 
    def disable_btn(self): 
        self.btn.config(state=tk.DISABLED) 
 
if __name__ == "__main__": 
    app = ButtonsApp() 
    app.mainloop()

The purpose of this program is to show several configuration options that can be used when creating a Button widget.

After executing the preceding code, you will get the following output:

How it works...

The most basic way of instantiation of Button is using the text option to set the button label and the command option that references the function to be invoked when the button is clicked.

In out example, we also added PhotoImage via the image option, which takes precedence over the text string. The compound option serves to combine image and text in the same button, determining the position where the image is placed. It accepts the following constants as valid values: CENTER, BOTTOM, LEFT, RIGHT, and TOP.

The second row of buttons is created with a list comprehension, using the list of RELIEF values. The label of each button corresponds to the name of the constant, so you can note the difference in the appearance of each button.

There's more...

We used an attribute to keep a reference to our PhotoImage instance, even though we are not using it outside our __init__ method. The reason is that images are cleared when they are garbage collected, which will happen if we declare it as a local variable and the method exists.

To avoid this, always remember to keep a reference to each PhotoImage object as long as the window where it is shown is still alive.

Creating text entries

The Entry widget represents a text input displayed in a single line. Along with the Label and Button classes, it is one of the most commonly used Tkinter classes.

How to do it...

This example shows how to create a login form with two entry instances for the username and password fields. Each character of password is displayed as an asterisk to avoid showing it in clear text:

import tkinter as tk 
 
class LoginApp(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        self.username = tk.Entry(self) 
        self.password = tk.Entry(self, show="*") 
        self.login_btn = tk.Button(self, text="Log in", 
                                   command=self.print_login) 
        self.clear_btn = tk.Button(self, text="Clear", 
                                   command=self.clear_form)         
        self.username.pack() 
        self.password.pack() 
        self.login_btn.pack(fill=tk.BOTH) 
        self.clear_btn.pack(fill=tk.BOTH) 
 
    def print_login(self): 
        print("Username: {}".format(self.username.get())) 
        print("Password: {}".format(self.password.get())) 
 
    def clear_form(self): 
        self.username.delete(0, tk.END) 
        self.password.delete(0, tk.END) 
        self.username.focus_set() 
 
if __name__ == "__main__": 
    app = LoginApp() 
    app.mainloop()

The Log in button prints the values in the console, whereas the Clear button removes the content of both entries and returns the focus to the entry for username:

How it works...

The Entry widgets are instantiated using the parent window or frame as the first argument and a set of optional keyword arguments to configure additional options. We did not specify any options for the entry corresponding to the username field. To keep the password secret, we specify the show argument with the string "*", which will display each typed character as an asterisk.

With the get() method, we will retrieve the current text as a string. This is used in the print_login method to show the entries' content in the standard output.

The delete() method takes two arguments that indicate the range of the characters that should be deleted. Keep in mind that the indices start at the position 0, and they do not include the character at the end of the range. If only one argument is passed, it deletes the character at that position.

In the clear_form() method, we delete from index 0 to the constant END, which means that the whole content is removed. Finally, we set the focus to the username entry.

There's more...

The content of an Entry widget can be modified programmatically with the insert() method, which takes two arguments:

  • index: The position to insert the text; note that entry positions are 0-indexed
  • string: The text to insert

A common pattern to reset the content of an entry with a default value can be achieved with a combination of delete() and insert():

entry.delete(0, tk.END) 
entry.insert(0, "default value") 

Another pattern is to append the text in the current position of the text cursor. Here, you can use the INSERT constant instead of having to calculate the numerical index:

entry.insert(tk.INSERT, "cursor here")

Like the Button class, the Entry class also accepts the relief and state options to modify its border style and state. Keep in mind that calls to delete() and insert() are ignored when the state is "disabled" or "readonly".

See also

  • The Tracing text changes recipe
  • The Validating a text entry recipe

Tracing text changes

Tk variables allow your applications to get notified when an input changes its value. There are four variable classes in Tkinter: BooleanVar, DoubleVar, IntVar, and StringVar. Each one wraps the value of the corresponding Python type, which should match the type of the input widget attached to the variable.

This feature is particularly useful if you want to automatically update certain parts of your application based on the current state of some input widgets.

How to do it...

In the following example, we will associate a StringVar instance to our entry with the textvariable option; this variable traces write operations with the show_message() method as callback:

import tkinter as tk 
 
class App(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        self.var = tk.StringVar() 
        self.var.trace("w", self.show_message) 
        self.entry = tk.Entry(self, textvariable=self.var) 
        self.btn = tk.Button(self, text="Clear", 
                             command=lambda: self.var.set("")) 
        self.label = tk.Label(self) 
        self.entry.pack() 
        self.btn.pack() 
        self.label.pack() 
 
    def show_message(self, *args): 
        value = self.var.get() 
        text = "Hello, {}!".format(value) if value else "" 
        self.label.config(text=text) 
 
if __name__ == "__main__": 
    app = App() 
    app.mainloop() 

When you type something into the Entry widget, the label updates its text with a message composed with the Tk variable value. For instance, if you type the word Phara, the label will show Hello, Phara!. If the entry is empty, the label will not show any text. To show you how to modify the variable's content programmatically, we added a button that clears the entry when you click on it:

How it works...

The first lines of our application constructor instantiate StringVar and attach a callback to the write mode. The valid mode values are as follows:

  • "w": Called when the variable is written
  • "r": Called when the variable is read
  • "u" (for unset): Called when the variable is deleted

When invoked, the callback function receives three arguments: the internal variable name, an empty string (it is used in other types of Tk variables), and the mode that triggered the operation. By declaring the method with *args, we make these arguments optional, because we are not using any of these values within the callback.

The get() method of Tk wrappers returns the current value of the variable, and the set() method updates its value. They also notify the corresponding observers, so both modifying the entry's content through the GUI or clicking on the Clear button will trigger the call to the show_message() method.

There's more...

Tk variables are optional for Entry widgets, but they are necessary for other widget classes to work correctly, such as the Checkbutton and Radiobutton classes.

See also

  • The Creating selections with radio buttons recipe
  • The Implementing switches with checkboxes recipe

Validating a text entry

Typically, text inputs represent fields that follow certain validation rules, such as having a maximum length or matching a specific format. Some applications allow typing any kind of content into these fields and trigger the validation when the whole form is submitted.

Under some circumstances, we want to prevent users from typing invalid content into a text field. We will take a look at how to implement this behavior using the validation options of the Entry widget.

How to do it...

The following application shows how to validate an entry using regular expressions:

import re 
import tkinter as tk 
 
class App(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        self.pattern = re.compile("^\w{0,10}$") 
        self.label = tk.Label(self, text="Enter your username") 
        vcmd = (self.register(self.validate_username), "%i", "%P") 
        self.entry = tk.Entry(self, validate="key", 
                              validatecommand=vcmd, 
                              invalidcommand=self.print_error) 
        self.label.pack() 
        self.entry.pack(anchor=tk.W, padx=10, pady=10) 
 
    def validate_username(self, index, username): 
        print("Modification at index " + index) 
        return self.pattern.match(username) is not None 
 
    def print_error(self): 
        print("Invalid username character") 
 
if __name__ == "__main__": 
    app = App() 
    app.mainloop() 

If you run this script and type a non-alphanumeric character in the Entry widget, it will keep the same content and print the error message. This will also happen when you try to type more than 10 valid characters since the regular expression also limits the content's length.

How it works...

With the validate option set to "key", we will activate the entry validation that gets triggered on any content modification. The value is "none" by default, which means that there is no validation.

Other possible values are "focusin" and "focusout", which validate when the widget gets or loses the focus, respectively, or simply "focus" to validate in both cases. Alternatively, we can use the "all" value to validate in all situations.

The validatecommand function is called each time the validation is triggered, and it should return true if the new content is valid, and false otherwise.

Since we need more information to determine whether the content is valid or not, we create a Tcl wrapper around our Python function using the register method of the Widget class. Then, you can add the percent substitution for each parameter that will be passed to the Python function. Finally, we will group these values as a Python tuple. This corresponds to the following line from our example:

vcmd = (self.register(self.validate_username), "%i", "%P") 

In general, you can use any of the following substitutions:

  • %d: Type of action; 1 for insertion, 0 for deletion, and -1 otherwise
  • %i: Index of the string being inserted or deleted
  • %P: Value of the entry if the modification is allowed
  • %s: Value of the entry before the modification
  • %S: String content that is being inserted or deleted
  • %v: The type of validation currently set
  • %V: Type of validation that triggered the action
  • %W: The name of the Entry widget

The invalidcommand option takes a function that is invoked when validatecommand returns false. The same percent substitutions can be applied to this option, but in our example, we directly passed the print_error() method of our class.

There's more...

The Tcl/Tk documentation suggests not mixing the validatecommand and the textvariable options since setting an invalid value to the Tk variable will turn off validation. The same occurs if the validatecommand function do not return a Boolean value.

In case you are not familiar with the re module, you can check out the detailed introduction to regular expressions in the official Python documentation at https://docs.python.org/3.6/howto/regex.html.

See also

  • The Creating text entries recipe

Selecting numerical values

Previous recipes cover how to work with text inputs; we may want to enforce some inputs to contain only numerical values. This is the use case for the Spinbox and Scale classes—both widgets allow users to select a numerical value from a range or a list of valid options, but there are several differences in the way they are displayed and configured.

How to do it...

This program has Spinbox and Scale for selecting an integer value from 0 to 5:

import tkinter as tk 
 
class App(tk.Tk):
    def __init__(self): 
        super().__init__() 
        self.spinbox = tk.Spinbox(self, from_=0, to=5) 
        self.scale = tk.Scale(self, from_=0, to=5, 
                              orient=tk.HORIZONTAL) 
        self.btn = tk.Button(self, text="Print values", 
                             command=self.print_values) 
        self.spinbox.pack() 
        self.scale.pack() 
        self.btn.pack() 
 
    def print_values(self): 
        print("Spinbox: {}".format(self.spinbox.get())) 
        print("Scale: {}".format(self.scale.get())) 
 
if __name__ == "__main__": 
    app = App()
    app.mainloop()

In the preceding code, for debugging purposes, we added a button that prints the value of each widget when you click on it:

How it works...

Both classes accept the from_ and to options to indicate the range of valid values—the trailing underscore is necessary because the from option was originally defined in Tcl/Tk, but it is a reserved keyword in Python.

A handy functionality of the Scale class is the resolution option, which sets the precision of the rounding. For instance, a resolution of 0.2 will allow the user to select the values 0.0, 0.2, 0.4, and so on. The value of this option is 1 by default, so the widget rounds all values to the nearest integer.

As usual, the value of each widget can be retrieved with the get() method. An important difference is that Spinbox returns the number as a string, whereas Scale returns an integer value or a float value if the rounding accepts decimal values.

There's more...

The Spinbox class has a similar configuration to the Entry widget, such as the textvariable and validate options. You can apply all these patterns to spinboxes with the main difference that it restricts to numerical values.

See also

  • The Tracing text changes recipe

Creating selections with radio buttons

With the Radiobutton widget, you can let the user select among several options. This pattern works well for a relatively small number of mutually exclusive choices.

How to do it...

You can connect multiple Radiobutton instances using a Tkinter variable so that when you click on a non-selected option, it will deselect whatever other option was previously selected.

In the following program, we created three radio buttons for the Red, Green, and Blue options. Each time you click on a radio button, it prints the lowercase name of the corresponding color:

import tkinter as tk

COLORS = [("Red", "red"), ("Green", "green"), ("Blue", "blue")]

class ChoiceApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.var = tk.StringVar()
        self.var.set("red")
        self.buttons = [self.create_radio(c) for c in COLORS]
        for button in self.buttons:
            button.pack(anchor=tk.W, padx=10, pady=5)

    def create_radio(self, option):
        text, value = option
        return tk.Radiobutton(self, text=text, value=value, 
                              command=self.print_option, 
                              variable=self.var)

    def print_option(self):
        print(self.var.get())

if __name__ == "__main__": 
    app = ChoiceApp()
    app.mainloop()

If you run this script, it will display the application with the Red radio button already selected:

How it works...

To avoid repeating the code of the Radiobutton initialization, we defined a utility method that is called from a list comprehension. We unpacked the values of each tuple of the COLORS list and then passed these local variables as options to Radiobutton. Remember to try to not repeat yourself whenever possible.

Since StringVar is shared among all the Radiobutton instances, they are automatically connected, and we force the user to select only one choice.

There's more...

We set a default value of "red" in our program; however, what would happen if we omit this line, and the value of StringVar does not match any of the radio button values? It will match the default value of the tristatevalue option, which is the empty string. This causes the widget to display in a special "tri-state" or indeterminate mode. Although this option can be modified with the config() method, a better practice is to set a sensible default value so the variable is initialized in a valid state.

Implementing switches with checkboxes

Choices between two alternatives are typically implemented with checkboxes and lists of options where each choice is independent from the rest. As we will see in the next example, these concepts can be implemented using the Checkbutton widget.

How to do it...

The following application shows how to create Checkbutton, which must be connected to an IntVar variable to be able to inspect the button state:

import tkinter as tk

class SwitchApp(tk.Tk):
    def __init__(self):
        super().__init__() 
        self.var = tk.IntVar() 
        self.cb = tk.Checkbutton(self, text="Active?",  
                                 variable=self.var, 
                                 command=self.print_value) 
        self.cb.pack() 
 
    def print_value(self): 
        print(self.var.get()) 
 
if __name__ == "__main__": 
    app = SwitchApp() 
    app.mainloop() 

In the preceding code, we simply printed the value of the widget each time it is clicked:

How it works...

Like the Button widget, the Checkbutton also accepts the command and text options.

With the onvalue and offvalue options, we can specify the values used when the button is on and off. We use an integer variable because these values are 1 and 0 by default, respectively; however, you can also set them to any other integer values.

There's more...

With Checkbuttons, it is also possible to use other variable types:

var = tk.StringVar() 
var.set("OFF") 
checkbutton_active = tk.Checkbutton(master, text="Active?", variable=self.var, 
                                    onvalue="ON", offvalue="OFF", 
                                    command=update_value)

The only restriction is to match onvalue and offvalue with the type of the Tkinter variable; in this case, since "ON" and "OFF" are strings, the variable should be a StringVar. Otherwise, the Tcl interpreter will raise an error when trying to set the corresponding value of a different type.

See also

  • The Tracing text changes recipe
  • The Creating selections with radio buttons recipe

Displaying a list of items

The Listbox widget contains text items that can be selected by the user with the mouse or keyboard. This selection can be individual or multiple, depending on the widget configuration.

How to do it...

The following program creates a list selection with the days of the week. There is a button to print the actual selection and a list of buttons to change the selection mode:

import tkinter as tk 
 
DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", 
        "Friday", "Saturday", "Sunday"] 
MODES = [tk.SINGLE, tk.BROWSE, tk.MULTIPLE, tk.EXTENDED] 
 
class ListApp(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        self.list = tk.Listbox(self)  
        self.list.insert(0, *DAYS) 
        self.print_btn = tk.Button(self, text="Print selection", 
                                   command=self.print_selection) 
        self.btns = [self.create_btn(m) for m in MODES] 
 
        self.list.pack() 
        self.print_btn.pack(fill=tk.BOTH) 
        for btn in self.btns: 
            btn.pack(side=tk.LEFT) 
 
    def create_btn(self, mode): 
        cmd = lambda: self.list.config(selectmode=mode) 
        return tk.Button(self, command=cmd, 
                         text=mode.capitalize()) 
 
    def print_selection(self): 
        selection = self.list.curselection() 
        print([self.list.get(i) for i in selection]) 
 
if __name__ == "__main__": 
    app = ListApp() 
    app.mainloop() 

You can try out changing the mode of selection and printing the selected items:

How it works...

We create an empty Listbox object and add all the text items with the insert() method. The 0 index indicates that the items should be added at the beginning of the list. In the following code snippet, we unpacked the DAYS list, but individual items can be appended at the end with the END constant:

self.list.insert(tk.END, "New item") 

The current selection is retrieved using the curselection() method. It returns the indices of the selected items to transform them to the corresponding text items we called the get() method for each index in a comprehension list. Finally, the list is printed in the standard output for debugging purposes.

In our example, the selectmode option can be changed programmatically to explore the different behaviors, as follows:

  • SINGLE: Single choice
  • BROWSE: Single choice that can be moved with the up and down keys
  • MULTIPLE: Multiple choice
  • EXTENDED: Multiple choice with ranges that can be selected with the Shift and Ctrl keys

There's more...

If the number of text items is large enough, it may be necessary to add a vertical scroll bar. You can easily connect it using the yscrollcommand option. In our example, we can wrap both widgets in a frame to keep the same layout. Remember to specify the fill option when packing the scroll so that it fills the available space in the y axis:

def __init__(self):
self.frame = tk.Frame(self) self.scroll = tk.Scrollbar(self.frame, orient=tk.VERTICAL) self.list = tk.Listbox(self.frame, yscrollcommand=self.scroll.set) self.scroll.config(command=self.list.yview) # ... self.frame.pack() self.list.pack(side=tk.LEFT) self.scroll.pack(side=tk.LEFT, fill=tk.Y)

Similarly, there is a xscrollcommand option for the horizontal axis.

See also

  • The Creating selections with radio buttons recipe

Handling mouse and keyboard events

Being able to react to events is one of the most basic but important topics in GUI application development since it determines how users can interact with the program.

Pressing keys of the keyboard and clicking on items with the mouse are some common types of events, which are automatically handled in some Tkinter classes. For instance, this behavior is already implemented on the command option of the Button widget class, which invokes the specified callback function.

Some events can get triggered without user interaction, such as changing the input focus programmatically from one widget to another.

How to do it...

You can attach an event binding to a widget using the bind method. The following example binds some mouse events to a Frame instance:

import tkinter as tk 
 
class App(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        frame = tk.Frame(self, bg="green", 
                         height=100, width=100) 
        frame.bind("<Button-1>", self.print_event) 
        frame.bind("<Double-Button-1>", self.print_event) 
        frame.bind("<ButtonRelease-1>", self.print_event) 
        frame.bind("<B1-Motion>", self.print_event) 
        frame.bind("<Enter>", self.print_event) 
        frame.bind("<Leave>", self.print_event) 
        frame.pack(padx=50, pady=50) 
 
    def print_event(self, event): 
        position = "(x={}, y={})".format(event.x, event.y) 
        print(event.type, "event", position) 
 
if __name__ == "__main__": 
    app = App() 
    app.mainloop() 

All events are handled by the print_event() method of our class, which prints the type of event and the position of the mouse in the console. You can try it out by clicking on the green frame with the mouse, and moving it around while it starts printing the event messages.

The following example contains an Entry widget with a couple of bindings; one for the event that gets triggered when the entry gets the focus, and another for all the key press events:

import tkinter as tk 
 
class App(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        entry = tk.Entry(self) 
        entry.bind("<FocusIn>", self.print_type)  
        entry.bind("<Key>", self.print_key) 
        entry.pack(padx=20, pady=20) 
 
    def print_type(self, event): 
        print(event.type) 
 
    def print_key(self, event): 
        args = event.keysym, event.keycode, event.char 
        print("Symbol: {}, Code: {}, Char: {}".format(*args)) 
 
if __name__ == "__main__": 
    app = App() 
    app.mainloop() 

The first message this program will output is the FocusIn event when you set the focus on the Entry widget. If you try it out, you will see that it will also show the events of keys that do not correspond to non-printable characters, such as arrow keys or the return key.

How it works...

The bind method is defined in the widget class and takes three arguments, an event sequence, a callback function, and an optional add string:

widget.bind(sequence, callback, add='') 

The sequence string uses the <modifier-type-detail> syntax.

In first place, modifiers are optional and allow you to specify additional combinations to the general type of the event:

  • Shift: When the user presses the Shift key
  • Alt: When the user presses the Alt key
  • Control: When the user presses the Ctrl key
  • Lock: When the user presses the Shift lock
  • Double: When the event happens twice in quick succession
  • Triple: When the event happens thrice in quick succession

Event types determine the general type of event:

  • ButtonPress or Button: Event generated when a mouse button is pressed
  • ButtonRelease: Event generated when a mouse button is released
  • Enter: Event generated when you move the mouse over a widget
  • Leave: Event generated when the mouse pointer leaves a widget
  • FocusIn: Event generated when the widget gets the input focus
  • FocusOut: Event generated when the widget loses the input focus
  • KeyPress or Key: Event generated when a key is pressed
  • KeyRelease: Event generated when a key is released
  • Motion: Event generated when the mouse is moved

The detail is also optional and serves to indicate the mouse button or key:

  • For mouse events, 1 is the left button, 2 is the middle button, and 3 is the right button.
  • For keyboard events, it is the key character. Special keys use the key symbol; some common examples are return, Tab, Esc, up, down, right, left, Backspace, and function keys (from F1 to F12).

The callback function takes an event parameter. For mouse events, it has the following attributes:

  • x and y: Current mouse position in pixels
  • x_root and y_root: Same as x and y, but relative to the left-upper corner of the screen
  • num: Mouse button number

For keyboard events, it contains these attributes:

  • char: Pressed character code as a string
  • keysym: Pressed key symbol
  • keycode: Pressed key code

In both cases, the event has the widget attribute, referencing the instance that generated the event, and type, which specifies the event type.

We strongly recommend that you define methods for the callback functions since you will also have the reference to the class instance, and therefore you can easily access each of the widget attributes.

Finally, the add parameter can be '', to replace the callback function if there was a previous binding, or '+' to add the callback and preserve the old ones.

There's more...

Apart from the event types described here, there are also other types that may be useful in some scenarios, such as the <Destroy> event that is generated when a widget is destroyed or the <Configure> event that is sent when the size or position of the widget changes.

You can check out the Tcl/Tk documentation for a complete list of event types at https://www.tcl.tk/man/tcl/TkCmd/bind.htm#M7.

See also

  • The Structuring a Tkinter application recipe

Setting the main window's icon, title, and size

The Tk instance differs from normal widgets in the way that it is configured, so we will explore some basic methods that allow us to customize how it is displayed.

How to do it...

This snippet creates a main window with a custom title and icon. It has 400px of width by 200px of height, with a separation of 10px in each axis to the upper-left corner of the screen:

import tkinter as tk 
 
class App(tk.Tk): 
    def __init__(self): 
        super().__init__() 
        self.title("My Tkinter app") 
        self.iconbitmap("python.ico") 
        self.geometry("400x200+10+10") 
 
if __name__ == "__main__": 
    app = App() 
    app.mainloop()

This program assumes that you have a valid ICO file called python.ico in the same directory where the script is placed and executed.

How it works...

The methods title() and iconbitmap() of the Tk class are very self-descriptive—the first one sets the window title, whereas the second one takes the path to the icon that is associated to the window.

The geometry() method configures the size of the window with a string that follows the following pattern:

{width}x{height}+{offset_x}+{offset_y}

In case you add more secondary windows to your application, these methods are also available in the Toplevel class.

There's more...

If you want to make the application fullscreen, replace the call to the geometry() method with self.state("zoomed").

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • • Integrate efficient Python GUI programming techniques with Tkinter
  • • Efficiently implement advanced MVC architectures in your Python GUI apps
  • • Solve all your problems related to Tkinter and Python GUI development

Description

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.

Who is this book for?

This book is for Python developers who are familiar with the basics of the language syntax, data structures, and OOP. You do not need previous experience with Tkinter or other GUI development libraries.

What you will learn

  • • Add widgets and handle user events
  • • Lay out widgets within windows using frames and the different geometry managers
  • • Configure widgets so that they have a customized appearance and behavior
  • • Improve the navigation of your apps with menus and dialogs
  • • Apply object-oriented programming techniques in Tkinter applications
  • • Use threads to achieve responsiveness and update the GUI
  • • Explore the capabilities of the canvas widget and the types of items that can be added to it
  • • Extend Tkinter applications with the TTK (themed Tkinter) module

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Mar 30, 2018
Length: 242 pages
Edition : 1st
Language : English
ISBN-13 : 9781788622301
Category :
Languages :
Tools :

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details

Publication date : Mar 30, 2018
Length: 242 pages
Edition : 1st
Language : English
ISBN-13 : 9781788622301
Category :
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 153.97
Tkinter GUI Programming by Example
$54.99
Tkinter GUI Application Development Cookbook
$43.99
Tkinter GUI Application Development Blueprints, Second Edition
$54.99
Total $ 153.97 Stars icon
Banner background image

Table of Contents

9 Chapters
Getting Started with Tkinter Chevron down icon Chevron up icon
Window Layout Chevron down icon Chevron up icon
Customizing Widgets Chevron down icon Chevron up icon
Dialogs and Menus Chevron down icon Chevron up icon
Object-Oriented Programming and MVC Chevron down icon Chevron up icon
Asynchronous Programming Chevron down icon Chevron up icon
Canvas and Graphics Chevron down icon Chevron up icon
Themed Widgets Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.4
(7 Ratings)
5 star 57.1%
4 star 28.6%
3 star 14.3%
2 star 0%
1 star 0%
Filter icon Filter
Top Reviews

Filter reviews by




René BELLE Oct 15, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Comme savent si bien le faire les Américains, cet ouvrage pratique et d'un niveau gradué présentent moultes séquences de code afin d'illustrer la programmation d'applications de classes en Python grâce à la bibliothèque Tkinter. La méthode de formation basée sur la lecture et l'expérimentation du code suivie d'un bref exposé du fonctionnement du programme est simple, claire et efficace dans le but de former l'étudiant que je suis. En tête d'ouvrage, l'index des séquences de code en fait un outil efficace pour piocher par ci, par là, des idées permettant de développer une application et d'assembler les modules comme les Legos qui étaient mon jeu favori entre 8 et 12 ans. C'est bien un Cookbook (livre de recettes) que nous avons là entre les mains. Bien qu'en anglais, il se lit très facilement et la mise en pratique du code permet de mémoriser les tournures syntaxiques, la grammaire et l'orthographe des commandes.C'est un ouvrage de base que je recommande à tous les amateurs de Python et d'applications graphiques faciles à développer rapidement. Pour l'instant au bout de 1500 pages de lectures sur le sujet (sur un total prévisionnel de 500à pages au courant de l'hiver), c'est mon préféré car le plus en rapport avec une pratique immédiate chère à l'autodidacte que je suis encore à 67 ans.
Amazon Verified review Amazon
HARSH GURJAR May 06, 2018
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Best book for beginners for Python tkinter. Just go for it.
Amazon Verified review Amazon
Amit Nath Gupta Jan 22, 2020
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Excellent to guide you through tkinterThanks for writing such a book.Author is great.Buy the kindle edition ,it is better
Amazon Verified review Amazon
KML Dec 24, 2018
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Bought this direct from Packt as they are offering a $5 per book holiday special. In the universe of Tkinter books, there are few good works. Tkinter in Action from Manning is one such but it's really outdated.This book does not attempt to cover everything but it does provide practical essentials that work, including threading which is critical in most non-trivial apps. Code quality is also excellent. Has the best explanation of the pack geometry manager. Hope the author can write a cookbook or one on advanced features.
Amazon Verified review Amazon
Clive Aug 12, 2020
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
I suspect this book will be useful, but contrary to what is described, I am not sold on it being easy for those who haven't used Tkinter to follow.Within the first chapter there are several references to object types which aren't explained. Sure you may be able to learn these code section by rote, but that doesn't help you to understand the software. For this reason I ended up purchasing another book to complement this one.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is included in a Packt subscription? Chevron down icon Chevron up icon

A subscription provides you with full access to view all Packt and licnesed content online, this includes exclusive access to Early Access titles. Depending on the tier chosen you can also earn credits and discounts to use for owning content

How can I cancel my subscription? Chevron down icon Chevron up icon

To cancel your subscription with us simply go to the account page - found in the top right of the page or at https://subscription.packtpub.com/my-account/subscription - From here you will see the ‘cancel subscription’ button in the grey box with your subscription information in.

What are credits? Chevron down icon Chevron up icon

Credits can be earned from reading 40 section of any title within the payment cycle - a month starting from the day of subscription payment. You also earn a Credit every month if you subscribe to our annual or 18 month plans. Credits can be used to buy books DRM free, the same way that you would pay for a book. Your credits can be found in the subscription homepage - subscription.packtpub.com - clicking on ‘the my’ library dropdown and selecting ‘credits’.

What happens if an Early Access Course is cancelled? Chevron down icon Chevron up icon

Projects are rarely cancelled, but sometimes it's unavoidable. If an Early Access course is cancelled or excessively delayed, you can exchange your purchase for another course. For further details, please contact us here.

Where can I send feedback about an Early Access title? Chevron down icon Chevron up icon

If you have any feedback about the product you're reading, or Early Access in general, then please fill out a contact form here and we'll make sure the feedback gets to the right team. 

Can I download the code files for Early Access titles? Chevron down icon Chevron up icon

We try to ensure that all books in Early Access have code available to use, download, and fork on GitHub. This helps us be more agile in the development of the book, and helps keep the often changing code base of new versions and new technologies as up to date as possible. Unfortunately, however, there will be rare cases when it is not possible for us to have downloadable code samples available until publication.

When we publish the book, the code files will also be available to download from the Packt website.

How accurate is the publication date? Chevron down icon Chevron up icon

The publication date is as accurate as we can be at any point in the project. Unfortunately, delays can happen. Often those delays are out of our control, such as changes to the technology code base or delays in the tech release. We do our best to give you an accurate estimate of the publication date at any given time, and as more chapters are delivered, the more accurate the delivery date will become.

How will I know when new chapters are ready? Chevron down icon Chevron up icon

We'll let you know every time there has been an update to a course that you've bought in Early Access. You'll get an email to let you know there has been a new chapter, or a change to a previous chapter. The new chapters are automatically added to your account, so you can also check back there any time you're ready and download or read them online.

I am a Packt subscriber, do I get Early Access? Chevron down icon Chevron up icon

Yes, all Early Access content is fully available through your subscription. You will need to have a paid for or active trial subscription in order to access all titles.

How is Early Access delivered? Chevron down icon Chevron up icon

Early Access is currently only available as a PDF or through our online reader. As we make changes or add new chapters, the files in your Packt account will be updated so you can download them again or view them online immediately.

How do I buy Early Access content? Chevron down icon Chevron up icon

Early Access is a way of us getting our content to you quicker, but the method of buying the Early Access course is still the same. Just find the course you want to buy, go through the check-out steps, and you’ll get a confirmation email from us with information and a link to the relevant Early Access courses.

What is Early Access? Chevron down icon Chevron up icon

Keeping up to date with the latest technology is difficult; new versions, new frameworks, new techniques. This feature gives you a head-start to our content, as it's being created. With Early Access you'll receive each chapter as it's written, and get regular updates throughout the product's development, as well as the final course as soon as it's ready.We created Early Access as a means of giving you the information you need, as soon as it's available. As we go through the process of developing a course, 99% of it can be ready but we can't publish until that last 1% falls in to place. Early Access helps to unlock the potential of our content early, to help you start your learning when you need it most. You not only get access to every chapter as it's delivered, edited, and updated, but you'll also get the finalized, DRM-free product to download in any format you want when it's published. As a member of Packt, you'll also be eligible for our exclusive offers, including a free course every day, and discounts on new and popular titles.