def __init__(self) -> None:
        self.root_group = displayio.Group()
        self._display = board.DISPLAY
        self._background_group = displayio.Group()
        self.root_group.append(self._background_group)
        self._text_group = displayio.Group()
        self.root_group.append(self._text_group)
        self._button_group = displayio.Group()
        self.root_group.append(self._button_group)

        if self._display.height > 250:
            self._text_group.scale = 2
            self._button_group.scale = 2

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, "AUDIO_OUT"):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, "SPEAKER"):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError("Board does not have an audio output!")

        self._background_file = None
        self._wavfile = None

        try:
            self._display.auto_brightness = False
        except AttributeError:
            pass
        self.backlight_fade(0)
        self._display.show(self.root_group)
        self.touchscreen = None
        self.mouse_cursor = None
        if hasattr(board, "TOUCH_XL"):
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(self._display.width, self._display.height),
            )
        elif hasattr(board, "BUTTON_CLOCK"):
            self.mouse_cursor = Cursor(self._display,
                                       display_group=self.root_group,
                                       cursor_speed=8)
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError("PYOA requires a touchscreen or cursor.")
        self._gamedirectory = None
        self._gamefilename = None
        self._game = None
        self._text = None
        self._background_sprite = None
        self._text_font = None
        self._left_button = None
        self._right_button = None
        self._middle_button = None
Пример #2
0
    def __init__(self):
        self.root_group = displayio.Group(max_size=15)

        self._background_group = displayio.Group(max_size=1)
        self.root_group.append(self._background_group)
        self._text_group = displayio.Group(max_size=1)
        self.root_group.append(self._text_group)
        self._button_group = displayio.Group(max_size=2)
        self.root_group.append(self._button_group)

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, 'AUDIO_OUT'):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, 'SPEAKER'):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError('Board does not have an audio output!')

        self._background_file = None
        self._wavfile = None

        board.DISPLAY.auto_brightness = False
        self.backlight_fade(0)
        board.DISPLAY.show(self.root_group)
        self.touchscreen = None
        if hasattr(board, 'TOUCH_XL'):
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(320, 240))
        elif hasattr(board, 'BUTTON_CLOCK'):
            self.mouse_cursor = Cursor(board.DISPLAY,
                                       display_group=self.root_group,
                                       cursor_speed=8)
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError('PYOA requires a touchscreen or cursor.')
        self._gamedirectory = None
        self._gamefilename = None
        self._game = None
        self._text = None
        self._background_sprite = None
        self._text_font = None
        self._left_button = None
        self._right_button = None
        self._middle_button = None
Пример #3
0
import time
import board
import displayio
from adafruit_cursorcontrol.cursorcontrol import Cursor
from adafruit_cursorcontrol.cursorcontrol_cursormanager import CursorManager

# Create the display
display = board.DISPLAY

# Create the display context
splash = displayio.Group(max_size=5)

# initialize the mouse cursor object
mouse_cursor = Cursor(display, display_group=splash)

# initialize the cursormanager
cursor = CursorManager(mouse_cursor)

# show displayio group
display.show(splash)

while True:
    cursor.update()
    if cursor.is_clicked:
        if mouse_cursor.hidden:
            mouse_cursor.show()
        else:
            mouse_cursor.hide()
    time.sleep(0.01)
