Compare commits

..

No commits in common. "master" and "setup-py-disabled" have entirely different histories.

12 changed files with 94 additions and 5382 deletions

View file

@ -6,7 +6,7 @@ Introduction
:alt: Documentation Status
.. image:: https://img.shields.io/discord/327254708534116352.svg
:target: https://adafru.it/discord
:target: https://discord.gg/nBQh6qu
:alt: Discord
.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Display_Text/workflows/Build%20CI/badge.svg

View file

@ -1,528 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c) 2020 Kevin Matocha
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`bitmap_label`
================================================================================
Text graphics handling for CircuitPython, including text boxes
* Author(s): Kevin Matocha
Implementation Notes
--------------------
**Hardware:**
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
"""
import displayio
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Text.git"
class Label(displayio.Group):
"""A label displaying a string of text that is stored in a bitmap.
Note: This ``bitmap_label.py`` library utilizes a bitmap to display the text.
This method is memory-conserving relative to ``label.py``.
For the bitmap_label library, the font, text, and line_spacing must be set at
instancing and are immutable. The ``max_glyphs`` parameter is ignored and is present
only for direct compatability with label.py.
For use cases where text changes are required after the initial instancing, please
use the `label.py` library.
For further reduction in memory usage, set save_text to False (text string will not
be stored).
The origin point set by ``x`` and ``y``
properties will be the left edge of the bounding box, and in the center of a M
glyph (if its one line), or the (number of lines * linespacing + M)/2. That is,
it will try to have it be center-left as close as possible.
:param Font font: A font class that has ``get_bounding_box`` and ``get_glyph``.
Must include a capital M for measuring character size.
:param str text: Text to display
:param int max_glyphs: Unnecessary parameter (provided only for direct compability
with label.py)
:param int color: Color of all text in RGB hex
:param int background_color: Color of the background, use `None` for transparent
:param double line_spacing: Line spacing of text to display
:param boolean background_tight: Set `True` only if you want background box to tightly
surround text
:param int padding_top: Additional pixels added to background bounding box at top
:param int padding_bottom: Additional pixels added to background bounding box at bottom
:param int padding_left: Additional pixels added to background bounding box at left
:param int padding_right: Additional pixels added to background bounding box at right
:param (double,double) anchor_point: Point that anchored_position moves relative to.
Tuple with decimal percentage of width and height.
(E.g. (0,0) is top left, (1.0, 0.5): is middle right.)
:param (int,int) anchored_position: Position relative to the anchor_point. Tuple
containing x,y pixel coordinates.
:param int scale: Integer value of the pixel scaling
:param bool save_text: Set True to save the text string as a constant in the
label structure. Set False to reduce memory use.
"""
# pylint: disable=unused-argument, too-many-instance-attributes, too-many-locals, too-many-arguments
# pylint: disable=too-many-branches, no-self-use
# Note: max_glyphs parameter is unnecessary, this is used for direct
# compatibility with label.py
def __init__(
self,
font,
x=0,
y=0,
text="",
max_glyphs=None, # This input parameter is ignored, only present for compatibility
# with label.py
color=0xFFFFFF,
background_color=None,
line_spacing=1.25,
background_tight=False,
padding_top=0,
padding_bottom=0,
padding_left=0,
padding_right=0,
anchor_point=None,
anchored_position=None,
save_text=True, # can reduce memory use if save_text = False
**kwargs
):
if text == "":
raise RuntimeError(
"Please provide text string, or use label.py for mutable text"
)
self._font = font
# Scale will be passed to Group using kwargs.
if "scale" in kwargs.keys():
self._scale = kwargs["scale"]
else:
self._scale = 1
self._line_spacing = line_spacing
self._save_text = save_text
if self._save_text: # text string will be saved
self._text = text
else:
self._text = None # save a None value since text string is not saved
# limit padding to >= 0
padding_top = max(0, padding_top)
padding_bottom = max(0, padding_bottom)
padding_left = max(0, padding_left)
padding_right = max(0, padding_right)
# Calculate the text bounding box
# Calculate tight box to provide bounding box dimensions to match label for
# anchor_position calculations
(
tight_box_x,
tight_box_y,
tight_x_offset,
tight_y_offset,
) = self._text_bounding_box(
text, font, self._line_spacing, background_tight=True,
)
if background_tight:
box_x = tight_box_x
box_y = tight_box_y
y_offset = tight_y_offset
x_offset = tight_x_offset
else:
(box_x, box_y, x_offset, y_offset) = self._text_bounding_box(
text, font, self._line_spacing, background_tight=background_tight,
)
# Calculate the background size including padding
box_x = box_x + padding_left + padding_right
box_y = box_y + padding_top + padding_bottom
# Create the two-color palette
self.palette = displayio.Palette(2)
self.background_color = background_color
self.color = color
# Create the bitmap and TileGrid
self.bitmap = displayio.Bitmap(box_x, box_y, len(self.palette))
# Place the text into the Bitmap
self._place_text(
self.bitmap,
text,
font,
self._line_spacing,
padding_left - x_offset,
padding_top + y_offset,
)
label_position_yoffset = int( # To calibrate with label.py positioning
(font.get_glyph(ord("M")).height) / 2
)
self.tilegrid = displayio.TileGrid(
self.bitmap,
pixel_shader=self.palette,
width=1,
height=1,
tile_width=box_x,
tile_height=box_y,
default_tile=0,
x=-padding_left + x_offset,
y=label_position_yoffset - y_offset - padding_top,
)
# instance the Group
# this Group will contain just one TileGrid with one contained bitmap
super().__init__(
max_size=1, x=x, y=y, **kwargs
) # this will include any arguments, including scale
self.append(self.tilegrid) # add the bitmap's tilegrid to the group
# Update bounding_box values. Note: To be consistent with label.py,
# this is the bounding box for the text only, not including the background.
self._bounding_box = (
self.tilegrid.x,
self.tilegrid.y,
tight_box_x,
tight_box_y,
)
self._anchored_position = anchored_position
self.anchor_point = anchor_point
self.anchored_position = (
self._anchored_position
) # sets anchored_position with setter after bitmap is created
@staticmethod
def _line_spacing_ypixels(font, line_spacing):
# Note: Scale is not implemented at this time, any scaling is pushed up to the Group level
return_value = int(line_spacing * font.get_bounding_box()[1])
return return_value
def _text_bounding_box(self, text, font, line_spacing, background_tight=False):
# This empirical approach checks several glyphs for maximum ascender and descender height
# (consistent with label.py)
glyphs = "M j'" # choose glyphs with highest ascender and lowest
# descender, will depend upon font used
try:
self._font.load_glyphs(text + glyphs)
except AttributeError:
# ignore if font does not have load_glyphs
pass
ascender_max = descender_max = 0
for char in glyphs:
this_glyph = font.get_glyph(ord(char))
if this_glyph:
ascender_max = max(ascender_max, this_glyph.height + this_glyph.dy)
descender_max = max(descender_max, -this_glyph.dy)
lines = 1
xposition = (
x_start
) = yposition = y_start = 0 # starting x and y position (left margin)
left = None
right = x_start
top = bottom = y_start
y_offset_tight = int((font.get_glyph(ord("M")).height) / 2)
# this needs to be reviewed (also in label.py), since it doesn't respond
# properly to the number of newlines.
newline = False
for char in text:
if char == "\n": # newline
newline = True
else:
my_glyph = font.get_glyph(ord(char))
if my_glyph is None: # Error checking: no glyph found
print("Glyph not found: {}".format(repr(char)))
else:
if newline:
newline = False
xposition = x_start # reset to left column
yposition = yposition + self._line_spacing_ypixels(
font, line_spacing
) # Add a newline
lines += 1
if xposition == x_start:
if left is None:
left = my_glyph.dx
else:
left = min(left, my_glyph.dx)
xright = xposition + my_glyph.width + my_glyph.dx
xposition += my_glyph.shift_x
right = max(right, xposition, xright)
if yposition == y_start: # first line, find the Ascender height
top = min(top, -my_glyph.height - my_glyph.dy + y_offset_tight)
bottom = max(bottom, yposition - my_glyph.dy + y_offset_tight)
if left is None:
left = 0
final_box_width = right - left
if background_tight:
final_box_height = bottom - top
final_y_offset = -top + y_offset_tight
else:
final_box_height = (lines - 1) * self._line_spacing_ypixels(
font, line_spacing
) + (ascender_max + descender_max)
final_y_offset = ascender_max
return (final_box_width, final_box_height, left, final_y_offset)
# pylint: disable=too-many-nested-blocks
def _place_text(
self,
bitmap,
text,
font,
line_spacing,
xposition,
yposition,
text_palette_index=1,
background_palette_index=0,
print_only_pixels=True, # print_only_pixels = True: only update the bitmap where the glyph
# pixel color is > 0. This is especially useful for script fonts where glyph
# bounding boxes overlap
# Set `print_only_pixels=False` to write all pixels
):
# placeText - Writes text into a bitmap at the specified location.
#
# Verify paletteIndex is working properly with * operator, especially
# if accommodating multicolored fonts
#
# Note: Scale is not implemented at this time, is pushed up to Group level
bitmap_width = bitmap.width
bitmap_height = bitmap.height
x_start = xposition # starting x position (left margin)
y_start = yposition
left = None
right = x_start
top = bottom = y_start
for char in text:
if char == "\n": # newline
xposition = x_start # reset to left column
yposition = yposition + self._line_spacing_ypixels(
font, line_spacing
) # Add a newline
else:
my_glyph = font.get_glyph(ord(char))
if my_glyph is None: # Error checking: no glyph found
print("Glyph not found: {}".format(repr(char)))
else:
if xposition == x_start:
if left is None:
left = my_glyph.dx
else:
left = min(left, my_glyph.dx)
right = max(
right,
xposition + my_glyph.shift_x,
xposition + my_glyph.width + my_glyph.dx,
)
if yposition == y_start: # first line, find the Ascender height
top = min(top, -my_glyph.height - my_glyph.dy)
bottom = max(bottom, yposition - my_glyph.dy)
glyph_offset_x = (
my_glyph.tile_index * my_glyph.width
) # for type BuiltinFont, this creates the x-offset in the glyph bitmap.
# for BDF loaded fonts, this should equal 0
for y in range(my_glyph.height):
for x in range(my_glyph.width):
x_placement = x + xposition + my_glyph.dx
y_placement = y + yposition - my_glyph.height - my_glyph.dy
if (bitmap_width > x_placement >= 0) and (
bitmap_height > y_placement >= 0
):
# Allows for remapping the bitmap indexes using paletteIndex
# for background and text.
palette_indexes = (
background_palette_index,
text_palette_index,
)
this_pixel_color = palette_indexes[
my_glyph.bitmap[
y * my_glyph.bitmap.width + x + glyph_offset_x
]
]
if not print_only_pixels or this_pixel_color > 0:
# write all characters if printOnlyPixels = False,
# or if thisPixelColor is > 0
bitmap[
y_placement * bitmap_width + x_placement
] = this_pixel_color
elif y_placement > bitmap_height:
break
xposition = xposition + my_glyph.shift_x
return (left, top, right - left, bottom - top) # bounding_box
@property
def bounding_box(self):
"""An (x, y, w, h) tuple that completely covers all glyphs. The
first two numbers are offset from the x, y origin of this group"""
return self._bounding_box
@property
def line_spacing(self):
"""The amount of space between lines of text, in multiples of the font's
bounding-box height. (E.g. 1.0 is the bounding-box height)"""
return self._line_spacing
@line_spacing.setter
def line_spacing(self, new_line_spacing):
raise RuntimeError(
"line_spacing is immutable for bitmap_label.py; use label.py for mutable line_spacing"
)
@property
def color(self):
"""Color of the text as an RGB hex number."""
return self._color
@color.setter
def color(self, new_color):
self._color = new_color
if new_color is not None:
self.palette[1] = new_color
self.palette.make_opaque(1)
else:
self.palette[1] = 0
self.palette.make_transparent(1)
@property
def background_color(self):
"""Color of the background as an RGB hex number."""
return self._background_color
@background_color.setter
def background_color(self, new_color):
self._background_color = new_color
if new_color is not None:
self.palette[0] = new_color
self.palette.make_opaque(0)
else:
self.palette[0] = 0
self.palette.make_transparent(0)
@property
def text(self):
"""Text to displayed."""
return self._text
@text.setter
def text(self, new_text):
raise RuntimeError(
"text is immutable for bitmap_label.py; use label.py library for mutable text"
)
@property
def font(self):
"""Font to use for text display."""
return self.font
@font.setter
def font(self, new_font):
raise RuntimeError(
"font is immutable for bitmap_label.py; use label.py library for mutable font"
)
@property
def anchor_point(self):
"""Point that anchored_position moves relative to.
Tuple with decimal percentage of width and height.
(E.g. (0,0) is top left, (1.0, 0.5): is middle right.)"""
return self._anchor_point
@anchor_point.setter
def anchor_point(self, new_anchor_point):
self._anchor_point = new_anchor_point
self.anchored_position = (
self._anchored_position
) # update the anchored_position using setter
@property
def anchored_position(self):
"""Position relative to the anchor_point. Tuple containing x,y
pixel coordinates."""
return self._anchored_position
@anchored_position.setter
def anchored_position(self, new_position):
self._anchored_position = new_position
# Set anchored_position
if (self._anchor_point is not None) and (self._anchored_position is not None):
self.x = int(
new_position[0]
- (self._bounding_box[0] * self._scale)
- round(self._anchor_point[0] * (self._bounding_box[2] * self._scale))
)
self.y = int(
new_position[1]
- (self._bounding_box[1] * self._scale)
- round(self._anchor_point[1] * self._bounding_box[3] * self._scale)
)

310
adafruit_display_text/label.py Executable file → Normal file
View file

@ -58,214 +58,84 @@ class Label(displayio.Group):
:param int color: Color of all text in RGB hex
:param double line_spacing: Line spacing of text to display"""
# pylint: disable=too-many-instance-attributes, too-many-locals
# This has a lot of getters/setters, maybe it needs cleanup.
def __init__(
self,
font,
*,
x=0,
y=0,
text="",
text=None,
max_glyphs=None,
color=0xFFFFFF,
background_color=None,
line_spacing=1.25,
background_tight=False,
padding_top=0,
padding_bottom=0,
padding_left=0,
padding_right=0,
anchor_point=None,
anchored_position=None,
**kwargs
):
if "scale" in kwargs.keys():
self._scale = kwargs["scale"]
else:
self._scale = 1
if not max_glyphs and not text:
raise RuntimeError("Please provide a max size, or initial text")
if not max_glyphs:
max_glyphs = len(text)
# add one to max_size for the background bitmap tileGrid
super().__init__(max_size=max_glyphs + 1, **kwargs)
super().__init__(max_size=max_glyphs, **kwargs)
self.width = max_glyphs
self._font = font
self.font = font
self._text = None
self._anchor_point = anchor_point
self._anchor_point = (0, 0)
self.x = x
self.y = y
self.height = self._font.get_bounding_box()[1]
self.palette = displayio.Palette(2)
if background_color is not None:
self.palette[0] = background_color
self.palette.make_opaque(0)
self._transparent_background = False
else:
self.palette[0] = 0
self.palette.make_transparent(0)
self._transparent_background = True
self.palette[1] = color
bounds = self.font.get_bounding_box()
self.height = bounds[1]
self._line_spacing = line_spacing
self._boundingbox = None
self._background_tight = (
background_tight # sets padding status for text background box
)
# Create the two-color text palette
self.palette = displayio.Palette(2)
self.palette[0] = 0
self.palette.make_transparent(0)
self.color = color
self._background_color = background_color
self._background_palette = displayio.Palette(1)
self._added_background_tilegrid = False
self._padding_top = padding_top
self._padding_bottom = padding_bottom
self._padding_left = padding_left
self._padding_right = padding_right
if text is not None:
self._update_text(str(text))
if (anchored_position is not None) and (anchor_point is not None):
self.anchored_position = anchored_position
def _create_background_box(self, lines, y_offset):
left = self._boundingbox[0]
if self._background_tight: # draw a tight bounding box
box_width = self._boundingbox[2]
box_height = self._boundingbox[3]
x_box_offset = 0
y_box_offset = self._boundingbox[1]
else: # draw a "loose" bounding box to include any ascenders/descenders.
# check a few glyphs for maximum ascender and descender height
# Enhancement: it would be preferred to access the font "FONT_ASCENT" and
# "FONT_DESCENT" in the imported BDF file
glyphs = "M j'" # choose glyphs with highest ascender and lowest
# descender, will depend upon font used
ascender_max = descender_max = 0
for char in glyphs:
this_glyph = self._font.get_glyph(ord(char))
if this_glyph:
ascender_max = max(ascender_max, this_glyph.height + this_glyph.dy)
descender_max = max(descender_max, -this_glyph.dy)
box_width = self._boundingbox[2] + self._padding_left + self._padding_right
x_box_offset = -self._padding_left
box_height = (
(ascender_max + descender_max)
+ int((lines - 1) * self.height * self._line_spacing)
+ self._padding_top
+ self._padding_bottom
)
y_box_offset = -ascender_max + y_offset - self._padding_top
box_width = max(0, box_width) # remove any negative values
box_height = max(0, box_height) # remove any negative values
background_bitmap = displayio.Bitmap(box_width, box_height, 1)
tile_grid = displayio.TileGrid(
background_bitmap,
pixel_shader=self._background_palette,
x=left + x_box_offset,
y=y_box_offset,
)
return tile_grid
def _update_background_color(self, new_color):
if new_color is None:
self._background_palette.make_transparent(0)
if self._added_background_tilegrid:
self.pop(0)
self._added_background_tilegrid = False
else:
self._background_palette.make_opaque(0)
self._background_palette[0] = new_color
self._background_color = new_color
lines = self._text.rstrip("\n").count("\n") + 1
y_offset = int((self._font.get_glyph(ord("M")).height) / 2)
if not self._added_background_tilegrid: # no bitmap is in the self Group
# add bitmap if text is present and bitmap sizes > 0 pixels
if (
(len(self._text) > 0)
and (
self._boundingbox[2] + self._padding_left + self._padding_right > 0
)
and (
self._boundingbox[3] + self._padding_top + self._padding_bottom > 0
)
):
if len(self) > 0:
self.insert(0, self._create_background_box(lines, y_offset))
else:
self.append(self._create_background_box(lines, y_offset))
self._added_background_tilegrid = True
else: # a bitmap is present in the self Group
# update bitmap if text is present and bitmap sizes > 0 pixels
if (
(len(self._text) > 0)
and (
self._boundingbox[2] + self._padding_left + self._padding_right > 0
)
and (
self._boundingbox[3] + self._padding_top + self._padding_bottom > 0
)
):
self[0] = self._create_background_box(lines, y_offset)
else: # delete the existing bitmap
self.pop(0)
self._added_background_tilegrid = False
def _update_text(
self, new_text
): # pylint: disable=too-many-locals ,too-many-branches, too-many-statements
def _update_text(self, new_text): # pylint: disable=too-many-locals
x = 0
y = 0
if self._added_background_tilegrid:
i = 1
else:
i = 0
tilegrid_count = i
try:
self._font.load_glyphs(new_text + "M")
except AttributeError:
# ignore if font does not have load_glyphs
pass
y_offset = int((self._font.get_glyph(ord("M")).height) / 2)
right = top = bottom = 0
left = None
old_c = 0
y_offset = int(
(
self.font.get_glyph(ord("M")).height
- new_text.count("\n") * self.height * self.line_spacing
)
/ 2
)
# print("y offset from baseline", y_offset)
left = right = top = bottom = 0
for character in new_text:
if character == "\n":
y += int(self.height * self._line_spacing)
x = 0
continue
glyph = self._font.get_glyph(ord(character))
glyph = self.font.get_glyph(ord(character))
if not glyph:
continue
right = max(right, x + glyph.shift_x, x + glyph.width + glyph.dx)
if x == 0:
if left is None:
left = glyph.dx
else:
left = min(left, glyph.dx)
right = max(right, x + glyph.width)
if y == 0: # first line, find the Ascender height
top = min(top, -glyph.height - glyph.dy + y_offset)
top = min(top, -glyph.height + y_offset)
bottom = max(bottom, y - glyph.dy + y_offset)
position_y = y - glyph.height - glyph.dy + y_offset
position_x = x + glyph.dx
if glyph.width > 0 and glyph.height > 0:
if (
not self._text
or old_c >= len(self._text)
or character != self._text[old_c]
):
try:
# pylint: disable=unexpected-keyword-arg
face = displayio.TileGrid(
glyph.bitmap,
pixel_shader=self.palette,
@ -284,25 +154,37 @@ class Label(displayio.Group):
x=position_x,
y=position_y,
)
if tilegrid_count < len(self):
self[tilegrid_count] = face
if i < len(self):
self[i] = face
else:
self.append(face)
tilegrid_count += 1
elif self._text and character == self._text[old_c]:
try:
self[i].position = (position_x, position_y)
except AttributeError:
self[i].x = position_x
self[i].y = position_y
x += glyph.shift_x
# TODO skip this for control sequences or non-printables.
i += 1
old_c += 1
# skip all non-prinables in the old string
while (
self._text
and old_c < len(self._text)
and (
self._text[old_c] == "\n"
or not self.font.get_glyph(ord(self._text[old_c]))
)
):
old_c += 1
# Remove the rest
if left is None:
left = 0
while len(self) > tilegrid_count: # i:
while len(self) > i:
self.pop()
self._text = new_text
self._boundingbox = (left, top, right - left, bottom - top)
if self.background_color is not None:
self._update_background_color(self._background_color)
self._boundingbox = (left, top, left + right, bottom - top)
@property
def bounding_box(self):
@ -327,22 +209,25 @@ class Label(displayio.Group):
@color.setter
def color(self, new_color):
self._color = new_color
if new_color is not None:
self.palette[1] = new_color
self.palette.make_opaque(1)
else:
self.palette[1] = 0
self.palette.make_transparent(1)
@property
def background_color(self):
"""Color of the background as an RGB hex number."""
return self._background_color
if not self._transparent_background:
return self.palette[0]
return None
@background_color.setter
def background_color(self, new_color):
self._update_background_color(new_color)
if new_color is not None:
self.palette[0] = new_color
self.palette.make_opaque(0)
self._transparent_background = False
else:
self.palette[0] = 0
self.palette.make_transparent(0)
self._transparent_background = True
@property
def text(self):
@ -351,27 +236,7 @@ class Label(displayio.Group):
@text.setter
def text(self, new_text):
try:
current_anchored_position = self.anchored_position
self._update_text(str(new_text))
self.anchored_position = current_anchored_position
except RuntimeError as run_error:
raise RuntimeError("Text length exceeds max_glyphs") from run_error
@property
def font(self):
"""Font to use for text display."""
return self._font
@font.setter
def font(self, new_font):
old_text = self._text
current_anchored_position = self.anchored_position
self._text = ""
self._font = new_font
self.height = self._font.get_bounding_box()[1]
self._update_text(str(old_text))
self.anchored_position = current_anchored_position
@property
def anchor_point(self):
@ -382,43 +247,18 @@ class Label(displayio.Group):
@anchor_point.setter
def anchor_point(self, new_anchor_point):
if self._anchor_point is not None:
current_anchored_position = self.anchored_position
self._anchor_point = new_anchor_point
self.anchored_position = current_anchored_position
else:
self._anchor_point = new_anchor_point
@property
def anchored_position(self):
"""Position relative to the anchor_point. Tuple containing x,y
pixel coordinates."""
if self._anchor_point is None:
return None
return (
int(
self.x
+ (self._boundingbox[0] * self._scale)
+ round(self._anchor_point[0] * self._boundingbox[2] * self._scale)
),
int(
self.y
+ (self._boundingbox[1] * self._scale)
+ round(self._anchor_point[1] * self._boundingbox[3] * self._scale)
),
self.x - self._boundingbox[2] * self._anchor_point[0],
self.y - self._boundingbox[3] * self._anchor_point[1],
)
@anchored_position.setter
def anchored_position(self, new_position):
if (self._anchor_point is None) or (new_position is None):
return # Note: anchor_point must be set before setting anchored_position
self.x = int(
new_position[0]
- (self._boundingbox[0] * self._scale)
- round(self._anchor_point[0] * (self._boundingbox[2] * self._scale))
)
self.y = int(
new_position[1]
- (self._boundingbox[1] * self._scale)
- round(self._anchor_point[1] * self._boundingbox[3] * self._scale)
)
self.x = int(new_position[0] - (self._boundingbox[2] * self._anchor_point[0]))
self.y = int(new_position[1] - (self._boundingbox[3] * self._anchor_point[1]))

