Skip to main content

A lightweight UI component library for MaixPy.

Project description

MaixPy-UI-Lib:一款为 MaixPy 开发的轻量级 UI 组件库

License Version

本项目是一款为 MaixPy 开发的轻量级 UI 组件库,遵循 Apache 2.0 协议。

欢迎给本项目提pr,要是觉得好用的话请给本项目点个star⭐


🖼️功能展示

以下画面均截取自本项目的 main.py

button

switch

slider

radiobutton

checkbox


📦 安装

pip install maixpy-ui-lib


🚀快速上手

您可以通过运行仓库中的示例程序 examples/demo.py 来快速熟悉本项目的基本功能和使用方法。


📖组件详解

1. 按钮 (Button)

按钮是最基本的交互组件,用于触发一个操作。

使用方式

  1. 创建一个 ButtonManager 实例。
  2. 创建 Button 实例,定义其矩形区域、标签文本和回调函数。
  3. 使用 manager.add_button() 将按钮实例添加到管理器中。
  4. 在主循环中,调用 manager.handle_events(img) 来处理触摸事件并绘制按钮。

示例

from maix import display, camera, app, touchscreen, image
from maixpy_ui import Button, ButtonManager
import time

# 1. 初始化硬件
disp = display.Display()
ts = touchscreen.TouchScreen()
cam = camera.Camera()

# 2. 定义回调函数
# 当按钮被点击时,这个函数会被调用
def on_button_click():
    print("Hello, World! The button was clicked.")
    # 你可以在这里执行任何操作,比如切换页面、拍照等

# 3. 初始化UI
# 创建一个按钮管理器
btn_manager = ButtonManager(ts, disp)

# 创建一个按钮实例
# rect: [x, y, 宽度, 高度]
# label: 按钮上显示的文字
# callback: 点击时调用的函数
hello_button = Button(
    rect=[240, 200, 160, 80],
    label="Click Me",
    text_scale=2.0,
    callback=on_button_click,
    bg_color=(0, 120, 220),       # 蓝色背景
    pressed_color=(0, 80, 180),   # 按下时深蓝色
    text_color=(255, 255, 255)    # 白色文字
)

# 将按钮添加到管理器
btn_manager.add_button(hello_button)

# 4. 主循环
print("Button example running. Press the button on the screen.")
while not app.need_exit():
    img = cam.read()
    
    # 在每一帧中,让管理器处理事件并绘制按钮
    btn_manager.handle_events(img)
    
    disp.show(img)
    time.sleep(0.02) # 降低CPU使用率

Button

创建一个可交互的按钮组件。该组件可以响应触摸事件,并在按下时改变外观,释放时执行回调函数。

构造函数: __init__
参数 类型 描述 默认值
rect Sequence[int] 按钮的位置和尺寸 [x, y, w, h]必需 -
label str 按钮上显示的文本。必需 -
callback `Callable None` 当按钮被点击时调用的函数。必需
bg_color `Sequence[int] None` 背景颜色 (R, G, B)。
pressed_color `Sequence[int] None` 按下状态的背景颜色 (R, G, B)。
text_color Sequence[int] 文本颜色 (R, G, B)。 (255, 255, 255)
border_color Sequence[int] 边框颜色 (R, G, B)。 (200, 200, 200)
border_thickness int 边框厚度(像素)。 2
text_scale float 文本的缩放比例。 1.5
font `str None` 使用的字体文件路径。
align_h str 水平对齐方式 ('left', 'center', 'right')。 'center'
align_v str 垂直对齐方式 ('top', 'center', 'bottom')。 'center'
方法 (Methods)
方法 参数 描述
draw(img) img (maix.image.Image): 将要绘制按钮的目标图像。 在指定的图像上绘制按钮。
handle_event(...) x (int): 触摸点的 X 坐标。
y (int): 触摸点的 Y 坐标。
pressed (`bool
int): 触摸屏是否被按下。<br>img_w (int): 图像缓冲区的宽度。<br>img_h (int): 图像缓冲区的高度。<br>disp_w (int): 显示屏的宽度。<br>disp_h (int`): 显示屏的高度。

ButtonManager

管理一组按钮的事件处理和绘制。

