Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add per-item alignment to text. #52

Merged
merged 2 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions docs/source/user_guide/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ that are drawn to it::
surface.add_shape('BACKGROUND', background)

# draw some black text in the main drawing layer
hello_tempe = Text([(112, 116)], [0x0000], ["Hello Tempe!"])
hello_tempe = Text([(160, 120)], [0x0000], ["Hello Tempe!"], [(CENTER, CENTER)])
surface.add_shape('DRAWING', hello_tempe)

To actually render the image, you need to provide a |Display| subclass
Expand Down Expand Up @@ -415,7 +415,7 @@ By default it uses the built-in :py:mod:`framebuf` font, which is a simple
but is not ideal for general interfaces: in particular it is available in
just one size.

For more better text rendering, Tempe currently can use bitmap fonts of the
For better text rendering, Tempe currently can use bitmap fonts of the
format produced by Peter Hinch's
`font_to_py script <https://github.com/peterhinch/micropython-font-to-py>`_,
or AntiRez's `microfont library <https://github.com/antirez/microfont>`_,
Expand All @@ -427,10 +427,18 @@ These fonts are typically shipped as modules::

from tempe.font import TempeFont
from tempe.fonts import roboto16bold
from tempe.text import CENTER

font = TempeFont(roboto16bold)

surface.text("DRAWING", [(64, 107)], [0x0000], ["Hello Tempe!"], font=font)
surface.text(
"DRAWING",
(160, 120),
"#000",
"Hello Tempe!",
(CENTER, CENTER),
font=font,
)

Using this in the original example from the tutorial will generate something
like the following:
Expand Down
24 changes: 14 additions & 10 deletions examples/line_plot_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from tempe.geometry import ColumnGeometry, PointsToLines
from tempe.markers import Marker
from tempe.surface import Surface
from tempe.text import CENTER, TOP, RIGHT


surface = Surface()
Expand Down Expand Up @@ -105,9 +106,10 @@ def scale_values(self, data):
)
surface.text(
"OVERLAY",
ColumnGeometry([Repeat(4), temp_labels]),
ColumnGeometry([Repeat(x - tick_length - 1), temp_labels]),
colors.grey_a,
[f"{t}" for t in label_temps],
(RIGHT, CENTER),
)

# Time axis: tick marks, grid lines, labels
Expand All @@ -123,18 +125,19 @@ def scale_values(self, data):
surface.vlines(
"OVERLAY",
ColumnGeometry([time_marks, Repeat(y1), Repeat(tick_length)]),
Repeat(colors.grey_c),
colors.grey_c,
)
surface.vlines(
"UNDERLAY",
ColumnGeometry([time_labels, Repeat(y), Repeat(h)]),
Repeat(colors.grey_f),
colors.grey_f,
)
surface.text(
"OVERLAY",
ColumnGeometry([time_labels, Repeat(y1 + 8)]),
Repeat(colors.grey_a),
colors.grey_a,
[f"{t % 24}:00" for t in label_times],
(CENTER, TOP),
)

# Plot title and additional information
Expand All @@ -143,16 +146,17 @@ def scale_values(self, data):

surface.text(
"DRAWING",
[[4, 0]],
[colors.grey_a],
["Temperature (°C)"],
(4, 0),
colors.grey_a,
"Temperature (°C)",
font=TempeFont(roboto16bold),
)
surface.text(
"DRAWING",
[[160, 0]],
[colors.grey_a],
["October 21-22, 2024"],
(320, 0),
colors.grey_a,
"October 21-22, 2024",
(RIGHT, TOP),
font=TempeFont(roboto16),
)

Expand Down
31 changes: 17 additions & 14 deletions examples/polar_plot_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from tempe.markers import Marker
from tempe.polar_geometry import polar_points, polar_r_lines
from tempe.surface import Surface
from tempe.text import LEFT, CENTER, RIGHT, TOP, BOTTOM


surface = Surface()
Expand Down Expand Up @@ -119,14 +120,15 @@ def scale_values(self, data):
quality_label_rs = air_quality_scale.scale_values(quality_label_values)
time_label_values = [1729551600 + i * 3600 for i in range(6, 24, 6)]
time_label_strings = ["0:00", "9:00", "12:00", "18:00"]
time_label_xs = [cx - 8, cx + max_r + 4, cx - 20, cx - max_r - 44]
time_label_ys = [cy - max_r - 12, cy - 4, cy + max_r + 4, cy - 4]
time_label_alignment = [(CENTER, BOTTOM), (LEFT, CENTER), (CENTER, TOP), (RIGHT, CENTER)]
time_label_xs = [cx, cx + max_r + 4, cx, cx - max_r - 4]
time_label_ys = [cy - max_r - 4, cy, cy + max_r + 4, cy]


