编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

  • iOS开发和进阶

  • Web开发和进阶

  • Linux应用开发

    • Linux应用开发
    • QML基础入门

      • QML基础入门
      • 嵌入式GUI技术全景
        • 1.1 案例引入
          • 1.1.1 一个嵌入式项目的 GUI 选型困境
          • 1.1.2 五大主流方案的"第一印象"
          • 1.1.3 本文要回答的核心问题
        • 1.2 嵌入式 GUI 的本质
          • 1.2.1 GUI 在嵌入式中的定位
          • 1.2.2 嵌入式与桌面 GUI 的本质差异
          • 1.2.3 图形渲染的硬件基础
        • 1.3 主流技术方案深度对比
          • 1.3.1 裸机方案:直接操作 Framebuffer
          • 1.3.2 LVGL:轻量级嵌入式图形库
          • 1.3.3 Qt Widgets:传统桌面控件体系
          • 1.3.4 Qt QML:声明式 GPU 加速界面
          • 1.3.5 HTML5/Electron:Web 技术栈方案
          • 1.3.6 Flutter Embedded:跨平台新势力
        • 1.4 选型决策与量化评估
          • 1.4.1 六维评估模型
          • 1.4.2 选型决策树
          • 1.4.3 典型场景推荐
        • 1.5 Qt/QML 技术栈全景
          • 1.5.1 Qt 框架模块家族
          • 1.5.2 Qt5 vs Qt6 核心差异
          • 1.5.3 QML 的设计哲学
        • 1.6 QML 渲染管线概览
          • 1.6.1 从 .qml 文件到屏幕像素
          • 1.6.2 Scene Graph 批处理原理
          • 1.6.3 QML vs HTML5 渲染管线对比
        • 1.7 嵌入式运行环境
          • 1.7.1 QPA 平台抽象层
          • 1.7.2 EGLFS:无窗口系统的 GPU 渲染
          • 1.7.3 LinuxFB:纯软件渲染退路
          • 1.7.4 Wayland:现代合成器方案
        • 1.8 QML 嵌入式生态能力
          • 1.8.1 硬件通信(串口/CAN/GPIO)
          • 1.8.2 多媒体(摄像头/音视频)
          • 1.8.3 网络与 OTA 升级
          • 1.8.4 数据库与本地存储
        • 1.9 完整项目示例
          • 1.9.1 项目结构
          • 1.9.2 CMakeLists.txt
          • 1.9.3 main.cpp
          • 1.9.4 QML 界面
          • 1.9.5 交叉编译与运行
        • 1.10 学习路径与速查表
          • 1.10.1 从 0 到 1 的十周路径
          • 1.10.2 技术选型速查表
          • 1.10.3 本专栏路线图
      • QML引擎与渲染原理
      • QML语法与类型系统
      • 属性绑定与响应式原理
      • 可视元素与布局原理
      • 事件处理与传播机制
      • 模型视图架构原理
      • 动画与状态机原理
      • Canvas与自定义渲染
      • QML与C++集成原理
      • 自定义SceneGraph节点
      • 交叉编译与部署
      • 嵌入式渲染后端
      • 性能优化与真机调试
    • QT核心库实践

  • IoT智能硬件开发

  • Apps
  • Linux应用开发
  • QML基础入门
杨充
2025-06-24
目录

嵌入式GUI技术全景

# 01.嵌入式GUI技术全景

从嵌入式 GUI 选型到 Qt/QML 架构全景——为什么 QML 是嵌入式交互界面的最优解?本文是整个专栏的"地图"。

# 目录介绍

  • 1.1 案例引入
    • 1.1.1 一个嵌入式项目的 GUI 选型困境
    • 1.1.2 五大主流方案的"第一印象"
    • 1.1.3 本文要回答的核心问题
  • 1.2 嵌入式 GUI 的本质
    • 1.2.1 GUI 在嵌入式中的定位
    • 1.2.2 嵌入式与桌面 GUI 的本质差异
    • 1.2.3 图形渲染的硬件基础
  • 1.3 主流技术方案深度对比
    • 1.3.1 裸机方案:直接操作 Framebuffer
    • 1.3.2 LVGL:轻量级嵌入式图形库
    • 1.3.3 Qt Widgets:传统桌面控件体系
    • 1.3.4 Qt QML:声明式 GPU 加速界面
    • 1.3.5 HTML5/Electron:Web 技术栈方案
    • 1.3.6 Flutter Embedded:跨平台新势力
  • 1.4 选型决策与量化评估
    • 1.4.1 六维评估模型
    • 1.4.2 选型决策树
    • 1.4.3 典型场景推荐
  • 1.5 Qt/QML 技术栈全景
    • 1.5.1 Qt 框架模块家族
    • 1.5.2 Qt5 vs Qt6 核心差异
    • 1.5.3 QML 的设计哲学
  • 1.6 QML 渲染管线概览
    • 1.6.1 从 .qml 文件到屏幕像素
    • 1.6.2 Scene Graph 批处理原理
    • 1.6.3 QML vs HTML5 渲染管线对比
  • 1.7 嵌入式运行环境
    • 1.7.1 QPA 平台抽象层
    • 1.7.2 EGLFS:无窗口系统的 GPU 渲染
    • 1.7.3 LinuxFB:纯软件渲染退路
    • 1.7.4 Wayland:现代合成器方案
  • 1.8 QML 嵌入式生态能力
    • 1.8.1 硬件通信(串口/CAN/GPIO)
    • 1.8.2 多媒体(摄像头/音视频)
    • 1.8.3 网络与 OTA 升级
    • 1.8.4 数据库与本地存储
  • 1.9 完整项目示例
    • 1.9.1 项目结构
    • 1.9.2 CMakeLists.txt
    • 1.9.3 main.cpp
    • 1.9.4 QML 界面
    • 1.9.5 交叉编译与运行
  • 1.10 学习路径与速查表
    • 1.10.1 从 0 到 1 的十周路径
    • 1.10.2 技术选型速查表
    • 1.10.3 本专栏路线图

# 1.1 案例引入

# 1.1.1 一个嵌入式项目的 GUI 选型困境

想象你接到一个项目:为一款工业 HMI 触摸屏开发 GUI 界面,硬件参数如下:

┌─────────────────────────────────────────┐
│  硬件规格                                │
│  • SoC: ARM Cortex-A53 四核 1.2GHz      │
│  • RAM: 512MB DDR3                       │
│  • GPU: Mali-400 MP2(支持 OpenGL ES 2.0)│
│  • 屏幕: 7 英寸 1024×600 电容触摸屏      │
│  • 存储: 4GB eMMC                        │
│  • OS: Yocto Linux 5.x                   │
├─────────────────────────────────────────┤
│  功能需求                                │
│  • 实时显示 8 路传感器数据(10Hz 刷新)   │
│  • 流畅的页面切换动画(目标 60fps)       │
│  • CAN 总线通信 + 串口数据采集           │
│  • 支持 OTA 远程升级                     │
│  • 开机到首屏 < 3 秒                     │
└─────────────────────────────────────────┘

