☰
QGroundControl 设置视图(Settings View)架构与 JSON 驱动的 QML 页面生成机制
2026/10/3 2:30:33 网站建设 项目流程
  • 无人机
  • 智能硬件

【免费下载链接】qgroundcontrol

Cross-platform ground control station for drones (Android, iOS, Mac OS, Linux, Windows)

项目地址:https://gitcode.com/gh_mirrors/qg/qgroundcontrol
点击查看免费下载

QGroundControl 的应用设置界面(Settings View)是一个"JSON 定义 + Python 生成器 + QML 渲染"的混合体系:绝大多数设置页由*.SettingsUI.json与*.SettingsGroup.json在构建期自动生成 QML,少数复杂页面(帮助、日志、调试类)仍为手写 QML。本文以 docs/en/qgc-dev-guide/views/settings.md 及其姊妹篇 docs/en/qgc-dev-guide/views/settings_generation.md 为骨架,结合仓库内真实源码与配置,完整讲解设置视图的组成、生成流水线、JSON Schema 各字段语义,以及如何新增一个设置项、新增一个设置页、甚至为自定义构建(QGC_CUSTOM_DIR)覆盖整个设置体系。读完本文,你将具备直接上手修改 QGC 设置 UI 的完整实战能力。

设置视图的组成:生成 QML 与手写 QML 的混合

QGC 的应用设置 UI 并非单一文件,而是由"生成的 QML"与"手写 QML"两类来源拼接而成:

  • 设置容器/侧边栏:由 src/QmlControls/AppSettings.qml 实现。该文件负责侧边栏页面列表、可展开的 section、顶部搜索框,以及"仅在有可用页面时才显示分隔线"等交互逻辑(文件内_pageAvailable、_dividerVisible、_searchQuery等函数即对应这些行为)。
  • 绝大多数设置内容页:由 src/AppSettings/pages 目录下的 JSON 定义在构建期生成,例如General.SettingsUI.json、FlyView.SettingsUI.json、Video.SettingsUI.json等。
  • 少量手写页面:仍以url形式直接被引用,不经过生成器,例如 Help(HelpSettings.qml)、Logging(AppLogging.qml)、Debug(DebugWindow.qml)等。

这种混合设计的直接证据见 src/AppSettings/CMakeLists.txt:qt_add_qml_module的QML_FILES同时包含生成输出(${_generated_qml})与一长串手写文件(AppLogging.qml、BluetoothSettings.qml、SerialSettings.qml、TcpSettings.qml、UdpSettings.qml、NtripConnectionSettings.qml等)。

生成流水线的运行时数据栈

