Skip to content

Commit 176d35b

Browse files
committed
add annotation widget
1 parent 531c81d commit 176d35b

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# SPDX-FileCopyrightText: 2021 Kevin Matocha
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""
5+
6+
`Annotation`
7+
================================================================================
8+
A widget for annotating other widgets or freeform positions.
9+
10+
* Author(s): Kevin Matocha
11+
12+
Implementation Notes
13+
--------------------
14+
15+
**Hardware:**
16+
17+
**Software and Dependencies:**
18+
19+
* Adafruit CircuitPython firmware for the supported boards:
20+
https://github.com/adafruit/circuitpython/releases
21+
22+
"""
23+
24+
# pylint: disable=too-many-arguments, too-many-locals, unused-argument
25+
26+
from adafruit_displayio_layout.widgets.widget import Widget
27+
from adafruit_display_shapes.line import Line
28+
from adafruit_display_text import bitmap_label
29+
from terminalio import FONT
30+
31+
32+
class Annotation(Widget):
33+
"""A widget to be used to annotate other widgets with text and lines, but can also
34+
be used freeform by using ``(x,y)`` parameter.
35+
36+
:param int x: x-direction pixel position for the end of the annotation line for
37+
freeform positioning, will be ignored if a ``widget`` and ``anchor_point`` and/or
38+
``anchored_position`` are provided
39+
:param int x: x-direction pixel position for the end of the annotation line for
40+
freeform positioning, will be ignored if a ``widget`` and ``anchor_point`` and/or
41+
``anchored_position`` are provided
42+
43+
:param Widget widget: the widget to be annotated, all dimensions are relative to
44+
this widget. The annotation line position will be defined by either
45+
the `anchor_point` (in relative dimensions of the size of the widget)
46+
or the `anchored_position` (in raw pixel dimensions relative to the origin
47+
of the widget).
48+
49+
:param str text: text to be displayed in the annotation.
50+
:param Font font: font to be used for the text.
51+
52+
:param anchor_point: starting point for the annotation line, where `anchor_point` is an
53+
(A,B) tuple in relative units of the size of the widget,
54+
for example (0.0, 0.0) is the upper left corner, and (1.0, 1.0) is the lower
55+
right corner of the widget. If `anchor_point` is `None`, then `anchored_position`
56+
is used to set the annotation line starting point, in widget size relative units
57+
(default is (0.0, 0.0))
58+
:type anchor_point: Tuple[float, float]
59+
60+
:param anchored_position: pixel position starting point for the annotation line
61+
where `anchored_position` is an (x,y) tuple in pixel units relative to the
62+
upper left corner of the widget, in pixel units (default is None)
63+
:type anchored_position: Tuple[int, int]
64+
65+
:param position_offset: Used to 'nudge' the line position to where you want, this
66+
is an (x,y) pixel offset added to the annotation line starting
67+
point, either set by `anchor_point` or `anchored_position` (in pixel units).
68+
:type position_offset: Tuple[int, int]
69+
70+
:param int delta_x: the pixel x-offset for the second end of the line where the text
71+
will reside, in pixel units (default: -15)
72+
:param int delta_y: the pixel y-offset for the second end of the line where the text
73+
will reside, in pixel units (default: -10)
74+
75+
:param int stroke: the annotation line width (in pixels). [NOT currently implemented]
76+
77+
:param int line_color: the color of the annotation line (default: 0xFFFFFF).
78+
:param int text_color: the color of the text, if set to `None` color will be
79+
set to `line_color` (default: same as `line_color`).
80+
81+
:param text_offset: a (x,y) pixel offset to adjust text position relative
82+
to annotation line, in pixel units (default: (0,-1))
83+
:type text_offset: Tuple[int, int]
84+
85+
:param Boolean text_under: set `True` for text to be placed below the
86+
annotation line (default: False)
87+
"""
88+
89+
def __init__(
90+
self,
91+
x=None,
92+
y=None,
93+
text=None,
94+
font=FONT,
95+
delta_x=-15,
96+
delta_y=-10,
97+
widget=None,
98+
anchor_point=(0.0, 0.0),
99+
anchored_position=None,
100+
position_offset=(0, 0),
101+
stroke=3, # Not currently implemented in adafruit_display_shapes/line.py
102+
line_color=0xFFFFFF,
103+
text_color=None,
104+
text_offset=(0, -1),
105+
text_under=False,
106+
):
107+
108+
if widget:
109+
if (x is not None) or (y is not None):
110+
print(
111+
"Note: Overriding (x,y) values with widget, anchor_point"
112+
" and/or anchored_position"
113+
)
114+
widget_width = widget.bounding_box[2]
115+
widget_height = widget.bounding_box[3]
116+
if anchor_point is not None:
117+
line_x0 = (
118+
widget.x
119+
+ round(widget_width * anchor_point[0])
120+
+ position_offset[0]
121+
)
122+
line_y0 = (
123+
widget.y
124+
+ round(widget_height * anchor_point[1])
125+
+ position_offset[1]
126+
)
127+
elif anchored_position is not None:
128+
line_x0 = widget.x + anchored_position[0] + position_offset[0]
129+
line_y0 = widget.y + anchored_position[1] + position_offset[1]
130+
else:
131+
raise ValueError("Must supply either anchor_point or anchored_position")
132+
elif (x is not None) and (y is not None):
133+
line_x0 = x
134+
line_y0 = y
135+
else:
136+
raise ValueError(
137+
"Must supply either (x,y) or widget and anchor_point or anchored_position"
138+
)
139+
140+
line_x1 = line_x0 + delta_x
141+
line_y1 = line_y0 + delta_y
142+
143+
text_anchor_point = (0.0, 1.0) # default: set text anchor to left corner
144+
underline_x_multiplier = 1
145+
146+
if delta_x < 0: # line is heading to the left, set text anchor to right corner
147+
text_anchor_point = (1.0, 1.0)
148+
underline_x_multiplier = -1
149+
150+
if (
151+
text_under
152+
): # if text is under the line, set to text_anchor_point to upper edge
153+
text_anchor_point = (text_anchor_point[0], 0.0)
154+
155+
if text_color is None:
156+
text_color = line_color
157+
158+
self._label = bitmap_label.Label(
159+
text=text,
160+
font=font,
161+
color=text_color,
162+
anchor_point=text_anchor_point,
163+
anchored_position=(line_x1 + text_offset[0], line_y1 + text_offset[1]),
164+
)
165+
166+
label_width = self._label.bounding_box[2]
167+
line_x2 = line_x1 + label_width * underline_x_multiplier + text_offset[0]
168+
# lengthen the line if the text is offset
169+
line_y2 = line_y1
170+
171+
self._line0 = Line(line_x0, line_y0, line_x1, line_y1, color=line_color)
172+
self._line1 = Line(line_x1, line_y1, line_x2, line_y2, color=line_color)
173+
174+
super().__init__(max_size=3)
175+
# Group elements:
176+
# 0. Line0 - from (x,y) to (x+delta_x, y+delta_y)
177+
# 1. Line1 - horizontal line for text
178+
# 2. Label
179+
180+
self.append(self._line0)
181+
self.append(self._line1)
182+
self.append(self._label)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# SPDX-FileCopyrightText: 2021 Kevin Matocha
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""
5+
Example of the Annotation widget to annotate a Switch widget or
6+
for freeform annotation.
7+
"""
8+
9+
import time
10+
import board
11+
import displayio
12+
import adafruit_touchscreen
13+
from adafruit_displayio_layout.widgets.switch_round import SwitchRound as Switch
14+
from adafruit_displayio_layout.widgets.annotation import Annotation
15+
16+
display = board.DISPLAY
17+
18+
ts = adafruit_touchscreen.Touchscreen(
19+
board.TOUCH_XL,
20+
board.TOUCH_XR,
21+
board.TOUCH_YD,
22+
board.TOUCH_YU,
23+
calibration=((5200, 59000), (5800, 57000)),
24+
size=(display.width, display.height),
25+
)
26+
27+
# Create the switch widget
28+
my_switch = Switch(190, 50)
29+
30+
# Create several annotations
31+
32+
# This annotation is positioned relative to the switch widget, with default values.
33+
switch_annotation = Annotation(
34+
widget=my_switch, # positions are relative to the switch
35+
text="Widget Annotation: Switch",
36+
)
37+
38+
# This annotation is positioned relative to the switch widget, with the line
39+
# going in the downard direction and anchored at the middle bottom of the switch.
40+
# The position is "nudged" downward using ``position_offset`` to create a 1 pixel
41+
# gap between the end of the line and the switch.
42+
# The text is positioned under the line by setting ``text_under`` to True.
43+
switch_annotation_under = Annotation(
44+
widget=my_switch, # positions are relative to the switch
45+
text="Annotation with: text_under = True",
46+
delta_x=-10,
47+
delta_y=15, # line will go in downward direction (positive y)
48+
anchor_point=(0.5, 1.0), # middle, bottom of switch
49+
position_offset=(0, 1), # nudge downward by one pixel
50+
text_under=True,
51+
)
52+
53+
# This is a freeform annotation that is positioned using (x,y) values at the bottom, right
54+
# corner of the display (display.width, display.height).
55+
# The line direction is
56+
freeform_annotation = Annotation(
57+
x=display.width, # uses freeform (x,y) position
58+
y=display.height,
59+
text="Freeform annotation (display.width, height)",
60+
)
61+
62+
my_group = displayio.Group(max_size=4)
63+
my_group.append(my_switch)
64+
my_group.append(switch_annotation)
65+
my_group.append(switch_annotation_under)
66+
my_group.append(freeform_annotation)
67+
68+
# Add my_group to the display
69+
display.show(my_group)
70+
71+
# Start the main loop
72+
while True:
73+
74+
p = ts.touch_point # get any touches on the screen
75+
76+
if p: # Check each switch if the touch point is within the switch touch area
77+
# If touched, then flip the switch with .selected
78+
if my_switch.contains(p):
79+
my_switch.selected(p)
80+
81+
time.sleep(0.05) # touch response on PyPortal is more accurate with a small delay

0 commit comments

Comments
 (0)