团队内部意见分歧:

  • 张工主张 LVGL:"轻量,资源占用低"
  • 李工推荐 Qt Widgets:"生态成熟,控件丰富"
  • 王工建议 HTML5 + Chromium:"前端人才好招"
  • 赵工力挺 Qt QML:"GPU 加速,动画流畅"

到底选哪个? 这不是拍脑袋能回答的——需要从渲染原理、硬件适配、性能天花板、开发效率、维护成本五个维度做系统评估。

# 1.1.2 五大主流方案的"第一印象"

┌───────────────────────────────────────────────────────────┐
│             嵌入式 GUI 技术栈全景图                          │
├─────────────┬────────────┬──────────┬──────────┬──────────┤
│  裸机 FB    │   LVGL     │ Qt Widget│  Qt QML  │  HTML5   │
│  手动画点   │  C 语言     │  C++控件  │  声明式   │  Web引擎  │
│  最低资源   │  MCU级别    │  CPU渲染  │  GPU加速  │  最重     │
│  无抽象     │  中等抽象   │  高抽象    │  高抽象   │  最高抽象  │
├─────────────┼────────────┼──────────┼──────────┼──────────┤
│  RAM: <1MB  │  RAM: 32KB+│  32MB+   │  64MB+   │  256MB+  │
│  Flash:<64K │  Flash:64K+│  16MB+   │  32MB+   │  128MB+  │
└─────────────┴────────────┴──────────┴──────────┴──────────┘

# 1.1.3 本文要回答的核心问题

  1. 嵌入式 GUI 的本质是什么?与桌面 GUI 有何根本差异?
  2. 各方案的渲染原理是什么?性能天花板在哪里?
  3. 如何建立量化的选型决策框架?
  4. Qt/QML 的技术架构是怎样的?为什么适合嵌入式?
  5. QML 如何在没有 X11/Wayland 的情况下直接输出到屏幕?
  6. 一个完整的嵌入式 QML 项目长什么样?

# 1.2 嵌入式 GUI 的本质

# 1.2.1 GUI 在嵌入式中的定位

┌─────────────────────────────────────────────┐
│  嵌入式系统分层架构                           │
│                                              │
│  ┌────────────────────────────────────┐     │
│  │   应用层(业务逻辑 + GUI 界面)      │     │
│  ├────────────────────────────────────┤     │
│  │   中间件(通信协议/数据库/OTA)      │     │
│  ├────────────────────────────────────┤     │
│  │   GUI 框架(Qt/LVGL/Web Engine)    │ ←── 本文焦点
│  ├────────────────────────────────────┤     │
│  │   图形后端(OpenGL ES/DRM/FB)      │     │
│  ├────────────────────────────────────┤     │
│  │   Linux 内核(驱动/调度/内存管理)   │     │
│  ├────────────────────────────────────┤     │
│  │   硬件(SoC/GPU/LCD/触摸屏)        │     │
│  └────────────────────────────────────┘     │
└─────────────────────────────────────────────┘

GUI 框架的核心职责:

  • 布局计算:确定每个 UI 元素的位置和尺寸
  • 渲染输出:将 UI 元素转化为像素数据写入显存
  • 事件处理:接收触摸/按键输入并派发到对应元素
  • 状态管理:维护 UI 与业务数据的同步

# 1.2.2 嵌入式与桌面 GUI 的本质差异

维度 桌面 GUI 嵌入式 GUI
窗口系统 有(X11/Wayland/Win32) 通常没有(直接输出到 LCD)
GPU 强大的独立显卡 集成 GPU(Mali/Vivante/PowerVR),可能无 GPU
内存 8~32GB 64MB~1GB
多窗口 多应用多窗口 通常单应用全屏
分辨率 1080p~4K 480×320~1920×1080
帧率要求 60fps(桌面操作) 30~60fps(看场景)
启动速度 可接受 10 秒以上 要求 < 3 秒到首屏
功耗约束 无限制 严格(电池/散热)
运行环境 完整 OS 精简 Linux/RTOS

核心差异:嵌入式 GUI 往往独占整个屏幕(没有窗口管理器),与硬件的耦合更紧密,资源约束更苛刻。

# 1.2.3 图形渲染的硬件基础

理解 GUI 选型,必须先理解渲染管线的硬件基础:

┌──────────────────────────────────────────────────────────────┐
│  LCD 显示原理                                                  │
│                                                               │
│  CPU/GPU → Framebuffer(显存) → LCD 控制器 → 液晶屏            │
│                                                               │
│  Framebuffer 本质:一块连续内存,存储每个像素的颜色值             │
│  1024×600×4字节(ARGB) = 2.4MB/帧                              │
│  60fps 需要 144MB/s 的带宽                                     │
└──────────────────────────────────────────────────────────────┘

两种渲染路径:
┌─────────────────────────────────────────────────────────────┐
│  路径 A:CPU 软件渲染(Software Rendering)                    │
│  CPU 逐像素计算颜色值 → 写入 Framebuffer                       │
│  优点:无需 GPU 驱动,兼容性好                                  │
│  缺点:CPU 占用高,复杂动画容易掉帧                             │
├─────────────────────────────────────────────────────────────┤
│  路径 B:GPU 硬件加速(Hardware Accelerated)                   │
│  CPU 提交绘制指令 → GPU 并行处理 → 输出到 Framebuffer          │
│  优点:适合大面积填充/纹理贴图/矩阵变换,CPU 几乎空闲           │
│  缺点:需要 GPU 驱动(EGL/OpenGL ES),增加系统复杂度           │
└─────────────────────────────────────────────────────────────┘

关键指标:在 ARM Cortex-A53 + Mali-400 的平台上:

  • 纯 CPU 软件渲染:复杂界面约 15~25fps
  • GPU 加速渲染:同样界面轻松 60fps,CPU 占用仅 5~10%

# 1.3 主流技术方案深度对比

# 1.3.1 裸机方案:直接操作 Framebuffer

原理:打开 /dev/fb0 设备文件,直接往 Framebuffer 内存中写像素数据。

// Linux Framebuffer 直接绘制——最底层的方式
#include <linux/fb.h>
#include <sys/mman.h>
#include <fcntl.h>

int fd = open("/dev/fb0", O_RDWR);
struct fb_var_screeninfo vinfo;
ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);

// 计算 framebuffer 大小
int screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
char *fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

