MaixPy-UI-Lib:一款为 MaixPy 开发的轻量级 UI 组件库
本项目是一款为 MaixPy 开发的轻量级 UI 组件库,遵循 Apache 2.0 协议。
欢迎给本项目提pr,要是觉得好用的话请给本项目点个star⭐
🖼️功能展示
以下画面均截取自本项目的 main.py
📦 安装
pip install maixpy-ui-lib
🚀快速上手
您可以通过运行仓库中的示例程序 examples/demo.py 来快速熟悉本项目的基本功能和使用方法。
📖组件详解
1. 按钮 (Button)
按钮是最基本的交互组件,用于触发一个操作。
使用方式
- 创建一个
ButtonManager实例。 - 创建
Button实例,定义其矩形区域、标签文本和回调函数。 - 使用
manager.add_button()将按钮实例添加到管理器中。 - 在主循环中,调用
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)。 | (50, 50, 50) |
pressed_color |
Sequence[int] | None |
按下状态的背景颜色 (R, G, B)。 | (0, 120, 220) |
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 |
使用的字体文件路径。 | 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): 触摸屏是否被按下。img_w (int): 图像缓冲区的宽度。img_h (int): 图像缓冲区的高度。disp_w (int): 显示屏的宽度。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)
滑块允许用户在一个连续的范围内选择一个值。
使用方式
- 创建一个
SliderManager实例。 - 创建
Slider实例,定义其区域、数值范围和回调函数。 - 使用
manager.add_slider()添加滑块。 - 在主循环中,调用
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 |
值改变时调用的函数,接收新值作为参数。 | 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 |
是否在拖动时显示数值提示框。 | True |
方法 (Methods)
| 方法 | 参数 | 描述 |
|---|---|---|
draw(img) |
img (maix.image.Image): 将要绘制滑块的目标图像。 |
在指定的图像上绘制滑块。 |
handle_event(...) |
x (int): 触摸点的 X 坐标。y (int): 触摸点的 Y 坐标。pressed (bool|int): 触摸屏是否被按下。img_w (int): 图像缓冲区的宽度。img_h (int): 图像缓冲区的高度。disp_w (int): 显示屏的宽度。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)
一个具有“开”和“关”两种状态的切换控件。
使用方式
- 创建一个
SwitchManager实例。 - 创建
Switch实例,定义其位置、初始状态和回调函数。 - 使用
manager.add_switch()添加开关。 - 在主循环中,调用
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 为开。 | False |
callback |
Callable | None |
状态切换时调用的函数,接收一个布尔值参数表示新状态。 | 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): 触摸屏是否被按下。img_w (int): 图像缓冲区的宽度。img_h (int): 图像缓冲区的高度。disp_w (int): 显示屏的宽度。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)
允许用户从一组选项中进行多项选择。
使用方式
- 创建一个
CheckboxManager实例。 - 创建多个
Checkbox实例,每个都有独立的回调和状态。 - 使用
manager.add_checkbox()添加它们。 - 在主循环中,调用
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 为选中。 | False |
callback |
Callable | None |
状态切换时调用的函数,接收一个布尔值参数表示新状态。 | 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): 触摸屏是否被按下。img_w (int): 图像缓冲区的宽度。img_h (int): 图像缓冲区的高度。disp_w (int): 显示屏的宽度。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)
允许用户从一组互斥的选项中只选择一项。
使用方式
- 创建一个
RadioManager实例。注意:RadioManager构造时需要接收default_value和一个全局callback。 - 创建
RadioButton实例,每个按钮必须有唯一的value。 - 使用
manager.add_radio()添加它们。 - 在主循环中,调用
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 |
选中项改变时调用的函数,接收新选中项的值作为参数。 | None |
方法 (Methods)
| 方法 | 参数 | 描述 |
|---|---|---|
add_radio(radio) |
radio (RadioButton): 要添加的 RadioButton 实例。 |
向管理器中添加一个单选框。 |
handle_events(img) |
img (maix.image.Image): 绘制单选框的目标图像。 |
处理所有单选框的事件并进行绘制。 |
6. 分辨率适配器 (ResolutionAdapter)
一个辅助工具类,用于自动适配不同分辨率的屏幕,以保持UI布局的一致性。
使用方式
- 创建一个
ResolutionAdapter实例,并指定目标屏幕尺寸和可选的设计基础分辨率。 - 基于您的设计基础分辨率,定义组件的原始
rect、position等参数。 - 调用
adapter.scale_rect()等方法,将原始参数转换为适配后的值。 - 使用转换后的值来创建您的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): 原始数值。 |
缩放一个通用数值,如半径、厚度等。 | float |
7. 页面与 UI 管理器 (Page and UIManager)
用于构建多页面应用,并管理页面间的树型导航。
使用方式
- 创建一个全局的
UIManager实例。 - 定义继承自
Page的自定义页面类,并在构造函数中为页面命名。 - 在父页面中,创建子页面的实例,并使用
parent.add_child()方法来构建页面树。 - 使用
ui_manager.set_root_page()设置应用的根页面。 - 在页面内部,通过
self.ui_manager调用导航方法,如navigate_to_child()、navigate_to_parent()、navigate_to_root()等。 - 在主循环中,持续调用
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则需要后续设置。 | None |
方法 (Methods)
| 方法 | 参数 | 描述 | 返回值 |
|---|---|---|---|
set_root_page(page) |
page (Page): 新的根页面实例。 |
设置或重置UI管理器的根页面,并清空历史。 | None |
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 |
navigate_to_relative_path(path) |
path (List[str]): 从当前页面开始的相对路径。 |
根据相对路径导航到指定页面。 | bool |
navigate_to_page(target_page) |
target_page (Page): 目标页面实例。 |
直接导航到指定页面。 | bool |
go_back() |
- | 返回到导航历史记录中的前一个页面。 | bool |
remove_page(page) |
page (Page): 要移除的页面实例。 |
移除指定的页面,并从父页面子页面列表中删除。 | bool |
clear_history() |
- | 清空导航历史记录。 | None |
get_current_path() |
- | 获取当前页面的完整路径。 | List[str] |
get_navigation_info() |
- | 获取包含当前路径、历史深度等信息的字典,用于调试或显示。 | dict |
update(img) |
img (maix.image.Image): 用于绘制的图像缓冲区。 |
更新当前活动页面的状态。此方法应在主循环中每帧调用。 | None |
⚖️许可协议
本项目基于 Apache License, Version 2.0 许可。详细信息请参阅代码文件中的许可证说明。
Metadata
Release files for maixpy-ui-lib 2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| maixpy_ui_lib-2.4.tar.gz | 446.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| maixpy_ui_lib-2.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 481.2 kB
Release files / maixpy_ui_lib-2.4.tar.gz
| Download URL | maixpy_ui_lib-2.4.tar.gz |
|---|---|
| Size | 446.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c90769319333f04b926dcbf5f60e2519ca0c0131f8d834ad96f0b810d12f2a81
|
|
BLAKE2b-256 checksum How to use checksums |
5b64b587dbed43c80f735dd6de2cd0748102eb7f22271d2b68df708707a27f03
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.4
|
Release files / maixpy_ui_lib-2.4-py3-none-any.whl
| Download URL | maixpy_ui_lib-2.4-py3-none-any.whl |
|---|---|
| Size | 34.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
28354ab7c9cf2e3f26ca198cb2933f1acfc20489af9ca9f0ab6b70652e5db86c
|
|
BLAKE2b-256 checksum How to use checksums |
839fb38cf72e8163ee1023e41c7ff92b0b1646e869299c313fabd34d3a16d29f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.4
|