Example #1
0
def append_wobbly_star_shape(group: displayio.Group, color):
    # Make a wobbly star.  The returned function wobbles its points a little.
    wobbly_star_points = [
        (8, 50),
        (33, 0),
        (58, 50),
        (0, 20),
        (66, 20),
    ]
    star_center_x = 170 - 25
    star_center_y = 120 - 33
    wobbly_star_polygon = vectorio.Polygon(points=wobbly_star_points)
    wobbly_star_shape = vectorio.VectorShape(shape=wobbly_star_polygon,
                                             pixel_shader=monochrome(color),
                                             x=star_center_x,
                                             y=star_center_y)
    group.append(wobbly_star_shape)

    def make_star_wobble():
        tremble = 4
        shake = 3
        trembling_points = [(random.randrange(x - tremble, x + tremble),
                             random.randrange(y - tremble, y + tremble))
                            for x, y in wobbly_star_points]
        wobbly_star_polygon.points = trembling_points
        wobbly_star_shape.x = random.randrange(star_center_x - shake,
                                               star_center_x + shake)
        wobbly_star_shape.y = random.randrange(star_center_y - shake,
                                               star_center_y + shake)

    return make_star_wobble
Example #2
0
def set_size(widget: Button, native: Native, width: int, height: int) -> None:
    """
    A tie-in to set the size of the native element based on the size hint and fixed style
    :param widget:
    :param width:
    :param height:
    """

    radius: int = min(
        widget.width // 2,
        widget.height // 2,
        _dimspecify(widget._impl_cache_["radius"], widget),
    )
    label_width: int = widget._impl_cache_["label_width"]
    margin = widget._margin_

    assert len(native) == 1, len(native)
    label: Label = native[0]
    label.x = (width - label_width) // 2
    label.y = height // 2

    rect = RoundRect(
        margin,  # label.x - rectpadding // 2,
        margin,
        width - 2 * margin,
        height - 2 * margin,
        radius,
        stroke=0,
    )

    native.insert(0, rect)
Example #3
0
def show_image(path):
    with open(path, 'rb') as f:
        bitmap = OnDiskBitmap(f)
        pixel_shader = ColorConverter()
        sprite = TileGrid(bitmap, pixel_shader=pixel_shader)
        group = Group()
        group.append(sprite)
        board.DISPLAY.show(group)
        board.DISPLAY.wait_for_frame()
Example #4
0
def build(
    widget: Label,
    *,
    size: float | int,
    align: align,
    fit_to: bool | str,
) -> tuple[Group, SizeHint]:

    assert isinstance(size, int), (
        "for now, text size on circuitpython must be an " +
        f"int, found object of type {type(size).__name__} with value size={size}"
    )

    is_state = isinstance(widget._text, str)

    LabelType: Type[LabelBase] = BitmapLabel if is_state else GlyphLabel
    kwargs = {"save_text": False} if is_state else {}

    # print(
    #     widget,
    #     widget.text,
    # )
    native_label: LabelBase = LabelType(
        font=FONT,
        text=fit_to if isinstance(fit_to, str) else widget.text,
        scale=size,
        **kwargs,
    )

    native = Group()
    native.append(native_label)

    widget._impl_cache_ = (size, align)

    margin = widget._margin_

    # w, h = (
    #     native_label.bounding_box[2],
    #     native_label.bounding_box[3],
    # )

    # print(
    #     widget,
    #     w / len(widget.text),
    #     (w, h),
    # ),
    return (
        native,
        (
            int(1.15 * native_label.bounding_box[2] * size) + margin * 2,
            int(1.15 * native_label.bounding_box[3] * size) + margin * 2,
        ),
    )
Example #5
0
    def _create_labels(self, x_axis, y_axis, days_pos, hours_pos, mins_pos):
        if not (x_axis or y_axis) or (x_axis and y_axis):
            raise ValueError(
                "Must provide either an x or y axis for text labels, not both."
            )

        self.display = Group(max_size=3)

        x_positions = [x_axis] * 3 if x_axis else [days_pos, hours_pos, mins_pos]
        y_positions = [y_axis] * 3 if y_axis else [days_pos, hours_pos, mins_pos]

        for x, y in zip(x_positions, y_positions):
            label = PlaceholderLabel(
                x, y, placeholder=self.placeholder, font=self.font, color=self.color
            )
            self.display.append(label)
def blankScreen(disp, pix):
    # pylint: disable=unused-argument
    """A blank screen used to hide any serial console output."""
    if disp is None:
        return

    disp.show(Group(max_size=1))
Example #7
0
def append_randart_shape(group: displayio.Group, color):
    # Make a random polygon to sit on the left side of the screen.
    # We'll update its points every now and then with the returned function.
    random_polygon = vectorio.Polygon(points=[(random.randrange(0, 100),
                                               random.randrange(0, 240))
                                              for _ in range(40)])
    random_shape = vectorio.VectorShape(
        shape=random_polygon,
        pixel_shader=monochrome(color),
    )
    group.append(random_shape)

    def new_randart():
        random_polygon.points = [(random.randrange(0, 100),
                                  random.randrange(0, 240)) for _ in range(40)]

    return new_randart
Example #8
0
def append_vectorio_shape(group: displayio.Group, color):
    # Making fonts with vector points is a pain but the memory benefits are pretty nice.
    # Also you can rotate points for spinny text if you want!
    v_polygon = vectorio.Polygon(points=[
        (0, 0),
        (10, 0),
        (18, 24),
        (26, 0),
        (36, 0),
        (22, 34),
        (10, 34),
    ])
    v_shape = vectorio.VectorShape(shape=v_polygon,
                                   pixel_shader=monochrome(color),
                                   x=160,
                                   y=16)
    group.append(v_shape)
    def playerListScreen(self):
        if self.disp is None:
            return

        self.emptyGroup(self.disp_group)
        # The two multiplier allows for rssi as separate label
        playerlist_group = Group(max_size=self.max_players * 2)
        self.showGroup(playerlist_group)