// 画一个红色矩形 (100,100) ~ (200,200)
for (int y = 100; y < 200; y++) {
    for (int x = 100; x < 200; x++) {
        long offset = (y * vinfo.xres + x) * 4;  // 4 字节/像素 (ARGB)
        fbp[offset + 0] = 0xFF;  // Blue
        fbp[offset + 1] = 0x00;  // Green
        fbp[offset + 2] = 0x00;  // Red (BGR 格式在某些平台)
        fbp[offset + 3] = 0xFF;  // Alpha
    }
}

优势:

  • 零依赖,二进制极小(几 KB)
  • 完全掌控每一个像素
  • 启动速度极快(无框架初始化开销)

劣势:

  • 没有布局系统(手动计算坐标)
  • 没有事件处理(手动读取 /dev/input/eventX)
  • 没有字体渲染(需要自己集成 FreeType)
  • 没有动画框架
  • 开发效率极低

适用场景:Boot Logo 显示、极简状态指示灯界面、资源极度受限(Flash < 64KB)。

# 1.3.2 LVGL:轻量级嵌入式图形库

架构原理:

┌──────────────────────────────────────────────┐
│  LVGL 架构                                    │
│                                               │
│  ┌──────────────────────────────────┐        │
│  │  应用代码(C 语言 API 调用)       │        │
│  ├──────────────────────────────────┤        │
│  │  控件层(btn/label/chart/table)  │        │
│  ├──────────────────────────────────┤        │
│  │  布局引擎(Flex/Grid)            │        │
│  ├──────────────────────────────────┤        │
│  │  绘制引擎(抗锯齿/Alpha混合)     │        │
│  ├──────────────────────────────────┤        │
│  │  显示驱动接口(flush_cb)         │        │
│  │  输入驱动接口(read_cb)          │        │
│  └──────────────────────────────────┘        │
│             ↕                                 │
│  ┌──────────────────────────────────┐        │
│  │  硬件:SPI/RGB LCD + 触摸 IC      │        │
│  └──────────────────────────────────┘        │
└──────────────────────────────────────────────┘

渲染机制:

  • 采用"脏区域"策略:只重绘变化的区域
  • CPU 软件渲染(可选 DMA2D 硬件加速)
  • 双缓冲或部分缓冲(减少内存需求)
// LVGL 典型用法
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 120, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);

lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Click Me");

性能数据(STM32H743, 480MHz, 无 GPU):

场景 LVGL 帧率 CPU 占用
静态界面 30fps 5%
列表滑动 25fps 40%
图表实时更新 20fps 55%
复杂动画 15fps 80%

适用边界:

  • ✅ MCU(STM32/ESP32/NXP i.MX RT)
  • ✅ 资源受限(RAM < 256KB, Flash < 2MB)
  • ✅ 简单交互(按钮/列表/表盘)
  • ❌ 复杂动画(粒子效果/页面过渡)
  • ❌ 高分辨率(> 800×480 时 CPU 扛不住)

# 1.3.3 Qt Widgets:传统桌面控件体系

架构原理:

┌──────────────────────────────────────────────┐
│  Qt Widgets 渲染管线                           │
│                                               │
│  QWidget::paintEvent(QPaintEvent*)            │
│       ↓                                       │
│  QPainter(高级绘图 API)                      │
│       ↓                                       │
│  QPaintEngine(绘图引擎抽象)                   │
│       ├── QRasterPaintEngine(CPU 软件渲染)    │
│       └── QOpenGLPaintEngine(GPU 加速,可选)  │
│       ↓                                       │
│  QBackingStore → Framebuffer / 窗口合成器      │
└──────────────────────────────────────────────┘

核心特点:

  • 每个 Widget 是一个矩形区域,拥有自己的 paintEvent()
  • 默认使用 CPU 软件渲染(QRasterPaintEngine)
  • 控件继承体系:QObject → QWidget → QAbstractButton → QPushButton
  • 布局管理器:QHBoxLayout/QVBoxLayout/QGridLayout

嵌入式适配问题:

  • 默认 CPU 渲染——在 ARM 上复杂界面帧率低
  • 控件系统设计时假设有窗口管理器(无窗口模式需要 hack)
  • 不支持硬件加速的动画
  • QSS 样式效率不高(每次重绘都要解析)

性能数据(ARM Cortex-A53, 1.2GHz, 无 GPU 加速):

场景 Qt Widgets 帧率 CPU 占用
静态界面 60fps 2%
列表滑动 30fps 45%
自定义绘图 25fps 60%
复杂动画 15fps 85%

# 1.3.4 Qt QML:声明式 GPU 加速界面

架构原理(核心优势所在):

┌──────────────────────────────────────────────────────────┐
│  Qt QML 渲染管线(Scene Graph 架构)                       │
│                                                           │
│  .qml 文件                                                │
│       ↓ QML 引擎解析                                      │
│  QObject 对象树(属性绑定/信号槽)                          │
│       ↓ 同步阶段(主线程)                                  │
│  Scene Graph 节点树                                        │
│       ↓ 渲染阶段(渲染线程,可并行)                        │
│  OpenGL ES 绘制指令                                        │
│       ↓                                                   │
│  GPU 执行 → Framebuffer → 屏幕                            │
│                                                           │
│  关键特性:                                                │
│  • 双线程模型(主线程 + 渲染线程)                          │
│  • 批量渲染(合并相同材质的节点,减少 Draw Call)            │
│  • 距离场字体渲染(GPU 加速的矢量文字)                     │
│  • 纹理图集(多个小图合并为一张大纹理)                     │
└──────────────────────────────────────────────────────────┘

为什么 QML 适合嵌入式:

┌──────────────────────────────────────────────────────────┐
│  QML 的五大嵌入式优势                                      │
│                                                           │
│  ① GPU 加速是默认行为                                      │
│     - 所有可视元素自动通过 OpenGL ES 渲染                   │
│     - 动画/变换/透明度全部由 GPU 处理                       │
│     - CPU 几乎只负责业务逻辑                               │
│                                                           │
│  ② 声明式语法 → 开发效率高                                 │
│     - UI 描述 = 最终呈现(所见即所得)                      │
│     - 属性绑定自动更新 UI(响应式)                         │
│     - 热重载(修改 .qml 文件,界面实时刷新)                │
│                                                           │
│  ③ 双线程模型 → UI 不卡顿                                  │
│     - 主线程处理逻辑/事件                                  │
│     - 渲染线程独立处理 GPU 提交                            │
│     - 即使主线程有短暂延迟,动画仍然流畅                    │
│                                                           │
│  ④ 无窗口系统直接运行                                      │
│     - EGLFS 插件:直接输出到 DRM/KMS                       │
│     - 无需 X11/Wayland,节省 50~100MB 内存                 │
│     - 启动更快(省去窗口管理器初始化)                      │
│                                                           │
│  ⑤ C++ 后端无缝集成                                        │
│     - QML 负责 UI 表现,C++ 负责性能敏感逻辑               │
│     - 元对象系统实现自动绑定(无需手写胶水代码)             │
└──────────────────────────────────────────────────────────┘

