The Basics of displaying an image

To display an image you first need an image that is in Bitmap format and 24-bits or less. To keep things organized you will want to keep your Bitmaps in a folder called images on your CIRCUITPY drive.

Displaying an Image

Loading an image takes a few steps involving open(), displayio.OnDiskBitmap(), displayio.TileGrid(), displayio.Group(), and board.DISPLAY.show(). So here is what it looks like for loading a small image icon:

Download: file
group = displayio.Group(max_size=1)
group.x = 100
group.y = 120

image_file = open("/images/Gus.bmp", "rb")
image = displayio.OnDiskBitmap(image_file)
image_sprite = displayio.TileGrid(image, pixel_shader=displayio.ColorConverter())

group.append(image_sprite)
board.DISPLAY.show(group)

while True:
    pass

So here is what happened there.

  1. The image can not be displayed unless it is in a Group so we set one up first.
  2. Then we setup the file using open() in read binary, "rb", mode.
  3. Next, we can load the file using displayio.OnDiskBitmap()
  4. Move the now loaded file data into the TileGrid.
  5. Add the newly made sprite into our Group.
  6. Then we can display the group.

The TileGrid basically holds all of the image data so it can be displayed. It is mostly used for sprites in games because it can break up and rearrange an image that has already been loaded into the memory. We are simply going to use TileGrid as a container for our image though.

Removing an Image

So now that we loaded an image we also need to know how to upload it so we can replace it with another image. This is done using group.pop() to delete the image from the group so that a new one can be loaded.

Download: file
group.pop()

So all together you can see that there are a few steps involved with displaying images. Now let us go over how we will be using this process to display and change a few images in our UI.

How we will use Images for the UI

The Startup Screen

It may take a few seconds to load all of the elements of your UI so why not start with a Loading screen?

To do this we will be using the set_background() function from the PyPortal library. This will let us easily load a full screen image onto the PyPortal while making it easy to replace that image later with our UI.

Download: file
# Display an image until the loop starts
pyportal.set_background('/images/loading.bmp')

That is it and the image will be displayed. However, the reason we will not use this for the rest of our code is because this function is just for loading one image into the background and we can not add it to the groups that we will be using later.

The Background Image

We will be using another fullscreen image as the background for our user interface but since that will have other elements loaded on top of it we will want to use Groups.

So lets set up a Group to hold the background then add it to our main display Group splash.

Download: file
bg_group = displayio.Group(max_size=1)
splash.append(bg_group)

Notice that bg_group can only hold 1 object because we set max_size to 1.  This is because we only ever want one image for our background at a time. Later we will see how that one image could be changed at any time without tanking our memory.

Other Image Display Groups

Now we need to make another Group to hold an Icon image that we will be changing via the UI. This one will also set the x and y position and add it to one of our Views rather than the main display Group.

Download: file
icon_group = displayio.Group(max_size=1)
icon_group.x = 180
icon_group.y = 120
icon_group.scale = 1
view2.append(icon_group)

Just like bg_group this will only hold one object so we do not bog down the system memory by loading to many images.

Adding this Group to the view2 Group rather than splash will make it so that the Icon can be visible or not visible just by showing or hiding the view2 Group without affecting the rest of the display.

The icon_group.scale = 1 part of the code is not needed unless you wanted to make the image bigger. Although it just makes the pixels of the image bigger so a scale of 2 would make each pixel 2x2 and so on. So images become more blocky and it cannot be used to make an image smaller than a scale of 1.

Set Image Function

So do you remember from the top of this page how many steps it took to load and unload an image? Well to make it easy for us to deal with switching images, we will be using a function to do all the load and unload BMP image stuff. This will help to keep our memory free as well as making it very easy to switch large and small images in the loop by just using set_image(Group, filename).

The following code will need to be added to your code.py file.

Download: file
# This will handel switching Images and Icons
def set_image(group, filename):
    """Set the image file for a given goup for display.
    This is most useful for Icons or image slideshows.
        :param group: The chosen group
        :param filename: The filename of the chosen image
    """
    print("Set image to ", filename)
    if group:
        group.pop()

    if not filename:
        return  # we're done, no icon desired
    try:
        if image_file:
            image_file.close
    except NameError:
        pass
    image_file = open(filename, "rb")
    image = displayio.OnDiskBitmap(image_file)
    try:
        image_sprite = displayio.TileGrid(image, pixel_shader=displayio.ColorConverter())
    except TypeError:
        image_sprite = displayio.TileGrid(image, pixel_shader=displayio.ColorConverter(), position=(0,0))
    group.append(image_sprite)

We will talk about how to use this function in the Usage section.

If you would like to see more information on loading images with CircuitPython, check out these links:

This guide was first published on Feb 10, 2020. It was last updated on Feb 10, 2020.
This page (Images) was last updated on Oct 21, 2020.