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 layered objects. The first 225 objects of the display make up the colored cells used for the image grid 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, and text color:
status_label = Label(font_0, text="", color=None) status_label.anchor_point = (0.5, 0.5) status_label.anchored_position = ((WIDTH // 2) + (GRID_X_OFFSET // 2), HEIGHT // 2) image_group.append(status_label) # image_group[225]
The anchored_position (x/y coordinates) and anchor_point (left/right/center justification) of the alarm label on the PyGamer's display screen are calculated to appear in the center of the image grid area. Other display label and value positions were determined and fine-tuned empirically. After defining the display object's attributes, it is appended to the image_group
display group.
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.
Define the Image Group
After playing a couple of musical tones and storing the elapsed time to mark the beginning of the display definition process, the image_group
definition list for display group objects comes next. The scale
argument adjusts image group element position and size parameters. For the PyGamer's display, no adjustment is required so scale=1
.
The time marker mkr_t0
along with seven other process time markers will be reported at the end of each displayed frame to calculate thermal camera code performance. This marker establishes the time that the display group definition phase began.
play_tone(440, 0.1) # Musical note A4 play_tone(880, 0.1) # Musical note A5 # ### Define the display group ### mkr_t0 = time.monotonic() # Time marker: Define Display Elements image_group = displayio.Group(scale=1)
Define the Thermal Image Display Group Layers
Next, the 225 square cells used to represent sensor array temperatures are defined and appended to image_group
. Two for
loops are used to step through each column and row of cells. Each square is defined as a rectangle with width and height equal to CELL_SIZE
. No color attribute is defined for the cell, making it transparent -- for now.
# Define the foundational thermal image grid cells; image_group[0:224] # image_group[#] = image_group[ (row * GRID_AXIS) + column ] for row in range(0, GRID_AXIS): for col in range(0, GRID_AXIS): cell_x = (col * CELL_SIZE) + GRID_X_OFFSET cell_y = row * CELL_SIZE cell = Rect( x=cell_x, y=cell_y, width=CELL_SIZE, height=CELL_SIZE, fill=None, outline=None, stroke=0, ) image_group.append(cell)
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, and font color. Next, the object's anchor_point
(justification) and anchored_position
(x/y coordinates) attributes are defined. After the attributes are defined, each object is appended to image_group
.
# Define labels and values status_label = Label(font_0, text="", color=None) status_label.anchor_point = (0.5, 0.5) status_label.anchored_position = ((WIDTH // 2) + (GRID_X_OFFSET // 2), HEIGHT // 2) image_group.append(status_label) # image_group[225] alarm_label = Label(font_0, text="alm", color=WHITE) alarm_label.anchor_point = (0, 0) alarm_label.anchored_position = (1, 16) image_group.append(alarm_label) # image_group[226] max_label = Label(font_0, text="max", color=RED) max_label.anchor_point = (0, 0) max_label.anchored_position = (1, 46) image_group.append(max_label) # image_group[227] min_label = Label(font_0, text="min", color=CYAN) min_label.anchor_point = (0, 0) min_label.anchored_position = (1, 106) image_group.append(min_label) # image_group[228] ave_label = Label(font_0, text="ave", color=YELLOW) ave_label.anchor_point = (0, 0) ave_label.anchored_position = (1, 76) image_group.append(ave_label) # image_group[229] alarm_value = Label(font_0, text=str(ALARM_F), color=WHITE) alarm_value.anchor_point = (0, 0) alarm_value.anchored_position = (1, 5) image_group.append(alarm_value) # image_group[230] max_value = Label(font_0, text=str(MAX_RANGE_F), color=RED) max_value.anchor_point = (0, 0) max_value.anchored_position = (1, 35) image_group.append(max_value) # image_group[231] min_value = Label(font_0, text=str(MIN_RANGE_F), color=CYAN) min_value.anchor_point = (0, 0) min_value.anchored_position = (1, 95) image_group.append(min_value) # image_group[232] ave_value = Label(font_0, text="---", color=YELLOW) ave_value.anchor_point = (0, 0) ave_value.anchored_position = (1, 65) image_group.append(ave_value) # image_group[233] min_histo = Label(font_0, text="", color=None) min_histo.anchor_point = (0, 0.5) min_histo.anchored_position = (GRID_X_OFFSET, 121) image_group.append(min_histo) # image_group[234] max_histo = Label(font_0, text="", color=None) max_histo.anchor_point = (1, 0.5) max_histo.anchored_position = (WIDTH - 2, 121) image_group.append(max_histo) # image_group[235] range_histo = Label(font_0, text="-RANGE-", color=None) range_histo.anchor_point = (0.5, 0.5) range_histo.anchored_position = ((WIDTH // 2) + (GRID_X_OFFSET // 2), 121) image_group.append(range_histo) # image_group[236]
Whew. We've imported libraries, listed the essential constants, established some helpers, and defined the elements of the display. After a quick aside to talk about how a display group can be accessed, it'll be time to bring it all together in the thermal camera's primary process.
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[225].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 225 colored cells 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.
Page last edited March 08, 2024
Text editor powered by tinymce.