There are several User Interface (UI) elements available to use with displayio. You can use these together to create all kinds of fun applications such as a calculator.

Referencing the Display

On boards that have a display, it is automatically initialized. to reference it, you simply need import board and assign it to the built-in display reference:

import board

display = board.DISPLAY

On board without a built-in display, we recommend taking a look at the CircuitPython pages regarding your specific display for how to initialize it. You would then assign display to the returned output of the display driver initialization. For instance, if you have an ILI9341 display, it would look something like this, though your pins may vary.

import board
import displayio
import adafruit_ili9341


spi = board.SPI()
tft_cs = board.D9
tft_dc = board.D10

display_bus = displayio.FourWire(
    spi, command=tft_dc, chip_select=tft_cs, reset=board.D6
display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240)


Groups are a way for displayio to keep track of all of the elements that it needs to draw. Subgroups can be inside of groups, but you must have at least one main group. For all of the elements on this page, you must first import displayio, so we'll start with making sure that's at the top of your file.

import displayio

You must also create at least one group that will be used as your main group.

my_display_group = displayio.Group(max_size=25)


The shapes are part of the the adafruit_display_shapes library. At the time of this writing, there are three shapes available. They work by generating a bitmap in the specific shape using displayio.


The rectangle is your most basic shape. It can either be filled, outlined, or both.

Rounded Rectangle

The rounded rectangle is a little more complex and is comprised of 4 lines and quarter circle corners.


The circle is based on the rounded rectangle and only draws the four corners without any width or height.


The triangle allows you to supply three sets of coordinates and will either draw an outline between those vertices, fill it in, or both.

To use shapes, you first need to import the shapes you want to use at the top of your file. For instance if you wanted to separate import all the shapes, you would add something like this.

from adafruit_display_shapes.rect import Rect
from import Circle
from adafruit_display_shapes.roundrect import RoundRect
from adafruit_display_shapes.triangle import Triangle

Next, you can draw a rectangle with something like:

rect = Rect(0, 0, 80, 40, fill=0x00FF00)

For a circle, you can create it with something like:

circle = Circle(100, 100, 20, fill=0x00FF00, outline=0xFF00FF)

For a triangle, you can create it with something like:

triangle = Triangle(170, 50, 120, 140, 210, 160, fill=0x00FF00, outline=0xFF00FF)

Or you can draw a rounded rectangle with something like:

roundrect = RoundRect(50, 100, 40, 80, 10, fill=0x0, outline=0xFF00FF, stroke=3)

Finally, you can add all of these shapes to your group.



For fonts, there are a couple options that you can use. You can create or provide a custom font file and use that for your label. If you don't want to provide a custom bitmap font, you can use the Built-in Terminal Font.

Built-in Terminal Font

The terminal font looks a little blocky, but at least you don't need a separate file.

This example comes from the  PyBadger Event Badge guide.

To use the Terminal Font, you first need to import terminalio by adding an import statement to the top of your file.

import terminalio

Then you simply pass the terminalio font to the UI element. For instance, with the label, you would do something like:

my_label = Label(terminalio.FONT, text="My Label Text", color=BLACK)

Bitmap Fonts

The Bitmap font uses the adafruit_bitmap_font library and requires a separate BDF (Bitmap Distribution Format) file, but looks nicer on the screen. It doesn't currently have anti-aliasing, so it still looks a little blocky on some fonts.

This example comes from the PyBadge Conference Badge With Unicode Fonts guide.

To use a Bitmap Font, you first need to copy your custom file over to your CIRCUITPY drive. We like to place fonts into a /fonts folder. To find out more about creating your own custom fonts, be sure to check out our Custom Fonts for CircuitPython Displays guide.

Next import bitmap_font by adding an import statement to the top of your file.

from adafruit_bitmap_font import bitmap_font

After that, you can create a font instance. For example, if you have a font file named Arial-12.bdf in the fonts folder, you would use the following line of code.

font = bitmap_font.load_font("/fonts/Arial-12.bdf")

Then you simply pass font instance to the UI element. For instance, with the label, you would do something like:

my_label = Label(font, text="My Label Text", color=BLACK)


The label requires the adafruit_display_text library. It requires a font to be passed in. This can either be the Terminal Font or a Custom Font. It allows you to display text and place it in your displayio group. The conference badge mentioned in the fonts section makes great use of labels.

To create a label, you first import the required library at the top of your file.

from adafruit_display_text.label import Label

Then you create the label.

my_label = Label(terminalio.FONT, text="My Label Text", color=BLACK)

Finally, add the label to a displayio group. This can either be your main group or a subgroup.



The button makes use of the adafruit_button library and builds on top of the adafruit_display_shapesadafruit_label, and adafruit_touchscreen libraries. A button is basically a shape and label together which can also handle presses as well as color inversion.

To use the button, you need to add the required libraries to the top of your file.

from adafruit_button import Button
import adafruit_touchscreen

Next create your button. There are lots of options and you can take a look at some of the examples provided in the button library to get an idea of the various things you can do.

my_button = Button(x=20, y=20, width=80, height=40,
                   label="My Button", label_font=terminalio.FONT)

The font is required, but again, you can provide either the built-in font or a custom font. Finally add it to your group.


Here's what the simple test example looks like showing many different variations.


Images are also available, although they are not used in this calculator project. There are a couple of different ways to display images with displayio.

ImageLoad Library

Imageload is the main class in the adafruit_imageload library provides an easy way to decode and display bitmaps. At the moment, it is only able to decode indexed 8-bit bitmaps. To use it, first include the library at the top of your file.

import adafruit_imageload

Second, Generate the Bitmap and Palette from the image:

my_bitmap, my_palette = adafruit_imageload.load("/my_bitmap.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)

Third, create a TileGrid from the Bitmap and Palette:

my_tilegrid = displayio.TileGrid(my_bitmap, pixel_shader=my_palette)

Finally add the TileGrid to your display group.



OnDiskBitmap is available directly through displayio and is very easy to use. The first step is to open the image file with read and binary modes and create a bitmap. Because of its flexibility and low memory use, this is the recommended way.

my_bitmap = displayio.OnDiskBitmap(open("/my_bitmap.bmp", "rb"))

The second step is to create a TileGrid from the image using the automatic color converter.

my_tilegrid = displayio.TileGrid(my_bitmap, pixel_shader=displayio.ColorConverter())

Finally add the TileGrid to your display group.


Calculator UI Elements

The PyPortal Calculator makes use of the Rectangle, Label, and Button Elements.

This guide was first published on Jul 13, 2019. It was last updated on Jul 13, 2019.

This page (UI Quickstart) was last updated on May 04, 2021.

Text editor powered by tinymce.