构造函数: __init__
参数 类型 描述
ts touchscreen.TouchScreen 触摸屏设备实例。必需
disp display.Display 显示设备实例。必需
方法 (Methods)
方法 参数 描述
add_button(button) button (Button): 要添加的 Button 实例。 向管理器中添加一个按钮。
handle_events(img) img (maix.image.Image): 绘制按钮的目标图像。 处理所有受管按钮的事件并进行绘制。

2. 滑块 (Slider)

滑块允许用户在一个连续的范围内选择一个值。

使用方式

  1. 创建一个 SliderManager 实例。
  2. 创建 Slider 实例,定义其区域、数值范围和回调函数。
  3. 使用 manager.add_slider() 添加滑块。
  4. 在主循环中,调用 manager.handle_events(img)

示例

from maix import display, camera, app, touchscreen, image
from maixpy_ui import Slider, SliderManager
import time

# 1. 初始化硬件
disp = display.Display()
ts = touchscreen.TouchScreen()
cam = camera.Camera()

# 全局变量,用于存储滑块的值
current_brightness = 128 

# 2. 定义回调函数
# 当滑块的值改变时,这个函数会被调用
def on_slider_update(value):
    global current_brightness
    current_brightness = value
    print(f"Slider value updated to: {value}")

# 3. 初始化UI
slider_manager = SliderManager(ts, disp)

brightness_slider = Slider(
    rect=[50, 230, 540, 20],
    scale=2.0,
    min_val=0,
    max_val=255,
    default_val=current_brightness,
    callback=on_slider_update,
    label="Slider"
)

slider_manager.add_slider(brightness_slider)

# 4. 主循环
print("Slider example running. Drag the slider.")
title_color = image.Color.from_rgb(255, 255, 255)

while not app.need_exit():
    img = cam.read()
    
    # 实时显示滑块的值
    img.draw_string(20, 20, f"Value: {current_brightness}", scale=2.0, color=title_color)
    
    # 处理滑块事件并绘制
    slider_manager.handle_events(img)
    
    disp.show(img)
    time.sleep(0.02)

Slider

创建一个可拖动的滑块组件,用于在一定范围内选择一个值。

构造函数: __init__
参数 类型 描述 默认值
rect Sequence[int] 滑块的位置和尺寸 [x, y, w, h]必需 -
scale float 滑块的整体缩放比例。 1.0
min_val int 滑块的最小值。 0
max_val int 滑块的最大值。 100
default_val int 滑块的默认值。 50
callback `Callable None` 值改变时调用的函数,接收新值作为参数。
label str 滑块上方的标签文本。 ""
track_color Sequence[int] 滑轨背景颜色 (R, G, B)。 (60, 60, 60)
progress_color Sequence[int] 滑轨进度条颜色 (R, G, B)。 (0, 120, 220)
handle_color Sequence[int] 滑块手柄颜色 (R, G, B)。 (255, 255, 255)
handle_border_color Sequence[int] 滑块手柄边框颜色 (R, G, B)。 (100, 100, 100)
handle_pressed_color Sequence[int] 按下时手柄颜色 (R, G, B)。 (220, 220, 255)
label_color Sequence[int] 标签文本颜色 (R, G, B)。 (200, 200, 200)
tooltip_bg_color Sequence[int] 拖动时提示框背景色 (R, G, B)。 (0, 0, 0)
tooltip_text_color Sequence[int] 拖动时提示框文本颜色 (R, G, B)。 (255, 255, 255)
show_tooltip_on_drag `bool int` 是否在拖动时显示数值提示框。
方法 (Methods)
方法 参数 描述
draw(img) img (maix.image.Image): 将要绘制滑块的目标图像。 在指定的图像上绘制滑块。
handle_event(...) x (int): 触摸点的 X 坐标。
y (int): 触摸点的 Y 坐标。
pressed (`bool
int): 触摸屏是否被按下。<br>img_w (int): 图像缓冲区的宽度。<br>img_h (int): 图像缓冲区的高度。<br>disp_w (int): 显示屏的宽度。<br>disp_h (int`): 显示屏的高度。

SliderManager

管理一组滑块的事件处理和绘制。

构造函数: __init__
参数 类型 描述
ts touchscreen.TouchScreen 触摸屏设备实例。必需
disp display.Display 显示设备实例。必需
方法 (Methods)
方法 参数 描述
add_slider(slider) slider (Slider): 要添加的 Slider 实例。 向管理器中添加一个滑块。
handle_events(img) img (maix.image.Image): 绘制滑块的目标图像。 处理所有受管滑块的事件并进行绘制。

