From 32201501eda4353ee18beb0580e742b35116a6fc Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Sun, 17 Jan 2021 14:09:38 -0600 Subject: [PATCH 1/5] Repaired error in anchor_point and anchor_position --- adafruit_display_text/archive/bitmap_label.py | 447 ++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 adafruit_display_text/archive/bitmap_label.py diff --git a/adafruit_display_text/archive/bitmap_label.py b/adafruit_display_text/archive/bitmap_label.py new file mode 100644 index 0000000..2c8a1aa --- /dev/null +++ b/adafruit_display_text/archive/bitmap_label.py @@ -0,0 +1,447 @@ +# 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:** + +.. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST + inline format: "* `Link Text `_" + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +.. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. + +# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register +""" + +import displayio + + +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( + text, font, line_spacing, background_tight=False +): # **** change default background_tight=False + + label_position_yoffset = int( # for calibration with label.py positioning + (font.get_glyph(ord("M")).height - font.get_bounding_box()[1] * line_spacing) + / 2 + ) + + # 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 + 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) + font_height = ascender_max + descender_max + font_yoffset = ascender_max + + + lines = 1 + + font_height = font.get_glyph(ord("M")).height + + + xposition = x_start = 0 # starting x position (left margin) + yposition = y_start = 0 + + left = right = x_start + top = bottom = y_start + + + y_offset_tight = int(( font.get_glyph(ord("M")).height - text.count("\n") *line_spacing_ypixels(font, line_spacing) + ) + / 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 == 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 + line_spacing_ypixels( + font, line_spacing + ) # Add a newline + lines += 1 + xposition += my_glyph.shift_x + right = max(right, xposition) + + 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) + + # width = my_glyph.width + # height = my_glyph.height + # dx = my_glyph.dx + # dy = my_glyph.dy + # shift_x = my_glyph.shift_x + # shift_y = my_glyph.shift_x + + + loose_height= (lines - 1) * line_spacing_ypixels(font, line_spacing) + (ascender_max + descender_max) + + + + label_calibration_offset=int(( font.get_glyph(ord("M")).height - text.count("\n") *line_spacing_ypixels(font, line_spacing) + ) + / 2) + + y_offset_tight = -top +label_calibration_offset # + + + + final_box_width=right-left + if background_tight: + final_box_height=bottom-top + final_y_offset=y_offset_tight + + else: + final_box_height=loose_height + final_y_offset=ascender_max + + return (final_box_width, final_box_height, 0, final_y_offset) # -x1_min is the x_offset + + +def place_text( + bitmap, + text, + font, + line_spacing, + xposition, + yposition, + text_palette_index=1, + background_palette_index=0, + scale=1, + 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 + + font_height = font.get_glyph(ord("M")).height + + bitmap_width = bitmap.width + bitmap_height = bitmap.height + + x_start = xposition # starting x position (left margin) + y_start = yposition + + left = right = x_start + top = bottom = y_start + + for char in text: + + if char == "\n": # newline + xposition = x_start # reset to left column + yposition = yposition + line_spacing_ypixels( + font, line_spacing + ) # Add a newline + + else: + + my_glyph = font.get_glyph(ord(char)) + + if my_glyph == None: # Error checking: no glyph found + print("Glyph not found: {}".format(repr(char))) + else: + + right = max(right, xposition + my_glyph.shift_x) + 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) + + width = my_glyph.width + height = my_glyph.height + dx = my_glyph.dx + dy = my_glyph.dy + shift_x = my_glyph.shift_x + shift_y = my_glyph.shift_x + glyph_offset_x = ( + my_glyph.tile_index * width + ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. + # for BDF loaded fonts, this should equal 0 + + y_offset = font_height - height + for y in range(height): + for x in range(width): + x_placement = x + xposition + dx + y_placement = y + yposition - height - dy + + if ( + (x_placement >= 0) + and (y_placement >= 0) + and (x_placement < bitmap_width) + and (y_placement < bitmap_height) + ): + + # 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 + shift_x + + return (left, top, right-left, bottom - top) # bounding_box + + +class Label(displayio.Group): + # Class variable + # To save memory, set Label._memory_saver=True and avoid storing the text string in the class. + # If set to False, the class saves the text string for future reference. *** use getter + _memory_saver = True + + def __init__( + self, + font, + x=0, + y=0, + text="", + max_glyphs=None, # This input parameter is ignored, only present for compatibility with label.py + # width, height, + 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=(0, 0), + anchored_position=None, + **kwargs + ): + + if text == "": + raise RuntimeError("Please provide text string") + + # 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 + + if self._memory_saver: # do not save the text in the instance + self._text = None + else: + self._text = text # text to be displayed + + # limit padding to >= 0 *** raise an error if negative padding is requested + 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, x_offset, tight_y_offset) = 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 + + else: + (box_x, box_y, x_offset, y_offset) = 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) + if background_color is not None: + self.palette[0] = background_color + else: + self.palette[0] = 0 + self.palette.make_transparent(0) + self.palette[1] = color + + # Create the bitmap and TileGrid + self.bitmap = displayio.Bitmap(box_x, box_y, len(self.palette)) + + # Place the text into the Bitmap + text_size = 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 + - text.count("\n") * font.get_bounding_box()[1] * self._line_spacing + ) + / 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, + y= label_position_yoffset - y_offset - padding_top, + ) + + # instance the Group with super... super().__init__( + # 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 + + ####### ******* + # Set the tileGrid position in the parent based upon anchor_point and anchor_position + # **** Should scale affect the placement of anchor_position? + + self.bounding_box = ( + self.tilegrid.x, + #self.tilegrid.y + (y_offset - tight_y_offset), + self.tilegrid.y, #+ (y_offset - tight_y_offset), + tight_box_x, + tight_box_y, + ) + + # self.bounding_box = ( + # self.tilegrid.x, + # self.tilegrid.y + (y_offset), + # tight_box_x, + # tight_box_y, + # ) + + + + # Update bounding_box values. Note: To be consistent with label.py, + # this is the bounding box for the text only, not including the background. + # ******** Need repair + # Create the TileGrid to hold the single Bitmap (self.bitmap) + + 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 + + @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): + 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): + new_x = int( + new_position[0] + - self._anchor_point[0] * (self.bounding_box[2] * self._scale) + ) + new_y = int( + new_position[1] + - (self._anchor_point[1] * self.bounding_box[3] * self.scale) + + round((self.bounding_box[3] * self.scale) / 2.0) + ) + self.x = new_x + self.y = new_y From 90f053ad057a27961f584dc14775ac6a44261ea0 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Sun, 17 Jan 2021 14:21:03 -0600 Subject: [PATCH 2/5] Revert "Repaired error in anchor_point and anchor_position" This reverts commit 32201501eda4353ee18beb0580e742b35116a6fc. --- adafruit_display_text/archive/bitmap_label.py | 447 ------------------ 1 file changed, 447 deletions(-) delete mode 100644 adafruit_display_text/archive/bitmap_label.py diff --git a/adafruit_display_text/archive/bitmap_label.py b/adafruit_display_text/archive/bitmap_label.py deleted file mode 100644 index 2c8a1aa..0000000 --- a/adafruit_display_text/archive/bitmap_label.py +++ /dev/null @@ -1,447 +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:** - -.. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST - inline format: "* `Link Text `_" - -**Software and Dependencies:** - -* Adafruit CircuitPython firmware for the supported boards: - https://github.com/adafruit/circuitpython/releases - -.. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. - -# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice -# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register -""" - -import displayio - - -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( - text, font, line_spacing, background_tight=False -): # **** change default background_tight=False - - label_position_yoffset = int( # for calibration with label.py positioning - (font.get_glyph(ord("M")).height - font.get_bounding_box()[1] * line_spacing) - / 2 - ) - - # 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 - 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) - font_height = ascender_max + descender_max - font_yoffset = ascender_max - - - lines = 1 - - font_height = font.get_glyph(ord("M")).height - - - xposition = x_start = 0 # starting x position (left margin) - yposition = y_start = 0 - - left = right = x_start - top = bottom = y_start - - - y_offset_tight = int(( font.get_glyph(ord("M")).height - text.count("\n") *line_spacing_ypixels(font, line_spacing) - ) - / 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 == 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 + line_spacing_ypixels( - font, line_spacing - ) # Add a newline - lines += 1 - xposition += my_glyph.shift_x - right = max(right, xposition) - - 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) - - # width = my_glyph.width - # height = my_glyph.height - # dx = my_glyph.dx - # dy = my_glyph.dy - # shift_x = my_glyph.shift_x - # shift_y = my_glyph.shift_x - - - loose_height= (lines - 1) * line_spacing_ypixels(font, line_spacing) + (ascender_max + descender_max) - - - - label_calibration_offset=int(( font.get_glyph(ord("M")).height - text.count("\n") *line_spacing_ypixels(font, line_spacing) - ) - / 2) - - y_offset_tight = -top +label_calibration_offset # - - - - final_box_width=right-left - if background_tight: - final_box_height=bottom-top - final_y_offset=y_offset_tight - - else: - final_box_height=loose_height - final_y_offset=ascender_max - - return (final_box_width, final_box_height, 0, final_y_offset) # -x1_min is the x_offset - - -def place_text( - bitmap, - text, - font, - line_spacing, - xposition, - yposition, - text_palette_index=1, - background_palette_index=0, - scale=1, - 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 - - font_height = font.get_glyph(ord("M")).height - - bitmap_width = bitmap.width - bitmap_height = bitmap.height - - x_start = xposition # starting x position (left margin) - y_start = yposition - - left = right = x_start - top = bottom = y_start - - for char in text: - - if char == "\n": # newline - xposition = x_start # reset to left column - yposition = yposition + line_spacing_ypixels( - font, line_spacing - ) # Add a newline - - else: - - my_glyph = font.get_glyph(ord(char)) - - if my_glyph == None: # Error checking: no glyph found - print("Glyph not found: {}".format(repr(char))) - else: - - right = max(right, xposition + my_glyph.shift_x) - 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) - - width = my_glyph.width - height = my_glyph.height - dx = my_glyph.dx - dy = my_glyph.dy - shift_x = my_glyph.shift_x - shift_y = my_glyph.shift_x - glyph_offset_x = ( - my_glyph.tile_index * width - ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. - # for BDF loaded fonts, this should equal 0 - - y_offset = font_height - height - for y in range(height): - for x in range(width): - x_placement = x + xposition + dx - y_placement = y + yposition - height - dy - - if ( - (x_placement >= 0) - and (y_placement >= 0) - and (x_placement < bitmap_width) - and (y_placement < bitmap_height) - ): - - # 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 + shift_x - - return (left, top, right-left, bottom - top) # bounding_box - - -class Label(displayio.Group): - # Class variable - # To save memory, set Label._memory_saver=True and avoid storing the text string in the class. - # If set to False, the class saves the text string for future reference. *** use getter - _memory_saver = True - - def __init__( - self, - font, - x=0, - y=0, - text="", - max_glyphs=None, # This input parameter is ignored, only present for compatibility with label.py - # width, height, - 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=(0, 0), - anchored_position=None, - **kwargs - ): - - if text == "": - raise RuntimeError("Please provide text string") - - # 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 - - if self._memory_saver: # do not save the text in the instance - self._text = None - else: - self._text = text # text to be displayed - - # limit padding to >= 0 *** raise an error if negative padding is requested - 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, x_offset, tight_y_offset) = 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 - - else: - (box_x, box_y, x_offset, y_offset) = 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) - if background_color is not None: - self.palette[0] = background_color - else: - self.palette[0] = 0 - self.palette.make_transparent(0) - self.palette[1] = color - - # Create the bitmap and TileGrid - self.bitmap = displayio.Bitmap(box_x, box_y, len(self.palette)) - - # Place the text into the Bitmap - text_size = 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 - - text.count("\n") * font.get_bounding_box()[1] * self._line_spacing - ) - / 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, - y= label_position_yoffset - y_offset - padding_top, - ) - - # instance the Group with super... super().__init__( - # 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 - - ####### ******* - # Set the tileGrid position in the parent based upon anchor_point and anchor_position - # **** Should scale affect the placement of anchor_position? - - self.bounding_box = ( - self.tilegrid.x, - #self.tilegrid.y + (y_offset - tight_y_offset), - self.tilegrid.y, #+ (y_offset - tight_y_offset), - tight_box_x, - tight_box_y, - ) - - # self.bounding_box = ( - # self.tilegrid.x, - # self.tilegrid.y + (y_offset), - # tight_box_x, - # tight_box_y, - # ) - - - - # Update bounding_box values. Note: To be consistent with label.py, - # this is the bounding box for the text only, not including the background. - # ******** Need repair - # Create the TileGrid to hold the single Bitmap (self.bitmap) - - 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 - - @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): - 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): - new_x = int( - new_position[0] - - self._anchor_point[0] * (self.bounding_box[2] * self._scale) - ) - new_y = int( - new_position[1] - - (self._anchor_point[1] * self.bounding_box[3] * self.scale) - + round((self.bounding_box[3] * self.scale) / 2.0) - ) - self.x = new_x - self.y = new_y From 30d001e69f8da67befaa7488ad8e1c5446169eef Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Sun, 17 Jan 2021 14:22:34 -0600 Subject: [PATCH 3/5] Fix bug for bitmap_label's anchor_point, anchored_position and scale --- adafruit_display_text/bitmap_label.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 26332b7..e1cbe18 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -198,15 +198,12 @@ def _reset_text( if padding_right is not None: self._padding_right = max(0, padding_right) if anchor_point is not None: - self.anchor_point = anchor_point + self._anchor_point = anchor_point if anchored_position is not None: self._anchored_position = anchored_position if save_text is not None: self._save_text = save_text - if ( - scale is not None - ): # Scale will be defined in local_group (Note: self should have scale=1) - self.scale = scale # call the setter + # if text is not provided as a parameter (text is None), use the previous value. if (text is None) and self._save_text: @@ -229,8 +226,8 @@ def _reset_text( 0, # zero height with text == "" ) # Clear out any items in the self Group, in case this is an update to the bitmap_label - for _ in self: - self.pop(0) + for _ in self.local_group: + self.local_group.pop(0) else: # The text string is not empty, so create the Bitmap and TileGrid and # append to the self Group @@ -308,11 +305,19 @@ def _reset_text( tight_box_y, ) + + if ( + scale is not None + ): # Scale will be defined in local_group (Note: self should have scale=1) + self.scale = scale # call the setter + self.anchored_position = ( self._anchored_position ) # set the anchored_position with setter after bitmap is created, sets the # x,y positions of the label + + @staticmethod def _line_spacing_ypixels(font, line_spacing): # Note: Scaling is provided at the Group level From fb5efb1186628eff3f63d459325ac763103066a1 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Sun, 17 Jan 2021 14:24:31 -0600 Subject: [PATCH 4/5] Ran black --- adafruit_display_text/bitmap_label.py | 94 ++++++++++++++------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index e1cbe18..faab8ea 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -48,43 +48,43 @@ 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``. - The ``max_glyphs`` parameter is ignored and is present - only for direct compatability with label.py. - - For further reduction in memory usage, set ``save_text=False`` (text string will not - be stored and ``line_spacing`` and ``font`` are immutable with ``save_text`` - set to ``False``). - - 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. - """ + Note: This ``bitmap_label.py`` library utilizes a bitmap to display the text. + This method is memory-conserving relative to ``label.py``. + The ``max_glyphs`` parameter is ignored and is present + only for direct compatability with label.py. + + For further reduction in memory usage, set ``save_text=False`` (text string will not + be stored and ``line_spacing`` and ``font`` are immutable with ``save_text`` + set to ``False``). + + 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, too-many-statements @@ -118,7 +118,11 @@ def __init__( # self Group will contain a single local_group which contains a Group (self.local_group) # which contains a TileGrid (self.tilegrid) which contains the text bitmap (self.bitmap) super().__init__( - max_size=1, x=x, y=y, scale=1, **kwargs, + max_size=1, + x=x, + y=y, + scale=1, + **kwargs, ) # the self group scale should always remain at 1, the self.local_group will # be used to set the scale @@ -204,7 +208,6 @@ def _reset_text( if save_text is not None: self._save_text = save_text - # if text is not provided as a parameter (text is None), use the previous value. if (text is None) and self._save_text: text = self._text @@ -244,7 +247,9 @@ def _reset_text( loose_box_y, loose_y_offset, ) = self._text_bounding_box( - text, self._font, self._line_spacing, + text, + self._font, + self._line_spacing, ) # calculate the box size for a tight and loose backgrounds if self._background_tight: @@ -305,7 +310,6 @@ def _reset_text( tight_box_y, ) - if ( scale is not None ): # Scale will be defined in local_group (Note: self should have scale=1) @@ -316,8 +320,6 @@ def _reset_text( ) # set the anchored_position with setter after bitmap is created, sets the # x,y positions of the label - - @staticmethod def _line_spacing_ypixels(font, line_spacing): # Note: Scaling is provided at the Group level @@ -649,8 +651,8 @@ def font(self, new_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.)""" + 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 @@ -663,7 +665,7 @@ def anchor_point(self, new_anchor_point): @property def anchored_position(self): """Position relative to the anchor_point. Tuple containing x,y - pixel coordinates.""" + pixel coordinates.""" return self._anchored_position @anchored_position.setter From 1dfe6950a1b78f3e028d782fdb090b5ab8ebb4ff Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 17 Jan 2021 15:59:48 -0600 Subject: [PATCH 5/5] docstring format --- adafruit_display_text/bitmap_label.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 1775515..07ab0dc 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -49,12 +49,12 @@ class Label(displayio.Group): 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) + 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 + 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 @@ -63,11 +63,10 @@ class Label(displayio.Group): 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. + 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. - """ + 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, too-many-statements