无人机智能硬件【免费下载链接】qgroundcontrolCross-platform ground control station for drones (Android, iOS, Mac OS, Linux, Windows)项目地址https://gitcode.com/gh_mirrors/qg/qgroundcontrol点击查看免费下载导读QGroundControlQGC的应用设置界面并非手工编写而是由 Python 生成器在构建时根据 JSON 定义自动产出 QML 页面。本文以 docs/tr/qgc-dev-guide/views/settings_generation.md 为骨架结合仓库内生成器源码、CMake 集成、真实页面定义与 UI 测试系统讲解这套生成机制的分层架构、控件选择规则、侧边栏与搜索的实现原理并给出向已有页面新增设置与新增整个设置页面的完整实操步骤以及自定义构建QGC_CUSTOM_DIR如何无侵入地增删改设置页面。读完本文你将具备独立扩展 QGC 设置系统、甚至为定制固件构建定制设置页面的能力。一、架构总览设置页的六层运行时栈QGC 的设置页生成遵循一条清晰的数据流从 C 事实Fact元数据一路流到最终渲染的 QML 界面。官方文档将其概括为六层Fact 元数据src/Settings/*.SettingsGroup.json—— 每个设置项的类型、默认值、枚举、标签、搜索关键词等事实元数据设置 Fact 访问器src/Settings/*Settings.h/.cc—— 将 JSON 元数据暴露为 CSettingsGroup子类上的Q_PROPERTY设置 UI 页面定义src/AppSettings/pages/*.SettingsUI.json—— 声明每个页面的分组布局与控件页面清单src/AppSettings/pages/SettingsPages.json—— 侧边栏页面的有序列表Python 生成器tools/generators/settings_qml—— 读取以上 JSON 并渲染 QML生成的 QML由src/QmlControls/AppSettings.qml加载展示。在构建期CMake 调用生成器把产物写入构建树build/generated随后这些文件被编译进QGroundControl.AppSettingsQML 模块见 src/AppSettings/CMakeLists.txt 中qt_add_qml_module(... URI QGroundControl.AppSettings ...)部分。从源码看这一层级的核心价值在于单一事实来源设置项的类型、枚举、默认值只定义一次*.SettingsGroup.jsonC 侧通过DEFINE_SETTINGFACT宏自动生成Fact*属性QML 侧则从同一份元数据自动推导控件类型避免了三处手工维护带来的漂移。二、生成器接线CMake 如何驱动代码生成生成流程由 src/AppSettings/CMakeLists.txt 统一编排自定义命令python -m tools.generators.settings_qml.generate_pages --output-dir build/generated通过qgc_add_qml_codegen(GenerateSettingsQml GENERATE_AT_CONFIGURE ...)在配置阶段执行输入src/AppSettings/pages/*.json页面清单与页面定义、src/Settings/*.SettingsGroup.json设置元数据以及生成器自身的*.py与templates/*.j2模板改动即触发重新生成输出每个pageDefinition对应一个页面 QML如GeneralSettings.qml、FlyViewSettings.qml外加一个SettingsPagesModel.qml页面模型。生成器入口为 tools/generators/settings_qml/generate_pages.py逻辑主体拆分为三个模块tools/generators/settings_qml/model.pyJSON 加载与模式校验dataclass 白名单键检查tools/generators/settings_qml/metadata.py读取*.SettingsGroup.json并解析SettingsManager.h的Q_PROPERTY建立JSON 主干 → accessor映射tools/generators/settings_qml/emit.py基于 Jinja2 模板tools/generators/settings_qml/templates渲染最终 QML。命令行工具同时支持--dry-run预览前 20 行输出与--list-outputs列出将生成的 QML 文件名供 CMake 计算输出清单两个模式后者在自定义构建叠加overlay场景下尤为关键见下文第五节。生成器的元数据发现机制值得注意的一个实现细节metadata.py中的stem_to_accessor()将*.SettingsGroup.json的文件主干名按 QGC 的 PascalCase → camelCase 约定转换为访问器名例如App.SettingsGroup.json→appSettings、ADSBVehicleManager.SettingsGroup.json→adsbVehicleManagerSettings、NTRIP.SettingsGroup.json→ntripSettings、RemoteID.SettingsGroup.json→remoteIDSettings。该函数通过正则解析 src/Settings/SettingsManager.h 中Q_PROPERTY(QObject *xxxSettings READ ...)的声明来校验映射是否合法取代了历史上手工维护且易过期的映射字典。若某个 JSON 文件找不到对应Q_PROPERTY生成器会输出警告并跳过该文件。三、控件自动选择与显式覆盖在*.SettingsUI.json中当某个控件省略control键时生成器会读取该 Fact 在*.SettingsGroup.json中的元数据来自动推断控件类型Fact 类型默认控件boolcheckboxFactCheckBoxSlider带enumStrings的枚举型 FactcomboboxLabelledFactComboBox数值型文本框LabelledFactTextField显式指定的control键则覆盖自动选择。支持的全部显式控件值如下值对应控件comboboxLabelledFactComboBoxtextfieldLabelledFactTextFieldcheckboxFactCheckBoxSliderslider滑块可配启用复选框与相邻按钮browse文件/目录浏览仅桌面端需配合showWhen: !ScreenTools.isMobilescaler百分比缩放器用于uiScalePercent等这一逻辑位于 tools/generators/settings_qml/emit.py 的_qml_control()先看显式类型未命中时才调用get_fact_type()与has_enum_strings()实现在 tools/generators/settings_qml/metadata.py做自动推断。另外页面若包含任一string 类型且无枚举的文本框模板会自动声明_stringFieldWidthScreenTools.defaultFontPixelWidth * 30以统一字符串输入框宽度见 page.qml.j2。真实示例General 页面的控件声明以 src/AppSettings/pages/General.SettingsUI.json 为参照可见上述规则的实战组合{ version: 1, fileType: SettingsUI, bindings: { _appSettings: QGroundControl.settingsManager.appSettings }, groups: [ { heading: General, keywords: [language, locale, color scheme, dark mode, theme], controls: [ { setting: appSettings.qLocaleLanguage, control: combobox }, { setting: appSettings.indoorPalette }, { 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 } }, { setting: appSettings.uiScalePercent, control: scaler }, { setting: appSettings.savePath, control: browse, showWhen: !ScreenTools.isMobile }, { setting: appSettings.clearSettingsNextBoot } ] } ] }对照 src/Settings/App.SettingsGroup.json 中的元数据可以验证推断逻辑clearSettingsNextBoot类型为bool故省略control会生成复选框indoorPalette带有enumStrings: Indoor,Outdoor省略control会生成下拉框audioVolume是double类型但显式指定了slideruiScalePercent为uint32但显式指定了scaler。bindings 与组件型分组bindings是页面级别的 QML 属性别名机制用于把冗长的访问器缩写为页面内可引用的短名称如上述_appSettings生成的绑定类型由_binding_qml_type()依据表达式形态自动推断以QGroundControl.开头→var含/||等→boolqsTr(/字符串字面量→string。分组除了普通控件列表还可以通过component键直接内嵌一个手工编写的 QML 组件此时controls可省略例如 RemoteID.SettingsUI.json 中嵌入的GcsPositionStatus与RemoteIDGpsLocation组件分组。组内还可通过missing数组以占位符形式记录尚未生成的复杂 UI说明模板 missing_placeholder.qml.j2。四、侧边栏、分区与搜索生成的 SettingsPagesModel.qml 对应模板 依据SettingsPages.json与每个页面定义构建并携带两类关键数据sections可展开侧边栏行的分区名取自sectionName缺省回退到heading以及每个分区的可见性表达式searchTerms搜索框使用的关键词组合最终由 src/QmlControls/AppSettings.qml 的_matchingSections()以indexOf大小写不敏感匹配。搜索词来源有四类页面名称分区标题 /sectionNameFact 元数据中的keywords字段如 App.SettingsGroup.json 中audioVolume的audio,volume,sound,voice,notifications使用component分组且无显式控件时分组级keywords。emit.py中generate_pages_model_qml()的实现在生成模型时还会把翻译函数包进去分区名与关键词通过qsTranslate(pageDefinition 文件名, 词)包装便于多语言翻译系统提取同时保留一份未翻译的小写原文用于搜索匹配。页面清单的真实结构src/AppSettings/pages/SettingsPages.json 是侧边栏的宪法General、Fly View、Plan View等条目按序排列中间可插入{ divider: true }视觉分隔每个条目由name侧边栏显示名、iconqrc:图标路径、qml输出文件名、pageDefinition对应*.SettingsUI.json构成。若条目只有qml而没有pageDefinition则被当作手工编写的 QML/URL 页面不参与生成——例如纯手写的AppLogging.qml、HelpSettings.qml、MockLink.qml。此外还支持visible表达式做条件显隐例如 NTRIP/RTK 页面仅在ntripSettings存在时显示Mock Link 与 Debug 页面仅在ScreenTools.isDebug时为真。五、自定义构建的设置页叠加Overlay自定义构建QGC_CUSTOM_DIR例如仓库中的custom-example可以在不覆盖仓库生成器产出的前提下对设置页进行增、替、挪、删。其机制分三个层面页面清单叠加在custom/src/AppSettings/pages/SettingsPages.json提供叠加文件配置期与库存清单合并合并逻辑位于 model.py 的_merge_overlay()与库存页面name相同的条目就地替换原页面此时不允许再带定位键新条目可通过insertAfter/insertBefore引用库存页面name定位插入否则追加到末尾{ remove: name }删除某个库存页面该条目不允许包含其他键。页面定义遮蔽同目录下的*.SettingsUI.json优先于同名库存定义resolve_page_def_path()先查自定义目录。自定义设置组若要引用 QGC 库存中不存在的事实在custom/src/Settings/Name.SettingsGroup.json提供事实元数据并把它编译进应用:/json资源前缀SettingsGroup通过kJsonFileTemplate :/json/%1.SettingsGroup.json加载见 SettingsGroup.h为其创建SettingsGroup子类重写QGCCorePlugin::registerCustomSettings声明于 src/API/QGCCorePlugin.h并调用SettingsManager::registerCustomSettingsGroup(accessor, new MySettings())由 manager 接管所有权API 见 src/Settings/SettingsManager.h。访问器必须是JSON 主干名转 camelCase 再加Settings如Custom.SettingsGroup.json→customSettings生成页才能解析QGroundControl.settingsManager.accessor.fact。当自定义目录存在时CMake 通过生成器的--list-outputs模式自动推导输出清单见 src/AppSettings/CMakeLists.txt 中的execute_process(... --list-outputs ...)分支并对 Windows 平台 CRLF 做归一化处理。元数据校验的守护逻辑metadata.py对自定义组有三重防护自定义组映射到的访问器若与库存SettingsManager的Q_PROPERTY冲突ValueError、或不符合 QML 标识符规则、或与另一个自定义组重复都会在生成阶段直接报错而不是等到运行时 QML 解析失败才暴露问题。这保证了生成器失败即 fail-fast。完整的 custom-example 参考仓库中的custom-example构建提供了上述所有能力的完整可运行示例页面清单叠加 custom-example/src/AppSettings/pages/SettingsPages.json新增Custom页并insertAfter: General页面定义 custom-example/src/AppSettings/pages/Custom.SettingsUI.json引用customSettings.showAttitudeWidget、customSettings.updateInterval、customSettings.operatorName三个事实事实元数据 custom-example/src/Settings/Custom.SettingsGroup.json含bool、带enumStrings的uint32、string三种类型恰好覆盖三种控件推断路径SettingsGroup子类 custom-example/src/Settings/CustomSettings.h 与实现插件注册重写 custom-example/src/CustomPlugin.ccsettingsManager-registerCustomSettingsGroup(QStringLiteral(customSettings), new CustomSettings());。六、实操一向已有生成页面新增一个设置按官方文档的步骤为现有页面如 General新增一个设置项需要改三处、重构建一次添加事实元数据在合适的src/Settings/Group.SettingsGroup.json中新增条目例如{ name: myNewSetting, shortDesc: Description shown in the settings page., type: bool, default: false, label: My New Setting, keywords: my,new,setting }在 C 设置类中暴露该 Fact在对应的*Settings.h中加入DEFINE_SETTINGFACT(myNewSetting)。该宏定义于 src/Settings/SettingsGroup.h会生成私有SettingsFact* _myNewSettingFact成员、Q_PROPERTY(Fact* myNewSetting READ myNewSetting CONSTANT)、Fact* myNewSetting()声明与static const char* myNewSettingName按该文件采用的模式在*Settings.cc中补充DECLARE_SETTINGSFACT(MySettings, myNewSetting)宏同样在 SettingsGroup.h 中定义负责_createSettingsFact的懒加载实现。设置类继承SettingsGroup其构造函数把name与 QSettings 分组名传入基类运行时通过_createSettingsFact从:/json资源加载元数据。在页面 JSON 中加入控件条目编辑src/AppSettings/pages/Page.SettingsUI.json在对应分组controls数组中追加{ setting: accessor.factName }例如{ setting: appSettings.myNewSetting }。setting路径必须与运行时QGroundControl.settingsManager.group.fact访问器完全一致否则生成的 QML 无法解析。model.py会用_SETTING_RE正则校验格式必须形如accessor.factName允许嵌套并在生成期报错而非运行期崩溃。构建CMake 的GENERATE_AT_CONFIGURE会检测 JSON 输入变化并自动重新生成该页面 QML无需手工维护构建产物。若省略步骤 2 中的 C 暴露或省略元数据中的labelSettingsGroup会在运行时记录缺失标签的日志见 src/Settings/SettingsGroup.cc文档中明确提到Missing labels are logged at runtime。七、实操二新增一个完整的生成设置页面创建页面定义在src/AppSettings/pages下新建例如MyFeature.SettingsUI.json内容遵循 tools/generators/settings_qml/README.md 中完整 schema顶层version/fileType必填、groups必填bindings/imports可选。登记页面清单在src/AppSettings/pages/SettingsPages.json的pages数组中新增条目字段包括name、icon、qml输出文件名、pageDefinition新 JSON 文件名可按需加visible表达式如需分隔线可插{ divider: true }。更新 CMake 输出清单在 src/AppSettings/CMakeLists.txt 的_generated_qml_names列表中加入新 QML 文件名。model.py会拒绝以SettingsPagesModel.qml为输出名或两个页面输出同名 QML 的冲突配置。构建 QGC生成并纳入新页面。若某个SettingsPages.json页面条目没有pageDefinition只有url它会被当作手写 QML/URL 内容处理、不做生成——这是文档强调的Important Notes也是SettingsPages.json中AppLogging、Help等条目不生成的原因。八、为 UI 测试而生的稳定 objectName 约定生成器会为页面、分组、控件发射稳定的objectName供 test/QmlUITests 下的 QML UI 测试通过QmlUITestBase::findVisibleItem精确定位避免脆弱的文本/遍历匹配元素objectName页面根settingsPage_PageName非[A-Za-z0-9_]字符剔除如settingsPage_RemoteID分组SettingsGroupLayout仅带标题的分组settingsGroup_Heading如settingsGroup_EUVehicleInfo文本框LabelledFactTextFieldsettingsTextField_factName复选框FactCheckBoxSlidersettingsCheckBox_factName页面名与标题在嵌入 objectName 前统一清洗为[A-Za-z0-9_]emit.py的_object_name()。生成期强校验若某标题清洗后为空字符串或同一页面两个标题清洗后撞名生成直接报错要求改名——避免 UI 测试静默匹配到错误对象。手写容器SettingsPage.qml的可滚动区域固定命名为settingsPageFlickable配合QmlUITestBase::scrollIntoView使用。test/QmlUITests/RemoteIDSettingsUITest.cc 是这套约定的实际使用者例如用settingsTextField_operatorIDEU/settingsTextField_operatorIDFAA校验欧盟/美国运营商 ID 输入框、用settingsGroup_EUVehicleInfo定位分组、用settingsCheckBox_sendOperatorID断言广播开关状态与可用性。九、注意事项与常见坑无pageDefinition即手写清单中只有url的条目走qrc:手写 QML 路径不经过生成器_generated_qml_names是显式清单新增输出文件名忘记登记会导致构建集成不完整自定义构建场景则由--list-outputs自动计算规避此问题setting路径必须可解析QGroundControl.settingsManager.group.fact访问器若不存在生成 QML 在运行时解析失败标签不可缺失Fact 元数据缺少label会在运行时被SettingsGroup记录日志开发调试时留意日志输出properties仅限browse/scalermodel.py对在其他控件上使用properties会直接报错且属性名不得是id/objectName/label/fact/enabled等模板已发射的保留名布尔与数值属性properties中的 JSON 布尔/数字会转为 QML 字面量字符串则原样作为 QML 表达式输出——例如让browse选文件而非文件夹properties: { selectFolder: false, nameFilters: [ qsTr(\OpenStreetMap files (*.osm)\) ] }来自 tools/generators/settings_qml/README.md 的完整示例showWhen/enableWhen的叠加控件显隐最终是(showWhen) fact.userVisible的逻辑与分组可见性则是sectionFilter与组级showWhen、组内控件userVisible的叠加_group_auto_vis()以 OR 聚合组内控件可见性自定义组访问器冲突自定义*.SettingsGroup.json映射到的访问器若与库存SettingsManager属性冲突或不符合 QML 标识符规范生成期即抛ValueError。十、附生成器的测试与命令行参考生成器自带 Python 单元测试 tools/tests/test_settings_qml_generator.py覆盖页面定义加载、控件类型推断、SettingsPagesModel.qml渲染、叠加合并等场景可在仓库根目录直接运行python3 -m tools.generators.settings_qml.generate_pages --output-dir src/AppSettings--dry-run可预览生成内容--list-outputs可只列出将生成的 QML 文件名CMake 在自定义构建场景下依赖此模式。完整 JSON schema 参考 tools/generators/settings_qml/README.md。结语QGC 的设置页生成体系把元数据定义、C 访问器、UI 布局、页面清单、代码生成、运行时加载串成一条可追踪的流水线改动 JSON 即自动获得经过类型推断的控件、搜索索引与测试钩子自定义构建还能在不动上游产物的前提下进行页面级叠加。理解这套机制后无论是为存量页面加一个开关还是为定制固件打造全新的设置页你都能用最小的代码量安全落地。赞分享无人机智能硬件【免费下载链接】qgroundcontrolCross-platform ground control station for drones (Android, iOS, Mac OS, Linux, Windows)项目地址https://gitcode.com/gh_mirrors/qg/qgroundcontrol点击查看免费下载相关推荐QGroundControl 设置页面自动生成机制详解从 JSON 定义到 QML 界面QGroundControl 设置页面自动生成机制详解从 JSON 定义到 QML 界面 QGroundControlQGC是无人机领域的跨平台地面站软件无人机智能硬件QGroundControl 车辆配置页 JSON 驱动生成机制从 VehicleConfig.json 到 Setup QML 页面的完整解析与自定义扩展指南QGroundControl 车辆配置页 JSON 驱动生成机制从 VehicleConfig.json 到 Setup QML 页面的完整解析与自定义扩展指无人机智能硬件QGroundControl 设置页面的 JSON 驱动生成机制从 SettingsGroup 元数据到 QML 渲染的完整指南QGroundControl 设置页面的 JSON 驱动生成机制从 SettingsGroup 元数据到 QML 渲染的完整指南 本指南以 QGroundCo无人机智能硬件上一篇Zag 计时器状态机 zag-js/timer 实战指南用有限状态机构建倒计时与秒表组件下一篇React useMemo 误用排查简单原始值表达式请直接计算不要包进 useMemoZCode rerender 最佳实践解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考