3. 开关 (Switch)

一个具有“开”和“关”两种状态的切换控件。

使用方式

  1. 创建一个 SwitchManager 实例。
  2. 创建 Switch 实例,定义其位置、初始状态和回调函数。
  3. 使用 manager.add_switch() 添加开关。
  4. 在主循环中,调用 manager.handle_events(img)

示例

from maix import display, camera, app, touchscreen, image
from maixpy_ui import Switch, SwitchManager
import time

# 1. 初始化硬件
disp = display.Display()
ts = touchscreen.TouchScreen()
cam = camera.Camera()

# 全局变量,用于存储开关状态
is_light_on = False

# 2. 定义回调函数
def on_switch_toggle(is_on):
    global is_light_on
    is_light_on = is_on
    status = "ON" if is_on else "OFF"
    print(f"Switch toggled. Light is now {status}.")

# 3. 初始化UI
switch_manager = SwitchManager(ts, disp)

light_switch = Switch(
    position=[280, 190],
    scale=2.0,
    is_on=is_light_on,
    callback=on_switch_toggle
)

switch_manager.add_switch(light_switch)

# 4. 主循环
print("Switch example running. Tap the switch.")
title_color = image.Color.from_rgb(255, 255, 255)
status_on_color = image.Color.from_rgb(30, 200, 30)
status_off_color = image.Color.from_rgb(80, 80, 80)

while not app.need_exit():
    img = cam.read()
    
    # 根据开关状态显示一个状态指示灯
    status_text = "Light: ON" if is_light_on else "Light: OFF"
    status_color = status_on_color if is_light_on else status_off_color
    img.draw_string(20, 20, status_text, scale=1.5, color=title_color)
    img.draw_rect(310, 280, 50, 50, color=status_color, thickness=-1)
    
    switch_manager.handle_events(img)
    
    disp.show(img)
    time.sleep(0.02)

Switch

创建一个开关(Switch)组件,用于在开/关两种状态之间切换。

构造函数: __init__
参数 类型 描述 默认值
position Sequence[int] 开关的左上角坐标 [x, y]必需 -
scale float 开关的整体缩放比例。 1.0
is_on `bool int` 开关的初始状态,True 为开。
callback `Callable None` 状态切换时调用的函数,接收一个布尔值参数表示新状态。
on_color Sequence[int] 开启状态下的背景颜色 (R, G, B)。 (30, 200, 30)
off_color Sequence[int] 关闭状态下的背景颜色 (R, G, B)。 (100, 100, 100)
handle_color Sequence[int] 手柄的颜色 (R, G, B)。 (255, 255, 255)
handle_pressed_color Sequence[int] 按下时手柄的颜色 (R, G, B)。 (220, 220, 255)
handle_radius_increase int 按下时手柄半径增加量。 2
方法 (Methods)
方法 参数 描述
toggle() - 切换开关的状态,并执行回调函数。
draw(img) img (maix.image.Image): 将要绘制开关的目标图像。 在指定的图像上绘制开关。
handle_event(...) x (int): 触摸点的 X 坐标。
y (int): 触摸点的 Y 坐标。
pressed (`bool
int): 触摸屏是否被按下。<br>img_w (int): 图像缓冲区的宽度。<br>img_h (int): 图像缓冲区的高度。<br>disp_w (int): 显示屏的宽度。<br>disp_h (int`): 显示屏的高度。

SwitchManager

管理一组开关的事件处理和绘制。

构造函数: __init__
参数 类型 描述
ts touchscreen.TouchScreen 触摸屏设备实例。必需
disp display.Display 显示设备实例。必需
方法 (Methods)
方法 参数 描述
add_switch(switch) switch (Switch): 要添加的 Switch 实例。 向管理器中添加一个开关。
handle_events(img) img (maix.image.Image): 绘制开关的目标图像。 处理所有受管开关的事件并进行绘制。

4. 复选框 (Checkbox)

允许用户从一组选项中进行多项选择。

使用方式

  1. 创建一个 CheckboxManager 实例。
  2. 创建多个 Checkbox 实例,每个都有独立的回调和状态。
  3. 使用 manager.add_checkbox() 添加它们。
  4. 在主循环中,调用 manager.handle_events(img)

