Define Display Group Layers

Within CircuitPython's displayio library, a display group is a list of label or graphic attributes that are defined for each object of the display. This section of the Thermal Camera's primary process module defines the image_group display group that the camera will use to show measured values, the sensor image or histogram, status message, and the histogram legend.

The camera's display group, image_group, consists of 77 layered objects that start with a black screen-sized background in the back-most position. The next 64 objects of the display make up the colored blocks used for the image area.

The status message label comes next, followed by the values and labels in the display sidebar area. Finally, objects that make up the histogram legend top off the stack of display objects in image_group.

The objects and their attributes are appended to the image_group list one-at-a-time when first defined. When appended to image_group, the attributes of each object are defined. For example, the alarm label alm is defined as a Label object with attributes that include the label's font, text contents, text color, and maximum character count (glyphs):

alarm_label = Label(font, text="alm", color=WHITE, max_glyphs=3)

The x/y position on the PyGamer's display screen is an attribute of the label object. Keeping with the grid block positioning scheme, the element_grid() helper is used to convert the block column/row position to the x/y coordinates of the board's display screen. In this case, element_grid() returns an x/y tuple for the intersection of column -1.8 and row 1.5:

pos = element_grid(-1.8, 1.5)

The alarm label's display screen x/y coordinate attributes are set using the x/y tuple, pos:

alarm_label.x = pos.x
alarm_label.y = pos.y

After defining the display object's attributes, it is appended to the image_group display group:

image_group.append(alarm_label)

This process is repeated, starting from the back of the display and progressing towards the front, as each new object is appended to the display group.

Let's look at how all of the display objects are defined for the Thermal Camera.

Define Image Group and Create the Background Layer

The image_group list needs to be defined before we can append any display group objects. The max_size argument is set to the number of objects that will be used. In this case, we'll need 77 objects (image_group[0:76]).

Now that the display group is ready, we'll define the background bitmap object and its attributes. The bitmap's size is the same as the display's width and height; color is black. The bitmap will be placed at x/y coordinate 0,0 (the upper left corner of the display). After it's defined, the background object is appended as the first object in the image_group list.

Download: file
### Define the display group ###
image_group = displayio.Group(max_size=77)

# Create a background color fill layer; image_group[0]
color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
color_palette = displayio.Palette(1)
color_palette[0] = BLACK
background = displayio.TileGrid(color_bitmap, pixel_shader=color_palette,
                                x=0, y=0)
image_group.append(background)

Define the Thermal Image Display Group Layers

Next, the 64 squares used to represent sensor array temperatures will be defined and appended. Two for loops are used to step through each column and row of squares. Each square is defined as a rectangle with width and height equal to ELEMENT_SIZE. No color attribute is defined for the square, making it transparent -- for now.

Download: file
# Define the foundational thermal image element layers; image_group[1:64]
#   image_group[#]=(row * 8) + column
for row in range(0, 8):
    for col in range(0, 8):
        pos_x, pos_y = element_grid(col, row)
        element = Rect(x=pos_x, y=pos_y,
                       width=ELEMENT_SIZE, height=ELEMENT_SIZE,
                       fill=None, outline=None, stroke=0)
        image_group.append(element)

Define the Text Label Display Group Layers

Finally, the remaining text objects that display legends and values are defined and appended to the image_group display group.

For each object, the label name is defined along with the font, text contents, font color, and the maximum number of characters to display. Next, the object's position attribute is defined using the element_grid() helper to calculate the hardware-specific x/y display coordinates. After the attributes are defined, each object is appended to image_group.

Download: file
# Define labels and values using element grid coordinates
status_label = Label(font, text="", color=BLACK, max_glyphs=6)
pos_x, pos_y = element_grid(2.5, 4)
status_label.x = pos_x
status_label.y = pos_y
image_group.append(status_label)  # image_group[65]

alarm_label = Label(font, text="alm", color=WHITE, max_glyphs=3)
pos_x, pos_y = element_grid(-1.8, 1.5)
alarm_label.x = pos_x
alarm_label.y = pos_y
image_group.append(alarm_label)  # image_group[66]

max_label = Label(font, text="max", color=RED, max_glyphs=3)
pos_x, pos_y = element_grid(-1.8, 3.5)
max_label.x = pos_x
max_label.y = pos_y
image_group.append(max_label)  # image_group[67]

min_label = Label(font, text="min", color=CYAN, max_glyphs=3)
pos_x, pos_y = element_grid(-1.8, 7.5)
min_label.x = pos_x
min_label.y = pos_y
image_group.append(min_label)  # image_group[68]

ave_label = Label(font, text="ave", color=YELLOW, max_glyphs=3)
pos_x, pos_y = element_grid(-1.8, 5.5)
ave_label.x = pos_x
ave_label.y = pos_y
image_group.append(ave_label)  # image_group[69]

alarm_value = Label(font, text=str(ALARM_F), color=WHITE, max_glyphs=5)
pos_x, pos_y = element_grid(-1.8, 0.5)
alarm_value.x = pos_x
alarm_value.y = pos_y
image_group.append(alarm_value)  # image_group[70]

max_value = Label(font, text=str(MAX_RANGE_F), color=RED, max_glyphs=5)
pos_x, pos_y = element_grid(-1.8, 2.5)
max_value.x = pos_x
max_value.y = pos_y
image_group.append(max_value)  # image_group[71]

min_value = Label(font, text=str(MIN_RANGE_F), color=CYAN, max_glyphs=5)
pos_x, pos_y = element_grid(-1.8, 6.5)
min_value.x = pos_x
min_value.y = pos_y
image_group.append(min_value)  # image_group[72]

ave_value = Label(font, text="---", color=YELLOW, max_glyphs=5)
pos_x, pos_y = element_grid(-1.8, 4.5)
ave_value.x = pos_x
ave_value.y = pos_y
image_group.append(ave_value)  # image_group[73]

min_histo = Label(font, text="", color=CYAN, max_glyphs=3)
pos_x, pos_y = element_grid(0.5, 7.5)
min_histo.x = pos_x
min_histo.y = pos_y
image_group.append(min_histo)  # image_group[74]

max_histo = Label(font, text="", color=RED, max_glyphs=3)
pos_x, pos_y = element_grid(6.5, 7.5)
max_histo.x = pos_x
max_histo.y = pos_y
image_group.append(max_histo)  # image_group[75]

range_histo = Label(font, text="", color=BLUE, max_glyphs=7)
pos_x, pos_y = element_grid(2.5, 7.5)
range_histo.x = pos_x
range_histo.y = pos_y
image_group.append(range_histo)  # image_group[76]

Fun Facts about Display Group Objects

Objects and their attributes in the display group can be accessed in two ways. The most commonly-used method is to assign a name attribute to the object. For example, the text of the status message label can be set to display the text WELCOME in this manner:

status_label.text = "WELCOME"

Objects in image_group can also be accessed by their indexed position in the display group. An index of 0 is the back-most object in the display group; the highest index value is front-most. The status message text can also be changed using the index:

image_group[65].text = "WELCOME"

The Thermal Camera uses both techniques. Named display objects are used whenever possible to clearly identify which object is being changed. For efficiency, however, the index position method is used when stepping through a sequence of image_group objects, as when displaying the 64 colored blocks for the sensor image. The index position method is also used by the setup_mode() helper when moving on-screen to select the alarm, maximum, or minimum parameter.

This guide was first published on Jan 29, 2020. It was last updated on Jan 29, 2020.

This page (Display) was last updated on Sep 30, 2020.