Example #10
0
def append_circle_shape(group: displayio.Group, color):
    # Make a circle that will revolve around the star while changing size
    min_circle_radius = 5
    max_circle_radius = 20
    circle_axis = 170, 120
    circle_revolution_radius = 60
    circle = vectorio.Circle(radius=max_circle_radius)
    circle_shape = vectorio.VectorShape(shape=circle,
                                        pixel_shader=monochrome(color),
                                        x=circle_axis[0],
                                        y=circle_axis[1])
    group.append(circle_shape)

    radians_in_circle = 2 * math.pi

    def revolve_circle():
        seconds_per_revolution = 8
        revolution_ratio = (time.monotonic() %
                            seconds_per_revolution) / seconds_per_revolution
        revolution_radians = revolution_ratio * radians_in_circle
        s = math.sin(revolution_radians)
        c = math.cos(revolution_radians)
        x = s * circle_revolution_radius + circle_axis[0]
        y = c * circle_revolution_radius + circle_axis[1]
        circle_shape.x = round(x)
        circle_shape.y = round(y)

    def resize_circle():
        seconds_per_size_cycle = 13
        size_ratio = abs(
            int(time.monotonic() %
                (2 * seconds_per_size_cycle) / seconds_per_size_cycle) -
            time.monotonic() % seconds_per_size_cycle / seconds_per_size_cycle)
        new_radius = min_circle_radius + size_ratio * (max_circle_radius -
                                                       min_circle_radius)
        circle.radius = int(new_radius)

    return revolve_circle, resize_circle