性能数据(ARM Cortex-A53 + Mali-400, OpenGL ES 2.0):

场景 Qt QML 帧率 CPU 占用 GPU 占用
静态界面 60fps 1% 5%
列表滑动 60fps 8% 25%
复杂页面切换动画 60fps 12% 40%
粒子效果(500粒子) 55fps 5% 60%
实时图表(10Hz) 60fps 10% 30%

# 1.3.5 HTML5/Electron:Web 技术栈方案

架构原理:

┌──────────────────────────────────────────────────────────┐
│  HTML5 嵌入式渲染管线                                      │
│                                                           │
│  HTML/CSS/JS                                              │
│       ↓ 解析                                              │
│  DOM 树 + CSSOM 树                                        │
│       ↓ Style Recalculation                               │
│  Render Tree                                              │
│       ↓ Layout (Reflow)                                   │
│  Layout Tree(每个元素的位置/尺寸)                         │
│       ↓ Paint                                             │
│  Paint Records(绘制指令列表)                              │
│       ↓ Composite                                         │
│  合成层 → GPU Rasterization → 屏幕                         │
│                                                           │
│  问题:                                                    │
│  • DOM 操作触发全量 Reflow(性能杀手)                      │
│  • V8/SpiderMonkey 引擎本身占 50~100MB                     │
│  • Chromium 内核 > 200MB 存储                              │
│  • 垃圾回收导致帧率不稳定(GC 停顿)                       │
└──────────────────────────────────────────────────────────┘

嵌入式 Web 方案的现实问题:

  • 内存:Chromium 基础占用 150~300MB(远超多数嵌入式设备)
  • 启动速度:引擎初始化 3~8 秒(不含 JS 加载)
  • 帧率稳定性:GC 触发时掉帧明显(20~50ms 停顿)
  • 人才假象:前端开发者不了解嵌入式调试、交叉编译、驱动适配

适用场景:智能电视/车机信息娱乐系统(RAM > 1GB, 多核高频 SoC)。

# 1.3.6 Flutter Embedded:跨平台新势力

架构原理:

┌──────────────────────────────────────────────────────────┐
│  Flutter Embedded 架构                                     │
│                                                           │
│  Dart 代码 → Widget Tree → Element Tree → RenderObject    │
│       ↓                                                   │
│  Skia 渲染引擎(C++, 类似 Chrome 的 2D 渲染库)            │
│       ↓                                                   │
│  OpenGL ES / Vulkan → Framebuffer                         │
│                                                           │
│  嵌入式后端:flutter-elinux / flutter-pi                    │
│  • 通过 DRM/KMS 直接输出                                   │
│  • Dart VM 约 20~30MB                                     │
└──────────────────────────────────────────────────────────┘

现状评估(2025年):

  • ✅ 渲染性能优秀(Skia GPU 加速)
  • ✅ 开发效率高(热重载/声明式)
  • ⚠️ 嵌入式支持仍不成熟(社区驱动,非官方)
  • ❌ 硬件通信生态弱(串口/CAN/GPIO 缺少稳定插件)
  • ❌ Dart VM 内存占用偏高
  • ❌ 缺少工业级案例验证

# 1.4 选型决策与量化评估

# 1.4.1 六维评估模型

              渲染性能
                ▲
               /|\
              / | \
             /  |  \
    开发效率 ←──┼──→ 内存占用
            \   |   /
             \  |  /
              \ | /
               \|/
        生态成熟度 ←→ 启动速度
                ↓
            维护成本

量化评分表(满分 10 分,针对 ARM Cortex-A53 + 512MB RAM 场景):

方案 渲染性能 内存占用 启动速度 开发效率 生态成熟度 维护成本 综合
裸机 FB 3 10 10 1 1 2 4.5
LVGL 5 9 9 5 6 5 6.5
Qt Widgets 5 6 6 7 9 7 6.7
Qt QML 9 7 7 9 9 8 8.2
HTML5 7 3 3 8 8 6 5.8
Flutter 8 5 5 8 4 5 5.8

# 1.4.2 选型决策树

你的设备有 GPU 吗?
├── 是 → 你的 RAM ≥ 128MB 吗?
│       ├── 是 → 需要复杂动画/多页面吗?
│       │       ├── 是 → 【Qt QML】 ← 最优解
│       │       └── 否 → 【Qt Widgets】也能胜任
│       └── 否 → 【LVGL + GPU 加速(可选)】
└── 否 → 你的 RAM ≥ 32MB 吗?
        ├── 是 → 界面复杂度如何?
        │       ├── 高 → 【Qt Widgets (软件渲染)】
        │       └── 低 → 【LVGL】
        └── 否 → 【LVGL】或【裸机 FB】

# 1.4.3 典型场景推荐

产品类型 典型硬件 推荐方案 理由
工业 HMI Cortex-A53 + Mali Qt QML 动画流畅、串口/CAN 生态好
智能家电 Cortex-A7 无 GPU LVGL 资源受限、界面简单
智能座舱 高通 8155 Qt QML + HTML5(混合) 多屏/多模态/生态丰富
医疗设备 i.MX6 + Vivante Qt QML 长期维护/认证需要稳定框架
充电桩 STM32MP1 LVGL 或 Qt QML 看界面复杂度
智能电视 4核A55 + Mali HTML5 应用生态/内容分发需要

# 1.5 Qt/QML 技术栈全景

# 1.5.1 Qt 框架模块家族

┌──────────────────────────────────────────────────────────────────┐
│  Qt 模块全景图                                                     │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │  核心模块(Core Modules)                                 │     │
│  │  ┌─────────┬─────────┬──────────┬──────────┬─────────┐ │     │
│  │  │ Qt Core │ Qt GUI  │ Qt QML   │ Qt Quick │ Qt Net  │ │     │
│  │  │ 核心库  │ 图形基础│ QML引擎  │ Quick控件│ 网络    │ │     │
│  │  └─────────┴─────────┴──────────┴──────────┴─────────┘ │     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │  扩展模块(Add-on Modules)                              │     │
│  │  ┌──────────┬──────────┬──────────┬──────────────────┐ │     │
│  │  │ Qt Serial│ Qt SQL   │ Qt Multi │ Qt Bluetooth     │ │     │
│  │  │ Port/Bus │ 数据库   │ media    │ 蓝牙             │ │     │
│  │  ├──────────┼──────────┼──────────┼──────────────────┤ │     │
│  │  │ Qt Charts│ Qt 3D    │ Qt WebSk │ Qt Virtual KB    │ │     │
│  │  │ 图表     │ 3D渲染   │ WebSocket│ 虚拟键盘         │ │     │
│  │  └──────────┴──────────┴──────────┴──────────────────┘ │     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │  工具链                                                   │     │
│  │  Qt Creator · qmake/CMake · qmlcachegen · MOC · RCC     │     │
│  └─────────────────────────────────────────────────────────┘     │
└──────────────────────────────────────────────────────────────────┘