示例

from maix import display, camera, app, touchscreen, image
from maixpy_ui import Checkbox, CheckboxManager
import time

# 1. 初始化硬件
disp = display.Display()
ts = touchscreen.TouchScreen()
cam = camera.Camera()

# 全局字典,用于存储每个复选框的状态
options = {'Checkbox A': True, 'Checkbox B': False}

# 2. 定义回调函数 (使用闭包来区分是哪个复选框被点击)
def create_checkbox_callback(key):
    def on_check_change(is_checked):
        options[key] = is_checked
        print(f"Option '{key}' is now {'checked' if is_checked else 'unchecked'}.")
    return on_check_change

# 3. 初始化UI
checkbox_manager = CheckboxManager(ts, disp)

checkbox_a = Checkbox(
    position=[80, 150],
    label="Checkbox A",
    is_checked=options['Checkbox A'],
    callback=create_checkbox_callback('Checkbox A'),
    scale=2.0
)
checkbox_b = Checkbox(
    position=[80, 300],
    label="Checkbox B",
    is_checked=options['Checkbox B'],
    callback=create_checkbox_callback('Checkbox B'),
    scale=2.0
)

checkbox_manager.add_checkbox(checkbox_a)
checkbox_manager.add_checkbox(checkbox_b)

# 4. 主循环
print("Checkbox example running. Tap the checkboxes.")
title_color = image.Color.from_rgb(255, 255, 255)
while not app.need_exit():
    img = cam.read()
    
    # 显示当前状态
    a_status = "ON" if options['Checkbox A'] else "OFF"
    b_status = "ON" if options['Checkbox B'] else "OFF"
    img.draw_string(20, 20, f"Checkbox A: {a_status}, Checkbox B: {b_status}", scale=1.5, color=title_color)
    
    checkbox_manager.handle_events(img)
    
    disp.show(img)
    time.sleep(0.02)

Checkbox

创建一个复选框(Checkbox)组件,可独立选中或取消。

构造函数: __init__
参数 类型 描述 默认值
position Sequence[int] 复选框的左上角坐标 [x, y]必需 -
label str 复选框旁边的标签文本。必需 -
scale float 复选框的整体缩放比例。 1.0
is_checked `bool int` 复选框的初始状态,True 为选中。
callback `Callable None` 状态切换时调用的函数,接收一个布尔值参数表示新状态。
box_color Sequence[int] 未选中时方框的颜色 (R, G, B)。 (200, 200, 200)
box_checked_color Sequence[int] 选中时方框的颜色 (R, G, B)。 (0, 120, 220)
check_color Sequence[int] 选中标记(对勾)的颜色 (R, G, B)。 (255, 255, 255)
text_color Sequence[int] 标签文本的颜色 (R, G, B)。 (200, 200, 200)
box_thickness int 方框边框的厚度。 2
方法 (Methods)
方法 参数 描述
toggle() - 切换复选框的选中状态,并执行回调。
draw(img) img (maix.image.Image): 将要绘制复选框的目标图像。 在指定的图像上绘制复选框。
handle_event(...) x (int): 触摸点的 X 坐标。
y (int): 触摸点的 Y 坐标。
pressed (`bool
int): 触摸屏是否被按下。<br>img_w (int): 图像缓冲区的宽度。<br>img_h (int): 图像缓冲区的高度。<br>disp_w (int): 显示屏的宽度。<br>disp_h (int`): 显示屏的高度。

CheckboxManager

管理一组复选框的事件处理和绘制。

构造函数: __init__
参数 类型 描述
ts touchscreen.TouchScreen 触摸屏设备实例。必需
disp display.Display 显示设备实例。必需
方法 (Methods)
方法 参数 描述
add_checkbox(checkbox) checkbox (Checkbox): 要添加的 Checkbox 实例。 向管理器中添加一个复选框。
handle_events(img) img (maix.image.Image): 绘制复选框的目标图像。 处理所有受管复选框的事件并进行绘制。

5. 单选框 (RadioButton)

允许用户从一组互斥的选项中只选择一项。

