Python GUI Cookbook - Create GUI forms and add Widgets

Keywords: Python Lambda Attribute Programming

Links to the original text: Python GUI Cookbook - Create GUI forms and add Widgets

Create the first Python GUI program

Using Python's built-in tkinter module, you can create a GUI in just four lines of code.

Here is the code:

#!/usr/bin/env python3

import tkinter as tk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Start GUI
window.mainloop()

Prevent window size from being adjusted

GUIs created with tkinter are resizable by default, but sometimes we want forms to remain specific in size, so we need to learn how to prevent users from resizing GUIs.

#!/usr/bin/env python3

import tkinter as tk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Disable resizing the GUI by passing in False/False
window.resizable(False, False)

# Enable resizing x-dimension, disable y-dimension
# window.resizable(True, False)

# Start GUI
window.mainloop()

Add label to the form

label is a simple widget that can add value to a form, explain the purpose of other widgets, and provide additional information.

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Add a Label
ttk.Label(window, text = 'A Label').grid(column=0, row=0)

# Start GUI
window.mainloop()

Here we import a separate module ttk from the tkinter package. The ttk module has some advanced widgets to make our GUI look better. ttk stands for themed tk.

  • In this example, we used the ttk.Label constructor to set the text property.
  • Using the grid layout manager
  • You can see that the form suddenly becomes much smaller, because we add widget s to the form, trigger optimization, and try to use small space to display widget s (s).

Create buttons and change their text properties

Here we will add a button widget and use it to change the properties of other widgets. We will learn about Python GUI callback function s and event handling mechanisms.

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Adding a Label that will get modified
a_label = ttk.Label(window, text = 'A Label')
a_label.grid(column=0, row=0)


# Button click Event Function
def click_me():
    action.configure(text='** I have been Clicked! **')
    a_label.configure(foreground='red')
    a_label.configure(text='A Red Label')

# Adding a Button
action = ttk.Button(window, text='Click Me!', command=click_me)
action.grid(column=1, row=0)

# Start GUI
window.mainloop()

GUIs are event-driven. Click the button to generate an event. We bind what happens when an event occurs to the callback function. Call it through the command attribute of ttk.Button widget. Note that no parentheses are needed for the call, just the name click_me.

Text Box widgets

In tkinter, a text box widget with only one line is called an Entry.

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Adding a Label that will get modified
a_label = ttk.Label(window, text = 'A Label')
a_label.grid(column=0, row=0)


# Modified Button click Event Function
def click_me():
    action.configure(text='Hello ' + name.get())

# Changing our Label
ttk.Label(window, text='Enter a name: ').grid(column=0, row=0)

# Adding a Text Box Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(window, width=12, textvariable=name)
name_entered.grid(column=0, row=1)

# Adding a Button
action = ttk.Button(window, text="Click Me!", command=click_me)   
action.grid(column=1, row=1)

# Start GUI
window.mainloop()

In tkinter, we need to declare the name variable tk.StringVar(). Because Tkinter is not Python. We can only use it in Python, but they are not in the same language.

Set focus on widgets and disable widgets

Just call the focus() method to set the focus for a widget.

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Adding a Label that will get modified
a_label = ttk.Label(window, text = 'A Label')
a_label.grid(column=0, row=0)


# Modified Button click Event Function
def click_me():
    action.configure(text='Hello ' + name.get())

# Changing our Label
ttk.Label(window, text='Enter a name:').grid(column=0, row=0)

# Adding a Text Box Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(window, width=12, textvariable=name)
name_entered.grid(column=0, row=1)

# Adding a Button
action = ttk.Button(window, text="Click Me!", command=click_me)   
action.grid(column=1, row=1)
action.configure(state='disabled') # Disable the Button Widget

name_entered.focus_set() # Place cursor into name Entry

# Start GUI
window.mainloop()

Combo box widgets

DropDown Combo

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Adding a Label that will get modified
a_label = ttk.Label(window, text = 'A Label')
a_label.grid(column=0, row=0)


# Modified Button click Event Function
def click_me():
    action.configure(text='Hello ' + name.get()+ ' ' + number_chosen.get())

# Changing our Label
ttk.Label(window, text='Enter a name:').grid(column=0, row=0)

# Adding a Textbox Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(window, width=12, textvariable=name)
name_entered.grid(column=0, row=1) # column 0

# Adding a Button
action = ttk.Button(window, text="Click Me!", command=click_me)   
action.grid(column=2, row=1) # change column to 2