Пример #4
0
    def __init__(self,
                 *,
                 url=None,
                 headers=None,
                 json_path=None,
                 regexp_path=None,
                 default_bg=0x000000,
                 status_neopixel=None,
                 text_font=None,
                 text_position=None,
                 text_color=0x808080,
                 text_wrap=False,
                 text_maxlen=0,
                 text_transform=None,
                 json_transform=None,
                 image_json_path=None,
                 image_resize=None,
                 image_position=None,
                 caption_text=None,
                 caption_font=None,
                 caption_position=None,
                 caption_color=0x808080,
                 image_url_path=None,
                 success_callback=None,
                 esp=None,
                 external_spi=None,
                 debug=False):

        self._debug = debug

        try:
            if hasattr(board, 'TFT_BACKLIGHT'):
                self._backlight = pulseio.PWMOut(board.TFT_BACKLIGHT)  # pylint: disable=no-member
            elif hasattr(board, 'TFT_LITE'):
                self._backlight = pulseio.PWMOut(board.TFT_LITE)  # pylint: disable=no-member
        except ValueError:
            self._backlight = None
        self.set_backlight(1.0)  # turn on backlight

        self._url = url
        self._headers = headers
        if json_path:
            if isinstance(json_path[0], (list, tuple)):
                self._json_path = json_path
            else:
                self._json_path = (json_path, )
        else:
            self._json_path = None

        self._regexp_path = regexp_path
        self._success_callback = success_callback

        if status_neopixel:
            self.neopix = neopixel.NeoPixel(status_neopixel, 1, brightness=0.2)
        else:
            self.neopix = None
        self.neo_status(0)

        try:
            os.stat(LOCALFILE)
            self._uselocal = True
        except OSError:
            self._uselocal = False

        if self._debug:
            print("Init display")
        self.splash = displayio.Group(max_size=15)

        if self._debug:
            print("Init background")
        self._bg_group = displayio.Group(max_size=1)
        self._bg_file = None
        self._default_bg = default_bg
        self.splash.append(self._bg_group)

        # show thank you and bootup file if available
        for bootscreen in ("/thankyou.bmp", "/pyportal_startup.bmp"):
            try:
                os.stat(bootscreen)
                board.DISPLAY.show(self.splash)
                for i in range(100, -1, -1):  # dim down
                    self.set_backlight(i / 100)
                    time.sleep(0.005)
                self.set_background(bootscreen)
                board.DISPLAY.wait_for_frame()
                for i in range(100):  # dim up
                    self.set_backlight(i / 100)
                    time.sleep(0.005)
                time.sleep(2)
            except OSError:
                pass  # they removed it, skip!

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, 'AUDIO_OUT'):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, 'SPEAKER'):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError('Board does not have a builtin speaker!')
        try:
            self.play_file("pyportal_startup.wav")
        except OSError:
            pass  # they deleted the file, no biggie!

        if esp:  # If there was a passed ESP Object
            if self._debug:
                print("Passed ESP32 to PyPortal")
            self._esp = esp
            if external_spi:  #If SPI Object Passed
                spi = external_spi
            else:  # Else: Make ESP32 connection
                spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
        else:
            if self._debug:
                print("Init ESP32")
            esp32_ready = DigitalInOut(board.ESP_BUSY)
            esp32_gpio0 = DigitalInOut(board.ESP_GPIO0)
            esp32_reset = DigitalInOut(board.ESP_RESET)
            esp32_cs = DigitalInOut(board.ESP_CS)
            spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

            self._esp = adafruit_esp32spi.ESP_SPIcontrol(
                spi, esp32_cs, esp32_ready, esp32_reset, esp32_gpio0)
        #self._esp._debug = 1
        for _ in range(3):  # retries
            try:
                print("ESP firmware:", self._esp.firmware_version)
                break
            except RuntimeError:
                print("Retrying ESP32 connection")
                time.sleep(1)
                self._esp.reset()
        else:
            raise RuntimeError("Was not able to find ESP32")
        requests.set_socket(socket, self._esp)

        if url and not self._uselocal:
            self._connect_esp()

        if self._debug:
            print("My IP address is",
                  self._esp.pretty_ip(self._esp.ip_address))

        # set the default background
        self.set_background(self._default_bg)
        board.DISPLAY.show(self.splash)

        if self._debug:
            print("Init SD Card")
        sd_cs = DigitalInOut(board.SD_CS)
        self._sdcard = None
        try:
            self._sdcard = adafruit_sdcard.SDCard(spi, sd_cs)
            vfs = storage.VfsFat(self._sdcard)
            storage.mount(vfs, "/sd")
        except OSError as error:
            print("No SD card found:", error)

        self._qr_group = None
        # Tracks whether we've hidden the background when we showed the QR code.
        self._qr_only = False

        if self._debug:
            print("Init caption")
        self._caption = None
        if caption_font:
            self._caption_font = bitmap_font.load_font(caption_font)
        self.set_caption(caption_text, caption_position, caption_color)

        if text_font:
            if isinstance(text_position[0], (list, tuple)):
                num = len(text_position)
                if not text_wrap:
                    text_wrap = [0] * num
                if not text_maxlen:
                    text_maxlen = [0] * num
                if not text_transform:
                    text_transform = [None] * num
            else:
                num = 1
                text_position = (text_position, )
                text_color = (text_color, )
                text_wrap = (text_wrap, )
                text_maxlen = (text_maxlen, )
                text_transform = (text_transform, )
            self._text = [None] * num
            self._text_color = [None] * num
            self._text_position = [None] * num
            self._text_wrap = [None] * num
            self._text_maxlen = [None] * num
            self._text_transform = [None] * num
            self._text_font = bitmap_font.load_font(text_font)
            if self._debug:
                print("Loading font glyphs")
            # self._text_font.load_glyphs(b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            #                             b'0123456789:/-_,. ')
            gc.collect()

            for i in range(num):
                if self._debug:
                    print("Init text area", i)
                self._text[i] = None
                self._text_color[i] = text_color[i]
                self._text_position[i] = text_position[i]
                self._text_wrap[i] = text_wrap[i]
                self._text_maxlen[i] = text_maxlen[i]
                self._text_transform[i] = text_transform[i]
        else:
            self._text_font = None
            self._text = None

        # Add any JSON translators
        self._json_transform = []
        if json_transform:
            if callable(json_transform):
                self._json_transform.append(json_transform)
            else:
                self._json_transform.extend(filter(callable, json_transform))

        self._image_json_path = image_json_path
        self._image_url_path = image_url_path
        self._image_resize = image_resize
        self._image_position = image_position
        if image_json_path or image_url_path:
            if self._debug:
                print("Init image path")
            if not self._image_position:
                self._image_position = (0, 0)  # default to top corner
            if not self._image_resize:
                self._image_resize = (320, 240)  # default to full screen
        if hasattr(board, 'TOUCH_XL'):
            if self._debug:
                print("Init touchscreen")
            # pylint: disable=no-member
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(320, 240))
            # pylint: enable=no-member

            self.set_backlight(1.0)  # turn on backlight
        elif hasattr(board, 'BUTTON_CLOCK'):
            if self._debug:
                print("Init cursor")
            self.mouse_cursor = Cursor(board.DISPLAY,
                                       display_group=self.splash,
                                       cursor_speed=8)
            self.mouse_cursor.hide()
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError(
                'PyPortal module requires either a touchscreen or gamepad.')

        gc.collect()
Пример #5
0
    def __init__(self, spi, display, splash_group, debug=False):
        # Speaker Enable
        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)

        self._display = display

        if hasattr(board, "AUDIO_OUT"):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, "SPEAKER"):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError("Board does not have a builtin speaker!")

        if debug:
            print("Init SD Card")
        sd_cs = board.SD_CS
        if not NATIVE_SD:
            sd_cs = DigitalInOut(sd_cs)
        self._sdcard = None

        try:
            self._sdcard = sdcardio.SDCard(spi, sd_cs)
            vfs = storage.VfsFat(self._sdcard)
            storage.mount(vfs, "/sd")
        except OSError as error:
            print("No SD card found:", error)

        try:
            if hasattr(board, "TFT_BACKLIGHT"):
                self._backlight = pulseio.PWMOut(board.TFT_BACKLIGHT)  # pylint: disable=no-member
            elif hasattr(board, "TFT_LITE"):
                self._backlight = pulseio.PWMOut(board.TFT_LITE)  # pylint: disable=no-member
        except ValueError:
            self._backlight = None
        self.set_backlight(1.0)  # turn on backlight
        # pylint: disable=import-outside-toplevel
        if hasattr(board, "TOUCH_XL"):
            import adafruit_touchscreen

            if debug:
                print("Init touchscreen")
            # pylint: disable=no-member
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(board.DISPLAY.width, board.DISPLAY.height),
            )
            # pylint: enable=no-member

            self.set_backlight(1.0)  # turn on backlight
        elif hasattr(board, "BUTTON_CLOCK"):
            from adafruit_cursorcontrol.cursorcontrol import Cursor
            from adafruit_cursorcontrol.cursorcontrol_cursormanager import CursorManager

            if debug:
                print("Init cursor")
            self.mouse_cursor = Cursor(board.DISPLAY,
                                       display_group=splash_group,
                                       cursor_speed=8)
            self.mouse_cursor.hide()
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError(
                "PyPortal module requires either a touchscreen or gamepad.")
        # pylint: enable=import-outside-toplevel

        gc.collect()