使用方式

  1. 创建一个 RadioManager 实例。注意RadioManager 构造时需要接收 default_value 和一个全局 callback
  2. 创建 RadioButton 实例,每个按钮必须有唯一的 value
  3. 使用 manager.add_radio() 添加它们。
  4. 在主循环中,调用 manager.handle_events(img)。管理器会自动处理互斥逻辑。

示例

from maix import display, camera, app, touchscreen, image
from maixpy_ui import RadioButton, RadioManager
import time

# 1. 初始化硬件
disp = display.Display()
ts = touchscreen.TouchScreen()
cam = camera.Camera()

# 全局变量,存储当前选择的模式
current_mode = None

# 2. 定义回调函数
# 这个回调由 RadioManager 调用,传入被选中项的 value
def on_mode_change(selected_value):
    global current_mode
    current_mode = selected_value
    print(f"Mode changed to: {selected_value}")

# 3. 初始化UI
# 创建 RadioManager,并传入默认值和回调
radio_manager = RadioManager(ts, disp, 
                           default_value=current_mode, 
                           callback=on_mode_change)

# 创建三个 RadioButton 实例,注意它们的 value 是唯一的
radio_a = RadioButton(position=[80, 100], label="Mode A", value="Mode A", scale=2.0)
radio_b = RadioButton(position=[80, 200], label="Mode B", value="Mode B", scale=2.0)
radio_c = RadioButton(position=[80, 300], label="Mode C", value="Mode C", scale=2.0)

# 将它们都添加到管理器中
radio_manager.add_radio(radio_a)
radio_manager.add_radio(radio_b)
radio_manager.add_radio(radio_c)

# 4. 主循环
print("Radio button example running. Select a mode.")
title_color = image.Color.from_rgb(255, 255, 255)
while not app.need_exit():
    img = cam.read()
    
    img.draw_string(20, 20, f"Current: {current_mode}", scale=1.8, color=title_color)
    
    radio_manager.handle_events(img)
    
    disp.show(img)
    time.sleep(0.02)

RadioButton

创建一个单选框(RadioButton)项。通常与 RadioManager 结合使用。

构造函数: __init__
参数 类型 描述 默认值
position Sequence[int] 单选框圆圈的左上角坐标 [x, y]必需 -
label str 按钮旁边的标签文本。必需 -
value any 与此单选框关联的唯一值。必需 -
scale float 组件的整体缩放比例。 1.0
circle_color Sequence[int] 未选中时圆圈的颜色 (R, G, B)。 (200, 200, 200)
circle_selected_color Sequence[int] 选中时圆圈的颜色 (R, G, B)。 (0, 120, 220)
dot_color Sequence[int] 选中时中心圆点的颜色 (R, G, B)。 (255, 255, 255)
text_color Sequence[int] 标签文本的颜色 (R, G, B)。 (200, 200, 200)
circle_thickness int 圆圈边框的厚度。 2
方法 (Methods)
方法 参数 描述
draw(img) img (maix.image.Image): 将要绘制单选框的目标图像。 在指定的图像上绘制单选框。

RadioManager

管理一个单选框组,确保只有一个按钮能被选中。

构造函数: __init__
参数 类型 描述 默认值
ts touchscreen.TouchScreen 触摸屏设备实例。必需 -
disp display.Display 显示设备实例。必需 -
default_value any 默认选中的按钮的值。 None
callback `Callable None` 选中项改变时调用的函数,接收新选中项的值作为参数。
方法 (Methods)
方法 参数 描述
add_radio(radio) radio (RadioButton): 要添加的 RadioButton 实例。 向管理器中添加一个单选框。
handle_events(img) img (maix.image.Image): 绘制单选框的目标图像。 处理所有单选框的事件并进行绘制。

6. 分辨率适配器 (ResolutionAdapter)

一个辅助工具类,用于自动适配不同分辨率的屏幕,以保持UI布局的一致性。

使用方式

  1. 创建一个 ResolutionAdapter 实例,并指定目标屏幕尺寸和可选的设计基础分辨率。
  2. 基于您的设计基础分辨率,定义组件的原始 rectposition 等参数。
  3. 调用 adapter.scale_rect() 等方法,将原始参数转换为适配后的值。
  4. 使用转换后的值来创建您的UI组件。

示例

from maix import display, camera, app, touchscreen
from maixpy_ui import Button, ButtonManager, ResolutionAdapter
import time