ttk.Label(window, text='Choose a number:').grid(column=1, row=0)
number = tk.StringVar()
number_chosen = ttk.Combobox(window, width=12, textvariable=number)
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1) # Combobox in column 1
number_chosen.current(0)

name_entered.focus() # Place cursor into name Entry

# Start GUI
window.mainloop()

If you want to restrict users to choose only the options given in the program, you pass Combobox's state property to the constructor

[...]

number = tk.StringVar()
number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1) # Combobox in column 1
number_chosen.current(0)

[...]

Create check buttons with different initial states

Checkbutton widgets

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Adding a Label that will get modified
a_label = ttk.Label(window, text = 'A Label')
a_label.grid(column=0, row=0)


# Modified Button click Event Function
def click_me():
    action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())

# Changing our Label
ttk.Label(window, text='Enter a name:').grid(column=0, row=0)

# Adding a Textbox Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(window, width=12, textvariable=name)
name_entered.grid(column=0, row=1) # column 0

# Adding a Button
action = ttk.Button(window, text="Click Me!", command=click_me)   
action.grid(column=2, row=1) # change column to 2

# Creating three checkbuttons
ttk.Label(window, text='Choose a number:').grid(column=1, row=0)
number = tk.StringVar()
number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1) # Combobox in column 1
number_chosen.current(0)

# Creating three checkbuttons
chVarDis = tk.IntVar()
check1 = tk.Checkbutton(window, text='Disabled', variable=chVarDis, state='disabled')
check1.select()
check1.grid(column=0, row=4, sticky=tk.W)

chVarUn = tk.IntVar()
check2 = tk.Checkbutton(window, text='UnChecked', variable=chVarUn)
check2.deselect()
check2.grid(column=1, row=4, sticky=tk.W)

chVarEn = tk.IntVar()
check3 = tk.Checkbutton(window, text='Enabled', variable=chVarEn)
check3.select()
check3.grid(column=2, row=4, sticky=tk.W)

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()

Setting the sticky property of the grid to tk.W means that the widget is aligned to the west of the grid.

Use radio button widgets

Radiobutton widgets

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Adding a Label that will get modified
a_label = ttk.Label(window, text = 'A Label')
a_label.grid(column=0, row=0)


# Modified Button click Event Function
def click_me():
    action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())

# Changing our Label
ttk.Label(window, text='Enter a name:').grid(column=0, row=0)

# Adding a Textbox Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(window, width=12, textvariable=name)
name_entered.grid(column=0, row=1) # column 0

# Adding a Button
action = ttk.Button(window, text="Click Me!", command=click_me)   
action.grid(column=2, row=1) # change column to 2

ttk.Label(window, text='Choose a number:').grid(column=1, row=0)
number = tk.StringVar()
number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1) # Combobox in column 1
number_chosen.current(0)

# Creating three checkbuttons
chVarDis = tk.IntVar()
check1 = tk.Checkbutton(window, text='Disabled', variable=chVarDis, state='disabled')
check1.select()
check1.grid(column=0, row=4, sticky=tk.W)

chVarUn = tk.IntVar()
check2 = tk.Checkbutton(window, text='UnChecked', variable=chVarUn)
check2.deselect()
check2.grid(column=1, row=4, sticky=tk.W)

chVarEn = tk.IntVar()
check3 = tk.Checkbutton(window, text='Enabled', variable=chVarEn)
check3.deselect()
check3.grid(column=2, row=4, sticky=tk.W)

# GUI callback function
def checkCallback(*ignoredArgs):
    if chVarUn.get():
        check3.configure(state='disabled')
    else:
        check3.configure(state='normal')
    if chVarEn.get():
        check2.configure(state='disabled')
    else:
        check2.configure(state='normal')

# trace the state of the two checkbutton
chVarUn.trace('w', lambda unused0, unused1, unused2 : checkCallback())
chVarEn.trace('w', lambda unused0, unused1, unused2 : checkCallback())

# Radiobutton Globals
COLOR1 = 'Blue'
COLOR2 = 'Gold'
COLOR3 = 'Red'

# Radiobutton Callback
def radCall():
    radSel = radVar.get()
    if radSel == 1:
        window.configure(background=COLOR1)
    elif radSel == 2:
        window.configure(background=COLOR2)
    elif radSel == 3:
        window.configure(background=COLOR3)

# Create three Radiobuttons using one variable
radVar = tk.IntVar()

rad1 = tk.Radiobutton(window, text=COLOR1, variable=radVar, 
    value=1, command=radCall)
