GUIs for Python

Improving the accessibility of Python-based research software

This project is maintained by ImperialCollegeLondon

Basic widgets

Pre-requisites

The following import statements need to be called before importing the widgets below:

Jupyter Widgets:

import ipywidgets as widgets

Tkinter:

from tkinter import ttk

and for some older widgets:

import tkinter as tk

Kivy:

Each widget lives in its own submodule of uix:

from kivy import uix

Common widgets

  Jupyter Tkinter Kivy
Button widgets.Button(...) ttk.Button(...) uix.button.Button(...)
Label widgets.Label(...) ttk.Label(...) uix.label.Label(...)
Entry
(1-line)
widgets.Text(...) ttk.Entry(...) uix.textinput.Textinput(..., multiline=False)
Text
(multi-line)
widgets.Textarea(...) tk.Text(...) uix.textinput.Textinput(...)
Radio buttons widgets.RadioButtons(...) ttk.Radiobutton(..., variable=…) uix.checkbox.CheckBox(..., group=…)
Checkbox widgets.RadioButtons(...) ttk.Checkbutton(...) uix.checkbox.CheckBox(...)
Dropdown widgets.Dropdown(...) ttk.Combobox(...) uix.spinner.Spinner(...)
Slider widgets.IntSlider(...)
widgets.FloatSlider(...)
tk.Scale(...) uix.slider.Slider(...)

Container and layout widgets

The above widgets do not exist on their own: they all need to be placed within a container (can be the top window of the application) and arranged in a certain way within the container. This task is performed by container/layout widgets.

Jupyter Widgets and Kivy work in a similar way:

The basic containers are vertical and horizontal boxes that can host several widgets in a row (HBox and VBox for Jupyer and BoxLayout or GridLayout for Kivy). The way of using them is:

  1. All the relevant widgets (buttons, labels, etc.) are first created. Nothing in their creation has anything to do with where they will be placed.
  2. The widgets are added to a container (including all other containers except for the top one), often with some information about their size, alignment, padding, if they should resize with the container, etc.
  3. The position of the children within the container is automatic in the order they are added. To have a more or less accurate position of the widgets, a combination of horizontal, vertical and grid containers have to be used.3
  4. The last, top level container does not need to be added to another container as it is assumed it fills the entire App window (Kivy) or output cell (Jupyter Widgets).

Example: The labels are placed next to each other left to right, with 0 on the left and 3 on the right. Note that hbox also has to be added to a container, unless it is the top container.

# Jupyter Widgets
hbox = widgets.HBox()
hbox.children = [widgets.Label(str(i)) for i in range(4)]

# Kivy
hbox = uix.boxlayout.BoxLayout()
for i in range(4):
    hbox.add_widget(uix.label.Label(text=str(i)))

3: Note that Kivy has a large variety of specialised layouts that do not follow the above description and that, ultimately, allow the user to put the widgets wherever they want using relative or absolute coordinates.

Tkinter follows a different approach:

In Tkinter, how things are arranged do not depend on the container (which will be ttk.Frame most of the times) but on the geometry manager used. The process in this case will be:

  1. During creation, all widgets are assigned a parent, meaning that the parent must exists when the widget is created.
  2. The widgets are placed somewhere within the parent, often with some information about their alignment, padding, etc. using a geometry manager 4:
    • The Pack geometry manager works like Jupyter Widgets and Kivy containers, arranging widgets automatically in the order they are being packed, either vertically or horizontally.
    • The Grid geometry manager allows to specify exact row and column for the children widgets, how many of these they should span as well as how they should resize with the container.
  3. The top container must be the tk.Tk main window, from which all children hang.

Example: For the Pack manager, the labels are placed next to each other left to right, with 0 on the left and 3 on the right. For the Grid manager, a specific row and column is chosen, in this case filling a diagonal. Note that hbox also has to be packed or grid in order to have it visible.

# Using Pack
hbox = ttk.Frame(master=parent_container)
for i in range(4):
    ttk.Label(master=hbox, text=str(i)).pack(side=tk.LEFT)

# Using Grid
hbox = ttk.Frame(master=parent_container)
for i in range(4):
    ttk.Label(master=hbox, text=str(i)).grid(column=i, row=i)

4: Like the specialised Kivy layouts, Tkinter also has the Place geometry manager that allows to place the widgets virtually anywhere in absolute or relative terms.

Notebook and PageLayout

Notebook (Jupyter Widgets and Tkinter) and PageLayout (Kivy) are both used to create a tab-based or multi-page layout, with the possibility of changing to one another by clicking on the tab/page border.

In both cases, it is recommended - although not necessary - that each of the tabs/pages to be a container/layout widget itself as those shown above with as many children widgets as needed.

Jupyter Widgets notebook with 3 tabs:

hbox1 = widgets.HBox()
hbox2 = widgets.HBox()
hbox3 = widgets.HBox()

book = widgets.Tab()
book.children = [hbox1, hbox2, hbox3]

Tkinter notebook with 3 tabs:

book = ttk.Notebook(master=parent_container)

hbox1 = ttk.Frame(master=book)
hbox2 = ttk.Frame(master=book)
hbox3 = ttk.Frame(master=book)

book.add(hbox1)
book.add(hbox2)
book.add(hbox3)

Kivy PageLayout with 3 pages:

hbox1 = uix.boxlayout.BoxLayout()
hbox2 = uix.boxlayout.BoxLayout()
hbox3 = uix.boxlayout.BoxLayout()

book = uix.pagelayout.PageLayout()

book.add_widget(hbox1)
book.add_widget(hbox2)
book.add_widget(hbox3)