Пример #6
0
class PYOA_Graphics:
    # pylint: disable=too-many-instance-attributes
    """A choose your own adventure game framework."""
    def __init__(self) -> None:
        self.root_group = displayio.Group()
        self._display = board.DISPLAY
        self._background_group = displayio.Group()
        self.root_group.append(self._background_group)
        self._text_group = displayio.Group()
        self.root_group.append(self._text_group)
        self._button_group = displayio.Group()
        self.root_group.append(self._button_group)

        if self._display.height > 250:
            self._text_group.scale = 2
            self._button_group.scale = 2

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, "AUDIO_OUT"):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, "SPEAKER"):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError("Board does not have an audio output!")

        self._background_file = None
        self._wavfile = None

        self._display.auto_brightness = False
        self.backlight_fade(0)
        self._display.show(self.root_group)
        self.touchscreen = None
        self.mouse_cursor = None
        if hasattr(board, "TOUCH_XL"):
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(self._display.width, self._display.height),
            )
        elif hasattr(board, "BUTTON_CLOCK"):
            self.mouse_cursor = Cursor(self._display,
                                       display_group=self.root_group,
                                       cursor_speed=8)
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError("PYOA requires a touchscreen or cursor.")
        self._gamedirectory = None
        self._gamefilename = None
        self._game = None
        self._text = None
        self._background_sprite = None
        self._text_font = None
        self._left_button = None
        self._right_button = None
        self._middle_button = None

    def load_game(self, game_directory: str) -> None:
        """Load a game.

        :param str game_directory: where the game files are stored
        """
        self._gamedirectory = game_directory
        self._text_font = terminalio.FONT
        # Possible Screen Sizes are:
        # 320x240 PyPortal and PyPortal Pynt
        # 160x128 PyBadge and PyGamer
        # 480x320 PyPortal Titano
        # 240x240 if we wanted to use HalloWing M4

        # Button Attributes
        btn_left = 10
        btn_right = btn_left + 180
        btn_mid = btn_left + 90
        button_y = 195
        button_width = 120
        button_height = 40
        if self._display.height < 200:
            button_y //= 2
            button_y += 10
            button_width //= 2
            button_height //= 2
            btn_right //= 2
            btn_mid //= 2
        elif self._display.height > 250:
            button_y = (button_y * 3) // 4
            button_y -= 20
            button_width = (button_width * 3) // 4
            button_height = (button_height * 3) // 4
            btn_right = (btn_right * 3) // 4
            btn_mid = (btn_right * 3) // 4
        self._left_button = Button(
            x=btn_left,
            y=button_y,
            width=button_width,
            height=button_height,
            label="Left",
            label_font=self._text_font,
            style=Button.SHADOWROUNDRECT,
        )
        self._right_button = Button(
            x=btn_right,
            y=button_y,
            width=button_width,
            height=button_height,
            label="Right",
            label_font=self._text_font,
            style=Button.SHADOWROUNDRECT,
        )
        self._middle_button = Button(
            x=btn_mid,
            y=button_y,
            width=button_width,
            height=button_height,
            label="Middle",
            label_font=self._text_font,
            style=Button.SHADOWROUNDRECT,
        )
        self._gamefilename = game_directory + "/cyoa.json"
        try:
            with open(  # pylint: disable=unspecified-encoding
                    self._gamefilename, "r") as game_file:
                self._game = json.load(game_file)
        except OSError as err:
            raise OSError("Could not open game file " +
                          self._gamefilename) from err

    def _fade_to_black(self) -> None:
        """Turn down the lights."""
        if self.mouse_cursor:
            self.mouse_cursor.is_hidden = True
        self.backlight_fade(0)
        # turn off background so we can render the text
        self.set_background(None, with_fade=False)
        self.set_text(None, None)
        for _ in range(len(self._button_group)):
            self._button_group.pop()
        if self.mouse_cursor:
            self.mouse_cursor.is_hidden = False

    def _display_buttons(self, card: Dict[str, str]) -> None:
        """Display the buttons of a card.

        :param card: The active card
        :type card: dict(str, str)
        """
        button01_text = card.get("button01_text", None)
        button02_text = card.get("button02_text", None)
        self._left_button.label = button01_text
        self._middle_button.label = button01_text
        self._right_button.label = button02_text
        if button01_text and not button02_text:
            # show only middle button
            self._button_group.append(self._middle_button)
        if button01_text and button02_text:
            self._button_group.append(self._right_button)
            self._button_group.append(self._left_button)

    def _display_background_for(self, card: Dict[str, str]) -> None:
        """If there's a background on card, display it.

        :param card: The active card
        :type card: dict(str, str)
        """
        self.set_background(card.get("background_image", None),
                            with_fade=False)

    def _display_text_for(self, card: Dict[str, str]) -> None:
        """Display the main text of a card.

        :param card: The active card
        :type card: dict(str, str)
        """
        text = card.get("text", None)
        text_color = card.get("text_color", 0x0)  # default to black
        text_background_color = card.get("text_background_color", None)
        if text:
            try:
                text_color = int(
                    text_color)  # parse the JSON string to hex int
            except ValueError:
                text_color = 0x0

            try:
                text_background_color = int(
                    text_background_color)  # parse the JSON string to hex int
            except ValueError:
                text_background_color = None
            except TypeError:
                text_background_color = None

            self.set_text(text,
                          text_color,
                          background_color=text_background_color)

    def _play_sound_for(self, card: Dict[str, str]) -> None:
        """If there's a sound, start playing it.

        :param card: The active card
        :type card: dict(str, str)
        """
        sound = card.get("sound", None)
        loop = card.get("sound_repeat", False)
        if sound:
            loop = loop == "True"
            print("Loop:", loop)
            self.play_sound(sound, wait_to_finish=False, loop=loop)

    def _wait_for_press(self, card: Dict[str, str]) -> str:
        """Wait for a button to be pressed.

        :param card: The active card
        :type card: dict(str, str)
        :return: The id of the destination card
        :rtype: str
        """
        button01_text = card.get("button01_text", None)
        button02_text = card.get("button02_text", None)
        point_touched = None
        while True:
            if self.touchscreen is not None:
                point_touched = self.touchscreen.touch_point
            else:
                self.cursor.update()
                if self.cursor.is_clicked is True:
                    point_touched = self.mouse_cursor.x, self.mouse_cursor.y
            if point_touched is not None:
                point_touched = (
                    point_touched[0] // self._button_group.scale,
                    point_touched[1] // self._button_group.scale,
                )
                print("touch: ", point_touched)
                if button01_text and not button02_text:
                    # showing only middle button
                    if self._middle_button.contains(point_touched):
                        print("Middle button")
                        return card.get("button01_goto_card_id", None)
                if button01_text and button02_text:
                    if self._left_button.contains(point_touched):
                        print("Left button")
                        return card.get("button01_goto_card_id", None)
                    if self._right_button.contains(point_touched):
                        print("Right button")
                        return card.get("button02_goto_card_id", None)
            time.sleep(0.1)

    def display_card(self, card_num: int) -> int:
        """Display and handle input on a card.

        :param int card_num: the index of the card to process
        :return: the card number of the selected card
        :rtype: int
        """
        card = self._game[card_num]
        print(card)
        print("*" * 32)
        print("****{:^24s}****".format(card["card_id"]))
        print("*" * 32)

        self._fade_to_black()
        self._display_buttons(card)
        self._display_background_for(card)
        self.backlight_fade(1.0)
        self._display_text_for(card)
        self._display.refresh(target_frames_per_second=60)

        self._play_sound_for(card)

        auto_adv = card.get("auto_advance", None)
        if auto_adv is not None:
            auto_adv = float(auto_adv)
            print("Auto advancing after %0.1f seconds" % auto_adv)
            time.sleep(auto_adv)
            return card_num + 1

        destination_card_id = self._wait_for_press(card)

        self.play_sound(None)  # stop playing any sounds
        for card_number, card_struct in enumerate(self._game):
            if card_struct.get("card_id", None) == destination_card_id:
                return card_number  # found the matching card!
        # eep if we got here something went wrong
        raise RuntimeError("Could not find card with matching 'card_id': ",
                           destination_card_id)

    def play_sound(self,
                   filename: Optional[str],
                   *,
                   wait_to_finish: bool = True,
                   loop: bool = False) -> None:
        """Play a sound

        :param filename: The filename of the sound to play. Use `None` to stop
            playing anything.
        :type filename: str or None
        :param bool wait_to_finish: Whether playing the sound should block
        :param bool loop: Whether the sound should loop
        """
        self._speaker_enable.value = False
        self.audio.stop()
        if self._wavfile:
            self._wavfile.close()
            self._wavfile = None

        if not filename:
            return  # nothing more to do, just stopped
        filename = self._gamedirectory + "/" + filename
        print("Playing sound", filename)
        self._display.refresh(target_frames_per_second=60)
        try:
            self._wavfile = open(filename, "rb")  # pylint: disable=consider-using-with
        except OSError as err:
            raise OSError("Could not locate sound file", filename) from err

        wavedata = audiocore.WaveFile(self._wavfile)
        self._speaker_enable.value = True
        self.audio.play(wavedata, loop=loop)
        if loop or not wait_to_finish:
            return
        while self.audio.playing:
            pass
        self._wavfile.close()
        self._wavfile = None
        self._speaker_enable.value = False

    def set_text(
        self,
        text: Optional[str],
        color: Optional[str],
        background_color: Optional[int] = None,
    ) -> None:
        """Display the test for a card.

        :param text: the text to display
        :type text: str or None
        :param color: the text color
        :type color: str or None
        :param background_color: the background color of the text
        :type background_color: int or None
        """
        if self._text_group:
            self._text_group.pop()
        if not text or not color:
            return  # nothing to do!
        text_wrap = 37
        if self._display.height < 130:
            text_wrap = 25
        text = self.wrap_nicely(text, text_wrap)
        text = "\n".join(text)
        print("Set text to", text, "with color", hex(color))
        text_x = 8
        text_y = 95
        if self._display.height < 130:
            text_x = 3
            text_y = 38
        elif self._display.height > 250:
            text_y = 50
        if text:
            self._text = Label(self._text_font, text=str(text))
            self._text.x = text_x
            self._text.y = text_y
            self._text.color = color
            if background_color:
                self._text.background_color = background_color
            self._text_group.append(self._text)

    def set_background(self,
                       filename: Optional[str],
                       *,
                       with_fade: bool = True) -> None:
        """The background image to a bitmap file.

        :param filename: The filename of the chosen background
        :type filename: str or None
        :param bool with_fade: If `True` fade out the backlight while loading the new background
            and fade in the backlight once completed.
        """
        print("Set background to", filename)
        if with_fade:
            self.backlight_fade(0)
        if self._background_group:
            self._background_group.pop()

        if filename:
            background = displayio.OnDiskBitmap(self._gamedirectory + "/" +
                                                filename)
            self._background_sprite = displayio.TileGrid(
                background,
                pixel_shader=background.pixel_shader,
            )
            self._background_group.append(self._background_sprite)
        if with_fade:
            self._display.refresh(target_frames_per_second=60)
            self.backlight_fade(1.0)

    def backlight_fade(self, to_light: float) -> None:
        """
        Adjust the TFT backlight. Fade from the current value to the ``to_light`` value

        :param float to_light: the desired backlight brightness between :py:const:`0.0` and
            :py:const:`1.0`.
        """
        from_light = self._display.brightness
        from_light = int(from_light * 100)
        to_light = max(0, min(100, int(to_light * 100)))
        delta = 1
        if from_light > to_light:
            delta = -1
        for val in range(from_light, to_light, delta):
            self._display.brightness = val / 100
            time.sleep(0.003)
        self._display.brightness = to_light / 100

    # return a list of lines with wordwrapping
    @staticmethod
    def wrap_nicely(string: str, max_chars: int) -> List[str]:
        """A helper that will return a list of lines with word-break wrapping.

        :param str string: The text to be wrapped.
        :param int max_chars: The maximum number of characters on a line before wrapping.
        :return: The list of lines
        :rtype: list(str)
        """
        # string = string.replace('\n', '').replace('\r', '') # strip confusing newlines
        words = string.split(" ")
        the_lines = []
        the_line = ""
        for w in words:
            if "\n" in w:
                _w1, _w2 = w.split("\n")
                the_line += " " + _w1
                the_lines.append(the_line)
                the_line = _w2
            elif len(the_line + " " + w) > max_chars:
                the_lines.append(the_line)
                the_line = "" + w
            else:
                the_line += " " + w
        if the_line:  # last line remaining
            the_lines.append(the_line)
        # remove first space from first line:
        the_lines[0] = the_lines[0][1:]
        return the_lines
