4. QGC界面框架:QML与C++混合编程、QML组件库、自定义控件
好,咱们进入第四章。这一章我打算把QGC的界面骨架拆开给你看。说白了,QGC的界面就是QML和C++搭伙干活——QML管界面长什么样,C++管数据怎么来、怎么算。我刚开始接触QGC时,也被这种混合模式搞晕过,后来摸清了门道,发现其实挺顺手的。
4.1 QML与C++混合编程:为什么非要这么搞?
你想想看,无人机地面站要实时显示飞行数据、地图、视频流,界面还得流畅响应。纯QML做不了复杂计算,纯C++写界面又太痛苦。所以Qt搞了个折中方案:QML负责界面描述,C++负责业务逻辑和数据模型。
QGC里最常见的混合方式有三种:
| 方式 | 适用场景 | 我常用的做法 |
|---|---|---|
| 注册C++对象到QML上下文 | 全局单例,比如设置管理器 | 在main.cpp里用engine.rootContext()->setContextProperty() |
| 注册C++类型到QML | 需要多次实例化的组件 | 用qmlRegisterType(),QML里直接当标签用 |
| 信号-槽跨语言通信 | 异步事件通知 | C++发信号,QML用onXxxChanged接收 |
核心原则:QML只管"画",C++只管"算"。界面逻辑尽量放QML,业务逻辑和数据模型放C++。这样后期改界面样式时,不用动底层代码。
4.2 QGC的QML组件库:你不需要从零造轮子
QGC自带了一套挺完整的QML组件库,放在src/QmlControls目录下。我建议你先翻一遍这个目录,看看里面有什么。很多你想要的控件,其实已经有人写好了。
常用的几个核心组件:
- QGCView:所有页面的基类,自带标题栏和返回按钮
- QGCButton:统一风格的按钮,支持主题色
- QGCLabel:带样式预设的文本标签
- QGCComboBox:下拉选择框,数据源绑定C++模型
- QGCMapDisplay:地图显示控件,封装了地图交互逻辑
- QGCValueLabel:数值显示控件,自动处理单位转换
我的习惯:拿到一个新需求,先翻QmlControls目录,看有没有现成的组件可以复用或继承。实在没有,再考虑自己写。这样能省不少时间。
4.3 自定义QML控件:三步走
当现成组件不够用时,就得自己动手了。我总结了一个三步走的套路:
- 画界面:用Rectangle、Text、Image等基础元素拼出外观
- 加属性:定义property,让外部可以设置颜色、文字、数据
- 绑信号:定义signal,让控件能通知外部发生了点击、拖动等事件
举个例子,一个带状态指示灯的按钮:
// StatusButton.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
Rectangle {
id: root
width: 120
height: 40
radius: 4
color: enabled ? "#3498db" : "#bdc3c7"
// 对外暴露的属性
property string labelText: "按钮"
property bool statusOn: false
// 对外暴露的信号
signal clicked()
// 状态指示灯
Rectangle {
id: indicator
width: 12
height: 12
radius: 6
anchors.left: parent.left
anchors.leftMargin: 8
anchors.verticalCenter: parent.verticalCenter
color: statusOn ? "#2ecc71" : "#e74c3c"
}
Text {
text: labelText
anchors.centerIn: parent
color: "#ffffff"
font.pixelSize: 14
}
MouseArea {
anchors.fill: parent
onClicked: root.clicked()
}
}
我曾经踩过的坑:自定义控件里直接用了id: root,结果在QML里引用时,root被覆盖了。后来我统一用id: control或id: container,避免和外部冲突。
4.4 知识结构图:QGC界面框架全景
下面这张图把QGC界面框架的核心关系画出来了。你看一眼,心里就有谱了。
4.5 实战建议:从哪开始改?
如果你刚接手QGC二次开发,我建议你按这个顺序来:
- 第一步:打开
src/QmlControls,把每个.qml文件看一遍,知道有什么组件可用 - 第二步:找一个现有页面(比如飞行仪表页),试着改几个属性,看看效果
- 第三步:写一个简单的自定义控件,注册到QML里,在页面上用起来
- 第四步:把C++里的一个数据模型暴露给QML,让控件显示真实数据
一个小技巧:调试QML时,在main.cpp里加上qputenv("QML_DEBUG", "1"),然后用Qt Creator的QML调试器,可以实时查看控件树和属性值。这个功能帮我省了不少排查时间。
嗯,这一章的内容就到这。你先把QML和C++的混合模式搞明白,后面做具体功能时就不会被界面卡住了。