嵌入式常用模块清单:

模块 用途 嵌入式场景
Qt Core 事件循环/信号槽/容器/IO 所有项目必须
Qt GUI 图形基础设施/字体/图片 所有 GUI 项目
Qt QML QML 引擎/JavaScript 解析 QML 项目必须
Qt Quick QML 可视化控件库 界面开发
Qt Quick Controls 高级控件(按钮/滑块/表格) 快速 UI 搭建
Qt SerialPort 串口通信 传感器/PLC 通信
Qt SerialBus CAN/Modbus 协议 工业总线
Qt Multimedia 摄像头/音视频播放 监控/娱乐
Qt Network TCP/UDP/HTTP 远程通信/OTA
Qt SQL 数据库访问 本地数据存储
Qt Charts 数据图表 监控仪表盘
Qt Virtual Keyboard 虚拟键盘 触摸屏输入

# 1.5.2 Qt5 vs Qt6 核心差异

维度 Qt5 Qt6
图形后端 OpenGL 为主 RHI 抽象层(支持 Vulkan/Metal/D3D/OpenGL)
QML 引擎 V4 引擎 改进 V4 + QML 类型编译器(性能提升 30%)
CMake qmake 为主,CMake 可选 CMake 为唯一官方构建系统
C++ 标准 C++11 C++17
属性绑定 仅 QML 中可用 C++ 中也可用(QProperty<T>)
模块变动 Qt Widgets/Qt Quick 并重 重心完全转向 Qt Quick
嵌入式支持 Boot2Qt (商业) 开源 + Boot2Qt
最低 OpenGL ES 2.0 ES 2.0(通过 RHI 适配)

选型建议:

  • 新项目优先 Qt6(长期支持到 2029+)
  • 已有 Qt5 项目迁移成本中等(主要是 CMake + API 微调)
  • 极低端硬件(OpenGL ES 2.0 勉强):Qt5 更稳定

# 1.5.3 QML 的设计哲学

┌──────────────────────────────────────────────────────────┐
│  QML 设计三原则                                            │
│                                                           │
│  ① 声明式 > 命令式                                        │
│     描述"是什么"而非"怎么做"                               │
│     Rectangle { width: 100; color: "red" }               │
│     而非 painter.fillRect(0,0,100,100, Qt::red)          │
│                                                           │
│  ② 属性绑定 = 响应式                                      │
│     width: parent.width * 0.5  ← 自动跟随父元素变化       │
│     类似 Excel 公式:A1 = B1 + C1(B1/C1 变时 A1 自动更新)│
│                                                           │
│  ③ UI 与逻辑分离                                          │
│     QML 负责"长什么样"(设计师也能修改)                    │
│     C++ 负责"干什么活"(性能关键路径)                      │
│     通过 Property/Signal 自动桥接                          │
└──────────────────────────────────────────────────────────┘

一个直观的 QML 示例:

// Dashboard.qml —— 一个实时速度表
import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 400; height: 400
    color: "#1a1a2e"

    // 表盘
    Canvas {
        id: gauge
        anchors.fill: parent
        property real speed: backend.currentSpeed  // ← 绑定 C++ 后端数据

        onSpeedChanged: requestPaint()  // speed 变化时自动重绘

        onPaint: {
            var ctx = getContext("2d")
            ctx.clearRect(0, 0, width, height)
            // 绘制表盘刻度...
            drawNeedle(ctx, speed)
        }
    }

    // 数字显示
    Text {
        anchors.bottom: parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        text: Math.round(backend.currentSpeed) + " km/h"
        color: "white"
        font.pixelSize: 32
    }
}

# 1.6 QML 渲染管线概览

# 1.6.1 从 .qml 文件到屏幕像素

┌──────────────────────────────────────────────────────────────────┐
│  QML 渲染管线(完整流程)                                           │
│                                                                   │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ 阶段 1:编译/加载                                           │  │
│  │  .qml 文件 → QML 编译器 → 字节码 (.qmlc)                   │  │
│  │  或运行时:.qml → 解析器 → AST → 字节码                     │  │
│  └────────────────────────────────┬───────────────────────────┘  │
│                                   ↓                               │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ 阶段 2:对象实例化(主线程)                                  │  │
│  │  字节码 → QObject 对象树(QQuickItem 层级)                  │  │
│  │  属性绑定引擎启动 → 依赖追踪图建立                           │  │
│  └────────────────────────────────┬───────────────────────────┘  │
│                                   ↓                               │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ 阶段 3:同步(主线程 → 渲染线程)                             │  │
│  │  QQuickItem 属性变更 → 同步到 QSGNode(Scene Graph 节点)    │  │
│  │  这是主线程和渲染线程的唯一交汇点                              │  │
│  └────────────────────────────────┬───────────────────────────┘  │
│                                   ↓                               │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ 阶段 4:批处理与优化(渲染线程)                               │  │
│  │  遍历 Scene Graph → 合并相同材质的节点 → 减少 Draw Call       │  │
│  │  构建渲染列表 → 排序(不透明先画,半透明后画)                 │  │
│  └────────────────────────────────┬───────────────────────────┘  │
│                                   ↓                               │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ 阶段 5:GPU 渲染                                            │  │
│  │  提交 OpenGL ES / Vulkan 指令 → GPU 执行                    │  │
│  │  → 结果写入 Framebuffer → LCD 控制器扫描输出                 │  │
│  └────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘

# 1.6.2 Scene Graph 批处理原理

为什么 Scene Graph 能高效渲染?

传统"逐控件绘制"模式(Qt Widgets):

绘制按钮A → 切换纹理 → 绘制按钮B → 切换纹理 → 绘制文本C → ...
每个控件 1 次 Draw Call → N 个控件 = N 次 Draw Call
GPU 状态切换代价高 → 性能瓶颈

Scene Graph 批处理模式(Qt QML):

收集所有节点 → 按材质/纹理分组 → 合并同组几何体 → 一次 Draw Call 绘制
100 个相同材质的矩形 → 合并为 1 次 Draw Call
Draw Call 数量: O(材质种类) 而非 O(元素数量)

具体示例:

界面上有 50 个 Rectangle + 20 个 Image + 30 个 Text

传统方式:100 次 Draw Call(每个元素一次)

Scene Graph 批处理:
  - 50 个纯色 Rectangle → 合并 → 1 次 Draw Call
  - 20 个 Image(假设来自同一纹理图集)→ 1 次 Draw Call
  - 30 个 Text(距离场字体渲染)→ 1 次 Draw Call
  - 总计:3 次 Draw Call(减少 97%!)

# 1.6.3 QML vs HTML5 渲染管线对比