View file

@ -3,8 +3,8 @@ This examples shows the use of anchor_point and anchored_position.
"""
import board
import terminalio
import displayio
from adafruit_display_text import label
import displayio
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240
@ -12,19 +12,19 @@ TEXT = "Hello"
text_area_top_left = label.Label(terminalio.FONT, text=TEXT)
text_area_top_left.anchor_point = (0.0, 0.0)
text_area_top_left.anchored_position = (0, 0)
text_area_top_left.anchored_position = (10, 10)
text_area_top_middle = label.Label(terminalio.FONT, text=TEXT)
text_area_top_middle.anchor_point = (0.5, 0.0)
text_area_top_middle.anchored_position = (DISPLAY_WIDTH / 2, 0)
text_area_top_middle.anchored_position = (DISPLAY_WIDTH / 2, 10)
text_area_top_right = label.Label(terminalio.FONT, text=TEXT)
text_area_top_right.anchor_point = (1.0, 0.0)
text_area_top_right.anchored_position = (DISPLAY_WIDTH, 0)
text_area_top_right.anchored_position = (DISPLAY_WIDTH - 10, 10)
text_area_middle_left = label.Label(terminalio.FONT, text=TEXT)
text_area_middle_left.anchor_point = (0.0, 0.5)
text_area_middle_left.anchored_position = (0, DISPLAY_HEIGHT / 2)
text_area_middle_left.anchored_position = (10, DISPLAY_HEIGHT / 2)
text_area_middle_middle = label.Label(terminalio.FONT, text=TEXT)
text_area_middle_middle.anchor_point = (0.5, 0.5)
@ -32,11 +32,11 @@ text_area_middle_middle.anchored_position = (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT /
text_area_middle_right = label.Label(terminalio.FONT, text=TEXT)
text_area_middle_right.anchor_point = (1.0, 0.5)
text_area_middle_right.anchored_position = (DISPLAY_WIDTH, DISPLAY_HEIGHT / 2)
text_area_middle_right.anchored_position = (DISPLAY_WIDTH - 10, DISPLAY_HEIGHT / 2)
text_area_bottom_left = label.Label(terminalio.FONT, text=TEXT)
text_area_bottom_left.anchor_point = (0.0, 1.0)
text_area_bottom_left.anchored_position = (0, DISPLAY_HEIGHT)
text_area_bottom_left.anchored_position = (10, DISPLAY_HEIGHT)
text_area_bottom_middle = label.Label(terminalio.FONT, text=TEXT)
text_area_bottom_middle.anchor_point = (0.5, 1.0)
@ -44,7 +44,7 @@ text_area_bottom_middle.anchored_position = (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT)
text_area_bottom_right = label.Label(terminalio.FONT, text=TEXT)
text_area_bottom_right.anchor_point = (1.0, 1.0)
text_area_bottom_right.anchored_position = (DISPLAY_WIDTH, DISPLAY_HEIGHT)
text_area_bottom_right.anchored_position = (DISPLAY_WIDTH - 10, DISPLAY_HEIGHT)
text_group = displayio.Group(max_size=9)
text_group.append(text_area_top_middle)

View file

@ -1,125 +0,0 @@
"""
This examples shows the use color and background_color
"""
import time
import board
import displayio
# from adafruit_st7789 import ST7789
from adafruit_ili9341 import ILI9341
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label
# Setup the SPI display
print("Starting the display...") # goes to serial only
displayio.release_displays()
spi = board.SPI()
tft_cs = board.D9 # arbitrary, pin not used
tft_dc = board.D10
tft_backlight = board.D12
tft_reset = board.D11
while not spi.try_lock():
spi.configure(baudrate=32000000)
spi.unlock()
display_bus = displayio.FourWire(
spi,
command=tft_dc,
chip_select=tft_cs,
reset=tft_reset,
baudrate=32000000,
polarity=1,
phase=1,
)
print("spi.frequency: {}".format(spi.frequency))
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240
# display = ST7789(display_bus, width=240, height=240, rotation=0, rowstart=80, colstart=0)
display = ILI9341(
display_bus,
width=DISPLAY_WIDTH,
height=DISPLAY_HEIGHT,
rotation=180,
auto_refresh=True,
)
display.show(None)
# font=terminalio.FONT # this is the Builtin fixed dimension font
font = bitmap_font.load_font("fonts/Helvetica-Bold-16.bdf")
text = []
text.append("none") # no ascenders or descenders
text.append("pop quops") # only descenders
text.append("MONSTERs are tall") # only ascenders
text.append("MONSTERs ate pop quops") # both ascenders and descenders
text.append("MONSTER quops\nnewline quops") # with newline
display.auto_refresh = True
myGroup = displayio.Group(max_size=6)
display.show(myGroup)
text_area = []
myPadding = 4
for i, thisText in enumerate(text):
text_area.append(
label.Label(
font,
text=thisText,
color=0xFFFFFF,
background_color=None,
background_tight=False,
padding_top=myPadding,
padding_bottom=myPadding,
padding_left=myPadding,
padding_right=myPadding,
)
)
this_x = 10
this_y = 10 + i * 40
text_area[i].x = 10
text_area[i].y = 3 + i * 50
text_area[i].anchor_point = (0, 0)
text_area[i].anchored_position = (this_x, this_y)
myGroup.append(text_area[i])
print("background color is {}".format(text_area[0].background_color))
while True:
time.sleep(2)
text_area[0].text = "text" # change some text in an existing text box
# Note: changed text must fit within existing number of characters
# when the Label was created
for area in text_area:
area.background_color = 0xFF0000
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = 0x000088
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = 0x00FF00
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = 0xFF0000
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = None
print("background color is {}".format(text_area[0].background_color))

View file

@ -1,12 +0,0 @@
import board
import terminalio
from adafruit_display_text import bitmap_label
text = "Hello world"
text_area = bitmap_label.Label(terminalio.FONT, text=text)
text_area.x = 10
text_area.y = 10
board.DISPLAY.show(text_area)
while True:
pass

View file

@ -1,222 +0,0 @@
# Sample for comparing label and bitmap_label positioning with Builtin or loaded BDF fonts
# pylint: disable=no-member
import gc
import board
import displayio
import terminalio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import bitmap_label
from adafruit_display_text import label
# pylint: disable=no-member
##########
# Use this Boolean variables to select which font style to use
##########
use_builtinfont = False # Set True to use the terminalio.FONT BuiltinFont,
fontToUse = terminalio.FONT
# Set False to use a BDF loaded font, see "fontFiles" below
##########
if not use_builtinfont:
# load the fonts
print("loading font...")
fontList = []
# Load some proportional fonts
fontFile = "fonts/Helvetica-Bold-16.bdf"
fontToUse = bitmap_font.load_font(fontFile)
# Set scaling factor for display text
my_scale = 1
# Setup the SPI display
if "DISPLAY" not in dir(board):
# Setup the LCD display with driver
# You may need to change this to match the display driver for the chipset
# used on your display
from adafruit_ili9341 import ILI9341
displayio.release_displays()
# setup the SPI bus
spi = board.SPI()
tft_cs = board.D9 # arbitrary, pin not used
tft_dc = board.D10
tft_backlight = board.D12
tft_reset = board.D11
while not spi.try_lock():
spi.configure(baudrate=32000000)
spi.unlock()
display_bus = displayio.FourWire(
spi,
command=tft_dc,
chip_select=tft_cs,
reset=tft_reset,
baudrate=32000000,
polarity=1,
phase=1,
)
# Number of pixels in the display
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240
# create the display
display = ILI9341(
display_bus,
width=DISPLAY_WIDTH,
height=DISPLAY_HEIGHT,
rotation=180, # The rotation can be adjusted to match your configuration.
auto_refresh=True,
native_frames_per_second=90,
)
# reset the display to show nothing.
display.show(None)
else:
# built-in display
display = board.DISPLAY
print("Display is started")
preload_glyphs = (
True # set this to True if you want to preload the font glyphs into memory
)
# preloading the glyphs will help speed up the rendering of text but will use more RAM
if preload_glyphs and not use_builtinfont:
# identify the glyphs to load into memory -> increases rendering speed
glyphs = (
b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/-_,.:?!'\n "
)
print("loading glyphs...")
fontToUse.load_glyphs(glyphs)
print("Glyphs are loaded.")
print("Fonts completed loading.")
# create group
long_string = "The purple snake\nbrings python fun\nto everyone."
label2_padding = 10
#####
# Create the "bitmap_label.py" versions of the text labels.
gc.collect()
bitmap_label_start = gc.mem_free()
bmap_label1 = bitmap_label.Label(
font=fontToUse,
text="bitmap_label",
color=0xFFFFFF,
background_color=0xFF0000,
padding_bottom=0,
padding_left=0,
padding_right=0,
padding_top=0,
background_tight=True,
line_spacing=1.25,
scale=my_scale,
anchor_point=(0.0, 0),
anchored_position=(10, 60),
)
bmap_label2 = bitmap_label.Label(
font=fontToUse,
text=long_string,
color=0x000000,
max_glyphs=len(long_string),
background_color=0xFFFF00,
padding_bottom=label2_padding,
padding_left=0,
padding_right=0,
padding_top=label2_padding,
background_tight=False,
line_spacing=1.25,
scale=my_scale,
anchor_point=(0.0, 0),
anchored_position=(10, 120),
)
gc.collect()
bitmap_label_end = gc.mem_free()
print("bitmap_label used: {} memory".format(bitmap_label_start - bitmap_label_end))
bmap_group = displayio.Group(max_size=4) # Create a group for displaying
bmap_group.append(bmap_label1)
bmap_group.append(bmap_label2)
#####
# Create the "label.py" versions of the text labels.
gc.collect()
label_start = gc.mem_free()
label1 = label.Label(
font=fontToUse,
text="label",
color=0xFFFFFF,
background_color=0xFF0000,
padding_bottom=0,
padding_left=0,
padding_right=0,
padding_top=0,
background_tight=True,
line_spacing=1.25,
scale=my_scale,
anchor_point=(1.0, 0),
anchored_position=(display.width - 10, 60),
)
label2 = label.Label(
font=fontToUse,
text=long_string,
color=0x000000,
max_glyphs=len(long_string),
background_color=0xFFFF00,
padding_bottom=label2_padding,
padding_left=0,
padding_right=0,
padding_top=label2_padding,
background_tight=False,
line_spacing=1.25,
scale=my_scale,
anchor_point=(1.0, 0),
anchored_position=(display.width - 10, 120),
)
gc.collect()
label_end = gc.mem_free()
print("label used: {} memory".format(label_start - label_end))
label_group = displayio.Group(max_size=4) # Create a group for displaying
label_group.append(label1)
label_group.append(label2)
print("***")
main_group = displayio.Group()
main_group.append(label_group)
main_group.append(bmap_group)
display.auto_refresh = True
display.show(main_group)
while True:
pass

View file

@ -1,9 +1,8 @@
import os
import board
import displayio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
from adafruit_bitmap_font import bitmap_font
# the current working directory (where this file is)
cwd = ("/" + __file__).rsplit("/", 1)[0]

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,2 @@
Adafruit-Blinka
adafruit-blinka-displayio

View file

@ -1,50 +0,0 @@
"""A setuptools based setup module.
See:
https://packaging.python.org/en/latest/distributing.html
https://github.com/pypa/sampleproject
"""
from setuptools import setup, find_packages
# To use a consistent encoding
from codecs import open
from os import path
here = path.abspath(path.dirname(__file__))
# Get the long description from the README file
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
long_description = f.read()
setup(
name="adafruit-circuitpython-display-text",
use_scm_version=True,
setup_requires=["setuptools_scm"],
description="Displays text using CircuitPython's displayio.",
long_description=long_description,
long_description_content_type="text/x-rst",
# The project's main homepage.
url="https://github.com/adafruit/Adafruit_CircuitPython_Display_Text",
# Author details
author="Adafruit Industries",
author_email="circuitpython@adafruit.com",
install_requires=["Adafruit-Blinka", "adafruit-blinka-displayio",],
# Choose your license
license="MIT",
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries",
"Topic :: System :: Hardware",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
],
# What does your project relate to?
keywords="adafruit blinka circuitpython micropython bitmap fonts text display tft lcd displayio",
# You can just specify the packages manually here if your project is
# simple. Or you can use find_packages().
packages=["adafruit_display_text"],
)

4
setup.py.disabled Normal file
View file

@ -0,0 +1,4 @@
"""
This library is not deployed to PyPI. It is either a board-specific helper library, or
does not make sense for use on or is incompatible with single board computers and Linux.
"""