# 1. 初始化硬件
disp = display.Display()
ts = touchscreen.TouchScreen()
# cam = camera.Camera(640,480)
cam = camera.Camera(320,240)

# 2. 创建分辨率适配器,并明确指定我们的设计是基于 640x480 的
adapter = ResolutionAdapter(
    display_width=cam.width(), 
    display_height=cam.height(),
    base_width=640,
    base_height=480
)

# 3. 基于 640x480 的画布来定义组件参数
original_rect = [160, 200, 320, 80]
original_font_scale = 3.0 

# 4. 使用适配器转换参数
scaled_rect = adapter.scale_rect(original_rect)
scaled_font_size = adapter.scale_value(original_font_scale) 

# 5. 使用缩放后的值创建组件
btn_manager = ButtonManager(ts, disp)
adapted_button = Button(
    rect=scaled_rect,
    label="Big Button",
    text_scale=scaled_font_size,
    callback=lambda: print("Adapted button clicked!")
)
btn_manager.add_button(adapted_button)

# 6. 主循环
print("ResolutionAdapter example running (640x480 base).")
while not app.need_exit():
    img = cam.read()
    btn_manager.handle_events(img)
    disp.show(img)
    time.sleep(0.02)

ResolutionAdapter

构造函数: __init__
参数 类型 描述 默认值
display_width int 目标显示屏的宽度。必需 -
display_height int 目标显示屏的高度。必需 -
base_width int UI设计的基准宽度。 320
base_height int UI设计的基准高度。 240
方法 (Methods)
方法 参数 描述 返回值
scale_position(x, y) x (int): 原始 X 坐标。
y (int): 原始 Y 坐标。
缩放一个坐标点 (x, y)。 Sequence[int]
scale_size(width, height) width (int): 原始宽度。
height (int): 原始高度。
缩放一个尺寸 (width, height)。 Sequence[int]
scale_rect(rect) rect (list[int]): 原始矩形 [x, y, w, h] 缩放一个矩形。 Sequence[int]
scale_value(value) value (`int float`): 原始数值。 缩放一个通用数值,如半径、厚度等。

7. 页面与 UI 管理器 (Page and UIManager)

用于构建多页面应用,并管理页面间的树型导航。

使用方式

  1. 创建一个全局的 UIManager 实例。
  2. 定义继承自 Page 的自定义页面类,并在构造函数中为页面命名。
  3. 在父页面中,创建子页面的实例,并使用 parent.add_child() 方法来构建页面树。
  4. 使用 ui_manager.set_root_page() 设置应用的根页面。
  5. 在页面内部,通过 self.ui_manager 调用导航方法,如 navigate_to_child()navigate_to_parent()navigate_to_root() 等。
  6. 在主循环中,持续调用 ui_manager.update(img) 来驱动当前活动页面的更新和绘制。

示例

from maix import display, camera, app, touchscreen, image
from maixpy_ui import Page, UIManager, Button, ButtonManager
import time

# --------------------------------------------------------------------------
# 1. 初始化硬件 & 全局资源
# --------------------------------------------------------------------------
disp = display.Display()
ts = touchscreen.TouchScreen()
screen_w, screen_h = disp.width(), disp.height()
cam = camera.Camera(screen_w, screen_h)

# 预创建颜色对象
COLOR_WHITE = image.Color(255, 255, 255)
COLOR_GREY = image.Color(150, 150, 150)
COLOR_GREEN = image.Color(30, 200, 30)
COLOR_BLUE = image.Color(0, 120, 220)

def get_background():
    if cam:
        img = cam.read()
        if img: return img
    return image.new(size=(screen_w, screen_h), color=(10, 20, 30))

# --------------------------------------------------------------------------
# 2. 定义页面类
# --------------------------------------------------------------------------

class BasePage(Page):
    """一个包含通用功能的页面基类,例如绘制调试信息"""
    def draw_path_info(self, img: image.Image):
        """在屏幕右下角绘制当前的导航路径"""
        info = self.ui_manager.get_navigation_info()
        path_str = " > ".join(info['current_path'])
        
        # 计算文本尺寸
        text_scale = 1.0
        text_size = image.string_size(path_str, scale=text_scale)
        
        # 计算绘制位置(右下角,留出一些边距)
        padding = 10
        text_x = screen_w - text_size.width() - padding
        text_y = screen_h - text_size.height() - padding
        
        # 绘制文本
        img.draw_string(text_x, text_y, path_str, scale=text_scale, color=COLOR_GREY)
        
    def update(self, img: image.Image):
        """子类应该重写此方法,并在末尾调用 super().update(img) 来绘制调试信息"""
        self.draw_path_info(img)