阶段 QML HTML5 (Chromium)
解析 .qml → AST → QObject(轻量) HTML→DOM + CSS→CSSOM(重量级)
布局 anchors/positioners(O(1) 增量) CSS Box Model + Reflow(O(n) 最坏)
样式 属性直接绑定 Style Recalculation(级联计算)
绘制 Scene Graph 批处理 Paint → Compositing Layers
GPU 提交 直接 GL 调用 需经合成器中转
动画 属性动画(GPU 直接插值) CSS Animation/JS rAF(需回到主线程)

结论:QML 少了 DOM、CSSOM、Style Recalc、Reflow 四个重量级阶段,渲染路径短 2~3 层。


# 1.7 嵌入式运行环境

# 1.7.1 QPA 平台抽象层

┌──────────────────────────────────────────────────────────┐
│  QPA (Qt Platform Abstraction) 架构                        │
│                                                           │
│  Qt 应用代码(与平台无关)                                  │
│       ↓                                                   │
│  QPlatformIntegration(平台集成接口)                       │
│       ↓ 根据 -platform 参数选择不同实现                     │
│  ┌─────────┬──────────┬────────────┬───────────────────┐ │
│  │  xcb    │  eglfs   │  linuxfb   │  wayland          │ │
│  │ X11窗口 │ EGL直接  │ FB纯软件   │ Wayland客户端      │ │
│  │ 桌面用  │ 嵌入式首选│ 无GPU备选  │ 多窗口嵌入式      │ │
│  └─────────┴──────────┴────────────┴───────────────────┘ │
└──────────────────────────────────────────────────────────┘

使用方式:
  ./myapp -platform eglfs      # GPU 加速直接渲染
  ./myapp -platform linuxfb    # CPU 软件渲染
  ./myapp -platform wayland    # Wayland 合成器
  export QT_QPA_PLATFORM=eglfs # 环境变量方式

# 1.7.2 EGLFS:无窗口系统的 GPU 渲染

EGLFS 是什么?

  • EGL = 嵌入式图形库接口(连接 OpenGL ES 和本地窗口系统)
  • FS = Full Screen(全屏,无窗口管理器)
  • 本质:Qt 直接通过 EGL/DRM/KMS 获取 GPU 上下文,输出到 LCD

EGLFS 启动流程:

┌──────────────────────────────────────────────────────────┐
│  EGLFS 初始化流程                                          │
│                                                           │
│  ① Qt 加载 libqeglfs.so 插件                              │
│       ↓                                                   │
│  ② 打开 DRM 设备 (/dev/dri/card0)                         │
│       ↓                                                   │
│  ③ 查询 Connector → CRTC → Encoder → 屏幕模式            │
│       ↓                                                   │
│  ④ 创建 GBM (Generic Buffer Manager) Surface              │
│       ↓                                                   │
│  ⑤ 初始化 EGL → 创建 EGLContext (OpenGL ES 2.0)          │
│       ↓                                                   │
│  ⑥ 创建 EGLSurface → 绑定到 GBM Surface                  │
│       ↓                                                   │
│  ⑦ Scene Graph 开始渲染                                    │
│       ↓                                                   │
│  ⑧ eglSwapBuffers() → DRM Page Flip → LCD 显示           │
└──────────────────────────────────────────────────────────┘

环境变量配置:

# 指定 DRM 设备
export QT_QPA_EGLFS_KMS_CONFIG=/etc/qt-eglfs-kms.json

# KMS 配置文件示例
{
  "device": "/dev/dri/card0",
  "outputs": [
    {
      "name": "HDMI-A-1",
      "mode": "1024x600@60",
      "size": { "width": 1024, "height": 600 }
    }
  ]
}

# 其他常用变量
export QT_QPA_EGLFS_PHYSICAL_WIDTH=154     # 物理宽度mm(用于 DPI 计算)
export QT_QPA_EGLFS_PHYSICAL_HEIGHT=86     # 物理高度mm
export QT_QPA_EGLFS_ROTATION=90            # 屏幕旋转
export QT_LOGGING_RULES="qt.qpa.*=true"    # 调试:打印 QPA 日志

# 1.7.3 LinuxFB:纯软件渲染退路

当设备没有 GPU 或 GPU 驱动不可用时,使用 LinuxFB:

┌──────────────────────────────────────────────────────────┐
│  LinuxFB 渲染路径                                          │
│                                                           │
│  Qt Scene Graph → QSG Software Renderer(CPU)            │
│       ↓                                                   │
│  QImage(内存中的位图)                                     │
│       ↓                                                   │
│  mmap(/dev/fb0) → 直接写入 Framebuffer                    │
│       ↓                                                   │
│  LCD 控制器扫描输出                                        │
│                                                           │
│  特点:                                                    │
│  • 无需 GPU 驱动                                           │
│  • CPU 占用高(所有渲染在 CPU 完成)                        │
│  • 适合简单界面 / 调试阶段                                 │
│  • 动画帧率受限(复杂界面 15~25fps)                       │
└──────────────────────────────────────────────────────────┘

使用方式:
  ./myapp -platform linuxfb:fb=/dev/fb0:size=1024x600

# 1.7.4 Wayland:现代合成器方案

┌──────────────────────────────────────────────────────────┐
│  Wayland 在嵌入式中的角色                                   │
│                                                           │
│  适用场景:需要多个 GUI 进程共享屏幕                        │
│  例如:仪表盘(全屏) + 弹窗通知(叠加) + 虚拟键盘(浮层)      │
│                                                           │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐               │
│  │ 仪表盘App │  │ 通知App  │  │ 键盘App  │               │
│  │ (Client) │  │ (Client) │  │ (Client) │               │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘               │
│       │ wl_surface   │              │                     │
│       ▼              ▼              ▼                     │
│  ┌────────────────────────────────────────┐              │
│  │  Wayland Compositor (如 Weston/QtWayland)│              │
│  │  负责窗口合成、输入分发                   │              │
│  └──────────────────┬─────────────────────┘              │
│                     ↓                                     │
│              DRM/KMS → LCD                                │
└──────────────────────────────────────────────────────────┘

相比 EGLFS:
  • EGLFS = 单应用独占屏幕(更简单、更高效)
  • Wayland = 多应用共享屏幕(更灵活、多一层合成开销)

# 1.8 QML 嵌入式生态能力

# 1.8.1 硬件通信(串口/CAN/GPIO)

// 串口通信 —— Qt SerialPort
#include <QSerialPort>
#include <QSerialPortInfo>

class SensorReader : public QObject {
    Q_OBJECT
    Q_PROPERTY(double temperature READ temperature NOTIFY temperatureChanged)

public:
    explicit SensorReader(QObject *parent = nullptr) : QObject(parent) {
        m_serial.setPortName("/dev/ttyS1");
        m_serial.setBaudRate(115200);
        m_serial.open(QIODevice::ReadOnly);
        connect(&m_serial, &QSerialPort::readyRead, this, &SensorReader::onData);
    }