Пример #7
0
class PYOA_Graphics():
    """A choose your own adventure game framework."""
    def __init__(self):
        self.root_group = displayio.Group(max_size=15)

        self._background_group = displayio.Group(max_size=1)
        self.root_group.append(self._background_group)
        self._text_group = displayio.Group(max_size=1)
        self.root_group.append(self._text_group)
        self._button_group = displayio.Group(max_size=2)
        self.root_group.append(self._button_group)

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, 'AUDIO_OUT'):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, 'SPEAKER'):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError('Board does not have an audio output!')

        self._background_file = None
        self._wavfile = None

        board.DISPLAY.auto_brightness = False
        self.backlight_fade(0)
        board.DISPLAY.show(self.root_group)
        self.touchscreen = None
        if hasattr(board, 'TOUCH_XL'):
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(320, 240))
        elif hasattr(board, 'BUTTON_CLOCK'):
            self.mouse_cursor = Cursor(board.DISPLAY,
                                       display_group=self.root_group,
                                       cursor_speed=8)
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError('PYOA requires a touchscreen or cursor.')
        self._gamedirectory = None
        self._gamefilename = None
        self._game = None
        self._text = None
        self._background_sprite = None
        self._text_font = None
        self._left_button = None
        self._right_button = None
        self._middle_button = None

    def load_game(self, game_directory):
        """Load a game.

        :param game_directory: where the game files are stored
        """
        self._gamedirectory = game_directory
        self._text_font = terminalio.FONT
        # Button Attributes
        btn_left = 10
        btn_right = btn_left + 180
        btn_mid = btn_left + 90
        button_y = 195
        button_width = 120
        button_height = 40
        if board.DISPLAY.height < 200:
            button_y /= 2
            button_y += 10
            button_width /= 2
            button_height /= 2
            btn_right /= 2
            btn_mid /= 2
        self._left_button = Button(x=int(btn_left),
                                   y=int(button_y),
                                   width=int(button_width),
                                   height=int(button_height),
                                   label="Left",
                                   label_font=self._text_font,
                                   style=Button.SHADOWROUNDRECT)
        self._right_button = Button(x=int(btn_right),
                                    y=int(button_y),
                                    width=int(button_width),
                                    height=int(button_height),
                                    label="Right",
                                    label_font=self._text_font,
                                    style=Button.SHADOWROUNDRECT)
        self._middle_button = Button(x=int(btn_mid),
                                     y=int(button_y),
                                     width=int(button_width),
                                     height=int(button_height),
                                     label="Middle",
                                     label_font=self._text_font,
                                     style=Button.SHADOWROUNDRECT)
        self._gamefilename = game_directory + "/cyoa.json"
        try:
            game_file = open(self._gamefilename, "r")
        except OSError:
            raise OSError("Could not open game file " + self._gamefilename)
        self._game = json.load(game_file)
        game_file.close()

    def _fade_to_black(self):
        """Turn down the lights."""
        self.mouse_cursor.is_hidden = True
        self.backlight_fade(0)
        # turn off background so we can render the text
        self.set_background(None, with_fade=False)
        self.set_text(None, None)
        for _ in range(len(self._button_group)):
            self._button_group.pop()
        self.mouse_cursor.is_hidden = False

    def _display_buttons(self, card):
        """Display the buttons of a card.

        :param card: The active card
        """
        button01_text = card.get('button01_text', None)
        button02_text = card.get('button02_text', None)
        self._left_button.label = button01_text
        self._middle_button.label = button01_text
        self._right_button.label = button02_text
        if button01_text and not button02_text:
            # show only middle button
            self._button_group.append(self._middle_button.group)
        if button01_text and button02_text:
            self._button_group.append(self._right_button.group)
            self._button_group.append(self._left_button.group)

    def _display_background_for(self, card):
        """If there's a background on card, display it.

        :param card: The active card
        """
        self.set_background(card.get('background_image', None),
                            with_fade=False)

    def _display_text_for(self, card):
        """Display the main text of a card.

        :param card: The active card
        """
        text = card.get('text', None)
        text_color = card.get('text_color', 0x0)  # default to black
        if text:
            try:
                text_color = int(
                    text_color)  # parse the JSON string to hex int
            except ValueError:
                text_color = 0x0
            self.set_text(text, text_color)

    def _play_sound_for(self, card):
        """If there's a sound, start playing it.

        :param card: The active card
        """
        sound = card.get('sound', None)
        loop = card.get('sound_repeat', False)
        if sound:
            loop = loop == "True"
            print("Loop:", loop)
            self.play_sound(sound, wait_to_finish=False, loop=loop)

    def _wait_for_press(self, card):
        """Wait for a button to be pressed.

        :param card: The active card

        Return the id of the destination card.
        """
        button01_text = card.get('button01_text', None)
        button02_text = card.get('button02_text', None)
        point_touched = None
        while True:
            if self.touchscreen is not None:
                point_touched = self.touchscreen.touch_point
            else:
                self.cursor.update()
                if self.cursor.is_clicked is True:
                    point_touched = self.mouse_cursor.x, self.mouse_cursor.y
            if point_touched is not None:
                print("touch: ", point_touched)
                if button01_text and not button02_text:
                    # showing only middle button
                    if self._middle_button.contains(point_touched):
                        print("Middle button")
                        return card.get('button01_goto_card_id', None)
                if button01_text and button02_text:
                    if self._left_button.contains(point_touched):
                        print("Left button")
                        return card.get('button01_goto_card_id', None)
                    if self._right_button.contains(point_touched):
                        print("Right button")
                        return card.get('button02_goto_card_id', None)
            time.sleep(0.1)

    def display_card(self, card_num):
        """Display and handle input on a card.

        :param card_num: the index of the card to process
        """
        card = self._game[card_num]
        print(card)
        print("*" * 32)
        print('****{:^24s}****'.format(card['card_id']))
        print("*" * 32)

        self._fade_to_black()
        self._display_buttons(card)
        self._display_background_for(card)
        self.backlight_fade(1.0)
        self._display_text_for(card)

        board.DISPLAY.refresh_soon()
        board.DISPLAY.wait_for_frame()

        self._play_sound_for(card)

        auto_adv = card.get('auto_advance', None)
        if auto_adv is not None:
            auto_adv = float(auto_adv)
            print("Auto advancing after %0.1f seconds" % auto_adv)
            time.sleep(auto_adv)
            return card_num + 1

        destination_card_id = self._wait_for_press(card)

        self.play_sound(None)  # stop playing any sounds
        for card_number, card_struct in enumerate(self._game):
            if card_struct.get('card_id', None) == destination_card_id:
                return card_number  # found the matching card!
        # eep if we got here something went wrong
        raise RuntimeError("Could not find card with matching 'card_id': ",
                           destination_card_id)

    def play_sound(self, filename, *, wait_to_finish=True, loop=False):
        """Play a sound

        :param filename: The filename of the sound to play
        :param wait_to_finish: Whether playing the sound should block
        :param loop: Whether the sound should loop
        """
        self._speaker_enable.value = False
        self.audio.stop()
        if self._wavfile:
            self._wavfile.close()
            self._wavfile = None

        if not filename:
            return  # nothing more to do, just stopped
        filename = self._gamedirectory + "/" + filename
        print("Playing sound", filename)
        board.DISPLAY.wait_for_frame()
        try:
            self._wavfile = open(filename, "rb")
        except OSError:
            raise OSError("Could not locate sound file", filename)

        wavedata = audioio.WaveFile(self._wavfile)
        self._speaker_enable.value = True
        self.audio.play(wavedata, loop=loop)
        if loop or not wait_to_finish:
            return
        while self.audio.playing:
            pass
        self._wavfile.close()
        self._wavfile = None
        self._speaker_enable.value = False

    def set_text(self, text, color):
        """Display the test for a card.

        :param text: the text to display
        :param color: the text color

        """
        if self._text_group:
            self._text_group.pop()
        if not text or not color:
            return  # nothing to do!
        text_wrap = 37
        if board.DISPLAY.height < 130:
            text_wrap = 25
        text = self.wrap_nicely(text, text_wrap)
        text = '\n'.join(text)
        print("Set text to", text, "with color", hex(color))
        text_x = 8
        text_y = 95
        if board.DISPLAY.height < 130:
            text_y = 38
            text_x = 3
        if text:
            self._text = Label(self._text_font, text=str(text))
            self._text.x = text_x
            self._text.y = text_y
            self._text.color = color
            self._text_group.append(self._text)

    def set_background(self, filename, *, with_fade=True):
        """The background image to a bitmap file.

        :param filename: The filename of the chosen background
        """
        print("Set background to", filename)
        if with_fade:
            self.backlight_fade(0)
        if self._background_group:
            self._background_group.pop()

        if filename:
            if self._background_file:
                self._background_file.close()
            self._background_file = open(self._gamedirectory + "/" + filename,
                                         "rb")
            background = displayio.OnDiskBitmap(self._background_file)
            self._background_sprite = displayio.TileGrid(
                background, pixel_shader=displayio.ColorConverter(), x=0, y=0)
            self._background_group.append(self._background_sprite)
        if with_fade:
            board.DISPLAY.refresh_soon()
            board.DISPLAY.wait_for_frame()
            self.backlight_fade(1.0)

    def backlight_fade(self, to_light):
        """Adjust the TFT backlight. Fade from one value to another
        """
        from_light = board.DISPLAY.brightness
        from_light = int(from_light * 100)
        to_light = max(0, min(1.0, to_light))
        to_light = int(to_light * 100)
        delta = 1
        if from_light > to_light:
            delta = -1
        for val in range(from_light, to_light, delta):
            board.DISPLAY.brightness = val / 100
            time.sleep(0.003)
        board.DISPLAY.brightness = to_light / 100

    # return a list of lines with wordwrapping
    #pylint: disable=invalid-name
    @staticmethod
    def wrap_nicely(string, max_chars):
        """A helper that will return a list of lines with word-break wrapping.

        :param str string: The text to be wrapped.
        :param int max_chars: The maximum number of characters on a line before wrapping.

        """
        #string = string.replace('\n', '').replace('\r', '') # strip confusing newlines
        words = string.split(' ')
        the_lines = []
        the_line = ""
        for w in words:
            if '\n' in w:
                w1, w2 = w.split('\n')
                the_line += ' ' + w1
                the_lines.append(the_line)
                the_line = w2
            elif len(the_line + ' ' + w) > max_chars:
                the_lines.append(the_line)
                the_line = '' + w
            else:
                the_line += ' ' + w
        if the_line:  # last line remaining
            the_lines.append(the_line)
        # remove first space from first line:
        the_lines[0] = the_lines[0][1:]
        return the_lines
    def __init__(self,
                 timezone="Pacific",
                 hour_24_12=False,
                 auto_dst=True,
                 alarm=False,
                 brightness=1.0,
                 debug=False):
        #input parameters
        self._timezone = timezone
        self._hour_24_12 = hour_24_12
        self._dst = False
        self._auto_dst = auto_dst
        self._alarm = alarm
        self._brightness = brightness
        self._cursor_scale = 1  # smallest cursor size
        self._cursor_show = True  # show cursor

        self._weekday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        self._month = [
            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
            "Oct", "Nov", "Dec"
        ]

        # Load the text font from the fonts folder
        self._font_0 = bitmap_font.load_font("/fonts/OpenSans-9.bdf")
        self._font_1 = bitmap_font.load_font("/fonts/Helvetica-Bold-36.bdf")

        # The board's integral display size
        WIDTH = board.DISPLAY.width  # 160 for PyGamer and PyBadge
        HEIGHT = board.DISPLAY.height  # 128 for PyGamer and PyBadge

        ELEMENT_SIZE = WIDTH // 4  # Size of element_grid blocks in pixels

        board.DISPLAY.brightness = self._brightness

        # Default colors
        BLACK = 0x000000
        RED = 0xFF0000
        ORANGE = 0xFF8811
        YELLOW = 0xFFFF00
        GREEN = 0x00FF00
        CYAN = 0x00FFFF
        BLUE = 0x0000FF
        VIOLET = 0x9900FF
        DK_VIO = 0x110022
        WHITE = 0xFFFFFF
        GRAY = 0x444455

        ### Define the display group ###
        self._image_group = displayio.Group(max_size=12)

        ### Fill the display group
        # Create a background color fill layer; image_group[0]
        self._color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
        self._color_palette = displayio.Palette(1)
        self._color_palette[0] = DK_VIO
        self._background = displayio.TileGrid(self._color_bitmap,
                                              pixel_shader=self._color_palette,
                                              x=0,
                                              y=0)
        self._image_group.append(self._background)

        # Define labels and values using element grid coordinates
        self._clock_digits = Label(self._font_1,
                                   text="06:23",
                                   color=WHITE,
                                   max_glyphs=5)
        self._clock_digits.x = 30
        self._clock_digits.y = HEIGHT // 2
        self._image_group.append(self._clock_digits)  # image_group[xx]

        self._clock_daydate = Label(self._font_0,
                                    text="Wed 02/05/2020",
                                    color=YELLOW,
                                    max_glyphs=16)
        self._clock_daydate.x = 27
        self._clock_daydate.y = 25
        self._image_group.append(self._clock_daydate)  # image_group[xx]

        self._clock_ampm = Label(self._font_0,
                                 text="PM",
                                 color=WHITE,
                                 max_glyphs=2)
        self._clock_ampm.x = 130
        self._clock_ampm.y = (HEIGHT // 2) - 8
        self._image_group.append(self._clock_ampm)  # image_group[xx]

        self._clock_dst = Label(self._font_0,
                                text="PST",
                                color=VIOLET,
                                max_glyphs=3)
        self._clock_dst.x = 130
        self._clock_dst.y = (HEIGHT // 2) + 8
        self._image_group.append(self._clock_dst)  # image_group[xx]

        self._clock_auto_dst = Label(self._font_0,
                                     text="AutoDST",
                                     color=VIOLET,
                                     max_glyphs=7)
        self._clock_auto_dst.x = 105
        self._clock_auto_dst.y = HEIGHT - 8
        self._image_group.append(self._clock_auto_dst)  # image_group[xx]

        self._clock_alarm = Label(self._font_0,
                                  text="ALARM",
                                  color=ORANGE,
                                  max_glyphs=5)
        self._clock_alarm.x = 5
        self._clock_alarm.y = HEIGHT - 8
        self._image_group.append(self._clock_alarm)  # image_group[xx]

        self._clock_name = Label(self._font_0,
                                 text="Clock_Builder",
                                 color=BLUE,
                                 max_glyphs=14)
        self._clock_name.x = 40
        self._clock_name.y = HEIGHT - 24
        self._image_group.append(self._clock_name)  # image_group[xx]

        # Initialize the cursor object and cursor CursorManager
        self._mouse_cursor = Cursor(board.DISPLAY,
                                    display_group=self._image_group)
        self._cursor = CursorManager(self._mouse_cursor)
        self._mouse_cursor.scale = self._cursor_scale

        if self._cursor_show and self._mouse_cursor.hidden:
            self._mouse_cursor.show()
        if not self._cursor_show and not self._mouse_cursor.hidden:
            self._mouse_cursor.hide()
        self._cursor.update()  # refresh the cursor

        # debug parameters
        self._debug = debug
        if self._debug:
            print("*Init:", self.__class__)
            print("*Init: ", self.__dict__)
class DisplayioDisplay:
    def __init__(self,
                 timezone="Pacific",
                 hour_24_12=False,
                 auto_dst=True,
                 alarm=False,
                 brightness=1.0,
                 debug=False):
        #input parameters
        self._timezone = timezone
        self._hour_24_12 = hour_24_12
        self._dst = False
        self._auto_dst = auto_dst
        self._alarm = alarm
        self._brightness = brightness
        self._cursor_scale = 1  # smallest cursor size
        self._cursor_show = True  # show cursor

        self._weekday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        self._month = [
            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
            "Oct", "Nov", "Dec"
        ]

        # Load the text font from the fonts folder
        self._font_0 = bitmap_font.load_font("/fonts/OpenSans-9.bdf")
        self._font_1 = bitmap_font.load_font("/fonts/Helvetica-Bold-36.bdf")

        # The board's integral display size
        WIDTH = board.DISPLAY.width  # 160 for PyGamer and PyBadge
        HEIGHT = board.DISPLAY.height  # 128 for PyGamer and PyBadge

        ELEMENT_SIZE = WIDTH // 4  # Size of element_grid blocks in pixels

        board.DISPLAY.brightness = self._brightness

        # Default colors
        BLACK = 0x000000
        RED = 0xFF0000
        ORANGE = 0xFF8811
        YELLOW = 0xFFFF00
        GREEN = 0x00FF00
        CYAN = 0x00FFFF
        BLUE = 0x0000FF
        VIOLET = 0x9900FF
        DK_VIO = 0x110022
        WHITE = 0xFFFFFF
        GRAY = 0x444455

        ### Define the display group ###
        self._image_group = displayio.Group(max_size=12)

        ### Fill the display group
        # Create a background color fill layer; image_group[0]
        self._color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
        self._color_palette = displayio.Palette(1)
        self._color_palette[0] = DK_VIO
        self._background = displayio.TileGrid(self._color_bitmap,
                                              pixel_shader=self._color_palette,
                                              x=0,
                                              y=0)
        self._image_group.append(self._background)

        # Define labels and values using element grid coordinates
        self._clock_digits = Label(self._font_1,
                                   text="06:23",
                                   color=WHITE,
                                   max_glyphs=5)
        self._clock_digits.x = 30
        self._clock_digits.y = HEIGHT // 2
        self._image_group.append(self._clock_digits)  # image_group[xx]

        self._clock_daydate = Label(self._font_0,
                                    text="Wed 02/05/2020",
                                    color=YELLOW,
                                    max_glyphs=16)
        self._clock_daydate.x = 27
        self._clock_daydate.y = 25
        self._image_group.append(self._clock_daydate)  # image_group[xx]

        self._clock_ampm = Label(self._font_0,
                                 text="PM",
                                 color=WHITE,
                                 max_glyphs=2)
        self._clock_ampm.x = 130
        self._clock_ampm.y = (HEIGHT // 2) - 8
        self._image_group.append(self._clock_ampm)  # image_group[xx]

        self._clock_dst = Label(self._font_0,
                                text="PST",
                                color=VIOLET,
                                max_glyphs=3)
        self._clock_dst.x = 130
        self._clock_dst.y = (HEIGHT // 2) + 8
        self._image_group.append(self._clock_dst)  # image_group[xx]

        self._clock_auto_dst = Label(self._font_0,
                                     text="AutoDST",
                                     color=VIOLET,
                                     max_glyphs=7)
        self._clock_auto_dst.x = 105
        self._clock_auto_dst.y = HEIGHT - 8
        self._image_group.append(self._clock_auto_dst)  # image_group[xx]

        self._clock_alarm = Label(self._font_0,
                                  text="ALARM",
                                  color=ORANGE,
                                  max_glyphs=5)
        self._clock_alarm.x = 5
        self._clock_alarm.y = HEIGHT - 8
        self._image_group.append(self._clock_alarm)  # image_group[xx]

        self._clock_name = Label(self._font_0,
                                 text="Clock_Builder",
                                 color=BLUE,
                                 max_glyphs=14)
        self._clock_name.x = 40
        self._clock_name.y = HEIGHT - 24
        self._image_group.append(self._clock_name)  # image_group[xx]

        # Initialize the cursor object and cursor CursorManager
        self._mouse_cursor = Cursor(board.DISPLAY,
                                    display_group=self._image_group)
        self._cursor = CursorManager(self._mouse_cursor)
        self._mouse_cursor.scale = self._cursor_scale

        if self._cursor_show and self._mouse_cursor.hidden:
            self._mouse_cursor.show()
        if not self._cursor_show and not self._mouse_cursor.hidden:
            self._mouse_cursor.hide()
        self._cursor.update()  # refresh the cursor

        # debug parameters
        self._debug = debug
        if self._debug:
            print("*Init:", self.__class__)
            print("*Init: ", self.__dict__)

    @property
    def zone(self):
        """The clock's time zone. Default is Pacific."""
        return self._timezone

    @zone.setter
    def zone(self, timezone):
        self._timezone = timezone

    @property
    def hour_24(self):
        """Display 24-hour or 12-hour AM/PM. Default is 12-hour (False)."""
        return self._hour_24_12

    @hour_24.setter
    def hour_24(self, hour_24_12):
        self._hour_24_12 = hour_24_12

    @property
    def dst(self):
        """Time is US DST. Default is Standard Time (False)."""
        return self._dst

    @dst.setter
    def dst(self, dst):
        self._dst = dst

    @property
    def auto_dst(self):
        """Automatically display US DST. Default is auto DST (True)."""
        return self._auto_dst

    @auto_dst.setter
    def auto_dst(self, auto_dst):
        self._auto_dst = auto_dst

    @property
    def alarm(self):
        """Alarm is activated. Default is no alarm (False)."""
        return self._alarm

    @alarm.setter
    def alarm(self, alarm=False):
        self._dst = alarm

    @property
    def brightness(self):
        """Display brightness (0 - 1.0). Default full brightness (1.0)."""
        return self._brightness

    @brightness.setter
    def brightness(self, brightness):
        self._brightness = brightness
        board.DISPLAY.brightness = self._brightness

    @property
    def cursor(self):
        """Cursor refresh and visibility. Default is visible (True)."""
        return self._cursor_show

    @cursor.setter
    def cursor(self, show=True):
        """Cursor refresh and visibility. Default is visible (True)."""
        self._cursor_show = show
        self._mouse_cursor.scale = self._cursor_scale
        if self._cursor_show and self._mouse_cursor.hidden:
            self._mouse_cursor.show()
        if not self._cursor_show and not self._mouse_cursor.hidden:
            self._mouse_cursor.hide()
        self._cursor.update()  # refresh the cursor

    @property
    def show(self):
        """Display time via Displayio"""
        return

    @show.setter
    def show(self, datetime):
        """Display time via REPL."""
        self._datetime = datetime

        if self._auto_dst and self._dst:  # changes the text to show DST
            self._clock_dst.text = self._timezone[0] + "DT"
        else:  # or Standard Time
            self._clock_dst.text = self._timezone[0] + "ST"

        if self._auto_dst:
            self._clock_auto_dst.text = "AutoDST"
        else:
            self._clock_auto_dst.text = "       "

        self._hour = self._datetime.tm_hour  # Format for 24-hour or 12-hour output
        if self._hour_24_12:  # 24-hour
            self._clock_ampm.text = "  "
        else:  # 12-hour clock with AM/PM
            self._clock_ampm.text = "AM"
            if self._hour >= 12:
                self._hour = self._hour - 12
                self._clock_ampm.text = "PM"
            if self._hour == 0:  # midnight hour fix
                self._hour = 12

        if self._alarm:
            self._clock_alarm.text = "ALARM"
        else:
            self._clock_alarm.text = "     "

        self._clock_name.text = ""  # add this feature later
        self._clock_daydate.text = "{} {} {:02d}, {:04d}".format(
            self._weekday[self._datetime.tm_wday],
            self._month[self._datetime.tm_mon - 1], self._datetime.tm_mday,
            self._datetime.tm_year)
        self._clock_digits.text = "{:02}:{:02}".format(self._hour,
                                                       self._datetime.tm_min)

        # Update display
        self._cursor.update()  # refresh the cursor
        board.DISPLAY.show(self._image_group)
        time.sleep(0.1)  # Allow display to load

        return