class PageA1(BasePage):
    """最深层的页面"""
    def __init__(self, ui_manager):
        super().__init__(ui_manager, name="page_a1")
        self.btn_manager = ButtonManager(ts, disp)
        self.btn_manager.add_button(Button([40, 150, 400, 80], "Back to Parent (-> Page A)", lambda: self.ui_manager.navigate_to_parent()))
        self.btn_manager.add_button(Button([40, 250, 400, 80], "Go Back in History", lambda: self.ui_manager.go_back()))
        self.btn_manager.add_button(Button([40, 350, 400, 80], "Go to Root (Home)", lambda: self.ui_manager.navigate_to_root(), bg_color=COLOR_GREEN))

    def update(self, img):
        img.draw_string(20, 20, "Page A.1 (Deepest)", scale=2.0, color=COLOR_WHITE)
        history = self.ui_manager.navigation_history
        prev_page_name = history[-1].name if history else "None"
        img.draw_string(20, 80, f"'Go Back' will return to '{prev_page_name}'.", scale=1.2, color=COLOR_GREY)
        self.btn_manager.handle_events(img)
        super().update(img) # 调用基类的方法来绘制路径信息

class PageA(BasePage):
    """中间层页面 A"""
    def __init__(self, ui_manager):
        super().__init__(ui_manager, name="page_a")
        self.btn_manager = ButtonManager(ts, disp)
        self.btn_manager.add_button(Button([80, 150, 350, 80], "Go to Page A.1", lambda: self.ui_manager.navigate_to_child("page_a1")))
        self.btn_manager.add_button(Button([20, 400, 250, 80], "Back to Parent", lambda: self.ui_manager.navigate_to_parent()))
        self.add_child(PageA1(self.ui_manager))

    def update(self, img):
        img.draw_string(20, 20, "Page A", scale=2.5, color=COLOR_WHITE)
        self.btn_manager.handle_events(img)
        super().update(img)

class PageB(BasePage):
    """中间层页面 B"""
    def __init__(self, ui_manager):
        super().__init__(ui_manager, name="page_b")
        self.btn_manager = ButtonManager(ts, disp)
        self.btn_manager.add_button(Button([80, 150, 350, 80], "Jump to Page A.1 by Path", lambda: self.ui_manager.navigate_to_path(["page_a", "page_a1"])))
        self.btn_manager.add_button(Button([20, 400, 250, 80], "Back to Parent", lambda: self.ui_manager.navigate_to_parent()))

    def update(self, img):
        img.draw_string(20, 20, "Page B", scale=2.5, color=COLOR_WHITE)
        img.draw_string(20, 80, "From here, we'll jump to A.1.", scale=1.2, color=COLOR_GREY)
        img.draw_string(20, 110, "This will make 'Go Back' and 'Back to Parent' different on the next page.", scale=1.2, color=COLOR_GREY)
        self.btn_manager.handle_events(img)
        super().update(img)

class RootPage(BasePage):
    """根页面"""
    def __init__(self, ui_manager):
        super().__init__(ui_manager, name="root")
        self.btn_manager = ButtonManager(ts, disp)
        self.btn_manager.add_button(Button([80, 150, 350, 80], "Path 1: Go to Page A", lambda: self.ui_manager.navigate_to_child("page_a")))
        self.btn_manager.add_button(Button([80, 300, 350, 80], "Path 2: Go to Page B", lambda: self.ui_manager.navigate_to_child("page_b")))
        self.add_child(PageA(self.ui_manager))
        self.add_child(PageB(self.ui_manager))

    def update(self, img):
        img.draw_string(20, 20, "Root Page (Home)", scale=2.5, color=COLOR_WHITE)
        img.draw_string(20, 80, "Try both paths to see how 'Go Back' behaves differently.", scale=1.2, color=COLOR_GREY)
        self.btn_manager.handle_events(img)
        super().update(img) # 调用基类的方法来绘制路径信息