    double temperature() const { return m_temp; }

signals:
    void temperatureChanged();

private slots:
    void onData() {
        QByteArray data = m_serial.readAll();
        m_temp = parseTemperature(data);
        emit temperatureChanged();  // QML 中绑定此属性的 UI 自动更新
    }

private:
    QSerialPort m_serial;
    double m_temp = 0.0;
};
// CAN 总线 —— Qt SerialBus
#include <QCanBus>
#include <QCanBusDevice>

class CanController : public QObject {
    Q_OBJECT
public:
    void init() {
        QString errorString;
        m_device = QCanBus::instance()->createDevice("socketcan", "can0", &errorString);
        if (m_device) {
            m_device->connectDevice();
            connect(m_device, &QCanBusDevice::framesReceived, this, &CanController::onFrames);
        }
    }

private slots:
    void onFrames() {
        while (m_device->framesAvailable()) {
            QCanBusFrame frame = m_device->readFrame();
            // frame.frameId(), frame.payload()
            processCanFrame(frame);
        }
    }

private:
    QCanBusDevice *m_device = nullptr;
};

# 1.8.2 多媒体(摄像头/音视频)

// QML 中直接使用摄像头
import QtMultimedia 5.15

Rectangle {
    width: 640; height: 480

    Camera {
        id: camera
        deviceId: "/dev/video0"
    }

    VideoOutput {
        anchors.fill: parent
        source: camera
    }

    // 拍照
    MouseArea {
        anchors.fill: parent
        onClicked: camera.imageCapture.capture()
    }
}

# 1.8.3 网络与 OTA 升级

// OTA 升级框架示例
class OtaManager : public QObject {
    Q_OBJECT
    Q_PROPERTY(int progress READ progress NOTIFY progressChanged)

public:
    Q_INVOKABLE void checkUpdate() {
        QNetworkRequest req(QUrl("https://ota.example.com/api/check"));
        auto *reply = m_nam.get(req);
        connect(reply, &QNetworkReply::finished, this, &OtaManager::onCheckDone);
    }

    Q_INVOKABLE void startDownload(const QString &url) {
        auto *reply = m_nam.get(QNetworkRequest(QUrl(url)));
        connect(reply, &QNetworkReply::downloadProgress, this, [this](qint64 recv, qint64 total) {
            m_progress = total > 0 ? (recv * 100 / total) : 0;
            emit progressChanged();
        });
    }

signals:
    void progressChanged();
    void updateAvailable(const QString &version, const QString &url);

private:
    QNetworkAccessManager m_nam;
    int m_progress = 0;
};

# 1.8.4 数据库与本地存储

// SQLite 本地存储
#include <QSqlDatabase>
#include <QSqlQuery>

class DataLogger : public QObject {
    Q_OBJECT
public:
    void init() {
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName("/data/sensor_log.db");
        db.open();

        QSqlQuery query;
        query.exec("CREATE TABLE IF NOT EXISTS readings ("
                   "id INTEGER PRIMARY KEY AUTOINCREMENT,"
                   "timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,"
                   "sensor_id INTEGER,"
                   "value REAL)");
    }

    Q_INVOKABLE void logReading(int sensorId, double value) {
        QSqlQuery query;
        query.prepare("INSERT INTO readings (sensor_id, value) VALUES (?, ?)");
        query.addBindValue(sensorId);
        query.addBindValue(value);
        query.exec();
    }
};

# 1.9 完整项目示例

# 1.9.1 项目结构

embedded-hmi/
├── CMakeLists.txt
├── src/
│   ├── main.cpp
│   └── sensorbackend.h
├── qml/
│   ├── main.qml
│   └── GaugeWidget.qml
└── deploy/
    ├── run.sh
    └── qt-eglfs-kms.json

# 1.9.2 CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(EmbeddedHMI VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt6 REQUIRED COMPONENTS Core Quick SerialPort)

qt_add_executable(embedded-hmi
    src/main.cpp
)

qt_add_qml_module(embedded-hmi
    URI EmbeddedHMI
    VERSION 1.0
    QML_FILES
        qml/main.qml
        qml/GaugeWidget.qml
)

target_link_libraries(embedded-hmi PRIVATE
    Qt6::Core
    Qt6::Quick
    Qt6::SerialPort
)

# 交叉编译时设置 sysroot
if(CMAKE_CROSSCOMPILING)
    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
endif()

# 1.9.3 main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "sensorbackend.h"

