照着现有应用的样子,写你的第一个 Luwu-OS 应用。本章给你一套可直接复制的最小模板,讲清主题、国际化和注册方式,让你能快速把一个新应用挂到主界面上跑起来。
本章包含:
小节 | 内容 | 难度 |
|---|---|---|
3.1 PySide6 应用模板 | 一个可直接运行的最小应用(模板代码) | ★★☆ |
3.2 主题系统 | 颜色/字号/资源 Token 与 QSS 辅助函数 | ★★☆ |
3.3 国际化 | 中英双语切换( | ★☆☆ |
3.4 创建新应用 | 从建目录到注册到主界面的完整步骤 | ★★☆ |
3.5 现有应用参考 | 官方各应用的技术栈一览(二次开发范例) | ★☆☆ |
🧭 阅读路径:第一次做应用,按 3.1 → 3.4 走一遍;想写得好,再回看 3.2 / 3.3 的主题与国际化学约定。
前置条件:
完成第一章,已能 SSH 登录机器狗;
读过第二章的目录结构与 IPC,了解
apps/与/tmp/luwu_run.fifo;会基本的 Python 3 与 PySide6(不熟也能照模板改)。
3.1 PySide6 应用模板
先看结论:在 Luwu-OS 里写一个应用,就是「一个继承 AppFrame 的类 + 一个 main()」。为什么是这种结构?因为 launcher(Qt C++)负责应用生命周期,应用只需把自己的界面渲染出来,并通过继承 AppFrame 拿到标题栏、角标、按键退出这些公共能力——你不需要自己写窗口框架。
#!/usr/bin/env python3
import os, sys, time, signal
# Luwu-OS 路径
LUWU_ROOT = os.environ.get("LUWU_ROOT", "/opt/luwu-os")
if LUWU_ROOT not in sys.path:
sys.path.insert(0, LUWU_ROOT)
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QKeyEvent
from PySide6.QtWidgets import QApplication, QWidget, QLabel
from libs.theme import apply_app_palette, Asset
from libs.ui import AppFrame
from libs.i18n import Translator
_T = Translator({
"cn": {"title": "我的应用", "corner_exit": "退出"},
"en": {"title": "My App", "corner_exit": "Exit"},
})
class MyPage(AppFrame):
def __init__(self):
super().__init__()
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.setTitle(_T("title"))
self.setCornerHints(
bl=(_T("corner_exit"), Asset.icon_back),
)
QTimer.singleShot(600000, self.close) # 10分钟自动退出
def keyPressEvent(self, ev: QKeyEvent):
if ev.key() == Qt.Key.Key_Back:
self.close()
def main():
signal.signal(signal.SIGINT, lambda *_: QApplication.instance().quit())
signal.signal(signal.SIGTERM, lambda *_: QApplication.instance().quit())
app = QApplication(sys.argv)
apply_app_palette(app)
w = MyPage()
w.showFullScreen()
sys.exit(app.exec())
if __name__ == "__main__":
main()
3.1.1 运行这个模板
把上面模板存成 apps/my_new_app/main.py,然后:
cd /opt/luwu-os/apps/my_new_app && python3 main.py # 裸跑:看逻辑(无主题/按键路由)
# 或让 launcher 完整启动(推荐,验证真实效果):
echo "apps/my_new_app/main.py" > /tmp/luwu_run.fifo
✅ 预期效果:实体屏全屏显示「我的应用」,标题栏右上角有「退出」角标;按 C 键(Back) 或左上角返回图标退出;10 分钟无操作自动关闭(
QTimer.singleShot(600000, ...))。⚠️ 裸跑若报
ModuleNotFoundError: PySide6或No module named 'libs',说明没在 Luwu-OS 环境里跑(需在机器狗、且LUWU_ROOT=/opt/lwu-os);若报Failed to open /dev/fb-spi,多为缺 launcher 上下文,改用 fifo 方式启动即可。
3.2 主题系统
Luwu-OS 采用与 launcher 视觉同源的浅色主题,颜色 / 字号 / 间距 / 圆角 / 资源路径常量定义在 libs/theme/tokens.py:
from libs.theme import Color, Font, Spacing, Radius, Asset, qss
# 颜色 Token(浅色主题)
Color.bg_solid # 纯色背景 #eaf0fb
Color.text_primary # 主文字色 #1a3a6e
Color.text_secondary # 次要文字色 #5d7299
Color.text_muted # 弱化文字色 #8aa1c7
Color.text_invert # 反色文字(深色背景上) #ffffff
Color.accent # 强调色 #3a8dff
Color.success # 成功色 #18a957
Color.warning # 警告色 #e69900
Color.danger # 危险色 #d6453d
Color.card_bg # 卡片背景 rgba(255,255,255,200)
# 字号 Token(点值)
Font.family = "Noto Sans CJK SC"
Font.title / Font.subtitle / Font.body / Font.hint / Font.caption
# 18 15 14 12 11
# 资源 Token(位于 launcher/assets/)
Asset.bg_image # 主背景图 bg_macos.png
Asset.icon_back # 返回图标
Asset.icon_enter # 确认/进入图标
Asset.icon_left # 左箭头
Asset.icon_right # 右箭头
# QSS 辅助(libs/theme/qss.py,真实函数签名)
qss.app_palette() # 应用全局基础 QSS(字体/滚动条等)
qss.app_root() # 应用根样式
qss.text(role="body", color=None) # 文字样式(role: title/subtitle/body/hint/caption)
qss.card(selected=False) # 卡片样式
qss.chip(state="info") # 状态芯片(info/success/warning/danger/muted)
qss.transparent() # 透明背景
qss.overlay_pill(role="body", color=None, strong=False) # 叠加胶囊(如提示条)
qss.corner_pill(color=None) # 圆角胶囊(如角标)
💡 子应用应直接使用这些组件与 token,禁止各自挑色 / 挑字号 / 手写
setStyleSheet(见libs/ui/__init__.py中的约定)。
实际用一次主题 token(一个带样式的标签):
from PySide6.QtWidgets import QLabel
from libs.theme import qss, Color
label = QLabel("提示信息")
label.setStyleSheet(qss.text(role="body", color=Color.warning))
这样文字就会套上主题的警告色,而不是你自己写死颜色;需要圆角卡片 / 状态芯片时用 qss.card() / qss.chip()。
3.3 国际化
语言配置唯一来源:/opt/luwu-os/configs/language.ini(内容仅为 cn 或 en 一行纯文本)。
from libs.i18n import Translator, t, get_lang, FONT_PATH
# 方式一:Translator 词典对象
_T = Translator({
"cn": {"hello": "你好"},
"en": {"hello": "Hello"},
})
label.setText(_T("hello"))
# 方式二:t() 快捷函数(单条翻译)
label.setText(t({"cn": "你好", "en": "Hello"}))
# 当前语言
lang = get_lang() # "cn" 或 "en"
中文字体路径 FONT_PATH:优先使用 model/msyh.ttc,fallback 到 DroidSansFallbackFull.ttf / DejaVuSans.ttf(见 libs/i18n.py 的 _FONT_CANDIDATES)。
语言何时生效?get_lang() 每次调用都会读取 configs/language.ini,因此运行中修改该文件、重启应用即可切换语言,无需重启系统:
echo "en" > /opt/luwu-os/configs/language.ini # 切成英文
echo "cn" > /opt/luwu-os/configs/language.ini # 切回中文
💡 官方应用在
main.py启动时读一次语言,运行中不自动热切换;你自定义的应用也照此处理即可。
3.4 创建新应用
mkdir -p /opt/luwu-os/apps/my_new_app创建
main.py(参考上方模板)注册到主界面:编辑
launcher/galleryview.cpp中的CARDS数组,加一行卡片定义(中文名、英文名、卡片图片、脚本路径):
// launcher/galleryview.cpp
const CardData CARDS[CARD_COUNT] = {
{"无线网络", "WiFi", "card_network.png", "apps/network/main.py"},
{"图形化编程", "Coding", "card_coding.png", "apps/coding/main.py"},
{"AI交互", "AI Chat", "card_ai.png", "apps/ai_chat_pro/main.py"},
{"示例程序", "Demos", "card_more.png", "apps/demo_page/main.py"},
{"系统设置", "Settings", "card_settings.png", "apps/settings/main.py"},
// 新增:{"我的应用", "My App", "card_mine.png", "apps/my_new_app/main.py"},
};
若希望出现在「示例程序」网格中,则编辑 launcher/demogridview.cpp 的演示程序数组(可带机型过滤标记,如 "@dog" / "!xgorider")。
⚠️
launcher/devicetable.h是机型注册表(新增 XGO 机型时使用),与新增应用无关,无需修改。
测试启动:
cd /opt/luwu-os/apps/my_new_app && python3 main.py # 直接运行
# 或由 launcher 完整启动(自动套用主题/按键路由):
echo "apps/my_new_app/main.py" > /tmp/luwu_run.fifo
⚠️ 常见问题: - 启动后没出现在主界面 → 检查
galleryview.cpp的CARDS是否新增了卡片(而不是替换); - 图标不显示 → 卡片图片路径写错,或图片不在launcher/assets/; - 点卡片没反应 →CARDS里的脚本路径应写成apps/xxx/main.py(相对LUWU_ROOT),不要写绝对路径; - 想做成「示例程序」页里的网格项 → 改demogridview.cpp,并留意机型过滤标记(@dog/!xgorider)。
3.5 现有应用参考(核心技术栈)
官方 apps/ 下的应用可作为二次开发的范例,各自的核心技术如下:
应用 | 描述 | 核心技术 |
|---|---|---|
| AI Chat Pro(新版语音对话,复用 | 语音对话、唤醒词、MQTT、云端绑定、表情动画 |
| 旧版 AI Chat Dev(开发版 AI, | LLM、TTS、ASR、ONNX 情绪识别 |
| Blockly 图形化编程 | Blockly、Python 代码生成 |
| 人脸检测与跟踪 | MediaPipe Face Detection、Picamera2 |
| 颜色小球识别与跟踪 | OpenCV 颜色过滤、PID 控制 |
| 手势命令控制 | MediaPipe Hands、ONNX |
| 蓝牙/USB 手柄操控 | evdev、蓝牙 HID、摇杆校准 |
| 网页远程遥控 | Flask Web 服务、MJPEG 推流 |
| 360° 环境扫描可视化 | YDLiDAR SDK、实时渲染 |
| 多机器人同步编舞 | MQTT 发布/订阅、时间同步 |
| 系统配置与设备信息 | 语言切换、音量控制 |
📦 完整的应用列表与界面见官方 luwu_os README。实际应用进程按需渲染到
/dev/fb-spi,并统一使用libs/ui与libs/theme组件,避免各应用自写渲染逻辑。
