Simple test¶
Ensure your device works with this simple test.
1# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
2# SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries
3#
4# SPDX-License-Identifier: Unlicense
Displayio Button Example¶
Illustrates how to use Display_Buttons and blinking vectorio.Circles with asyncio
1# SPDX-FileCopyrightText: 2022 Tim Cocks for Adafruit Industries
2#
3# SPDX-License-Identifier: MIT
4"""
5Example that illustrates how to use Displayio Buttons to modify
6some blinking circles. One button inverts colors, the others change
7the interval length of the blink for one of the circles.
8"""
9
10import asyncio
11import adafruit_touchscreen
12import displayio
13import terminalio
14import vectorio
15import board
16from adafruit_button import Button
17
18# use built-in display
19display = board.DISPLAY
20
21# explicitly set the display to default orientation in-case it was changed
22display.rotation = 0
23
24# --| Button Config |-------------------------------------------------
25# invert color Button
26BUTTON_1_X = 10
27BUTTON_1_Y = 80
28BUTTON_1_LABEL = "Invert Color"
29
30# slower interval Button
31BUTTON_2_X = 200
32BUTTON_2_Y = 160
33BUTTON_2_LABEL = "Slower"
34
35# faster interval Button
36BUTTON_3_X = 200
37BUTTON_3_Y = 80
38BUTTON_3_LABEL = "Faster"
39
40# shared button configurations
41BUTTON_WIDTH = 100
42BUTTON_HEIGHT = 50
43BUTTON_STYLE = Button.ROUNDRECT
44BUTTON_FILL_COLOR = 0x00FFFF
45BUTTON_OUTLINE_COLOR = 0xFF00FF
46BUTTON_LABEL_COLOR = 0x000000
47# --| Button Config |-------------------------------------------------
48
49# Setup touchscreen (PyPortal)
50ts = adafruit_touchscreen.Touchscreen(
51 board.TOUCH_XL,
52 board.TOUCH_XR,
53 board.TOUCH_YD,
54 board.TOUCH_YU,
55 calibration=((5200, 59000), (5800, 57000)),
56 size=(display.width, display.height),
57)
58
59# initialize color button
60invert_color_btn = Button(
61 x=BUTTON_1_X,
62 y=BUTTON_1_Y,
63 width=BUTTON_WIDTH,
64 height=BUTTON_HEIGHT,
65 style=BUTTON_STYLE,
66 fill_color=BUTTON_FILL_COLOR,
67 outline_color=BUTTON_OUTLINE_COLOR,
68 label=BUTTON_1_LABEL,
69 label_font=terminalio.FONT,
70 label_color=BUTTON_LABEL_COLOR,
71)
72
73# initialize interval slower button
74interval_slower_btn = Button(
75 x=BUTTON_2_X,
76 y=BUTTON_2_Y,
77 width=BUTTON_WIDTH,
78 height=BUTTON_HEIGHT,
79 style=BUTTON_STYLE,
80 fill_color=BUTTON_FILL_COLOR,
81 outline_color=BUTTON_OUTLINE_COLOR,
82 label=BUTTON_2_LABEL,
83 label_font=terminalio.FONT,
84 label_color=BUTTON_LABEL_COLOR,
85)
86
87# initialize interval faster button
88interval_faster_btn = Button(
89 x=BUTTON_3_X,
90 y=BUTTON_3_Y,
91 width=BUTTON_WIDTH,
92 height=BUTTON_HEIGHT,
93 style=BUTTON_STYLE,
94 fill_color=BUTTON_FILL_COLOR,
95 outline_color=BUTTON_OUTLINE_COLOR,
96 label=BUTTON_3_LABEL,
97 label_font=terminalio.FONT,
98 label_color=BUTTON_LABEL_COLOR,
99)
100
101
102# Button state data object. Will hold either true of false whether button is currently pressed
103class ButtonState:
104 # pylint: disable=too-few-public-methods
105 def __init__(self, initial_state):
106 self.state = initial_state
107
108
109# Interval length data object. Holds the amount of time in ms the interval should last for
110class Interval:
111 # pylint: disable=too-few-public-methods
112 def __init__(self, initial_value):
113 self.value = initial_value
114
115
116# main group to show things on the display
117main_group = displayio.Group()
118
119# Initialize first circle
120palette_1 = displayio.Palette(2)
121palette_1[0] = 0x125690
122palette_1[1] = 0x125690
123circle_1 = vectorio.Circle(pixel_shader=palette_1, radius=15, x=20, y=20)
124
125# Initialize second circle
126palette_2 = displayio.Palette(2)
127palette_2[0] = 0x12FF30
128palette_2[1] = 0x12FF30
129circle_2 = vectorio.Circle(pixel_shader=palette_2, radius=15, x=60, y=20)
130
131# add everything to the group, so it gets displayed
132main_group.append(circle_1)
133main_group.append(circle_2)
134main_group.append(invert_color_btn)
135main_group.append(interval_slower_btn)
136main_group.append(interval_faster_btn)
137
138
139async def blink(palette, interval, count, button_state): # Don't forget the async!
140 """
141 blink coroutine. Hides and shows a vectorio shape by
142 using make_transparent() and make_opaque() on it's palette.
143
144 :param displayio.Palette palette: The palette to change colors on for blinking
145 :param Interval interval: The Interval data object containing the interval length to use
146 :param int count: The number of times to repeat the blink. -1 for indefinite loop
147 :param ButtonState button_state: The ButtonState data object for the invert color button
148 """
149 while count < 0 or count > 0:
150 # if the color button is pressed
151 if button_state.state:
152 # if the color is still on default
153 if palette[0] == palette[1]:
154 # invert the color by subtracting from white
155 palette[0] = 0xFFFFFF - palette[0]
156
157 # if the color button is not pressed
158 else:
159 # set the color back to default
160 palette[0] = palette[1]
161
162 # hide the circle
163 palette.make_opaque(0)
164 # wait interval length
165 await asyncio.sleep(interval.value / 1000) # Don't forget the await!
166
167 # show the circle
168 palette.make_transparent(0)
169 # wait interval length
170 await asyncio.sleep(interval.value / 1000) # Don't forget the await!
171
172 # decrement count if it's positive
173 if count > 0:
174 count -= 1
175
176
177def handle_color_button(touch_event, color_button, button_state):
178 """
179 Check if the color button is pressed, and updates
180 the ButtonState data object as appropriate
181
182 :param touch_event: The touch point object from touchscreen
183 :param Button color_button: The button to check for presses on
184 :param ButtonState button_state: ButtonState data object to set
185 the current value into
186 """
187
188 # if there is a touch event
189 if touch_event:
190 # if the color button is being touched
191 if color_button.contains(touch_event):
192 # set selected to change button color
193 color_button.selected = True
194 # set button_state so other coroutines can access it
195 button_state.state = True
196
197 # the color button is not being touched
198 else:
199 # set selected to change button color back to default
200 color_button.selected = False # if touch is dragged outside of button
201 # set button_state so other coroutines can access it.
202 button_state.state = False
203
204 # there are no touch events
205 else:
206 # if the color button is currently the pressed color
207 if color_button.selected:
208 # set selected back to false to change button back to default color
209 color_button.selected = False
210 # set button_state so other coroutines can access it
211 button_state.state = False
212
213
214def handle_interval_buttons(touch_event, button_slower, button_faster, interval):
215 """
216 Will check for presses on
217 the faster and slower buttons and updated the data in the
218 Interval data object as appropriate
219
220 :param touch_event: Touch point object from touchscreen
221 :param Button button_slower: The slower button object
222 :param Button button_faster: The faster button object
223 :param Interval interval: The Interval data object to store state
224 """
225 # if there are any touch events
226 if touch_event:
227 # if the slower button is being touched
228 if button_slower.contains(touch_event):
229 # if it just became pressed. i.e. was not pressed last frame
230 if not button_slower.selected:
231 # set selected to change the button color
232 button_slower.selected = True
233
234 # increment the interval length and store it on the data object
235 interval.value += 100
236 print("new interval val: {}".format(interval.value))
237
238 # if the slower button is not being touched
239 else:
240 # set selected to put the slower button back to default color
241 button_slower.selected = False
242
243 # if the faster button is being touched
244 if button_faster.contains(touch_event):
245 # if it just became pressed. i.e. was not pressed last frame
246 if not button_faster.selected:
247 # set selected to change the button color
248 button_faster.selected = True
249 # if the interval is large enough to decrement
250 if interval.value >= 100:
251 # decrement interval value and store it on the data object
252 interval.value -= 100
253 print("new interval val: {}".format(interval.value))
254
255 # if the faster button is not being touched
256 else:
257 # set selected back to false to change color back to default
258 button_faster.selected = False
259
260 # there are no touch events
261 else:
262 # if slower button is the pressed color
263 if button_slower.selected:
264 # set it back to default color
265 button_slower.selected = False
266
267 # if the faster button is pressed color
268 if button_faster.selected:
269 # set it back to default color
270 button_faster.selected = False
271
272
273async def monitor_buttons(
274 button_slower, button_faster, color_button, interval, button_state
275):
276 """
277 monitor_buttons coroutine.
278
279 :param Button button_slower: The slower button object
280 :param Button button_faster: The faster button object
281 :param Button color_button: The invert color button object
282 :param Interval interval: The Interval data object to store state
283 :param ButtonState button_state: The ButtonState data object to
284 store color button state
285 """
286 while True:
287 # get current touch data from overlay
288 p = ts.touch_point
289
290 # handle touch event data
291 handle_color_button(p, color_button, button_state)
292 handle_interval_buttons(p, button_slower, button_faster, interval)
293
294 # allow other tasks to do work
295 await asyncio.sleep(0)
296
297
298# main coroutine
299async def main(): # Don't forget the async!
300 # create data objects
301 color_btn_state = ButtonState(False)
302 interval_1 = Interval(550)
303 interval_2 = Interval(350)
304
305 # create circle blink tasks
306 circle_1_task = asyncio.create_task(
307 blink(palette_1, interval_1, -1, color_btn_state)
308 )
309 circle_2_task = asyncio.create_task(
310 blink(palette_2, interval_2, 20, color_btn_state)
311 )
312
313 # create buttons task
314 button_task = asyncio.create_task(
315 monitor_buttons(
316 interval_slower_btn,
317 interval_faster_btn,
318 invert_color_btn,
319 interval_1,
320 color_btn_state,
321 )
322 )
323
324 # start all of the tasks
325 await asyncio.gather(
326 circle_1_task, circle_2_task, button_task
327 ) # Don't forget the await!
328
329
330# show main_group so it's visible on the display
331display.root_group = main_group
332
333# start the main coroutine
334asyncio.run(main())