From b290f05afd0852d2b666d2d127b75f0ea546aa9b Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 29 Apr 2022 18:32:39 -0500 Subject: [PATCH 1/7] starting tab layout --- .../layouts/tab_layout.py | 182 ++++++++++++++++ examples/displayio_layout_tab_layout_test.py | 199 ++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 adafruit_displayio_layout/layouts/tab_layout.py create mode 100644 examples/displayio_layout_tab_layout_test.py diff --git a/adafruit_displayio_layout/layouts/tab_layout.py b/adafruit_displayio_layout/layouts/tab_layout.py new file mode 100644 index 0000000..c40b925 --- /dev/null +++ b/adafruit_displayio_layout/layouts/tab_layout.py @@ -0,0 +1,182 @@ + +try: + from typing import Optional, Union + from fontio import BuiltinFont + from adafruit_bitmap_font.bdf import BDF + from adafruit_bitmap_font.pcf import PCF +except ImportError: + pass + +import board +import terminalio +import displayio +import adafruit_imageload +from adafruit_display_text.bitmap_label import Label +from adafruit_imageload.tilegrid_inflator import inflate_tilegrid +from adafruit_displayio_layout.layouts.page_layout import PageLayout + +class TabLayout(displayio.Group): + def __init__(self, + x: int = 0, y: int = 0, + display: displayio.Display = board.DISPLAY, + tab_text_scale: int = 1, + custom_font: Optional[Union[BuiltinFont, BDF, PCF]] = terminalio.FONT, + inactive_tab_spritesheet: Optional[str] = None, + active_tab_spritesheet: Optional[str] = None, + active_tab_text_color: Optional[int, tuple[int, int, int]] = 0x999999, + inactive_tab_text_color: Optional[int, tuple[int, int, int]] = 0xfffff, + inactive_tab_transparent_indexes: Optional[Union[int, tuple[int, int]]] = None, + active_tab_transparent_indexes: Optional[Union[int, tuple[int, int]]] = None, + tab_count: int = None, + ): + + if inactive_tab_spritesheet is None: + raise AttributeError("Must pass active_tab_spritesheet") + if active_tab_spritesheet is None: + raise AttributeError("Must pass inactive_tab_spritesheet") + if tab_count is None: + raise AttributeError("Must pass tab_count") + + super().__init__(x=x, y=y) + self.tab_count = tab_count + self._active_bmp, self._active_palette = adafruit_imageload.load(active_tab_spritesheet) + self._inactive_bmp, self._inactive_palette = adafruit_imageload.load(inactive_tab_spritesheet) + + if isinstance(active_tab_transparent_indexes, int): + self._active_palette.make_transparent(active_tab_transparent_indexes) + elif isinstance(active_tab_transparent_indexes, tuple): + for index in active_tab_transparent_indexes: + self._active_palette.make_transparent(index) + else: + raise AttributeError("active_tab_transparent_indexes must be int or tuple") + + if isinstance(inactive_tab_transparent_indexes, int): + self._inactive_palette.make_transparent(inactive_tab_transparent_indexes) + elif isinstance(inactive_tab_transparent_indexes, tuple): + for index in inactive_tab_transparent_indexes: + self._inactive_palette.make_transparent(index) + else: + raise AttributeError("inactive_tab_transparent_indexes must be int or tuple") + + self.tab_height = self._active_bmp.height + self.display = display + self.active_tab_text_color = active_tab_text_color + self.inactive_tab_text_color = inactive_tab_text_color + self.custom_font = custom_font + self.tab_text_scale = tab_text_scale + self.tab_group = displayio.Group() + self.tab_dict = {} + self.page_layout = PageLayout(x=x, y=y + self.tab_height) + + self.append(self.tab_group) + self.append(self.page_layout) + + def _draw_tabs(self): + for i, page_dict in enumerate(self.page_layout._page_content_list): + if i not in self.tab_dict: + print(f"creating tab {i}") + _new_tab_group = displayio.Group() + _tab_tilegrid = inflate_tilegrid(bmp_obj=self._inactive_bmp, bmp_palette=self._inactive_palette, + target_size=((self.display.width // self.tab_count) // ( + self._active_bmp.width // 3), 3)) + + _tab_tilegrid.x = (self.display.width // self.tab_count) * i + _new_tab_group.append(_tab_tilegrid) + + _tab_label = Label(self.custom_font, text=page_dict["page_name"], + color=self.inactive_tab_text_color, scale=self.tab_text_scale) + + _tab_label.anchor_point = (0.5, 0.5) + _tab_label.anchored_position = ( + _tab_tilegrid.x + + ((_tab_tilegrid.width * _tab_tilegrid.tile_width) // 2), + (_tab_tilegrid.height * _tab_tilegrid.tile_height) // 2 + ) + _new_tab_group.append(_tab_label) + + if i == self.page_layout.showing_page_index: + _tab_tilegrid.bitmap = self._active_bmp + _tab_tilegrid.pixel_shader = self._active_palette + _tab_label.color = self.active_tab_text_color + self.tab_dict[i] = _new_tab_group + self.tab_group.append(_new_tab_group) + + def _update_active_tab(self): + for i in range(len(self.page_layout)): + if i == self.page_layout.showing_page_index: + self.tab_group[i][0].bitmap = self._active_bmp + self.tab_group[i][0].pixel_shader = self._active_palette + self.tab_group[i][1].color = self.active_tab_text_color + else: + self.tab_group[i][0].bitmap = self._inactive_bmp + self.tab_group[i][0].pixel_shader = self._inactive_palette + self.tab_group[i][1].color = self.inactive_tab_text_color + + def add_content(self, tab_content, tab_name): + self.page_layout.add_content(tab_content, tab_name) + self._draw_tabs() + + + def show_page(self, page_name=None, page_index=None): + """ + Show the specified page, and hide all other pages. + + :param string page_name: The name of a page to show + :param int page_index: The index of a page to show + :return: None + """ + + self.page_layout.show_page(page_name=page_name, page_index=page_index) + self._update_active_tab() + + + @property + def showing_page_index(self): + """ + Index of the currently showing page + :return int: showing_page_index + """ + return self.page_layout.showing_page_index + + @showing_page_index.setter + def showing_page_index(self, new_index): + self.show_page(page_index=new_index) + + @property + def showing_page_name(self): + """ + Name of the currently showing page + :return string: showing_page_name + """ + return self.page_layout.showing_page_name + + @showing_page_name.setter + def showing_page_name(self, new_name): + self.show_page(page_name=new_name) + + @property + def showing_page_content(self): + """ + The content object for the currently showing page + :return Displayable: showing_page_content + """ + return self.page_layout.showing_page_content + + def next_page(self, loop=True): + """ + Hide the current page and show the next one in the list by index + :param bool loop: whether to loop from the last page back to the first + :return: None + """ + + self.page_layout.next_page(loop=loop) + self._update_active_tab() + + def previous_page(self, loop=True): + """ + Hide the current page and show the previous one in the list by index + :param bool loop: whether to loop from the first page to the last one + :return: None + """ + self.page_layout.previous_page(loop=loop) + self._update_active_tab() diff --git a/examples/displayio_layout_tab_layout_test.py b/examples/displayio_layout_tab_layout_test.py new file mode 100644 index 0000000..1942d3a --- /dev/null +++ b/examples/displayio_layout_tab_layout_test.py @@ -0,0 +1,199 @@ +# import time +# import displayio +# import board +# import terminalio +# from adafruit_displayio_layout.layouts.page_layout import PageLayout +# from adafruit_display_text.bitmap_label import Label +# import vectorio +# +# display = board.DISPLAY +# +# # Make the display context +# main_group = displayio.Group() +# display.show(main_group) +# +# test_page_layout = PageLayout(x=0, y=0) +# +# page_1_lbl = Label(font=terminalio.FONT, text="Page One!", anchor_point=(0, 0), anchored_position=(10, 10), scale=3) +# page_2_lbl = Label(font=terminalio.FONT, text="Page Two!", anchor_point=(0, 0), anchored_position=(10, 10), scale=3) +# other_lbl = Label(font=terminalio.FONT, text="Something Different!", anchor_point=(0, 0), anchored_position=(10, 50), scale=3) +# +# test_page_layout.add_content(page_1_lbl, "page_1") +# test_page_layout.add_content(page_2_lbl, "page_2") +# test_page_layout.add_content(other_lbl, "page_3") +# +# #main_group.append(other_lbl) +# +# main_group.append(test_page_layout) +# +# print("showing page 1") +# +# test_page_layout.show_page(page_name="page_2") +# +# +# test_page_layout.showing_page_index = 0 +# +# #print(test_page_layout.showing_page_index) +# #print(test_page_layout.showing_page_name) +# +# colors = displayio.Palette(3) +# colors[0] = 0xdddd00 +# colors[1] = 0x00dddd +# colors[2] = 0x00dd00 +# rect = vectorio.Rectangle(pixel_shader=colors, width=100,height=20, x=10, y=100) +# +# test_page_layout.get_page(page_index=2)["content"][0].color = 0xFF00FF +# test_page_layout.get_page(page_name="page_2")["content"].append(rect) +# +# while True: +# time.sleep(1) +# test_page_layout.previous_page() +# +# +# # test_page_layout.show_page(page_name="page_1") +# # time.sleep(1) +# # test_page_layout.show_page(page_name="page_2") +# # time.sleep(1) +# pass + + +# SPDX-FileCopyrightText: 2022 Tim C +# +# SPDX-License-Identifier: MIT +""" +Make a PageLayout and illustrate all of it's features +""" +import time +import displayio +import board +import terminalio +from adafruit_display_text.bitmap_label import Label +from adafruit_display_shapes.rect import Rect +from adafruit_display_shapes.circle import Circle +from adafruit_display_shapes.triangle import Triangle +from adafruit_displayio_layout.layouts.tab_layout import TabLayout +from adafruit_bitmap_font import bitmap_font + +# built-in display +display = board.DISPLAY +# display.rotation = 90 +display.rotation = 0 + +# create and show main_group +main_group = displayio.Group() +display.show(main_group) + +font = bitmap_font.load_font("fonts/Helvetica-Bold-16.bdf") + +# create the page layout +test_page_layout = TabLayout(x=0, y=0, + display=board.DISPLAY, + tab_height=28, tab_text_scale=1, + custom_font=font, + inactive_tab_spritesheet="bmps/test_bmp_6.bmp", + active_tab_spritesheet="bmps/test_bmp_7.bmp", + showing_tab_text_color=0x00aa59, + tab_text_color=0xeeeeee, + inactive_tab_transparent_indexes=(0, 1), + active_tab_transparent_indexes=(0, 1), + tab_count=4) + +# make 3 pages of content +page_1_group = displayio.Group() +page_2_group = displayio.Group() +page_3_group = displayio.Group() +page_4_group = displayio.Group() + +# labels +page_1_lbl = Label( + font=terminalio.FONT, + scale=2, + text="This is the first page!", + anchor_point=(0, 0), + anchored_position=(10, 10), +) +page_2_lbl = Label( + font=terminalio.FONT, + scale=2, + text="This page is the second page!", + anchor_point=(0, 0), + anchored_position=(10, 10), +) +page_3_lbl = Label( + font=terminalio.FONT, + scale=2, + text="The third page is fun!", + anchor_point=(0, 0), + anchored_position=(10, 10), +) + +page_4_lbl = Label( + font=terminalio.FONT, + scale=2, + text="The fourth page is where it's at", + anchor_point=(0, 0), + anchored_position=(10, 10), +) + +# shapes +square = Rect(x=20, y=70, width=40, height=40, fill=0x00DD00) +circle = Circle(50, 100, r=30, fill=0xDD00DD) +triangle = Triangle(50, 0, 100, 50, 0, 50, fill=0xDDDD00) +rectangle = Rect(x=80, y=60, width=100, height=50, fill=0x0000DD) + +triangle.x = 80 +triangle.y = 70 + +# add everything to their page groups +page_1_group.append(square) +page_1_group.append(page_1_lbl) +page_2_group.append(page_2_lbl) +page_2_group.append(circle) +page_3_group.append(page_3_lbl) +page_3_group.append(triangle) +page_4_group.append(page_4_lbl) +page_4_group.append(rectangle) + +# add the pages to the layout, supply your own page names +test_page_layout.add_content(page_1_group, "One") +test_page_layout.add_content(page_2_group, "Two") +test_page_layout.add_content(page_3_group, "Thr") +test_page_layout.add_content(page_4_group, "For") +# test_page_layout.add_content(displayio.Group(), "page_5") + +# add it to the group that is showing on the display +main_group.append(test_page_layout) + +#test_page_layout.tab_tilegrids_group[3].x += 50 + +# change page with function by name +# test_page_layout.show_page(page_name="page_3") +# print("showing page index:{}".format(test_page_layout.showing_page_index)) +# time.sleep(1) + +# change page with function by index +test_page_layout.show_page(page_index=0) +print("showing page name: {}".format(test_page_layout.showing_page_name)) +time.sleep(1) + +# change page by updating the page name property +# test_page_layout.showing_page_name = "page_3" +# print("showing page index: {}".format(test_page_layout.showing_page_index)) +# time.sleep(1) + +# change page by updating the page index property +test_page_layout.showing_page_index = 1 +print("showing page name: {}".format(test_page_layout.showing_page_name)) +time.sleep(5) + +another_text = Label(terminalio.FONT, text="And another thing!", scale=2, color=0x00ff00, anchor_point=(0, 0), + anchored_position=(100, 100)) +test_page_layout.showing_page_content.append(another_text) + +print("starting loop") + + +while True: + time.sleep(1) + # change page by next page function. It will loop by default + test_page_layout.next_page() From 2b9aaa134d4645e155e1c0e00679ba888c2c810d Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 30 Apr 2022 06:09:57 -0500 Subject: [PATCH 2/7] change to default font and add sprites to exampes dir --- examples/bmps/test_bmp_6.bmp | Bin 0 -> 1738 bytes examples/bmps/test_bmp_7.bmp | Bin 0 -> 1730 bytes examples/displayio_layout_tab_layout_test.py | 24 ++++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 examples/bmps/test_bmp_6.bmp create mode 100644 examples/bmps/test_bmp_7.bmp diff --git a/examples/bmps/test_bmp_6.bmp b/examples/bmps/test_bmp_6.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ab415097779b7f14ab1768f930e80f266fec6b18 GIT binary patch literal 1738 zcmZ?rJ;lZV2B&~z4G`M^u>lY>GOz$iAOOlM>p`#pkPE^L4F6#W%yV)NDh4x1Aehk9 z{s)@*pYcC~zrR0&e}ErDK&&@IP`(R8P@ywJP^lwBaDxp)NUH@y*i;>c@YyO1k*gRO zB9@3T#IBTNs8|%iaORK_PM~v!fw25xoZ6MZ`UhH`QvXq?{|&AEPoeuLj{i~nm2d%~ z^3PBYzazMOucYJyDnE|mmX86-|2c_Ue$IIY2ACveI4faQ747GYPQeg|Yc&;d8F%lCq<2U>7u88!u3XTZjT3^=nh1YL#e>@!g9 fUlY>GOz$iAOOlM>p?IdkPE^L4F6#W%yV)NDh4x1Aehk9 z{s)@*pAl#-5CrABFa#AkGX#}7G6Xl+Fod*PFoaFjVF;hC!VtNNfgxgv7(?tzNrs9= z5e%yqDd7ZKK((XtID Date: Fri, 6 May 2022 18:10:14 -0500 Subject: [PATCH 3/7] working on tab_layout and simpletest example --- .../layouts/page_layout.py | 28 ++-- .../layouts/tab_layout.py | 155 ++++++++++++++---- docs/api.rst | 12 ++ .../{test_bmp_7.bmp => active_tab_sprite.bmp} | Bin examples/bmps/active_tab_sprite.bmp.license | 2 + ...test_bmp_6.bmp => inactive_tab_sprite.bmp} | Bin examples/bmps/inactive_tab_sprite.bmp.license | 2 + examples/displayio_layout_tab_layout_test.py | 127 +++++--------- 8 files changed, 192 insertions(+), 134 deletions(-) rename examples/bmps/{test_bmp_7.bmp => active_tab_sprite.bmp} (100%) create mode 100644 examples/bmps/active_tab_sprite.bmp.license rename examples/bmps/{test_bmp_6.bmp => inactive_tab_sprite.bmp} (100%) create mode 100644 examples/bmps/inactive_tab_sprite.bmp.license diff --git a/adafruit_displayio_layout/layouts/page_layout.py b/adafruit_displayio_layout/layouts/page_layout.py index bee92ba..1dc1798 100644 --- a/adafruit_displayio_layout/layouts/page_layout.py +++ b/adafruit_displayio_layout/layouts/page_layout.py @@ -53,11 +53,11 @@ def __init__( self.x = x self.y = y - self._page_content_list = [] + self.page_content_list = [] self._cur_showing_index = 0 def add_content(self, page_content, page_name=None): - """Add a child to the grid. + """Add a child to the page layout. :param page_content: the content for the page typically a Group :param page_name: the name of this page @@ -72,10 +72,10 @@ def add_content(self, page_content, page_name=None): "page_name": page_name, } - if len(self._page_content_list) > 0: + if len(self.page_content_list) > 0: _page_group.hidden = True - self._page_content_list.append(sub_view_obj) + self.page_content_list.append(sub_view_obj) self.append(_page_group) def _check_args(self, page_name, page_index): @@ -95,16 +95,16 @@ def _check_args(self, page_name, page_index): ) if page_index is not None: - if page_index >= len(self._page_content_list): + if page_index >= len(self.page_content_list): raise KeyError( "KeyError at index {} in list length {}".format( - page_index, len(self._page_content_list) + page_index, len(self.page_content_list) ), ) if page_name is not None: _found = False - for page in self._page_content_list: + for page in self.page_content_list: if not _found: if page_name == page["page_name"]: _found = True @@ -125,10 +125,10 @@ def get_page(self, page_name=None, page_index=None): self._check_args(page_name, page_index) if page_index is not None: - return self._page_content_list[page_index] + return self.page_content_list[page_index] if page_name is not None: - for cell in self._page_content_list: + for cell in self.page_content_list: if cell["page_name"] == page_name: return cell @@ -149,7 +149,7 @@ def show_page(self, page_name=None, page_index=None): self._check_args(page_name, page_index) - for cur_index, page in enumerate(self._page_content_list): + for cur_index, page in enumerate(self.page_content_list): if page_name is not None: if page["page_name"] == page_name: self._cur_showing_index = cur_index @@ -182,7 +182,7 @@ def showing_page_name(self): Name of the currently showing page :return string: showing_page_name """ - return self._page_content_list[self._cur_showing_index]["page_name"] + return self.page_content_list[self._cur_showing_index]["page_name"] @showing_page_name.setter def showing_page_name(self, new_name): @@ -194,7 +194,7 @@ def showing_page_content(self): The content object for the currently showing page :return Displayable: showing_page_content """ - return self._page_content_list[self._cur_showing_index]["content"][0] + return self.page_content_list[self._cur_showing_index]["content"][0] def next_page(self, loop=True): """ @@ -203,7 +203,7 @@ def next_page(self, loop=True): :return: None """ - if self._cur_showing_index + 1 < len(self._page_content_list): + if self._cur_showing_index + 1 < len(self.page_content_list): self.show_page(page_index=self._cur_showing_index + 1) else: if not loop: @@ -223,4 +223,4 @@ def previous_page(self, loop=True): if not loop: print("No more pages") else: - self.show_page(page_index=len(self._page_content_list) - 1) + self.show_page(page_index=len(self.page_content_list) - 1) diff --git a/adafruit_displayio_layout/layouts/tab_layout.py b/adafruit_displayio_layout/layouts/tab_layout.py index c40b925..61c7d76 100644 --- a/adafruit_displayio_layout/layouts/tab_layout.py +++ b/adafruit_displayio_layout/layouts/tab_layout.py @@ -1,13 +1,35 @@ +# SPDX-FileCopyrightText: 2022 Tim Cocks +# +# SPDX-License-Identifier: MIT +""" +`tab_layout` +================================================================================ + +A layout that organizes pages into tabs. + + +* Author(s): Tim Cocks + +Implementation Notes +-------------------- + +**Hardware:** + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" try: - from typing import Optional, Union + from typing import Optional, Union, Tuple from fontio import BuiltinFont from adafruit_bitmap_font.bdf import BDF from adafruit_bitmap_font.pcf import PCF except ImportError: pass -import board import terminalio import displayio import adafruit_imageload @@ -15,21 +37,55 @@ from adafruit_imageload.tilegrid_inflator import inflate_tilegrid from adafruit_displayio_layout.layouts.page_layout import PageLayout + class TabLayout(displayio.Group): - def __init__(self, - x: int = 0, y: int = 0, - display: displayio.Display = board.DISPLAY, - tab_text_scale: int = 1, - custom_font: Optional[Union[BuiltinFont, BDF, PCF]] = terminalio.FONT, - inactive_tab_spritesheet: Optional[str] = None, - active_tab_spritesheet: Optional[str] = None, - active_tab_text_color: Optional[int, tuple[int, int, int]] = 0x999999, - inactive_tab_text_color: Optional[int, tuple[int, int, int]] = 0xfffff, - inactive_tab_transparent_indexes: Optional[Union[int, tuple[int, int]]] = None, - active_tab_transparent_indexes: Optional[Union[int, tuple[int, int]]] = None, - tab_count: int = None, - ): + """ + A layout that organizes children into a grid table structure. + + :param int x: x location the layout should be placed. Pixel coordinates. + :param int y: y location the layout should be placed. Pixel coordinates. + :param displayio.Display display: The Display object to show the tab layout on. + :param int tab_text_scale: Size of the text shown in the tabs. + Whole numbers 1 and greater are valid + :param Optional[Union[BuiltinFont, BDF, PCF]] custom_font: A pre-loaded font object to use + for the tab labels + :param str inactive_tab_spritesheet: Filepath of the spritesheet to show for inactive tabs. + :param str active_tab_spritesheet: Filepath of the spritesheet to show for the active tab. + :param Optional[int, tuple[int, int, int]] active_tab_text_color: Hex or tuple color to use + for the active tab label + :param Optional[int, tuple[int, int, int]] inactive_tab_text_color: Hex or tuple color to + use for inactive tab labels + :param Optional[Union[int, tuple[int, int]]] inactive_tab_transparent_indexes: single index + or tuple of multiple indexes to be made transparent in the inactive tab sprite palette. + :param Optional[Union[int, tuple[int, int]]] active_tab_transparent_indexes: single index + or tuple of multiple indexes to be made transparent in the active tab sprite palette. + :param int tab_count: How many tabs to draw in the layout. Positive whole numbers are valid. + """ + + # pylint: disable=too-many-instance-attributes, too-many-arguments, invalid-name, too-many-branches + def __init__( + self, + x: int = 0, + y: int = 0, + display: Optional[displayio.Display] = None, + tab_text_scale: int = 1, + custom_font: Optional[Union[BuiltinFont, BDF, PCF]] = terminalio.FONT, + inactive_tab_spritesheet: Optional[str] = None, + active_tab_spritesheet: Optional[str] = None, + active_tab_text_color: Optional[Union[int, Tuple[int, int, int]]] = 0x999999, + inactive_tab_text_color: Optional[Union[int, Tuple[int, int, int]]] = 0xFFFFF, + inactive_tab_transparent_indexes: Optional[Union[int, Tuple[int, int]]] = None, + active_tab_transparent_indexes: Optional[Union[int, Tuple[int, int]]] = None, + tab_count: int = None, + ): + + if display is None: + # pylint: disable=import-outside-toplevel + import board + + if hasattr(board, "DISPLAY"): + display = board.DISPLAY if inactive_tab_spritesheet is None: raise AttributeError("Must pass active_tab_spritesheet") if active_tab_spritesheet is None: @@ -39,8 +95,12 @@ def __init__(self, super().__init__(x=x, y=y) self.tab_count = tab_count - self._active_bmp, self._active_palette = adafruit_imageload.load(active_tab_spritesheet) - self._inactive_bmp, self._inactive_palette = adafruit_imageload.load(inactive_tab_spritesheet) + self._active_bmp, self._active_palette = adafruit_imageload.load( + active_tab_spritesheet + ) + self._inactive_bmp, self._inactive_palette = adafruit_imageload.load( + inactive_tab_spritesheet + ) if isinstance(active_tab_transparent_indexes, int): self._active_palette.make_transparent(active_tab_transparent_indexes) @@ -56,7 +116,9 @@ def __init__(self, for index in inactive_tab_transparent_indexes: self._inactive_palette.make_transparent(index) else: - raise AttributeError("inactive_tab_transparent_indexes must be int or tuple") + raise AttributeError( + "inactive_tab_transparent_indexes must be int or tuple" + ) self.tab_height = self._active_bmp.height self.display = display @@ -72,25 +134,35 @@ def __init__(self, self.append(self.page_layout) def _draw_tabs(self): - for i, page_dict in enumerate(self.page_layout._page_content_list): + for i, page_dict in enumerate(self.page_layout.page_content_list): if i not in self.tab_dict: print(f"creating tab {i}") _new_tab_group = displayio.Group() - _tab_tilegrid = inflate_tilegrid(bmp_obj=self._inactive_bmp, bmp_palette=self._inactive_palette, - target_size=((self.display.width // self.tab_count) // ( - self._active_bmp.width // 3), 3)) + _tab_tilegrid = inflate_tilegrid( + bmp_obj=self._inactive_bmp, + bmp_palette=self._inactive_palette, + target_size=( + (self.display.width // self.tab_count) + // (self._active_bmp.width // 3), + 3, + ), + ) _tab_tilegrid.x = (self.display.width // self.tab_count) * i _new_tab_group.append(_tab_tilegrid) - _tab_label = Label(self.custom_font, text=page_dict["page_name"], - color=self.inactive_tab_text_color, scale=self.tab_text_scale) + _tab_label = Label( + self.custom_font, + text=page_dict["page_name"], + color=self.inactive_tab_text_color, + scale=self.tab_text_scale, + ) _tab_label.anchor_point = (0.5, 0.5) _tab_label.anchored_position = ( - _tab_tilegrid.x + - ((_tab_tilegrid.width * _tab_tilegrid.tile_width) // 2), - (_tab_tilegrid.height * _tab_tilegrid.tile_height) // 2 + _tab_tilegrid.x + + ((_tab_tilegrid.width * _tab_tilegrid.tile_width) // 2), + (_tab_tilegrid.height * _tab_tilegrid.tile_height) // 2, ) _new_tab_group.append(_tab_label) @@ -113,10 +185,15 @@ def _update_active_tab(self): self.tab_group[i][1].color = self.inactive_tab_text_color def add_content(self, tab_content, tab_name): + """Add a child to the tab layout. + + :param tab_content: the content for the tab typically a Group + :param tab_name: the name of this tab, will be shown inside the tab + + :return: None""" self.page_layout.add_content(tab_content, tab_name) self._draw_tabs() - def show_page(self, page_name=None, page_index=None): """ Show the specified page, and hide all other pages. @@ -129,7 +206,6 @@ def show_page(self, page_name=None, page_index=None): self.page_layout.show_page(page_name=page_name, page_index=page_index) self._update_active_tab() - @property def showing_page_index(self): """ @@ -140,7 +216,8 @@ def showing_page_index(self): @showing_page_index.setter def showing_page_index(self, new_index): - self.show_page(page_index=new_index) + if self.showing_page_index != new_index: + self.show_page(page_index=new_index) @property def showing_page_name(self): @@ -180,3 +257,21 @@ def previous_page(self, loop=True): """ self.page_layout.previous_page(loop=loop) self._update_active_tab() + + def handle_touch_events(self, touch_event): + """ + Check if the touch event is on the tabs and if so change to the touched tab. + + :param tuple touch_event: tuple containing x and y coordinates of the + touch event in indexes 0 and 1. + :return: None + """ + + if touch_event: + if 0 <= touch_event[1] <= self.tab_height: + + touched_tab_index = touch_event[0] // ( + self.display.width // self.tab_count + ) + print(f"{touch_event[0]} - {touched_tab_index}") + self.showing_page_index = touched_tab_index diff --git a/docs/api.rst b/docs/api.rst index b517167..a707e30 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -6,6 +6,18 @@ .. automodule:: adafruit_displayio_layout.layouts.grid_layout :members: + :private-members: + :member-order: bysource + +.. automodule:: adafruit_displayio_layout.layouts.page_layout + :members: + :private-members: + :member-order: bysource + +.. automodule:: adafruit_displayio_layout.layouts.tab_layout + :members: + :private-members: + :member-order: bysource .. automodule:: adafruit_displayio_layout.widgets.widget :members: diff --git a/examples/bmps/test_bmp_7.bmp b/examples/bmps/active_tab_sprite.bmp similarity index 100% rename from examples/bmps/test_bmp_7.bmp rename to examples/bmps/active_tab_sprite.bmp diff --git a/examples/bmps/active_tab_sprite.bmp.license b/examples/bmps/active_tab_sprite.bmp.license new file mode 100644 index 0000000..8f7990c --- /dev/null +++ b/examples/bmps/active_tab_sprite.bmp.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2022 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT diff --git a/examples/bmps/test_bmp_6.bmp b/examples/bmps/inactive_tab_sprite.bmp similarity index 100% rename from examples/bmps/test_bmp_6.bmp rename to examples/bmps/inactive_tab_sprite.bmp diff --git a/examples/bmps/inactive_tab_sprite.bmp.license b/examples/bmps/inactive_tab_sprite.bmp.license new file mode 100644 index 0000000..8f7990c --- /dev/null +++ b/examples/bmps/inactive_tab_sprite.bmp.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2022 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT diff --git a/examples/displayio_layout_tab_layout_test.py b/examples/displayio_layout_tab_layout_test.py index c79ab3c..6759f5e 100644 --- a/examples/displayio_layout_tab_layout_test.py +++ b/examples/displayio_layout_tab_layout_test.py @@ -1,67 +1,8 @@ -# import time -# import displayio -# import board -# import terminalio -# from adafruit_displayio_layout.layouts.page_layout import PageLayout -# from adafruit_display_text.bitmap_label import Label -# import vectorio -# -# display = board.DISPLAY -# -# # Make the display context -# main_group = displayio.Group() -# display.show(main_group) -# -# test_page_layout = PageLayout(x=0, y=0) -# -# page_1_lbl = Label(font=terminalio.FONT, text="Page One!", anchor_point=(0, 0), anchored_position=(10, 10), scale=3) -# page_2_lbl = Label(font=terminalio.FONT, text="Page Two!", anchor_point=(0, 0), anchored_position=(10, 10), scale=3) -# other_lbl = Label(font=terminalio.FONT, text="Something Different!", anchor_point=(0, 0), anchored_position=(10, 50), scale=3) - -# test_page_layout.add_content(page_1_lbl, "page_1") -# test_page_layout.add_content(page_2_lbl, "page_2") -# test_page_layout.add_content(other_lbl, "page_3") -# -# #main_group.append(other_lbl) -# -# main_group.append(test_page_layout) -# -# print("showing page 1") -# -# test_page_layout.show_page(page_name="page_2") -# -# -# test_page_layout.showing_page_index = 0 -# -# #print(test_page_layout.showing_page_index) -# #print(test_page_layout.showing_page_name) -# -# colors = displayio.Palette(3) -# colors[0] = 0xdddd00 -# colors[1] = 0x00dddd -# colors[2] = 0x00dd00 -# rect = vectorio.Rectangle(pixel_shader=colors, width=100,height=20, x=10, y=100) -# -# test_page_layout.get_page(page_index=2)["content"][0].color = 0xFF00FF -# test_page_layout.get_page(page_name="page_2")["content"].append(rect) -# -# while True: -# time.sleep(1) -# test_page_layout.previous_page() -# -# -# # test_page_layout.show_page(page_name="page_1") -# # time.sleep(1) -# # test_page_layout.show_page(page_name="page_2") -# # time.sleep(1) -# pass - - # SPDX-FileCopyrightText: 2022 Tim C # # SPDX-License-Identifier: MIT """ -Make a PageLayout and illustrate all of it's features +Make a TabLayout and illustrate the most basic features and usage. """ import time import displayio @@ -72,34 +13,35 @@ from adafruit_display_shapes.circle import Circle from adafruit_display_shapes.triangle import Triangle from adafruit_displayio_layout.layouts.tab_layout import TabLayout -from adafruit_bitmap_font import bitmap_font + +CHANGE_DELAY = 1.0 # Seconds to wait before auto-advancing to the next tab # built-in display display = board.DISPLAY -# display.rotation = 90 -display.rotation = 0 # create and show main_group main_group = displayio.Group() display.show(main_group) -#font = bitmap_font.load_font("fonts/Helvetica-Bold-16.bdf") font = terminalio.FONT # create the page layout -test_page_layout = TabLayout(x=0, y=0, - display=board.DISPLAY, - tab_text_scale=2, - custom_font=font, - inactive_tab_spritesheet="bmps/test_bmp_6.bmp", - active_tab_spritesheet="bmps/test_bmp_7.bmp", - active_tab_text_color=0x00aa59, - inactive_tab_text_color=0xeeeeee, - inactive_tab_transparent_indexes=(0, 1), - active_tab_transparent_indexes=(0, 1), - tab_count=4) - -# make 3 pages of content +test_page_layout = TabLayout( + x=0, + y=0, + display=board.DISPLAY, + tab_text_scale=2, + custom_font=font, + inactive_tab_spritesheet="bmps/inactive_tab_sprite.bmp", + active_tab_spritesheet="bmps/active_tab_sprite.bmp", + active_tab_text_color=0x00AA59, + inactive_tab_text_color=0xEEEEEE, + inactive_tab_transparent_indexes=(0, 1), + active_tab_transparent_indexes=(0, 1), + tab_count=4, +) + +# make page content Groups page_1_group = displayio.Group() page_2_group = displayio.Group() page_3_group = displayio.Group() @@ -116,7 +58,7 @@ page_2_lbl = Label( font=terminalio.FONT, scale=2, - text="This page is the second page!", + text="This page is the\nsecond page!", anchor_point=(0, 0), anchored_position=(10, 10), ) @@ -131,16 +73,16 @@ page_4_lbl = Label( font=terminalio.FONT, scale=2, - text="The fourth page is where it's at", + text="The fourth page\nis where it's at", anchor_point=(0, 0), anchored_position=(10, 10), ) # shapes square = Rect(x=20, y=70, width=40, height=40, fill=0x00DD00) -circle = Circle(50, 100, r=30, fill=0xDD00DD) +circle = Circle(50, 120, r=30, fill=0xDD00DD) triangle = Triangle(50, 0, 100, 50, 0, 50, fill=0xDDDD00) -rectangle = Rect(x=80, y=60, width=100, height=50, fill=0x0000DD) +rectangle = Rect(x=80, y=80, width=100, height=50, fill=0x0000DD) triangle.x = 80 triangle.y = 70 @@ -160,13 +102,10 @@ test_page_layout.add_content(page_2_group, "Two") test_page_layout.add_content(page_3_group, "Thr") test_page_layout.add_content(page_4_group, "For") -# test_page_layout.add_content(displayio.Group(), "page_5") # add it to the group that is showing on the display main_group.append(test_page_layout) -#test_page_layout.tab_tilegrids_group[3].x += 50 - # change page with function by name test_page_layout.show_page(page_name="Thr") print("showing page index:{}".format(test_page_layout.showing_page_index)) @@ -187,15 +126,23 @@ print("showing page name: {}".format(test_page_layout.showing_page_name)) time.sleep(5) -another_text = Label(terminalio.FONT, text="And another thing!", scale=2, color=0x00ff00, anchor_point=(0, 0), - anchored_position=(100, 100)) +another_text = Label( + terminalio.FONT, + text="And another thing!", + scale=2, + color=0x00FF00, + anchor_point=(0, 0), + anchored_position=(100, 100), +) test_page_layout.showing_page_content.append(another_text) print("starting loop") +prev_change_time = time.monotonic() while True: - time.sleep(1) - # change page by next page function. It will loop by default - test_page_layout.next_page() - pass + now = time.monotonic() + if prev_change_time + CHANGE_DELAY <= now: + prev_change_time = now + # change page by next page function. It will loop by default + test_page_layout.next_page() From bdb03c95009b1a4a53e130ffa6f9c5fb01702ea2 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 6 May 2022 18:18:43 -0500 Subject: [PATCH 4/7] rename to simpletest --- ...b_layout_test.py => displayio_layout_tab_layout_simpletest.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{displayio_layout_tab_layout_test.py => displayio_layout_tab_layout_simpletest.py} (100%) diff --git a/examples/displayio_layout_tab_layout_test.py b/examples/displayio_layout_tab_layout_simpletest.py similarity index 100% rename from examples/displayio_layout_tab_layout_test.py rename to examples/displayio_layout_tab_layout_simpletest.py From 801bdcb23e8dca18d3b55768f5957abfef634527 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 6 May 2022 20:59:31 -0500 Subject: [PATCH 5/7] tab layout touch example --- .../displayio_layout_tab_layout_touchtest.py | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 examples/displayio_layout_tab_layout_touchtest.py diff --git a/examples/displayio_layout_tab_layout_touchtest.py b/examples/displayio_layout_tab_layout_touchtest.py new file mode 100644 index 0000000..d8cd5bf --- /dev/null +++ b/examples/displayio_layout_tab_layout_touchtest.py @@ -0,0 +1,137 @@ +# SPDX-FileCopyrightText: 2022 Tim C +# +# SPDX-License-Identifier: MIT +""" +Make a TabLayout change tabs with the touchscreen +""" +import displayio +import board +import terminalio +import adafruit_touchscreen +from adafruit_display_text.bitmap_label import Label +from adafruit_display_shapes.rect import Rect +from adafruit_display_shapes.circle import Circle +from adafruit_display_shapes.triangle import Triangle +from adafruit_displayio_layout.layouts.tab_layout import TabLayout + +# built-in display +display = board.DISPLAY + +# ------------ Touchscreen setup --------------- # +# See: https://learn.adafruit.com/making-a-pyportal-user-interface-displayio/display +display = board.DISPLAY # create the display object + +screen_width = display.width +screen_height = display.height +ts = adafruit_touchscreen.Touchscreen( + board.TOUCH_XL, + board.TOUCH_XR, + board.TOUCH_YD, + board.TOUCH_YU, + calibration=((5200, 59000), (5800, 57000)), + size=(screen_width, screen_height), +) + +# create and show main_group +main_group = displayio.Group() +display.show(main_group) + +font = terminalio.FONT + +# create the page layout +test_page_layout = TabLayout( + x=0, + y=0, + display=board.DISPLAY, + tab_text_scale=2, + custom_font=font, + inactive_tab_spritesheet="bmps/inactive_tab_sprite.bmp", + active_tab_spritesheet="bmps/active_tab_sprite.bmp", + active_tab_text_color=0x00AA59, + inactive_tab_text_color=0xEEEEEE, + inactive_tab_transparent_indexes=(0, 1), + active_tab_transparent_indexes=(0, 1), + tab_count=4, +) + +# make page content Groups +page_1_group = displayio.Group() +page_2_group = displayio.Group() +page_3_group = displayio.Group() +page_4_group = displayio.Group() + +# labels +page_1_lbl = Label( + font=terminalio.FONT, + scale=2, + text="This is the first page!", + anchor_point=(0, 0), + anchored_position=(10, 10), +) +page_2_lbl = Label( + font=terminalio.FONT, + scale=2, + text="This page is the\nsecond page!", + anchor_point=(0, 0), + anchored_position=(10, 10), +) +page_3_lbl = Label( + font=terminalio.FONT, + scale=2, + text="The third page is fun!", + anchor_point=(0, 0), + anchored_position=(10, 10), +) + +page_4_lbl = Label( + font=terminalio.FONT, + scale=2, + text="The fourth page\nis where it's at", + anchor_point=(0, 0), + anchored_position=(10, 10), +) + +# shapes +square = Rect(x=20, y=70, width=40, height=40, fill=0x00DD00) +circle = Circle(50, 120, r=30, fill=0xDD00DD) +triangle = Triangle(50, 0, 100, 50, 0, 50, fill=0xDDDD00) +rectangle = Rect(x=80, y=80, width=100, height=50, fill=0x0000DD) + +triangle.x = 80 +triangle.y = 70 + +# add everything to their page groups +page_1_group.append(square) +page_1_group.append(page_1_lbl) +page_2_group.append(page_2_lbl) +page_2_group.append(circle) +page_3_group.append(page_3_lbl) +page_3_group.append(triangle) +page_4_group.append(page_4_lbl) +page_4_group.append(rectangle) + +# add the pages to the layout, supply your own page names +test_page_layout.add_content(page_1_group, "One") +test_page_layout.add_content(page_2_group, "Two") +test_page_layout.add_content(page_3_group, "Thr") +test_page_layout.add_content(page_4_group, "For") + +# add it to the group that is showing on the display +main_group.append(test_page_layout) + + +# add something new after the TabLayout was already created +another_text = Label( + terminalio.FONT, + text="And another thing!", + scale=2, + color=0x00FF00, + anchor_point=(0, 0), + anchored_position=(100, 100), +) +test_page_layout.showing_page_content.append(another_text) + +while True: + touch = ts.touch_point + if touch: + test_page_layout.handle_touch_events(touch) From 20a64dfcf5ca3b9c14684c9f380cb9ac34c711d2 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 6 May 2022 21:07:08 -0500 Subject: [PATCH 6/7] use showing instead of active throughout API --- .../layouts/tab_layout.py | 26 +++++++++---------- .../displayio_layout_tab_layout_simpletest.py | 6 ++--- .../displayio_layout_tab_layout_touchtest.py | 6 ++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/adafruit_displayio_layout/layouts/tab_layout.py b/adafruit_displayio_layout/layouts/tab_layout.py index 61c7d76..ffcea7b 100644 --- a/adafruit_displayio_layout/layouts/tab_layout.py +++ b/adafruit_displayio_layout/layouts/tab_layout.py @@ -50,14 +50,14 @@ class TabLayout(displayio.Group): :param Optional[Union[BuiltinFont, BDF, PCF]] custom_font: A pre-loaded font object to use for the tab labels :param str inactive_tab_spritesheet: Filepath of the spritesheet to show for inactive tabs. - :param str active_tab_spritesheet: Filepath of the spritesheet to show for the active tab. - :param Optional[int, tuple[int, int, int]] active_tab_text_color: Hex or tuple color to use + :param str showing_tab_spritesheet: Filepath of the spritesheet to show for the active tab. + :param Optional[int, tuple[int, int, int]] showing_tab_text_color: Hex or tuple color to use for the active tab label :param Optional[int, tuple[int, int, int]] inactive_tab_text_color: Hex or tuple color to use for inactive tab labels :param Optional[Union[int, tuple[int, int]]] inactive_tab_transparent_indexes: single index or tuple of multiple indexes to be made transparent in the inactive tab sprite palette. - :param Optional[Union[int, tuple[int, int]]] active_tab_transparent_indexes: single index + :param Optional[Union[int, tuple[int, int]]] showing_tab_transparent_indexes: single index or tuple of multiple indexes to be made transparent in the active tab sprite palette. :param int tab_count: How many tabs to draw in the layout. Positive whole numbers are valid. """ @@ -72,11 +72,11 @@ def __init__( tab_text_scale: int = 1, custom_font: Optional[Union[BuiltinFont, BDF, PCF]] = terminalio.FONT, inactive_tab_spritesheet: Optional[str] = None, - active_tab_spritesheet: Optional[str] = None, - active_tab_text_color: Optional[Union[int, Tuple[int, int, int]]] = 0x999999, + showing_tab_spritesheet: Optional[str] = None, + showing_tab_text_color: Optional[Union[int, Tuple[int, int, int]]] = 0x999999, inactive_tab_text_color: Optional[Union[int, Tuple[int, int, int]]] = 0xFFFFF, inactive_tab_transparent_indexes: Optional[Union[int, Tuple[int, int]]] = None, - active_tab_transparent_indexes: Optional[Union[int, Tuple[int, int]]] = None, + showing_tab_transparent_indexes: Optional[Union[int, Tuple[int, int]]] = None, tab_count: int = None, ): @@ -88,7 +88,7 @@ def __init__( display = board.DISPLAY if inactive_tab_spritesheet is None: raise AttributeError("Must pass active_tab_spritesheet") - if active_tab_spritesheet is None: + if showing_tab_spritesheet is None: raise AttributeError("Must pass inactive_tab_spritesheet") if tab_count is None: raise AttributeError("Must pass tab_count") @@ -96,16 +96,16 @@ def __init__( super().__init__(x=x, y=y) self.tab_count = tab_count self._active_bmp, self._active_palette = adafruit_imageload.load( - active_tab_spritesheet + showing_tab_spritesheet ) self._inactive_bmp, self._inactive_palette = adafruit_imageload.load( inactive_tab_spritesheet ) - if isinstance(active_tab_transparent_indexes, int): - self._active_palette.make_transparent(active_tab_transparent_indexes) - elif isinstance(active_tab_transparent_indexes, tuple): - for index in active_tab_transparent_indexes: + if isinstance(showing_tab_transparent_indexes, int): + self._active_palette.make_transparent(showing_tab_transparent_indexes) + elif isinstance(showing_tab_transparent_indexes, tuple): + for index in showing_tab_transparent_indexes: self._active_palette.make_transparent(index) else: raise AttributeError("active_tab_transparent_indexes must be int or tuple") @@ -122,7 +122,7 @@ def __init__( self.tab_height = self._active_bmp.height self.display = display - self.active_tab_text_color = active_tab_text_color + self.active_tab_text_color = showing_tab_text_color self.inactive_tab_text_color = inactive_tab_text_color self.custom_font = custom_font self.tab_text_scale = tab_text_scale diff --git a/examples/displayio_layout_tab_layout_simpletest.py b/examples/displayio_layout_tab_layout_simpletest.py index 6759f5e..20ad427 100644 --- a/examples/displayio_layout_tab_layout_simpletest.py +++ b/examples/displayio_layout_tab_layout_simpletest.py @@ -33,11 +33,11 @@ tab_text_scale=2, custom_font=font, inactive_tab_spritesheet="bmps/inactive_tab_sprite.bmp", - active_tab_spritesheet="bmps/active_tab_sprite.bmp", - active_tab_text_color=0x00AA59, + showing_tab_spritesheet="bmps/active_tab_sprite.bmp", + showing_tab_text_color=0x00AA59, inactive_tab_text_color=0xEEEEEE, inactive_tab_transparent_indexes=(0, 1), - active_tab_transparent_indexes=(0, 1), + showing_tab_transparent_indexes=(0, 1), tab_count=4, ) diff --git a/examples/displayio_layout_tab_layout_touchtest.py b/examples/displayio_layout_tab_layout_touchtest.py index d8cd5bf..47c5cbd 100644 --- a/examples/displayio_layout_tab_layout_touchtest.py +++ b/examples/displayio_layout_tab_layout_touchtest.py @@ -46,11 +46,11 @@ tab_text_scale=2, custom_font=font, inactive_tab_spritesheet="bmps/inactive_tab_sprite.bmp", - active_tab_spritesheet="bmps/active_tab_sprite.bmp", - active_tab_text_color=0x00AA59, + showing_tab_spritesheet="bmps/active_tab_sprite.bmp", + showing_tab_text_color=0x00AA59, inactive_tab_text_color=0xEEEEEE, inactive_tab_transparent_indexes=(0, 1), - active_tab_transparent_indexes=(0, 1), + showing_tab_transparent_indexes=(0, 1), tab_count=4, ) From adca2d7958807aa55f69ee53b5348a117d6b4284 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 9 May 2022 18:36:45 -0500 Subject: [PATCH 7/7] docs warning and error message for cp version --- adafruit_displayio_layout/layouts/tab_layout.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/adafruit_displayio_layout/layouts/tab_layout.py b/adafruit_displayio_layout/layouts/tab_layout.py index ffcea7b..e3ee2ca 100644 --- a/adafruit_displayio_layout/layouts/tab_layout.py +++ b/adafruit_displayio_layout/layouts/tab_layout.py @@ -42,6 +42,9 @@ class TabLayout(displayio.Group): """ A layout that organizes children into a grid table structure. + .. warning:: + Requires CircuitPython version 7.3.0-beta.2 or newer + :param int x: x location the layout should be placed. Pixel coordinates. :param int y: y location the layout should be placed. Pixel coordinates. :param displayio.Display display: The Display object to show the tab layout on. @@ -167,7 +170,15 @@ def _draw_tabs(self): _new_tab_group.append(_tab_label) if i == self.page_layout.showing_page_index: - _tab_tilegrid.bitmap = self._active_bmp + try: + _tab_tilegrid.bitmap = self._active_bmp + except AttributeError as e: + print(e) + raise ( + AttributeError( + "TabLayout requires CircuitPython version 7.3.0-beta.2 or newer." + ) + ) from e _tab_tilegrid.pixel_shader = self._active_palette _tab_label.color = self.active_tab_text_color self.tab_dict[i] = _new_tab_group