Example #11
0
def build(
    widget: Button,
    *,
    radius: int,
    size: float | int,
    fit_to_text: bool,
) -> tuple[Native, tuple[int, int]]:
    """
    Creates the native element for the std tg-gui widget based on the _fixed_style_attrs_.
    Those _fixed_style_attrs_ are passed as kwargs to this function.
    :param widget: the tg-gui widget instance to build the native for
    :param **style_attrs: the fixed style attributes that are set at build time
    :return: a tuple of the native widget and suggested size
    """
    native = Group()

    label = Label(font=FONT, x=0, y=0, text=widget.text, scale=size)
    native.append(label)

    _, _, w, h = label.bounding_box
    w *= size
    h = int(1.15 * h * size)
    r = (
        min(radius, w // 2 - 1, h // 2 - 1)
        if isinstance(radius, int)
        else min(w // 2, h // 2) - 1
    )
    padding = round(1.25 * r)

    widget._impl_cache_ = dict(radius=radius, label_width=w)
    return (
        native,
        (
            w + padding + widget._margin_ * 2,
            int(h * 1.2) + widget._margin_ * 2,
        ),
    )
Example #12
0
    def _update_display(self):
        self._update_display_strings()
        banner = label.Label(FONT, text=BANNER_STRING, color=COLOR)
        state = label.Label(FONT, text=self.state_str, color=COLOR)
        detector_result = label.Label(FONT,
                                      text=self.detection_result_str,
                                      color=COLOR)
        duration = label.Label(FONT, text=self.duration_str, color=COLOR)
        min_pressure_label = label.Label(FONT,
                                         text=self.min_press_str,
                                         color=COLOR)
        high_pressure_label = label.Label(FONT,
                                          text=self.high_press_str,
                                          color=COLOR)
        pressure_label = label.Label(FONT, text=self.press_str, color=COLOR)

        banner.x = 0
        banner.y = 0 + Y_OFFSET

        state.x = 10
        state.y = 10 + Y_OFFSET

        detector_result.x = 10
        detector_result.y = 20 + Y_OFFSET

        duration.x = 10
        duration.y = 30 + Y_OFFSET

        min_pressure_label.x = 0
        min_pressure_label.y = BOTTOM_ROW - 10

        pressure_label.x = DISPLAY_WIDTH - pressure_label.bounding_box[2]
        pressure_label.y = BOTTOM_ROW

        high_pressure_label.x = 0
        high_pressure_label.y = BOTTOM_ROW

        splash = Group()
        splash.append(banner)
        splash.append(state)
        splash.append(detector_result)
        splash.append(duration)
        splash.append(min_pressure_label)
        splash.append(high_pressure_label)
        splash.append(pressure_label)

        self.display.show(splash)
    def introductionScreen(self):
        # pylint: disable=too-many-locals,too-many-branches,too-many-statements
        """Introduction screen."""
        if self.disp is not None:
            self.emptyGroup(self.disp_group)
            intro_group = Group(max_size=7)
            welcometo_dob = Label(self.font,
                                  text="Welcome To",
                                  scale=3,
                                  color=IWELCOME_COL_FG)
            welcometo_dob.x = (self.width - 10 * 3 * self.font_width) // 2
            # Y pos on screen looks lower than I would expect
            welcometo_dob.y = 3 * self.font_height // 2
            intro_group.append(welcometo_dob)

            extra_space = 8
            spacing = 3 * self.sprite_size + extra_space
            y_adj = (-6, -2, -2)
            for idx, sprite in enumerate(self.sprites):
                s_group = Group(scale=3, max_size=1)
                s_group.x = -96
                s_group.y = round((self.height - 1.5 * self.sprite_size) / 2 +
                                  (idx - 1) * spacing) + y_adj[idx]
                s_group.append(sprite)
                intro_group.append(s_group)

            arena_dob = Label(self.font,
                              text="Arena",
                              scale=3,
                              color=IWELCOME_COL_FG)
            arena_dob.x = (self.width - 5 * 3 * self.font_width) // 2
            arena_dob.y = self.height - 3 * self.font_height // 2
            intro_group.append(arena_dob)

            self.showGroup(intro_group)

        # The color modification here is fragile as it only works
        # if the text colour is blue, i.e. data is in lsb only
        self.sample.play("welcome-to")
        while self.sample.playing():
            if self.disp is not None and intro_group[0].color < WELCOME_COL_FG:
                intro_group[0].color += 0x10
                time.sleep(0.120)

        onscreen_x_pos = 96

        # Move each sprite onto the screen while saying its name with wav file
        anims = (("rock", 10, 1, 0.050), ("paper", 11, 2, 0.050),
                 ("scissors", 7, 3, 0.050))
        for idx, (audio_name, x_shift, grp_idx, delay_s) in enumerate(anims):
            if self.disp is None:
                self.showChoice(idx)  # Use for NeoPixels
            self.sample.play(audio_name)
            # Audio needs to be long enough to finish movement
            while self.sample.playing():
                if self.disp is not None:
                    if intro_group[grp_idx].x < onscreen_x_pos:
                        intro_group[grp_idx].x += x_shift
                        time.sleep(delay_s)

        # Set NeoPixels back to black
        if self.disp is None:
            self.pix.fill(BLACK)

        self.sample.play("arena")
        while self.sample.playing():
            if self.disp is not None and intro_group[4].color < WELCOME_COL_FG:
                intro_group[4].color += 0x10
                time.sleep(0.060)

        # Button Guide for those with a display
        if self.disp is not None:
            left_dob = Label(self.font,
                             text="< Select    ",
                             scale=2,
                             color=INFO_COL_FG,
                             background_color=INFO_COL_BG)
            left_width = len(left_dob.text) * 2 * self.font_width
            left_dob.x = -left_width
            left_dob.y = self.button_y_pos
            intro_group.append(left_dob)

            right_dob = Label(self.font,
                              text=" Transmit >",
                              scale=2,
                              color=INFO_COL_FG,
                              background_color=INFO_COL_BG)
            right_width = len(right_dob.text) * 2 * self.font_width
            right_dob.x = self.width
            right_dob.y = self.button_y_pos
            intro_group.append(right_dob)

            # Move left button text onto screen, then right
            steps = 20
            for x_pos in [
                    left_dob.x + round(left_width * x / steps)
                    for x in range(1, steps + 1)
            ]:
                left_dob.x = x_pos
                time.sleep(0.06)

            for x_pos in [
                    right_dob.x - round(right_width * x / steps)
                    for x in range(1, steps + 1)
            ]:
                right_dob.x = x_pos
                time.sleep(0.06)

            time.sleep(8)  # leave on screen for further 8 seconds
    def showChoice(self,
                   ch_idx,
                   game_no=None,
                   round_no=None,
                   rounds_tot=None,
                   won_sf=None,
                   drew_sf=None,
                   lost_sf=None):
        """Show player's choice on NeoPixels or display.
           The display also has game and round info and win/draw summary.
           This removes all the graphical objects and re-adds them which
           causes visible flickering during an update - this could be
           improved by only replacing / updating what needs changing.
        """
        if self.disp is None:
            self.pix.fill(BLACK)
            self.pix[self.choiceToPixIdx(ch_idx)] = CHOICE_COL[ch_idx]
            return

        self.emptyGroup(self.disp_group)
        # Would be slightly better to create this Group once and re-use it
        round_choice_group = Group(max_size=3)

        if round_no is not None:
            title_dob = Label(self.font,
                              text="Game {:d}  Round {:d}/{:d}".format(
                                  game_no, round_no, rounds_tot),
                              scale=2,
                              color=TITLE_TXT_COL_FG)
            title_dob.x = round(
                (self.width - len(title_dob.text) * 2 * self.font_width) // 2)
            title_dob.y = round(self.font_height // 2)
            round_choice_group.append(title_dob)

        if won_sf is not None:
            gamesum_dob = Label(self.font,
                                text="Won {:d} Drew {:d} Lost {:d}".format(
                                    won_sf, drew_sf, lost_sf),
                                scale=2,
                                color=TITLE_TXT_COL_FG)
            gamesum_dob.x = round(
                (self.width - len(gamesum_dob.text) * 2 * self.font_width) //
                2)
            gamesum_dob.y = round(self.height - 2 * self.font_height // 2)
            round_choice_group.append(gamesum_dob)

        s_group = Group(scale=3, max_size=1)
        s_group.x = 32
        s_group.y = (self.height - 3 * self.sprite_size) // 2
        s_group.append(self.sprites[ch_idx])
        round_choice_group.append(s_group)

        self.showGroup(round_choice_group)
Example #15
0
    magnet_units_dob.y = magnet_value_dob.y

voltage_value_dob = Label(font=terminalio.FONT,
                          text="----.-",
                          scale=FONT_SCALE,
                          color=0x00c0c0)
voltage_value_dob.y = 30

voltage_units_dob = Label(font=terminalio.FONT,
                          text="mV",
                          scale=FONT_SCALE,
                          color=0x00c0c0)
voltage_units_dob.y = voltage_value_dob.y
voltage_units_dob.x = len(voltage_value_dob.text) * FONT_WIDTH * FONT_SCALE

screen_group = Group()
if magnetometer is not None:
    screen_group.append(magnet_value_dob)
    screen_group.append(magnet_units_dob)
screen_group.append(voltage_value_dob)
screen_group.append(voltage_units_dob)

# Initialise some displayio objects and append them
# The following four variables are set by these two functions
# voltage_barneg_dob, voltage_sep_dob, voltage_barpos_dob
# magnet_circ_dob
voltage_bar_set(0)
if magnetometer is not None:
    magnet_circ_set(0)

# Start-up splash screen
Example #16
0
class EventTheme(BaseTheme):
    """
    A Theme for an event countdown date.

    The theme supports days, hours, and minutes labels
    at the provided positions on the provided axis, but
    can easily be extended in the future.

    Raises:
        ValueError: Provide either an x or a
            y axis for the text labels.
    """

    placeholder = "--"
    formatter = "{:>2}"
    label_positions = {"days": 0, "hours": 1, "mins": 2}

    def __init__(self, *args, pos, x_axis=None, y_axis=None, **kwargs):
        """
        Given a series of positions for the time labels and an axis
        (either an x or a y), create a new theme representing an
        event countdown.

        Args:
            pos (tuple): A tuple of (int, int, int) representing positions
                for the days, hours, and minutes labels.
            x_axis (int, optional): The x axis on which to place labels.
                Either the x_axis or y_axis is optional, but not both.
                Defaults to None.
            y_axis (int, optional): The y axis on which to place labels.
                Either the x_axis or y_axis is optional, but not both.
                Defaults to None.
        """
        super().__init__(*args, **kwargs)
        self._create_labels(x_axis, y_axis, *pos)

    def _create_labels(self, x_axis, y_axis, days_pos, hours_pos, mins_pos):
        if not (x_axis or y_axis) or (x_axis and y_axis):
            raise ValueError(
                "Must provide either an x or y axis for text labels, not both."
            )

        self.display = Group(max_size=3)

        x_positions = [x_axis] * 3 if x_axis else [days_pos, hours_pos, mins_pos]
        y_positions = [y_axis] * 3 if y_axis else [days_pos, hours_pos, mins_pos]

        for x, y in zip(x_positions, y_positions):
            label = PlaceholderLabel(
                x, y, placeholder=self.placeholder, font=self.font, color=self.color
            )
            self.display.append(label)

    def _update_label(self, label, new_value):
        pos = self.label_positions[label]
        old_value = self.display[pos].text
        if new_value != old_value:
            self.display[pos].text = new_value

    def update_time(self, days, hours, mins):
        """
        Update the time displayed.

        Args:
            days (int): Days
            hours (int): Hours
            mins (int): Minutes
        """
        self._update_label("days", self.formatter.format(days))
        self._update_label("hours", self.formatter.format(hours))
        self._update_label("mins", self.formatter.format(mins))

    @property
    def days(self):
        return self.display[self.label_positions["days"]].text

    @property
    def hours(self):
        return self.display[self.label_positions["hours"]].text

    @property
    def mins(self):
        return self.display[self.label_positions["mins"]].text
    def __init__(
            self,
            font: Union[BuiltinFont, BDF, PCF],
            x: int = 0,
            y: int = 0,
            text: str = "",
            color: int = 0xFFFFFF,
            background_color: int = None,
            line_spacing: float = 1.25,
            background_tight: bool = False,
            padding_top: int = 0,
            padding_bottom: int = 0,
            padding_left: int = 0,
            padding_right: int = 0,
            anchor_point: Tuple[float, float] = None,
            anchored_position: Tuple[int, int] = None,
            scale: int = 1,
            base_alignment: bool = False,
            tab_replacement: Tuple[int, str] = (4, " "),
            label_direction: str = "LTR",
            **kwargs,  # pylint: disable=unused-argument
    ) -> None:
        # pylint: disable=too-many-arguments, too-many-locals

        super().__init__(x=x, y=y, scale=1)

        self._font = font
        self._text = text
        self._palette = Palette(2)
        self._color = 0xFFFFFF
        self._background_color = None
        self._line_spacing = line_spacing
        self._background_tight = background_tight
        self._padding_top = padding_top
        self._padding_bottom = padding_bottom
        self._padding_left = padding_left
        self._padding_right = padding_right
        self._anchor_point = anchor_point
        self._anchored_position = anchored_position
        self._base_alignment = base_alignment
        self._label_direction = label_direction
        self._tab_replacement = tab_replacement
        self._tab_text = self._tab_replacement[1] * self._tab_replacement[0]

        if "max_glyphs" in kwargs:
            print(
                "Please update your code: 'max_glyphs' is not needed anymore.")

        self._ascent, self._descent = self._get_ascent_descent()
        self._bounding_box = None

        self.color = color
        self.background_color = background_color

        # local group will hold background and text
        # the self group scale should always remain at 1, the self._local_group will
        # be used to set the scale of the label
        self._local_group = Group(scale=scale)
        self.append(self._local_group)

        self._baseline = -1.0

        if self._base_alignment:
            self._y_offset = 0
        else:
            self._y_offset = self._ascent // 2
class TouchKeys:
    '''Touch keyboard for pyportal usage.'''

    # Settings
    BUTTON_WIDTH, BUTTON_HEIGHT = 48, 40
    BUTTON_COLOR, BUTTON_TEXT_COLOR = LIGHT_GRAY, BLACK
    BUTTON_MARGIN, PADDING = 0, 0
    MAX_CHARS = 300
    LEFT_START, TOP_START = 1, 114
    CAPITALIZED = True

    Coords = namedtuple("Point", "x y")
    buttons = []

    alpha_keys_caps = [
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'Q', 'W', 'E', 'R',
        'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K',
        'L', 'DEL', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 'CAPS',
        'SYM', ' ', 'RETURN', '???'
    ]

    alpha_keys_lower = [
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'q', 'w', 'e', 'r',
        't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k',
        'l', 'DEL', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 'CAPS',
        'SYM', ' ', 'RETURN', '???'
    ]

    symbol_keys = [
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '`', '~', '!', '@',
        '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '_', '+', '{', '}',
        '[', ']', ';', "'", ':', '"', '<', '>', '?', ',', '.', '/', '\\', '|',
        'ABC', 'RETURN', '???'
    ]

    key_sets = [alpha_keys_caps, alpha_keys_lower, symbol_keys]

    # draw rectangle to hold the text stuff.
    text_area = Rect(0,
                     2,
                     screen_width - 0,
                     108,
                     fill=DARK_GRAY,
                     outline=PINK_ISH)
    # create label area for text to show in.
    text_box = Label(font2,
                     text="",
                     color=LIGHT_GRAY,
                     max_glyphs=MAX_CHARS,
                     x=10,
                     y=16)

    # define all the groups for the OSK for each key set:
    main_group = Group(max_size=(len(alpha_keys_caps) + 4))

    main_group.append(text_area)
    main_group.append(text_box)

    def __init__(self):
        '''load the parts and display the keyboard and text entry box'''
        print('Loading keyboard')
        self.load_buttons(self.key_sets[0])

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        splash.pop()

    def edit_text(self):
        pass

    # Some button functions
    def button_grid(self, row, col):
        return self.Coords(
            self.BUTTON_MARGIN * (row + self.PADDING) +
            self.BUTTON_WIDTH * row + self.LEFT_START,
            self.BUTTON_MARGIN * (col + self.PADDING) +
            self.BUTTON_HEIGHT * col + self.TOP_START)

    def add_button(self,
                   row,
                   col,
                   label,
                   width=1,
                   color=LIGHT_GRAY,
                   text_color=BLACK):
        pos = self.button_grid(row, col)
        new_button = Button(x=pos.x,
                            y=pos.y,
                            width=self.BUTTON_WIDTH * width +
                            self.BUTTON_MARGIN * (width - 1),
                            height=self.BUTTON_HEIGHT,
                            label=label,
                            label_font=font,
                            label_color=text_color,
                            fill_color=color,
                            style=Button.SHADOWRECT)
        self.buttons.append(new_button)
        return new_button

    def find_button(self, label):
        result = None
        for _, btn in enumerate(self.buttons):
            if btn.label == label:
                result = btn
        return result

    def load_buttons(self, key_set):
        x, y = 0, 0
        btn_width = 1
        for char in key_set:
            if y == 4:
                btn_width = 2
                if x > 0:
                    x += 1
            self.add_button(x, y, char, width=btn_width)
            x += 1
            if x == 10:
                x = 0
                y += 1
        for btn in self.buttons:
            self.main_group.append(btn.group)

    def switch_alpha_symbol(self, input_char):
        # print('SWITCH TO ALPHA or SYMBOL KEY SET!')
        if input_char == 'ABC':
            if self.CAPITALIZED:
                cap = 0
            elif not self.CAPITALIZED:
                cap = 1
            # print(f'Cap setting: {cap}')
            for b, btn in enumerate(self.buttons):
                btn.label = self.key_sets[cap][b]
        elif input_char == 'SYM':
            for b, btn in enumerate(self.buttons):
                btn.label = self.key_sets[2][b]
        time.sleep(0.75)

    def delete_text_char(self):
        # print('THIS NEEDS TO DELETE SOMETHING')
        if len(self.text_box.text) > 0:
            self.text_box.text = self.text_box.text[:-1]

    def switch_capitalization(self):
        if self.CAPITALIZED:
            # print('Switch to Lower Case set')
            for b, btn in enumerate(self.buttons):
                # print(self.key_sets[0][b])
                btn.label = self.key_sets[1][b]
        elif not self.CAPITALIZED:
            # print('Switch to Upper Case Set')
            for b, btn in enumerate(self.buttons):
                # print(self.key_sets[1][b])
                btn.label = self.key_sets[0][b]
        self.CAPITALIZED = not self.CAPITALIZED
        time.sleep(0.75)

    def update_text(self, text_from_buttons):
        self.text_box.text += text_from_buttons

    def show(self, target_group):
        '''Get the keyboard to show on the display :crosses fingers:'''
        # print('SHOW ME THE KEYBOARD!')
        target_group.append(self.main_group)
        text_input = ''
        touched = False
        while True:
            touch = ts.touch_point
            if touch:
                # print(touch)
                for btn in self.buttons:
                    if btn.contains(touch) and touched == False:
                        touched = True
                        input_char = btn.label
                        # print(input_char)
                        if input_char == 'DEL':
                            # remove the last char added.
                            text_input = text_input[:-1]
                            self.delete_text_char()
                            # print(text_input)
                        elif input_char == 'CAPS':
                            self.switch_capitalization()
                        elif input_char == 'SYM' or input_char == 'ABC':
                            self.switch_alpha_symbol(input_char)
                        elif input_char == 'RETURN':
                            # return the typed shit to the outside world!
                            return text_input
                        elif input_char == '???':
                            print('Uhh.....')
                        else:
                            self.update_text(input_char)
                            text_input += input_char
                            # print(text_input)
                    elif touched and not btn.contains(touch):
                        # print('buttons released')
                        touched = False
                time.sleep(0.05)
Example #19
0
    elif who == "opp":
        sg_idx = rps_dob_idx[1] + c_idx
    else:
        raise ValueError("who is mine or opp")

    ### An even number will leave colours on original values
    for _ in range(5 * 2):
        tmp_col = screen_group[sg_idx].color
        screen_group[sg_idx].color = screen_group[sg_idx].background_color
        screen_group[sg_idx].background_color = tmp_col
        time.sleep(0.5)


### The 6x14 terminalio classic font
FONT_WIDTH, FONT_HEIGHT = terminalio.FONT.get_bounding_box()
screen_group = Group(max_size=len(choices) * 2 + 1 + 1)

### The position of the two players RPS Label objects inside screen_group
rps_dob_idx = []

### Create the simple arrow cursors
left_col = 20
right_col = display.width // 2 + left_col
for x_pos in (left_col, right_col):
    y_pos = top_y_pos
    rps_dob_idx.append(len(screen_group))
    for label_text in choices:
        rps_dob = Label(terminalio.FONT,
                        text=label_text,
                        scale=2,
                        color=DEFAULT_TXT_COL_FG,
# SPDX-License-Identifier: MIT
"""
CircuitPython simple sensor data display demo using LC709203 battery monitor and TFT display
"""
import time
import board
import terminalio
from displayio import Group
from adafruit_display_text import bitmap_label
from adafruit_lc709203f import LC709203F

text_area = bitmap_label.Label(terminalio.FONT, scale=2)
text_area.anchor_point = (0.5, 0.5)
text_area.anchored_position = (board.DISPLAY.width // 2, board.DISPLAY.height // 2)

main_group = Group()

main_group.append(text_area)

print("LC709203F test")
print("Make sure LiPoly battery is plugged into the board!")

sensor = LC709203F(board.I2C())

print("IC version:", hex(sensor.ic_version))

board.DISPLAY.show(main_group)

while True:
    text_area.text = "Battery:\n{:.1f} Volts \n{}%".format(sensor.cell_voltage, sensor.cell_percent)
    time.sleep(1)
    def showGameResultScreen(self, pla, sco, rounds_tot=None):
        # pylint: disable=unused-argument,too-many-locals,too-many-statements
        """Display a high score table with a visual bubble sort
           slow enough to see the algorithm at work."""
        self.fadeUpDown("down")
        self.emptyGroup(self.disp_group)

        # Score list group + background + question mark for sorting
        gs_group = Group(max_size=4)

        # Pale grey large GAME SCORES background
        bg_scale = 6
        sbg_dob1 = Label(self.font, text="GAME", scale=bg_scale, color=GS_COL)
        sbg_dob1.x = (self.width - 4 * bg_scale * self.font_width) // 2
        sbg_dob1.y = self.height // 4
        sbg_dob2 = Label(self.font,
                         text="SCORES",
                         scale=bg_scale,
                         color=GS_COL)
        sbg_dob2.x = (self.width - 6 * bg_scale * self.font_width) // 2
        sbg_dob2.y = self.height // 4 * 3
        gs_group.append(sbg_dob1)
        gs_group.append(sbg_dob2)
        self.showGroup(gs_group)
        self.fadeUpDown("up")

        # Calculate maximum length player name
        # and see if scores happen to already be in order
        max_len = 0
        prev_score = sco[0]
        descending = True
        for idx, (name, _) in enumerate(pla):
            max_len = max(max_len, len(name))
            if sco[idx] > prev_score:
                descending = False
            prev_score = sco[idx]

        fmt = "{:" + str(max_len) + "s} {:2d}"
        x_pos = (self.width - (max_len + 3) * 2 * self.font_width) // 2
        scale = 2
        spacing = 4 if len(pla) <= 6 else 0
        top_y_pos = round((self.height - len(pla) * scale * self.font_height -
                           (len(pla) - 1) * spacing) / 2 +
                          scale * self.font_height / 2)
        scores_group = Group(max_size=len(pla))
        gs_group.append(scores_group)
        for idx, (name, _) in enumerate(pla):
            op_dob = Label(
                self.font,
                text=fmt.format(name, sco[idx]),
                scale=2,
                color=(PLAYER_NAME_COL_FG if idx == 0 else OPP_NAME_COL_FG))
            op_dob.x = x_pos
            op_dob.y = top_y_pos + idx * (scale * self.font_height + spacing)
            scores_group.append(op_dob)
            time.sleep(0.2)

        # Sort the entries if needed
        sort_scores = list(sco)  # Make an independent local copy
        if not descending:
            empty_group = Group()  # minor hack to aid swaps in scores_group
            step = 3
            qm_dob = Label(self.font, text="?", scale=2, color=QM_SORT_FG)
            qm_dob.x = round(x_pos - 1.5 * scale * self.font_width)
            gs_group.append(qm_dob)
            while True:
                swaps = 0
                for idx in range(0, len(sort_scores) - 1):
                    above_score = sort_scores[idx]
                    above_y = scores_group[idx].y
                    below_y = scores_group[idx + 1].y
                    qm_dob.y = (above_y + below_y) // 2
                    if above_score < sort_scores[idx + 1]:
                        qm_dob.text = "<"
                        qm_dob.color = QM_SORTING_FG
                        swaps += 1

                        # make list of steps
                        range_y = below_y - above_y
                        offsets = list(range(step, range_y + 1, step))
                        # Ensure this goes to the exact final position
                        if offsets[-1] != range_y:
                            offsets.append(range_y)

                        for offset in offsets:
                            scores_group[idx].y = above_y + offset
                            scores_group[idx + 1].y = below_y - offset
                            time.sleep(0.050)

                        # swap the scores around
                        sort_scores[idx] = sort_scores[idx + 1]
                        sort_scores[idx + 1] = above_score

                        # swap the graphical objects around using empty_group
                        # to avoid ValueError: Layer already in a group
                        old_above_dob = scores_group[idx]
                        old_below_dob = scores_group[idx + 1]
                        scores_group[idx + 1] = empty_group
                        scores_group[idx] = old_below_dob
                        scores_group[idx + 1] = old_above_dob

                        qm_dob.text = "?"
                        qm_dob.color = QM_SORT_FG
                        time.sleep(0.2)
                    else:
                        time.sleep(0.6)

                if swaps == 0:
                    break  # Sort complete if no values were swapped
            gs_group.remove(qm_dob)
Example #22
0
 def on_container_build(_, widget: Widget):
     assert widget._native_ is None
     widget._native_ = group = Group()
     group.hidden = False
Example #23
0
    cam.size = size
    if cam.width > width:
        continue
    if cam.height > height:
        continue
    try:
        bitmap = Bitmap(cam.width, cam.height, 65535)
        break
    except MemoryError:
        continue

print(width, height, cam.width, cam.height)
if bitmap is None:
    raise SystemExit("Could not allocate a bitmap")

g = Group(scale=1, x=(width - cam.width) // 2, y=(height - cam.height) // 2)
tg = TileGrid(
    bitmap,
    pixel_shader=ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED))
g.append(tg)
display.show(g)

t0 = time.monotonic_ns()
display.auto_refresh = False
while True:
    cam.capture(bitmap)
    bitmap.dirty()
    display.refresh(minimum_frames_per_second=0)
    t1 = time.monotonic_ns()
    print("fps", 1e9 / (t1 - t0))
    t0 = t1
Example #24
0
    ### Inputs
    _button_a = digitalio.DigitalInOut(board.BUTTON_A)
    _button_a.switch_to_input(pull=digitalio.Pull.UP)
    _button_b = digitalio.DigitalInOut(board.BUTTON_B)
    _button_b.switch_to_input(pull=digitalio.Pull.UP)
    button_left = lambda: not _button_a.value
    button_right = lambda: not _button_b.value

### The 6x14 terminalio classic font
FONT_WIDTH, FONT_HEIGHT = terminalio.FONT.get_bounding_box()

rows = 10
row_y = 30
row_spacing = FONT_HEIGHT + 2
rows_group = Group()

### I have foreground and background colours I can use here

### 1234567890123456789012345678901234567890
### aa:bb:cc:dd:ee:ff -101 12345678901234567
for _ in range(rows):
    row_label = Label(font=terminalio.FONT, text="", color=0xc0c000)
    row_label.y = row_y
    row_y += row_spacing
    rows_group.append(row_label)

summary_label = Label(font=terminalio.FONT, text="", color=0x00c0c0)

summary_label.y = 220
    def showPlayerVPlayerScreen(self, me_name, op_name, my_ch_idx, op_ch_idx,
                                result, summary, win, draw, void):
        # pylint: disable=too-many-locals,too-many-branches,too-many-statements
        """Display a win, draw, lose or error message."""
        self.fadeUpDown("down")
        self.emptyGroup(self.disp_group)

        if void:
            error_tot = 3
            error_group = Group(max_size=error_tot + 1)
            # Opponent's name helps pinpoint the error
            op_dob = Label(self.font,
                           text=op_name,
                           scale=2,
                           color=OPP_NAME_COL_FG)
            op_dob.x = 40
            op_dob.y = self.font_height
            error_group.append(op_dob)
            self.showGroup(error_group)
            self.fadeUpDown("up", duration=0.4)
            if result is not None:
                self.sample.play(result)
            font_scale = 2
            # Attempting to put the three pieces of "Error!" text on screen
            # synchronised with the sound sample repeating the word
            for idx in range(error_tot):
                error_dob = Label(self.font,
                                  text="Error!",
                                  scale=font_scale,
                                  color=ERROR_COL_FG)
                error_dob.x = 40
                error_dob.y = 60 + idx * 60
                error_group.append(error_dob)
                time.sleep(0.5)  # Small attempt to synchronise audio with text
                font_scale += 1

        else:
            # Would be slightly better to create this Group once and re-use it
            pvp_group = Group(max_size=3)

            # Add player's name and sprite just off left side of screen
            # and opponent's just off right
            player_detail = [[
                me_name, self.sprites[my_ch_idx], -16 - 3 * self.sprite_size,
                PLAYER_NAME_COL_FG, PLAYER_NAME_COL_BG
            ],
                             [
                                 op_name, self.opp_sprites[op_ch_idx],
                                 16 + self.width, OPP_NAME_COL_FG,
                                 OPP_NAME_COL_BG
                             ]]
            idx_lr = [0, 1]  # index for left and right sprite
            if win:
                player_detail.reverse()  # this player is winner so put last
                idx_lr.reverse()

            # Add some whitespace around winner's name
            player_detail[1][0] = " " + player_detail[1][0] + " "

            for (name, sprite, start_x, fg, bg) in player_detail:
                s_group = Group(scale=2,
                                max_size=2)  # Audio is choppy at scale=3
                s_group.x = start_x
                s_group.y = (self.height - 2 *
                             (self.sprite_size + self.font_height)) // 2

                s_group.append(sprite)
                p_name_dob = Label(
                    self.font,
                    text=name,
                    scale=1,  # This is scaled by the group
                    color=fg,
                    background_color=bg)
                # Centre text below sprite - values are * Group scale
                p_name_dob.x = (self.sprite_size -
                                len(name) * self.font_width) // 2
                p_name_dob.y = self.sprite_size + 4
                s_group.append(p_name_dob)

                pvp_group.append(s_group)

            if draw:
                sum_text = "Draw"
            elif win:
                sum_text = "You win"
            else:
                sum_text = "You lose"
            # Text starts invisible (BLACK) and color is later changed
            summary_dob = Label(self.font, text=sum_text, scale=3, color=BLACK)
            summary_dob.x = round(
                (self.width - 3 * self.font_width * len(sum_text)) / 2)
            summary_dob.y = round(self.height - (3 * self.font_height / 2))
            pvp_group.append(summary_dob)

            self.showGroup(pvp_group)
            self.fadeUpDown("up", duration=0.4)

            # Start audio half way through animations
            if draw:
                # Move sprites onto the screen leaving them at either side
                for idx in range(16):
                    pvp_group[idx_lr[0]].x += 6
                    pvp_group[idx_lr[1]].x -= 6
                    if idx == 8 and result is not None:
                        self.sample.play(result)
                    time.sleep(0.2)
            else:
                # Move sprites together, winning sprite overlaps and covers loser
                for idx in range(16):
                    pvp_group[idx_lr[0]].x += 10
                    pvp_group[idx_lr[1]].x -= 10
                    if idx == 8 and result is not None:
                        self.sample.play(result)
                    time.sleep(0.2)

            self.sample.wait()  # Wait for first sample to finish

            if summary is not None:
                self.sample.play(summary)

            # Flash colours for win, fad up to blue for rest
            if not draw and win:
                colours = [YELLOW_COL, ORANGE_COL, RED_COL] * 5
            else:
                colours = [DRAWLOSE_COL * sc // 15 for sc in range(1, 15 + 1)]
            for col in colours:
                summary_dob.color = col
                time.sleep(0.120)

        self.sample.wait()  # Ensure second sample has completed
Example #26
0
    elif who == "opp":
        sg_idx = rps_dob_idx[1] + c_idx
    else:
        raise ValueError("who is mine or opp")

    # An even number will leave colours on original values
    for _ in range(5 * 2):
        tmp_col = screen_group[sg_idx].color
        screen_group[sg_idx].color = screen_group[sg_idx].background_color
        screen_group[sg_idx].background_color = tmp_col
        time.sleep(0.5)


# The 6x14 terminalio classic font
FONT_WIDTH, FONT_HEIGHT = terminalio.FONT.get_bounding_box()
screen_group = Group()

# The position of the two players RPS Label objects inside screen_group
rps_dob_idx = []

# Create the simple arrow cursors
left_col = 20
right_col = display.width // 2 + left_col
for x_pos in (left_col, right_col):
    y_pos = top_y_pos
    rps_dob_idx.append(len(screen_group))
    for label_text in choices:
        rps_dob = Label(terminalio.FONT,
                        text=label_text,
                        scale=2,
                        color=DEFAULT_TXT_COL_FG,
Example #27
0
voltage_value_dob = Label(font=terminalio.FONT,
                          text="----.-",
                          scale=FONT_SCALE,
                          color=0x00c0c0)
voltage_value_dob.y = 30

voltage_units_dob = Label(font=terminalio.FONT,
                          text="mV",
                          scale=FONT_SCALE,
                          color=0x00c0c0)
voltage_units_dob.y = voltage_value_dob.y
voltage_units_dob.x = len(voltage_value_dob.text) * FONT_WIDTH * FONT_SCALE

### 9 elements, 4 added immediately, 4 later, 1 spare for on-screen text
screen_group = Group(max_size=4 + 4 + 1)
if magnetometer is not None:
    screen_group.append(magnet_value_dob)
    screen_group.append(magnet_units_dob)
screen_group.append(voltage_value_dob)
screen_group.append(voltage_units_dob)

### Initialise some displayio objects and append them
### The following four variables are set by these two functions
### voltage_barneg_dob, voltage_sep_dob, voltage_barpos_dob
### magnet_circ_dob
voltage_bar_set(0)
if magnetometer is not None:
    magnet_circ_set(0)

### Start-up splash screen
Example #28
0
    ### Inputs
    _button_a = digitalio.DigitalInOut(board.BUTTON_A)
    _button_a.switch_to_input(pull=digitalio.Pull.UP)
    _button_b = digitalio.DigitalInOut(board.BUTTON_B)
    _button_b.switch_to_input(pull=digitalio.Pull.UP)
    button_left = lambda: not _button_a.value
    button_right = lambda: not _button_b.value

### The 6x14 terminalio classic font
FONT_WIDTH, FONT_HEIGHT = terminalio.FONT.get_bounding_box()

rows = 10
row_y = 30
row_spacing = FONT_HEIGHT + 2
rows_group = Group(max_size=rows)

### I have foreground and background colours I can use here

### 1234567890123456789012345678901234567890
### aa:bb:cc:dd:ee:ff -101 12345678901234567
for idx in range(rows):
    row_label = Label(
        font=terminalio.FONT,
        text="",
        max_glyphs=40,  ### maximum that will fit 240/6
        color=0xc0c000)
    row_label.y = row_y
    row_y += row_spacing
    rows_group.append(row_label)
                     board.TOUCH_YD,
                     board.TOUCH_YU,
                     calibration=((5200, 59000), (5800, 57000)),
                     size=(screen_width, screen_height))

elif display.rotation == 90:
    ts = Touchscreen(board.TOUCH_YU,
                     board.TOUCH_YD,
                     board.TOUCH_XL,
                     board.TOUCH_XR,
                     calibration=((5200, 59000), (5800, 57000)),
                     size=(240, 320))

elif display.rotation == 180:
    ts = Touchscreen(board.TOUCH_XR,
                     board.TOUCH_XL,
                     board.TOUCH_YU,
                     board.TOUCH_YD,
                     calibration=((5200, 59000), (5800, 57000)),
                     size=(320, 240))

elif display.rotation == 270:
    ts = Touchscreen(board.TOUCH_YD,
                     board.TOUCH_YU,
                     board.TOUCH_XR,
                     board.TOUCH_XL,
                     calibration=((5200, 59000), (5800, 57000)),
                     size=(240, 320))

splash = Group(max_size=10)
    def showChoice(self,
                   ch_idx,
                   game_no=None,
                   round_no=None,
                   rounds_tot=None,
                   won_sf=None,
                   drew_sf=None,
                   lost_sf=None):
        """TODO DOC"""
        if self.disp is None:
            self.pix.fill(BLACK)
            self.pix[self.choiceToPixIdx(ch_idx)] = CHOICE_COL[ch_idx]
            return

        self.emptyGroup(self.disp_group)
        ### Would be slightly better to create this Group once and re-use it
        round_choice_group = Group(max_size=3)

        if round_no is not None:
            title_dob = Label(self.font,
                              text="Game {:d}  Round {:d}/{:d}".format(
                                  game_no, round_no, rounds_tot),
                              scale=2,
                              color=TITLE_TXT_COL_FG)
            title_dob.x = round(
                (self.width - len(title_dob.text) * 2 * self.font_width) // 2)
            title_dob.y = round(self.font_height // 2)
            round_choice_group.append(title_dob)

        if won_sf is not None:
            gamesum_dob = Label(self.font,
                                text="Won {:d} Drew {:d} Lost {:d}".format(
                                    won_sf, drew_sf, lost_sf),
                                scale=2,
                                color=TITLE_TXT_COL_FG)
            gamesum_dob.x = round(
                (self.width - len(gamesum_dob.text) * 2 * self.font_width) //
                2)
            gamesum_dob.y = round(self.height - 2 * self.font_height // 2)
            round_choice_group.append(gamesum_dob)

        s_group = Group(scale=3, max_size=1)
        s_group.x = 32
        s_group.y = (self.height - 3 * self.sprite_size) // 2
        s_group.append(self.sprites[ch_idx])
        round_choice_group.append(s_group)

        self.showGroup(round_choice_group)