rad1.grid(column=0, row=5, sticky=tk.W, columnspan=3)

rad2 = tk.Radiobutton(window, text=COLOR2, variable=radVar, 
    value=2, command=radCall)
rad2.grid(column=1, row=5, sticky=tk.W, columnspan=3)

rad3 = tk.Radiobutton(window, text=COLOR3, variable=radVar, 
    value=3, command=radCall)
rad3.grid(column=2, row=5, sticky=tk.W, columnspan=3)


name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()

Use scrolled text widgets with scrolled text

Scrolled Text is much larger than simple Entry and spans multiple lines. Vertical scrollbars are automatically enabled when the text is larger than the height of the Scrolled Text widget.

import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext

[...]

# Using a scrlled text control
scrol_w = 40
scrol_h = 3
scr = scrolledtext.ScrolledText(window, width=scrol_w, height=scrol_h, wrap=tk.WORD)
scr.grid(column=0, columnspan=3)

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()

  • By setting the wrap attribute to tk.WORD, ScrolledText widget is told to break lines through words. The default is tk.CHAR, which breaks lines with characters.
  • For ScrolledText widget, setting the columnspan attribute of the grid to 3 enables the widget to span three columns. By default, there is only one column.

Restructure

You can see that there is a lot of redundancy in the code above, and here we will refactor them.

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# Adding a Label that will get modified
a_label = ttk.Label(window, text = 'A Label')
a_label.grid(column=0, row=0)


# Modified Button click Event Function
def click_me():
    action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())

# Changing our Label
ttk.Label(window, text='Enter a name:').grid(column=0, row=0)

# Adding a Textbox Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(window, width=12, textvariable=name)
name_entered.grid(column=0, row=1) # column 0

# Adding a Button
action = ttk.Button(window, text="Click Me!", command=click_me)   
action.grid(column=2, row=1) # change column to 2

ttk.Label(window, text='Choose a number:').grid(column=1, row=0)
number = tk.StringVar()
number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1) # Combobox in column 1
number_chosen.current(0)

# Creating three checkbuttons
chVarDis = tk.IntVar()
check1 = tk.Checkbutton(window, text='Disabled', variable=chVarDis, state='disabled')
check1.select()
check1.grid(column=0, row=4, sticky=tk.W)

chVarUn = tk.IntVar()
check2 = tk.Checkbutton(window, text='UnChecked', variable=chVarUn)
check2.deselect()
check2.grid(column=1, row=4, sticky=tk.W)

chVarEn = tk.IntVar()
check3 = tk.Checkbutton(window, text='Enabled', variable=chVarEn)
check3.deselect()
check3.grid(column=2, row=4, sticky=tk.W)

# GUI callback function
def checkCallback(*ignoredArgs):
    if chVarUn.get():
        check3.configure(state='disabled')
    else:
        check3.configure(state='normal')
    if chVarEn.get():
        check2.configure(state='disabled')
    else:
        check2.configure(state='normal')

# trace the state of the two checkbutton
chVarUn.trace('w', lambda unused0, unused1, unused2 : checkCallback())
chVarEn.trace('w', lambda unused0, unused1, unused2 : checkCallback())

# First, we change our Radiobutton globals variables into a list
colors = ['Blue', 'Gold', 'Red']

# We have also changed the callback function to be zero-based, using list
# instead of module-level global variables
# Radiobutton Callback
def radCall():
    radSel = radVar.get()
    if radSel == 1:
        window.configure(background=colors[0]) # now zero-based and using list
    elif radSel == 2:
        window.configure(background=colors[1])
    elif radSel == 3:
        window.configure(background=colors[3])

# Create three Radiobuttons using one variable
radVar = tk.IntVar()

# Next we are selecting a non-existing index value for radVar
radVar.set(99)

# Now we are creating all three Radiobutton widgets within one loop
for col in range(3):
    curRad = tk.Radiobutton(window, text=colors[col], variable=radVar, 
        value=col, command=radCall)
    curRad.grid(column=col, row=5, sticky=tk.W)

# Using a scrlled text control
scrol_w = 40
scrol_h = 3
scr = scrolledtext.ScrolledText(window, width=scrol_w, height=scrol_h, wrap=tk.WORD)
scr.grid(column=0, columnspan=3)

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()

Running the program will find the same as above, but the code is more concise and clear.

Reference

  • Python GUI Programming Cookbook - Second Edition by Burkhard A. Meier

Posted by itazev on Thu, 10 Jan 2019 20:21:10 -0800