surface.circles(
"BACKGROUND",
RowGeometry([[cx, cy, max_r]]),
Repeat(colors.black),
(cx, cy, max_r),
colors.black,
clip=(cx - max_r, cy - max_r, 2 * max_r + 1, 2 * max_r + 1),
)
surface.circles(
Expand All @@ -138,7 +140,7 @@ def scale_values(self, data):
quality_label_rs,
]
),
Repeat(colors.grey_3),
colors.grey_3,
fill=False,
clip=(cx - max_r, cy - max_r, 2 * max_r + 1, 2 * max_r + 1),
)
Expand All @@ -155,7 +157,7 @@ def scale_values(self, data):
]
),
),
Repeat(colors.grey_3),
colors.grey_3,
clip=(cx - max_r, cy - max_r, 2 * max_r + 1, 2 * max_r + 1),
)

Expand All @@ -165,16 +167,17 @@ def scale_values(self, data):
surface.text(
"OVERLAY",
quality_label_geometry,
Repeat(colors.grey_7),
colors.grey_7,
[f"{value:d}" for value in quality_label_values],
clip=(cx, cy - max_r - 4, 64, 100),
)
time_label_geometry = ColumnGeometry([time_label_xs, time_label_ys])
surface.text(
"OVERLAY",
time_label_geometry,
Repeat(colors.grey_7),
colors.grey_7,
time_label_strings,
time_label_alignment,
clip=(0, 0, 320, 240),
)
gc.collect()
Expand All @@ -185,16 +188,16 @@ def scale_values(self, data):

surface.text(
"DRAWING",
[[4, 0]],
[colors.grey_a],
["Air Quality (ppb)"],
(4, 0),
colors.grey_a,
"Air Quality (ppb)",
font=TempeFont(roboto16bold),
)
surface.text(
"DRAWING",
[[4, 20]],
[colors.grey_8],
["20/8/24--\n22/8/24"],
(4, 20),
colors.grey_8,
"20/8/24--\n22/8/24",
font=TempeFont(roboto16),
)

Expand Down
8 changes: 6 additions & 2 deletions examples/scatter_plot_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from tempe.markers import Marker
from tempe.polar_geometry import polar_rects
from tempe.surface import Surface
from tempe.text import TOP, BOTTOM, CENTER, RIGHT


surface = Surface()
Expand Down Expand Up @@ -251,9 +252,10 @@ def scale_values(self, data):
)
surface.text(
"DRAWING",
RowGeometry.from_lists([[cx - 12, cy - 45], [cx - 20, cy + 38]]),
RowGeometry.from_lists([[cx, cy - 35], [cx, cy + 38]]),
Repeat(colors.grey_a),
["0:00", "12:00"],
[(CENTER, BOTTOM), (CENTER, TOP)]
)

sample_humidities = [40, 50, 60, 70]
Expand Down Expand Up @@ -311,9 +313,10 @@ def scale_values(self, data):
)
surface.text(
"OVERLAY",
ColumnGeometry([Repeat(4), temp_labels]),
ColumnGeometry([Repeat(x - tick_length), temp_labels]),
Repeat(colors.grey_a),
[f"{t}" for t in label_temps],
(RIGHT, CENTER),
)

# Air Quality axis: tick marks, grid lines, labels
Expand All @@ -336,6 +339,7 @@ def scale_values(self, data):
ColumnGeometry([air_quality_labels, Repeat(y1 + 8)]),
Repeat(colors.grey_a),
[str(t) for t in label_air_quality],
(CENTER, TOP),
)

# Plot title and additional information
Expand Down
7 changes: 5 additions & 2 deletions examples/update_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from tempe import colors
from tempe.font import TempeFont
from tempe.surface import Surface
from tempe.text import TOP, RIGHT


# a buffer one half the size of the screen
Expand All @@ -25,17 +26,19 @@ async def init_surface(surface):