# --------------------------------------------------------------------------
# 3. 主程序逻辑
# --------------------------------------------------------------------------
if __name__ == "__main__":
    ui_manager = UIManager()
    root_page = RootPage(ui_manager)
    ui_manager.set_root_page(root_page)

    print("Navigation demo with persistent path display running.")

    while not app.need_exit():
        img = get_background()
        ui_manager.update(img)
        disp.show(img)
        time.sleep(0.02)

Page

页面(Page)的基类,支持树型父子节点结构。所有具体的UI页面都应继承此类。

构造函数: __init__
参数 类型 描述 默认值
ui_manager UIManager 用于页面导航的 UIManager 实例。必需 -
name str 页面的唯一名称标识符,用于在父页面中查找。 ""
方法 (Methods)
方法 参数 描述 返回值
add_child(page) page (Page): 要添加的子页面实例。 将一个页面添加为当前页面的子节点,以构建页面树。 -
remove_child(page) page (Page): 要移除的子页面实例。 从当前页面移除一个子节点。 bool
get_child(name) name (str): 子页面的名称。 根据名称获取子页面,用于自定义导航逻辑。 Page | None
on_enter() - 当页面进入视图时调用。子类可重写以实现初始化逻辑。 -
on_exit() - 当页面离开视图时调用。子类可重写以实现清理逻辑。 -
on_child_enter() child (Page): 进入视图的子页面。 当此页面的一个子页面进入视图时调用。父页面可重写。 -
on_child_exit() child (Page): 离开视图的子页面。 当此页面的一个子页面离开视图时调用。父页面可重写。 -
update(img) img (maix.image.Image): 用于绘制的图像缓冲区。 每帧调用的更新和绘制方法。子类必须重写此方法 -

UIManager

UI 管理器,基于树型页面结构提供灵活的导航功能。

构造函数: __init__
参数 类型 描述 默认值
root_page `Page None` 根页面实例,如果为None则需要后续设置。
方法 (Methods)
方法 参数 描述 返回值
set_root_page(page) page (Page): 新的根页面实例。 设置或重置UI管理器的根页面,并清空历史。 -
get_current_page() - 获取当前活动的页面。 Page | None
navigate_to_child(name) name (str): 子页面的名称。 导航到当前页面的指定名称的子页面。 bool
navigate_to_parent() - 导航到当前页面的父页面。 bool
navigate_to_root() - 直接导航到树的根页面。 bool
navigate_to_path(path) path (List[str]): 从根页面开始的绝对路径。 根据绝对路径导航到指定页面。 bool
go_back() - 返回到导航历史记录中的前一个页面。 bool
get_navigation_info() - 获取包含当前路径、历史深度等信息的字典,用于调试或显示。 dict
update(img) img (maix.image.Image): 用于绘制的图像缓冲区。 更新当前活动页面的状态。此方法应在主循环中每帧调用。 -

⚖️许可协议

本项目基于 Apache License, Version 2.0 许可。详细信息请参阅代码文件中的许可证说明。

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

maixpy_ui_lib-2.3.tar.gz (446.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

maixpy_ui_lib-2.3-py3-none-any.whl (34.2 kB view details)

Uploaded Python 3

File details

Details for the file maixpy_ui_lib-2.3.tar.gz.

File metadata

  • Download URL: maixpy_ui_lib-2.3.tar.gz
  • Upload date:
  • Size: 446.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.4

File hashes

Hashes for maixpy_ui_lib-2.3.tar.gz
Algorithm Hash digest
SHA256 7e9483ad6c2c12695f5ea3d120f724596490978a6251a43b8481784fea5d9893
MD5 f0336edf76da61cec6752b334d7b684a
BLAKE2b-256 6e8db016600c3e8e7d138f44930f203e26b084fb9e5a4171d24b645e82d10b3b

See more details on using hashes here.

File details

Details for the file maixpy_ui_lib-2.3-py3-none-any.whl.

File metadata

  • Download URL: maixpy_ui_lib-2.3-py3-none-any.whl
  • Upload date:
  • Size: 34.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.4

File hashes

Hashes for maixpy_ui_lib-2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 f26d536d3d63dcd3e4871829d50354fe829996d85b25e01b1c3a670bba84d87e
MD5 ceb74aaf86a2e5ffc3a9e2436db7f308
BLAKE2b-256 e52402646864403c2690e102785884a175f4e4af2ec3324182ad478572330647

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page