XGO-Rider2Wiki 首页
Wiki 首页/XGO-Rider2/Luwu-OS 用户手册 / 开发篇
XGO-Rider2 知识库Luwu-OS 用户手册 / 开发篇

03 应用开发

适用版本新版 Luwu-OS
版本说明
系列共用文档切换机型,继续阅读本篇
共用内容按本机型的执行器、外设和版本使用。

照着现有应用的样子,写你的第一个 Luwu-OS 应用。本章给你一套可直接复制的最小模板,讲清主题、国际化和注册方式,让你能快速把一个新应用挂到主界面上跑起来。

本章包含:

小节

内容

难度

3.1 PySide6 应用模板

一个可直接运行的最小应用(模板代码)

★★☆

3.2 主题系统

颜色/字号/资源 Token 与 QSS 辅助函数

★★☆

3.3 国际化

中英双语切换(Translator / t / get_lang)

★☆☆

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 创建新应用

  1. mkdir -p /opt/luwu-os/apps/my_new_app

  2. 创建 main.py(参考上方模板)

  3. 注册到主界面:编辑 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 机型时使用),与新增应用无关,无需修改。

  1. 测试启动:

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 / voice_chat

AI Chat Pro(新版语音对话,复用 apps/ai 的唤醒词/表情模块)

语音对话、唤醒词、MQTT、云端绑定、表情动画

ai

旧版 AI Chat Dev(开发版 AI,http://<机器狗IP>:5000 配置页)

LLM、TTS、ASR、ONNX 情绪识别

coding

Blockly 图形化编程

Blockly、Python 代码生成

face_follow

人脸检测与跟踪

MediaPipe Face Detection、Picamera2

ball_track

颜色小球识别与跟踪

OpenCV 颜色过滤、PID 控制

gesture

手势命令控制

MediaPipe Hands、ONNX

gamepad

蓝牙/USB 手柄操控

evdev、蓝牙 HID、摇杆校准

rc_mode

网页远程遥控

Flask Web 服务、MJPEG 推流

radar

360° 环境扫描可视化

YDLiDAR SDK、实时渲染

group_perform

多机器人同步编舞

MQTT 发布/订阅、时间同步

settings

系统配置与设备信息

语言切换、音量控制

📦 完整的应用列表与界面见官方 luwu_os README。实际应用进程按需渲染到 /dev/fb-spi,并统一使用 libs/ui 与 libs/theme 组件,避免各应用自写渲染逻辑。

资料来源XGO 官方 Wiki
返回 XGO-Rider2 知识库目录