time_field = surface.text(
"DRAWING",
(10, 10),
(230, 10),
"#aaa",
"",
(RIGHT, TOP),
font=TempeFont(roboto32boldnumbers),
clip=(10, 10, 229, 40),
)
temp_field = surface.text(
"DRAWING",
(10, 50),
(230, 50),
colors.grey_a,
"",
(RIGHT, TOP),
font=TempeFont(roboto32boldnumbers),
clip=(10, 50, 229, 40),
)
Expand Down
2 changes: 2 additions & 0 deletions src/tempe/font.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __init__(self, mod):
self.height = mod.height()
self.baseline = mod.baseline()
self.monospaced = mod.monospaced()
self.max_width = mod.max_width()
self._mvfont = memoryview(mod._mvfont)
self._msvp = memoryview(mod._mvsp)
self._cache = {}
Expand Down Expand Up @@ -84,6 +85,7 @@ def __init__(self, mod):
self.height = mod.height
self.baseline = mod.baseline
self.monospaced = mod.monospaced
self.max_width = mod.max_width
self._mvfont = memoryview(mod._mvfont)
self._msvp = memoryview(mod._mvsp)
self._cache = {}
Expand Down
30 changes: 29 additions & 1 deletion src/tempe/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ def refresh(self, display, working_buffer):
self._damage = []
self.refresh_needed.clear()

async def arefresh(self, display, working_buffer):
"""Refresh the surface on the display."""
while True:
while self._damage:
rect = self._damage.pop(0)
x, y, w, h = rect
# handle buffer too small
buffer_rows = (len(working_buffer) // (w * self.pixel_size)) - 1
for start_row in range(0, h, buffer_rows):
raster_rows = min(buffer_rows, h - start_row)
raster = Raster(working_buffer, x, y + start_row, w, raster_rows)
self.draw(raster)
display.blit(working_buffer, x, y + start_row, w, raster_rows)
await asyncio.sleep(0) # note: self._damage may be modified here
self.refresh_needed.clear()
await self.refresh_needed.wait()

def damage(self, rect):
"""Mark a rectangle as needing to be refreshed."""
if not any(contains(rect, rect2) for rect2 in self._damage):
Expand Down Expand Up @@ -157,6 +174,7 @@ def text(
geometry,
colors,
texts,
alignments=None,
font=None,
line_spacing=0,
clip=None,
Expand All @@ -166,7 +184,8 @@ def text(
geometry = self._check_geometry(geometry, 2)
colors = self._check_colors(colors)
texts = self._check_texts(texts)
text = Text(geometry, colors, texts, font=font, line_spacing=line_spacing, clip=clip)
alignments = self._check_alignments(alignments)
text = Text(geometry, colors, texts, alignments, font=font, line_spacing=line_spacing, clip=clip)
self.add_shape(layer, text)
return text

Expand Down Expand Up @@ -228,6 +247,15 @@ def _check_texts(self, texts):
else:
return texts

def _check_alignments(self, alignments):
from .text import LEFT, TOP
if alignments is None:
return Repeat((LEFT, TOP))
elif isinstance(alignments, tuple) and len(alignments) == 2:
return Repeat(alignments)
else:
return alignments

def _check_bitmaps(self, bitmaps):
if isinstance(bitmaps, framebuf.FrameBuffer):
return Repeat(bitmaps)
Expand Down
7 changes: 6 additions & 1 deletion src/tempe/surface.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ from .shapes import (
)
from .markers import Marker, Markers, Points
from .bitmaps import Bitmaps, ColoredBitmaps
from .text import Text
from .text import Text, HALIGN, VALIGN, LEFT, TOP
from .util import contains

#: The default set of layers used by Surfaces, in order from back to front.
Expand Down Expand Up @@ -333,6 +333,7 @@ class Surface:
geometry: Geometry[tuple[int, int]] | tuple[int, int],
colors: Iterable[int] | int,
text: Iterable[str] | str,
alignments: Iterable[tuple[HALIGN, VALIGN]] | tuple[HALIGN, VALIGN] = (LEFT, TOP),
font: AbstractFont | None = None,
line_spacing: int = 0,
clip: tuple[int, int, int, int] | None = None,
Expand All @@ -349,6 +350,10 @@ class Surface:
The colors of each string, or a color to use for all strings.
text : Iterable[str] | str
The strings to display at each point, or a single string to use at all points.
alignments : Iterable[tuple[HALIGN, VALIGN]] | tuple[HALIGN, VALIGN]
The the alignments of the text relative to the point specified in the
geometry. The default is `(LEFT, TOP)`, which places the top-left corner
of the text at the specified points.
font : AbstractFont | None
The font to use for the text. If None, the default FrameBuffer
8x8 monospaced font will be used.
Expand Down
Loading