3. Qt核心概念回顾

从 LVGL 迁移到 Qt,最大的认知转变不是“换一套 API”,而是从“手动绘制 + 定时器轮询”转向“对象树 + 事件驱动 + 声明式描述”。LVGL 中我们习惯自己管理 lv_obj_t 的父子关系、手动调用 lv_timer_handler()、用 lv_obj_set_style_* 逐项设置样式;而 Qt 把这些职责拆分为若干清晰的核心机制。理解这些机制之间的分工,是后续迁移工作的地基。

一、QWidget 与 QML:两种界面构建范式

Qt 提供两套并行的 UI 技术栈。QWidget 是传统的命令式 C++ 方案,控件是真实对象,通过继承和成员函数配置;QML 是声明式方案,用类似 JSON 的语法描述界面,底层由 Qt Quick 场景图渲染,天然适合动画与触摸交互。

  • QWidget:成熟稳定,控件丰富,适合桌面工具、工业上位机;样式依赖 QStyle 与样式表,动画能力弱。
  • QML:声明式、属性绑定、内置动画与状态机,适合嵌入式触摸屏、仪表盘;逻辑可用 C++ 后端暴露给 QML。

迁移时若原 LVGL 界面以静态控件为主,QWidget 迁移成本更低;若大量使用动画、滑动、渐变,QML 更贴近 LVGL 的视觉表现力。两者可以混合:用 QQuickWidget 把 QML 嵌入 QWidget 窗口。

二、信号槽:对象间的解耦通信

LVGL 用回调函数 lv_obj_add_event_cb() 绑定事件,回调与对象强耦合。Qt 的信号槽机制把“谁发出”和“谁接收”彻底分离:发送方只负责 emit,接收方通过 connect 注册,双方无需知道彼此类型。

// 传统写法:编译期检查
connect(btn, &QPushButton::clicked, this, &MainWindow::onBtnClicked);

// 新式 lambda:可捕获上下文
connect(slider, &QSlider::valueChanged, this, [=](int v){
    label->setText(QString::number(v));
});

// 跨线程:自动排队
connect(worker, &Worker::finished, this, &MainWindow::handleDone,
        Qt::QueuedConnection);

关键点:信号槽默认自动连接,跨线程时自动转为队列连接;连接类型可显式指定 Qt::DirectConnection、QueuedConnection、BlockingQueuedConnection。迁移中,LVGL 的 LV_EVENT_VALUE_CHANGED 通常对应 valueChanged 信号,LV_EVENT_CLICKED 对应 clicked。

三、事件系统:从底层输入到高层信号

Qt 的事件系统是信号槽的上游。鼠标、键盘、触摸、定时器、绘制请求都先以 QEvent 子类形式派发给目标对象,经过 event() 分发到 mousePressEvent()、keyPressEvent()、paintEvent() 等虚函数。若事件未被处理,可调用 ignore() 向上冒泡给父对象。

与 LVGL 的差异在于:LVGL 的事件回调直接挂在对象上,而 Qt 的事件有“过滤器”机制——installEventFilter() 可让一个对象监视另一个对象的事件,这在做全局快捷键、拖拽拦截时非常有用。

迁移提示:LVGL 的 lv_indev 输入设备在 Qt 中由平台插件自动处理,通常无需手动读取触摸坐标;若需自定义手势,重写 event() 或安装事件过滤器即可。

四、样式表、布局管理器与图形视图框架

这三者分别解决“外观”“排布”“复杂图形”三个问题。样式表(QSS)语法接近 CSS,可统一设置颜色、边框、圆角、背景图;布局管理器(QHBoxLayout、QVBoxLayout、QGridLayout、QFormLayout)自动计算控件位置与尺寸,替代 LVGL 中手动 lv_obj_align() 的繁琐;图形视图框架(QGraphicsView / QGraphicsScene / QGraphicsItem)用于管理大量图元、支持缩放、旋转、碰撞检测,适合流程图、组态图、地图类界面。

LVGL 概念Qt 对应机制迁移要点
lv_obj_t 对象树QObject 父子树 / QWidget 父子父对象析构自动释放子对象
lv_obj_add_event_cb信号槽 connect解耦,支持 lambda 与跨线程
lv_timer_handlerQEventLoop / QTimer由事件循环自动驱动
lv_obj_set_style_*QSS / QPalette样式表可集中管理
lv_obj_align / lv_layoutQLayout 布局管理器自动适配窗口缩放
lv_canvas / 手绘QGraphicsView 框架图元化,支持交互与变换

下面这张图把 Qt 六大核心机制的组织关系与数据流向串联起来,帮助建立整体心智模型。

Qt 核心机制关系图 输入事件 鼠标 / 键盘 / 触摸 事件系统 QEvent event() / 事件过滤器 信号槽 connect emit / 队列连接 QWidget(命令式) C++ 控件 / QStyle / QSS QML(声明式) Qt Quick / 属性绑定 / 动画 布局管理器 QHBox / QGrid / QForm 样式表 QSS 颜色 / 边框 / 圆角 图形视图框架 QGraphicsView / Scene 事件驱动 → 信号槽解耦 → 界面构建(QWidget/QML)→ 布局、样式、图形视图支撑 迁移核心:把 LVGL 的手动轮询与回调,映射为 Qt 的事件循环与信号槽

五、迁移中的常见误区与复盘

误区一:在 QWidget 中继续用定时器轮询刷新界面。Qt 的事件循环已经驱动重绘,频繁调用 update() 反而造成闪烁,应通过属性变化触发信号。
误区二:把 LVGL 的绝对坐标直接搬到 Qt。窗口缩放后布局会错乱,应改用 QLayout 或 QML 锚点。
误区三:忽略对象树的所有权。Qt 中父对象析构会删除子对象,若在栈上创建又交给父对象,会造成二次释放。

复盘建议:迁移前先画一张“LVGL 对象 → Qt 对象”的映射表,明确每个控件由谁创建、由谁销毁、通过哪个信号通信。把事件回调改造成信号槽时,优先使用新式 connect 语法以获得编译期检查。对于复杂自绘界面,评估是否值得引入 QGraphicsView,而不是继续在 paintEvent() 中手写全部绘制逻辑。掌握这六大机制后,后续的控件映射、样式迁移与性能调优才有稳定的参照系。