Skip to content

Commit 168cbf6

Browse files
authored
Merge pull request #27 from kmatch98/annotation_PR
Add Annotation widget
2 parents 21f55ef + f99604e commit 168cbf6

File tree

6 files changed

+289
-0
lines changed

6 files changed

+289
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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 terminalio import FONT
27+
from adafruit_display_text import bitmap_label
28+
from adafruit_display_shapes.line import Line
29+
from adafruit_displayio_layout.widgets.widget import Widget
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, ``(x,y)`` will be ignored if a ``widget`` and ``anchor_point`` and/or
38+
``anchored_position`` are provided.
39+
:param int y: y-direction pixel position for the end of the annotation line for
40+
freeform positioning.
41+
42+
:param Widget widget: the widget to be annotated, all dimensions are relative to
43+
this widget. The annotation line position will be defined by either
44+
the ``anchor_point`` (in relative dimensions of the size of the widget)
45+
or the ``anchored_position`` (in raw pixel dimensions relative to the origin
46+
of the widget).
47+
48+
:param str text: text to be displayed in the annotation.
49+
:param Font font: font to be used for the text.
50+
51+
:param anchor_point: starting point for the annotation line, where ``anchor_point`` is an
52+
(A,B) tuple in relative units of the size of the widget,
53+
for example (0.0, 0.0) is the upper left corner, and (1.0, 1.0) is the lower
54+
right corner of the widget. If ``anchor_point`` is `None`, then ``anchored_position``
55+
is used to set the annotation line starting point, in widget size relative units
56+
(default is (0.0, 0.0)).
57+
:type anchor_point: Tuple[float, float]
58+
59+
:param anchored_position: pixel position starting point for the annotation line
60+
where ``anchored_position`` is an (x,y) tuple in pixel units relative to the
61+
upper left corner of the widget, in pixel units (default is None).
62+
:type anchored_position: Tuple[int, int]
63+
64+
:param position_offset: Used to *nudge* the line position to where you want, this
65+
is an (x,y) pixel offset added to the annotation line starting
66+
point, either set by ``anchor_point`` or ``anchored_position`` (in pixel units).
67+
:type position_offset: Tuple[int, int]
68+
69+
:param int delta_x: the pixel x-offset for the second end of the line where the text
70+
will reside, in pixel units (default: -15).
71+
:param int delta_y: the pixel y-offset for the second end of the line where the text
72+
will reside, in pixel units (default: -10).
73+
74+
:param int stroke: the annotation line width (in pixels). [NOT currently implemented]
75+
76+
:param int line_color: the color of the annotation line (default: 0xFFFFFF).
77+
:param int text_color: the color of the text, if set to `None` color will be
78+
set to ``line_color`` (default: same as ``line_color``).
79+
80+
:param text_offset: a (x,y) pixel offset to adjust text position relative
81+
to annotation line, in pixel units (default: (0,-1)).
82+
:type text_offset: Tuple[int, int]
83+
84+
:param Boolean text_under: set `True` for text to be placed below the
85+
annotation line (default: False).
86+
87+
.. figure:: annotation_example.png
88+
:scale: 125 %
89+
:align: center
90+
:alt: Example of the annotation widget.
91+
92+
Example of the annotation widget showing two widget
93+
annotations (using ``widget`` and ``anchor_point`` input parameters) and a
94+
freeform annotation (using ``x`` and ``y`` input parameters).
95+
96+
File location: *examples/displayio_layout_annotation_simpletest.py*
97+
"""
98+
99+
def __init__(
100+
self,
101+
x=None,
102+
y=None,
103+
text=None,
104+
font=FONT,
105+
delta_x=-15,
106+
delta_y=-10,
107+
widget=None,
108+
anchor_point=(0.0, 0.0),
109+
anchored_position=None,
110+
position_offset=(0, 0),
111+
stroke=3, # Not currently implemented in adafruit_display_shapes/line.py
112+
line_color=0xFFFFFF,
113+
text_color=None,
114+
text_offset=(0, -1),
115+
text_under=False,
116+
):
117+
118+
if widget:
119+
if (x is not None) or (y is not None):
120+
print(
121+
"Note: Overriding (x,y) values with widget, anchor_point"
122+
" and/or anchored_position"
123+
)
124+
widget_width = widget.bounding_box[2]
125+
widget_height = widget.bounding_box[3]
126+
if anchor_point is not None:
127+
line_x0 = (
128+
widget.x
129+
+ round(widget_width * anchor_point[0])
130+
+ position_offset[0]
131+
)
132+
line_y0 = (
133+
widget.y
134+
+ round(widget_height * anchor_point[1])
135+
+ position_offset[1]
136+
)
137+
elif anchored_position is not None:
138+
line_x0 = widget.x + anchored_position[0] + position_offset[0]
139+
line_y0 = widget.y + anchored_position[1] + position_offset[1]
140+
else:
141+
raise ValueError("Must supply either anchor_point or anchored_position")
142+
elif (x is not None) and (y is not None):
143+
line_x0 = x
144+
line_y0 = y
145+
else:
146+
raise ValueError(
147+
"Must supply either (x,y) or widget and anchor_point or anchored_position"
148+
)
149+
150+
line_x1 = line_x0 + delta_x
151+
line_y1 = line_y0 + delta_y
152+
153+
text_anchor_point = (0.0, 1.0) # default: set text anchor to left corner
154+
underline_x_multiplier = 1
155+
156+
if delta_x < 0: # line is heading to the left, set text anchor to right corner
157+
text_anchor_point = (1.0, 1.0)
158+
underline_x_multiplier = -1
159+
160+
if (
161+
text_under
162+
): # if text is under the line, set to text_anchor_point to upper edge
163+
text_anchor_point = (text_anchor_point[0], 0.0)
164+
165+
if text_color is None:
166+
text_color = line_color
167+
168+
self._label = bitmap_label.Label(
169+
text=text,
170+
font=font,
171+
color=text_color,
172+
anchor_point=text_anchor_point,
173+
anchored_position=(line_x1 + text_offset[0], line_y1 + text_offset[1]),
174+
)
175+
176+
label_width = self._label.bounding_box[2]
177+
line_x2 = line_x1 + label_width * underline_x_multiplier + text_offset[0]
178+
# lengthen the line if the text is offset
179+
line_y2 = line_y1
180+
181+
self._line0 = Line(line_x0, line_y0, line_x1, line_y1, color=line_color)
182+
self._line1 = Line(line_x1, line_y1, line_x2, line_y2, color=line_color)
183+
184+
super().__init__(max_size=3)
185+
# Group elements:
186+
# 0. Line0 - from (x,y) to (x+delta_x, y+delta_y)
187+
# 1. Line1 - horizontal line for text
188+
# 2. Label
189+
190+
self.append(self._line0)
191+
self.append(self._line1)
192+
self.append(self._label)

docs/annotation_example.png

4.86 KB
Loading

docs/annotation_example.png.license

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SPDX-FileCopyrightText: 2021 Kevin Matocha
2+
3+
SPDX-License-Identifier: MIT

docs/api.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
:members:
3535
:member-order: bysource
3636

37+
.. automodule:: adafruit_displayio_layout.widgets.annotation
38+
:members:
39+
:member-order: bysource
40+
3741
.. automodule:: adafruit_displayio_layout.widgets.icon_animated
3842
:members:
3943
:member-order: bysource

docs/examples.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ Create multiple sliding switch with various sizes and orientations.
3434
:caption: examples/displayio_layout_switch_multiple.py
3535
:linenos:
3636

37+
Annotation example
38+
------------------
39+
40+
Displays annotations, examples relative to SwitchRound widget or as freeform.
41+
42+
.. literalinclude:: ../examples/displayio_layout_annotation_simpletest.py
43+
:caption: examples/displayio_layout_annotation_simpletest.py
44+
:linenos:
45+
3746
Dial simple test
3847
----------------
3948

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)