Simple test

Ensure your device works with this simple test.

examples/asyncio_simpletest.py
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

examples/asyncio_displayio_button.py
  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())