从底层事实数据到最终渲染页面,设置体系共分 6 层,这也是理解整个机制的主线:

  1. Fact 元数据:src/Settings/*.SettingsGroup.json—— 定义每个设置项(Fact)的类型、标签、枚举、范围、默认值与关键词。例如 src/Settings/App.SettingsGroup.json 中的preferredFirmwareClass为uint32,enumStrings为"No preference,ArduPilot,PX4 Pro",enumValues为"0,3,12",默认值0。
  2. Settings Fact 访问器:src/Settings/*Settings.h/.cc—— 每个设置组对应一个SettingsGroup子类,通过DEFINE_SETTINGFACT(<factName>)/DECLARE_SETTINGSFACT宏把 JSON 元数据暴露为 C++ Fact 对象。
  3. 设置 UI 页面定义:src/AppSettings/pages/*.SettingsUI.json—— 描述每个页面的分组、控件布局与可见/可用条件。
  4. 页面列表:src/AppSettings/pages/SettingsPages.json—— 决定侧边栏中页面的顺序、图标、是否生成、可见性表达式与分隔线。
  5. Python 生成器:tools/generators/settings_qml—— 读取 3、4 两层 JSON 与第 1 层元数据,输出页面 QML。
  6. 生成的 QML:被 src/QmlControls/AppSettings.qml 加载,编译进QGroundControl.AppSettingsQML 模块。

构建期 CMake 运行生成器,把生成的 QML 放入构建树,再统一编译进上述 QML 模块(参见 src/AppSettings/CMakeLists.txt 中的qgc_add_qml_codegen与qt_add_qml_module(URI QGroundControl.AppSettings ...))。

CMake 如何接入生成器

生成环节在 src/AppSettings/CMakeLists.txt 中配置:

  • 自定义命令执行:python -m tools.generators.settings_qml.generate_pages --output-dir <build>/generated
  • 输入:
    • src/AppSettings/pages/*.json(页面定义)
    • src/Settings/*.SettingsGroup.json(设置元数据)
  • 输出:
    • 各页面 QML(如GeneralSettings.qml、FlyViewSettings.qml)
    • SettingsPagesModel.qml(侧边栏模型)

生成器入口为 tools/generators/settings_qml/generate_pages.py,核心逻辑在 tools/generators/settings_qml/page_generator.py。从generate_pages.py的源码可以确认完整的命令行参数:

参数说明
--output-dir, -o生成 QML 的输出目录(与--list-outputs二选一)
--dry-run, -n只打印将要生成的内容,不写文件
--custom-pages-dir自定义构建的页面目录,可提供SettingsPages.json覆盖层与同名页面定义遮蔽
--custom-settings-dir自定义构建的设置目录,提供额外的*.SettingsGroup.json事实元数据
--list-outputs只打印将要生成的 QML 文件名(每行一个),供 CMake 在覆盖层生效时动态计算输出清单

手动运行生成器做一次试生成的命令为(需在仓库根目录执行):

python3 -m tools.generators.settings_qml.generate_pages --output-dir src/AppSettings

注意:实际构建由 CMake 驱动(GENERATE_AT_CONFIGURE+CONFIGURE_DEPENDS),修改 JSON 或生成器源码后重新构建即自动再生成,日常开发通常无需手工执行。

SettingsPages.json:侧边栏页面列表

src/AppSettings/pages/SettingsPages.json 定义设置侧边栏的页面顺序。顶层结构为:

{ "version": 1, "fileType": "SettingsPages", "pages": [ { "divider": true }, { "name": "General", "qml": "GeneralSettings.qml", "icon": "qrc:/res/QGCLogoWhite.svg", "pageDefinition": "General.SettingsUI.json" } ] }

页面条目(Page Entry)的键定义:

键类型说明
namestring侧边栏显示名称
iconstring侧边栏图标的qrc:路径
qmlstring输出文件名(如GeneralSettings.qml)
pageDefinitionstring用于生成的*.SettingsUI.json文件名
urlstring手写 QML 页面的qrc:URL(绕过生成)
visiblestringQML 表达式;为假时该条目隐藏
dividerbool插入可视分隔线而非页面条目

约束:每个页面条目必须且只能有pageDefinition(生成)或url(手写)二者之一。仓库真实条目正好覆盖了全部形态:

  • 生成页:General、Fly View、Plan View、ADSB Server、Comm Links、App Logging、Maps、NTRIP/RTK、PX4 Log Transfer、Remote ID、Telemetry、Video、3D View;
  • 手写页(无pageDefinition,只有qml):App Log Viewer(AppLogging.qml)、Help(HelpSettings.qml)、Mock Link、Debug、Palette Test;
  • 分隔线:两处{ "divider": true };
  • 条件可见页:NTRIP/RTK要求ntripSettings存在;PX4 Log Transfer要求showPX4LogTransferOptions且 PX4 固件受支持;Video、3D View分别绑定videoSettings.userVisible、viewer3DSettings.userVisible;Mock Link、Debug、Palette Test仅在ScreenTools.isDebug时出现。

SettingsUI.json:单页布局定义与完整 Schema

*.SettingsUI.json描述单个设置页的布局。顶层对象:

键类型必填说明
fileType"SettingsUI"是必须为"SettingsUI"
version1是Schema 版本
bindingsobject否命名的 QML 属性绑定(如访问器别名)
groupsarray of group是页面展示的设置分组

bindings是给长访问器起别名的标准方式,别名可在页面任意位置作为 QML 属性使用:

"bindings": { "_appSettings": "QGroundControl.settingsManager.appSettings" }

src/AppSettings/pages/General.SettingsUI.json 正是这样做的:顶层声明_appSettings绑定,随后在audioVolume滑块的enableCheckbox.onClicked中直接使用_appSettings.audioMuted.rawValue。

group

一个可折叠分组,可带标题:

键类型必填说明
headingstring否分组标题(可翻译)
headingDescriptionstring否标题下动态描述的 QML 表达式
showWhenstring否QML 表达式;为假时隐藏整组
enableWhenstring否QML 表达式;为假时禁用整组
sectionNamestring否树形导航显示名,缺省回退到heading
keywordsarray of string否额外搜索词
componentstring否嵌入的手写 QML 组件名(替代控件生成)
missingarray of string否尚未生成的复杂 UI 的说明(仅文档用途)
controlsarray of control见说明组内控件;设置了component时可不提供

General.SettingsUI.json中的Units分组即用showWhen: "QGroundControl.settingsManager.unitsSettings.userVisible"做条件显示,且每个单位设置都显式声明"control": "combobox"。

control

键类型必填说明
settingstring是指向 Fact 的点分路径,如"appSettings.qLocaleLanguage"
labelstring否覆盖标签;留空则用fact.label
controlstring否显式控件类型(见下文);省略时自动检测
showWhenstring否额外可见性 QML 表达式(与fact.userVisible做逻辑与)
enableWhenstring否绑定到enabled的 QML 表达式
placeholderstring否文本框占位符
propertiesobject否给browse/scaler控件注入的额外 QML 属性绑定
enableCheckboxobject否滑块的启用复选框(见下文)
buttonobject否相邻按钮(见下文)

setting的值必须是"<settingsGroupAccessor>.<factName>"形式,访问器与 C++SettingsManager属性名一致(如appSettings、flyViewSettings、autoConnectSettings)。在General.SettingsUI.json中可以看到多种典型写法:appSettings.qLocaleLanguage(combobox)、appSettings.indoorPalette(省略 control,自动检测)、appSettings.uiScalePercent(scaler)、appSettings.savePath(browse +showWhen: "!ScreenTools.isMobile")、unitsSettings.horizontalDistanceUnits(combobox,跨设置组访问)。

控件类型与自动选择逻辑

当*.SettingsUI.json省略control时,生成器会读取*.SettingsGroup.json元数据中的 Fact 类型来自动选择控件:

Fact 类型默认控件
boolcheckbox(FactCheckBoxSlider)
带enumStringscombobox(LabelledFactComboBox)
数值textfield(LabelledFactTextField)

显式control值可覆盖自动选择:

值实际控件
comboboxLabelledFactComboBox
textfieldLabelledFactTextField
checkboxFactCheckBoxSlider
slider滑块,可带启用复选框与相邻按钮
browse文件/路径浏览器(仅桌面端;需配showWhen: "!ScreenTools.isMobile")
scaler百分比缩放控件(用于uiScalePercent)

slider 的扩展键

enableCheckbox:{ "checked": "expr", "onClicked": "body" };button:{ "text": "label", "onClicked": "body", "enabled": "expr" }。仓库中audioVolume是一个完整范例:

{ "setting": "appSettings.audioVolume", "control": "slider", "enableCheckbox": { "checked": "!_appSettings.audioMuted.rawValue", "onClicked": "{ if (enableCheckBoxChecked && _appSettings.audioVolume.rawValue <= 0) _appSettings.audioVolume.rawValue = 75; _appSettings.audioMuted.rawValue = !enableCheckBoxChecked }" }, "button": { "text": "Test", "onClicked": "QGroundControl.testAudioOutput()", "enabled": "!_appSettings.audioMuted.rawValue && _appSettings.audioVolume.rawValue > 0" } }

即:勾选启用复选框时若音量为 0 则自动拉到 75,并同步audioMuted状态;"Test" 按钮只有未静音且音量大于 0 时才可用,点击触发QGroundControl.testAudioOutput()。

browse / scaler 的 properties

properties把 QML 属性名映射为值注入控件:布尔与数字按 QML 字面量输出,字符串原样作为 QML 表达式输出。例如让browse选择文件而非文件夹:

{ "setting": "viewer3DSettings.osmFilePath", "control": "browse", "showWhen": "!ScreenTools.isMobile", "properties": { "selectFolder": false, "nameFilters": "[ qsTr(\"OpenStreetMap files (*.osm)\") ]" } }

objectName 约定(UI 测试钩子)

生成页面会输出稳定的objectName,供 test/QmlUITests/ 通过QmlUITestBase::findVisibleItem稳定定位控件,避免脆弱的文本/遍历匹配:

条目objectName
页面根settingsPage_<PageName>(非[A-Za-z0-9_]字符剔除,如settingsPage_RemoteID)
分组(SettingsGroupLayout,仅带标题的分组)settingsGroup_<Heading>(同样剔除非法字符,如settingsGroup_EUVehicleInfo)
文本框(LabelledFactTextField)settingsTextField_<factName>
复选框(FactCheckBoxSlider)settingsCheckBox_<factName>

页面名与标题在嵌入 objectName 前会净化到[A-Za-z0-9_];若标题净化后为空串,或同一页面两个标题净化后产生相同 objectName,生成会直接报错,需重命名标题解决。手写页面 src/AppSettings/pages/../QmlControls/SettingsPage.qml 的内容 Flickable 命名为settingsPageFlickable,配合QmlUITestBase::scrollIntoView使用,参考示例见test/QmlUITests/RemoteIDSettingsUITest.cc。

侧边栏、分节与搜索

生成的SettingsPagesModel.qml由SettingsPages.json与每个页面定义构建,包含:

  • sections:可展开侧边栏行的分节名称;
  • searchTerms:页面/分节/Fact 关键词 token,供 src/QmlControls/AppSettings.qml 中的搜索框使用。

搜索词来源:

  • 页面名称
  • 分节标题/名称
  • Fact 元数据中的keywords
  • 使用component分组(未提供显式控件)时的组级keywords

这与 src/Settings/App.SettingsGroup.json 的字段一一对应:每个 Fact 都带keywords(如"firmware,ardupilot,px4"),页面 JSON 的分组也带keywords(如General页的"language,locale,color scheme,dark mode,...")。

实战一:向现有生成页面新增一个设置项

三步走,构建后自动生效:

  1. 添加 Fact 元数据:在合适的src/Settings/<Group>.SettingsGroup.json中新增条目。可参考 src/Settings/App.SettingsGroup.json 的既有字段:name、type、default、label、shortDesc/longDesc、keywords,枚举型还需enumStrings/enumValues,数值型可加min/max/userMin/userMax/units/decimalPlaces。
  2. 暴露 Fact:在对应的*Settings.h中加DEFINE_SETTINGFACT(<factName>);按该文件既有模式需要时,在*Settings.cc中确保DECLARE_SETTINGSFACT存在。
  3. 添加控件条目:在src/AppSettings/pages/<Page>.SettingsUI.json中加{ "setting": "<accessor>.<factName>" },可叠加control、showWhen、enableWhen等键。完整 JSON Schema 参考 tools/generators/settings_qml/README.md。

最后重新构建 QGC,CMake 会自动重新生成该页 QML(因为_page_definitions与_settings_metadata均带CONFIGURE_DEPENDS)。

实战二:新增一个完整的生成设置页

  1. 在src/AppSettings/pages创建新页面定义 JSON,如MyFeature.SettingsUI.json。
  2. 在 src/AppSettings/pages/SettingsPages.json 新增条目:name、icon、qml(输出文件名)、pageDefinition(新 JSON 文件名),可选visible表达式。
  3. 更新 src/AppSettings/CMakeLists.txt 的生成输出清单:把新 QML 文件名加进_generated_qml_names(该清单是显式的,漏加会导致构建集成不完整)。
  4. 构建 QGC,新页面即被生成并纳入QGroundControl.AppSettings模块。

实战三:自定义构建(QGC_CUSTOM_DIR)覆盖设置体系

自定义构建(QGC_CUSTOM_DIR)可以不覆盖仓库内生成的 QML,而是通过"覆盖层"增加、替换、重排或删除生成页:

  1. 页面列表覆盖层:创建<custom>/src/AppSettings/pages/SettingsPages.json。其条目在配置期与内置页面列表合并:
    • name与内置页相同的条目原位替换该页;
    • 新条目可用insertAfter/insertBefore(引用内置页name)控制位置,否则追加到末尾;
    • { "remove": "<name>" }删除内置页。
  2. 页面定义:把*.SettingsUI.json放进同一自定义 pages 目录;与内置定义同名的文件遮蔽内置定义。
  3. 自定义设置组:当需要引用内置 QGC 不存在的 Fact 时:
    • 在<custom>/src/Settings/<Name>.SettingsGroup.json添加 Fact 元数据,并把它以:/json资源前缀编译进应用;
    • 为其创建SettingsGroup子类;
    • 重写QGCCorePlugin::registerCustomSettings,调用SettingsManager::registerCustomSettingsGroup("<accessor>", new MySettings())(管理器接管所有权)。访问器必须是 JSON 文件名的 camelCase 词干加Settings,例如Custom.SettingsGroup.json→customSettings,这样生成页才能解析QGroundControl.settingsManager.<accessor>.<fact>。自定义词干若与内置SettingsManager访问器冲突会被拒绝。

CMake 在自定义目录存在时自动接线(src/AppSettings/CMakeLists.txt 会追加--custom-pages-dir、--custom-settings-dir),生成输出清单由生成器的--list-outputs模式动态计算,无需手工维护(Python 在 Windows 上输出 CRLF,CMake 已做\r归一化处理)。仓库中的 custom-example 自定义构建提供了上述全部机制的完整可运行示例:覆盖层新增自定义设置页、其页面定义、自定义设置组(Fact 元数据 +SettingsGroup子类)与插件注册覆写。

重要注意事项汇总

  • SettingsPages.json中无pageDefinition的页面被视为手写 QML/URL 内容,不参与生成。
  • CMake 的_generated_qml_names列表是显式的,新增输出文件名忘记登记会导致构建集成不完整。
  • *.SettingsUI.json中的setting路径必须匹配合法的QGroundControl.settingsManager.<group>.<fact>访问器。
  • Fact 标签应写入元数据;缺失标签会在运行时由SettingsGroup记录日志。

结语:从 JSON 到界面的一次性映射

整体来看,QGC 设置视图的设计哲学是"声明式 + 生成式":*.SettingsGroup.json提供事实(类型、枚举、范围、关键词),*.SettingsUI.json提供布局(分组、控件、可见性),SettingsPages.json提供导航结构,三者经 tools/generators/settings_qml 的 Python 生成器(模板位于tools/generators/settings_qml/templates/*.j2)在构建期翻译成稳定的 QML,再由 src/QmlControls/AppSettings.qml 运行时加载渲染。无论是日常添加一个开关、构建一个全新设置页,还是为定制版本做完整的分支覆盖,都可以在不手写 QML 的前提下,仅靠编辑 JSON 与重新构建完成——这正是这套机制对二次开发最大的价值所在。

  • 无人机
  • 智能硬件

【免费下载链接】qgroundcontrol

Cross-platform ground control station for drones (Android, iOS, Mac OS, Linux, Windows)

项目地址:https://gitcode.com/gh_mirrors/qg/qgroundcontrol
点击查看免费下载

相关推荐

上一篇:vnpy 价差交易模块 SpreadTrading 实战指南:价差合约构建、算法执行与策略开发
下一篇:New API 日文版指南精读:部署、环境变量与多机集群配置实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询