int main(int argc, char *argv[])
{
    // 嵌入式优化:禁用不必要的特性
    qputenv("QT_IM_MODULE", "none");  // 无输入法
    qputenv("QT_QPA_FONTDIR", "/usr/share/fonts");

    QGuiApplication app(argc, argv);

    // 创建后端对象
    SensorBackend backend;
    backend.init("/dev/ttyS1");

    QQmlApplicationEngine engine;

    // 将 C++ 后端暴露给 QML
    engine.rootContext()->setContextProperty("sensorBackend", &backend);

    engine.loadFromModule("EmbeddedHMI", "Main");

    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

# 1.9.4 QML 界面

// qml/main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

ApplicationWindow {
    id: root
    visible: true
    width: 1024; height: 600
    title: "Industrial HMI"
    color: "#0d1117"

    // 顶部状态栏
    Rectangle {
        id: statusBar
        anchors.top: parent.top
        width: parent.width; height: 40
        color: "#161b22"

        RowLayout {
            anchors.fill: parent
            anchors.margins: 8

            Text {
                text: "工业监控系统 v1.0"
                color: "#c9d1d9"
                font.pixelSize: 16
            }

            Item { Layout.fillWidth: true }

            Text {
                text: Qt.formatDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")
                color: "#8b949e"
                font.pixelSize: 14

                Timer {
                    interval: 1000; running: true; repeat: true
                    onTriggered: parent.text = Qt.formatDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")
                }
            }
        }
    }

    // 仪表盘网格
    GridLayout {
        anchors.top: statusBar.bottom
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.margins: 16
        columns: 4
        rowSpacing: 12
        columnSpacing: 12

        Repeater {
            model: 8
            GaugeWidget {
                Layout.fillWidth: true
                Layout.fillHeight: true
                sensorName: "传感器 " + (index + 1)
                currentValue: sensorBackend.values[index] || 0
                maxValue: 100
                unit: "°C"
            }
        }
    }
}
// qml/GaugeWidget.qml
import QtQuick 2.15

Rectangle {
    id: gauge
    radius: 8
    color: "#21262d"
    border.color: "#30363d"
    border.width: 1

    property string sensorName: "Sensor"
    property real currentValue: 0
    property real maxValue: 100
    property string unit: ""

    Column {
        anchors.centerIn: parent
        spacing: 8

        Text {
            anchors.horizontalCenter: parent.horizontalCenter
            text: gauge.sensorName
            color: "#8b949e"
            font.pixelSize: 12
        }

        Text {
            anchors.horizontalCenter: parent.horizontalCenter
            text: currentValue.toFixed(1) + " " + unit
            color: currentValue > maxValue * 0.8 ? "#f85149" : "#58a6ff"
            font.pixelSize: 24
            font.bold: true

            Behavior on color { ColorAnimation { duration: 300 } }
        }

        // 进度条
        Rectangle {
            width: gauge.width * 0.7
            height: 6
            radius: 3
            color: "#30363d"
            anchors.horizontalCenter: parent.horizontalCenter

            Rectangle {
                width: parent.width * Math.min(currentValue / maxValue, 1.0)
                height: parent.height
                radius: 3
                color: currentValue > maxValue * 0.8 ? "#f85149" : "#58a6ff"

                Behavior on width { NumberAnimation { duration: 200 } }
                Behavior on color { ColorAnimation { duration: 300 } }
            }
        }
    }
}

# 1.9.5 交叉编译与运行

# === 交叉编译(在 x86 开发机上) ===

# 1. 设置交叉编译工具链
export CROSS_COMPILE=aarch64-linux-gnu-
export SYSROOT=/opt/aarch64-sysroot

# 2. 使用 CMake 工具链文件
cmake -B build \
  -DCMAKE_TOOLCHAIN_FILE=cmake/aarch64-toolchain.cmake \
  -DCMAKE_SYSROOT=$SYSROOT \
  -DQt6_DIR=$SYSROOT/usr/lib/cmake/Qt6

cmake --build build -j$(nproc)

# === 目标板运行 ===

# 3. 传输到目标板
scp build/embedded-hmi root@192.168.1.100:/opt/app/

# 4. 在目标板上运行
ssh root@192.168.1.100
export QT_QPA_PLATFORM=eglfs
export QT_QPA_EGLFS_KMS_CONFIG=/opt/app/qt-eglfs-kms.json
/opt/app/embedded-hmi
# deploy/run.sh —— 目标板启动脚本
#!/bin/bash
export QT_QPA_PLATFORM=eglfs
export QT_QPA_EGLFS_KMS_CONFIG=/opt/app/qt-eglfs-kms.json
export QT_QPA_EGLFS_PHYSICAL_WIDTH=154
export QT_QPA_EGLFS_PHYSICAL_HEIGHT=86
export QT_IM_MODULE=none
export QT_LOGGING_RULES="qt.qpa.*=false"

# 关闭光标
echo 0 > /sys/class/graphics/fbcon/cursor_blink

exec /opt/app/embedded-hmi "$@"

# 1.10 学习路径与速查表

# 1.10.1 从 0 到 1 的十周路径

┌──────────────────────────────────────────────────────────────────┐
│  嵌入式 Qt/QML 开发十周学习路径                                     │
│                                                                   │
│  第 1 周:环境搭建                                                 │
│  • 安装 Qt Creator + Qt 6.x SDK                                   │
│  • 桌面运行第一个 QML 程序                                         │
│  • 理解 qmake/CMake 基本用法                                      │
│                                                                   │
│  第 2 周:QML 语法基础                                             │
│  • 基本类型(int/real/string/color/date)                          │
│  • 属性声明与绑定                                                  │
│  • 信号与处理器(onClicked/onTriggered)                           │
│                                                                   │
│  第 3 周:可视元素与布局                                            │
│  • Rectangle/Text/Image/MouseArea                                  │
│  • anchors 锚定布局                                                │
│  • Row/Column/Grid/Flow 定位器                                     │
│  • RowLayout/ColumnLayout/GridLayout                              │
│                                                                   │
│  第 4 周:组件与复用                                                │
│  • 自定义 QML 组件                                                 │
│  • Loader/Component 动态加载                                       │
│  • StackView 页面导航                                              │
│                                                                   │
│  第 5 周:动画与状态机                                              │
│  • PropertyAnimation/NumberAnimation/ColorAnimation                │
│  • State + Transition                                              │
│  • Behavior(属性行为)                                            │
│                                                                   │
│  第 6 周:Model-View                                               │
│  • ListModel/ObjectModel                                           │
│  • ListView/GridView/PathView                                      │
│  • delegate 代理组件设计                                           │
│                                                                   │
│  第 7 周:C++ 集成                                                  │
│  • Q_PROPERTY 暴露属性到 QML                                       │
│  • Q_INVOKABLE 暴露方法                                            │
│  • 注册自定义 QObject 类型                                         │
│                                                                   │
│  第 8 周:硬件通信                                                  │
│  • Qt SerialPort 串口通信                                          │
│  • Qt SerialBus (CAN/Modbus)                                      │
│  • QProcess 执行系统命令                                           │
│                                                                   │
│  第 9 周:嵌入式部署                                                │
│  • 交叉编译工具链配置                                              │
│  • EGLFS 配置与调试                                                │
│  • 启动速度优化                                                    │
│                                                                   │
│  第 10 周:综合项目                                                 │
│  • 完成一个工业 HMI 项目                                           │
│  • 性能分析(QML Profiler)                                        │
│  • 打包发布 + 开机自启                                             │
└──────────────────────────────────────────────────────────────────┘

# 1.10.2 技术选型速查表

决策因素 选 QML 选 LVGL 选 HTML5
GPU 可用 ✅ 必须 可选 ✅ 需要
RAM ≥ 128MB ≥ 32KB ≥ 512MB
动画需求 复杂/60fps 简单/30fps 中等
开发语言 QML + C++ C HTML/CSS/JS
人才获取 C++/Qt 工程师 嵌入式 C 工程师 前端工程师
硬件通信 丰富(串口/CAN/蓝牙) 需自实现 需 Native 桥接
长期维护 Qt 商业支持 社区 Chromium 跟进困难
认证需求 有工业认证案例 少 少

# 1.10.3 本专栏路线图

本专栏 14 篇文章路线图:

 ① 嵌入式GUI技术全景 ← 你在这里
     ↓
 ②③④ 原理篇:引擎/语法/绑定
     ↓
 ⑤⑥ 界面篇:布局/事件
     ↓
 ⑦⑧⑨ 高级篇:Model-View/动画/Canvas
     ↓
 ⑩⑪ 集成篇:C++/SceneGraph
     ↓
 ⑫⑬⑭ 嵌入式篇:交叉编译/渲染后端/性能优化

下一篇:02.QML引擎与渲染原理 —— 深入 QML 引擎内部:从 .qml 文件解析到 Scene Graph 渲染的完整链路。

上次更新: 2026/06/25, 14:22:45
QML基础入门
QML引擎与渲染原理

← QML基础入门 QML引擎与渲染原理→

最近更新
01
CSS选择器入门
06-23
02
CSS定位与层级
06-23
03
CSS盒模型详解
06-23
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式