【QGC】深入解析 QGC 配置管理

引言

在软件开发中,配置管理是一项至关重要的任务,它能帮助我们灵活地管理应用程序的各种参数和设置。QGroundControl(QGC)作为一款强大的开源无人机地面站软件,其配置管理系统设计精巧,值得我们深入学习。本文将详细介绍 QGC 配置管理的核心技术,并结合代码示例进行讲解。

一、QGC 配置管理概述

QGC 的配置管理主要通过 SettingsGroup 类及其相关宏来实现。这些机制允许开发者将不同的设置分组管理,同时支持设置项的创建、访问和可见性控制。下面我们逐步分析其关键组成部分。

1. 头文件保护与依赖引入

SettingsGroup.h

#pragma once
#include "SettingsFact.h"

#pragma once 确保头文件只被编译一次,避免重复包含。SettingsFact.h 引入了处理设置项的相关类。

2. 宏定义设置组名称和 QSettings 组名

SettingsGroup.h

#define DEFINE_SETTING_NAME_GROUP() \static const char* name; \static const char* settingsGroup;

该宏用于在设置组类中声明静态成员变量 namesettingsGroup,分别表示设置组的名称和在 QSettings 中使用的组名。

3. 声明设置组类

SettingsGroup.h

#define DECLARE_SETTINGGROUP(NAME, GROUP) \const char* NAME ## Settings::name = #NAME; \const char* NAME ## Settings::settingsGroup = GROUP; \NAME ## Settings::NAME ## Settings(QObject* parent) \: SettingsGroup(name, settingsGroup, parent)

此宏声明了设置组类的静态成员变量 namesettingsGroup 的定义,并定义了设置组类的构造函数。

4. 声明设置项事实

SettingsGroup.h

#define DECLARE_SETTINGSFACT(CLASS, NAME) \const char* CLASS::NAME ## Name = #NAME; \Fact* CLASS::NAME() \{ \if (!_ ## NAME ## Fact) { \_ ## NAME ## Fact = _createSettingsFact(NAME ## Name); \} \return _ ## NAME ## Fact; \}

该宏声明了设置项事实的名称和访问函数,确保设置项事实对象只被创建一次。

5. 定义设置项事实

SettingsGroup.h

#define DEFINE_SETTINGFACT(NAME) \private: \SettingsFact* _ ## NAME ## Fact = nullptr; \public: \Q_PROPERTY(Fact* NAME READ NAME CONSTANT) \Fact* NAME(); \static const char* NAME ## Name;

此宏在设置组类中定义设置项事实的私有成员变量、Qt 属性和访问函数。

6. SettingsGroup 类实现

SettingsGroup.h

class SettingsGroup : public QObject
{Q_OBJECTpublic:SettingsGroup(const QString &name, const QString &settingsGroup, QObject* parent = nullptr);Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged)virtual bool    visible             () { return _visible; }virtual void    setVisible          (bool vis) { _visible = vis; emit visibleChanged(); }signals:void            visibleChanged      ();protected:SettingsFact*   _createSettingsFact(const QString& factName);bool            _visible;QString         _name;QString         _settingsGroup;QMap<QString, FactMetaData*> _nameToMetaDataMap;private:static constexpr const char* kJsonFile = ":/json/%1.SettingsGroup.json";
};

SettingsGroup 类是配置管理的核心类,提供了设置组的创建、可见性控制等功能。

二、配置管理技术方案

1. 类继承与模块化设计

理论

类继承是面向对象编程的核心特性之一,子类可以继承父类的属性和方法,实现代码复用和功能扩展。模块化设计将系统划分为独立的模块,每个模块负责特定功能,提高代码的可维护性和可扩展性。

应用场景

在配置管理中,当多个配置类有共同的属性和方法时,可定义一个基类来封装这些通用功能,子类继承基类并添加各自特有的配置项和方法。

示例代码
// app_settings_example.h// 基类:SettingsGroup
class SettingsGroup {
public:SettingsGroup() = default;virtual ~SettingsGroup() = default;virtual void loadSettings() = 0;virtual void saveSettings() = 0;
};// 子类:AppSettings
class AppSettings : public SettingsGroup {
public:AppSettings() = default;~AppSettings() override = default;void loadSettings() override {// 实现加载应用设置的逻辑std::cout << "Loading app settings..." << std::endl;}void saveSettings() override {// 实现保存应用设置的逻辑std::cout << "Saving app settings..." << std::endl;}
};

2. 宏定义配置项

理论

宏定义是 C++ 预处理阶段的文本替换机制,通过定义宏可以简化代码编写,提高代码的可维护性和可读性。

应用场景

在配置管理中,当需要批量定义相似的配置项时,使用宏可以减少重复代码。

示例代码
// app_settings_example.h#include <iostream>
#include <string>// 定义宏
#define DEFINE_SETTINGFACT(name) \
private: \std::string name##Value; \
public: \void set##name(const std::string& value) { name##Value = value; } \std::string get##name() const { return name##Value; }class AppSettings {DEFINE_SETTINGFACT(offlineEditingFirmwareClass)DEFINE_SETTINGFACT(offlineEditingVehicleClass)
};

3. Qt 属性系统

理论

Qt 属性系统是 Qt 框架提供的一种机制,允许在类中定义属性,这些属性可以像普通成员变量一样使用,同时支持信号-槽机制和 QML 绑定。

应用场景

在 Qt 应用程序的配置管理中,使用属性系统可以方便地将配置项暴露给 QML 界面,实现界面与后端数据的同步。

示例代码
// app_settings_example.h#include <QObject>
#include <QString>class AppSettings : public QObject {Q_OBJECTQ_PROPERTY(QString missionSavePath READ missionSavePath WRITE setMissionSavePath NOTIFY missionSavePathChanged)public:explicit AppSettings(QObject* parent = nullptr) : QObject(parent) {}QString missionSavePath() const { return m_missionSavePath; }void setMissionSavePath(const QString& path) {if (m_missionSavePath != path) {m_missionSavePath = path;emit missionSavePathChanged();}}signals:void missionSavePathChanged();private:QString m_missionSavePath;
};

4. 信号与槽机制

理论

信号与槽是 Qt 框架的核心机制之一,用于对象间的通信。当信号被发射时,与之连接的槽函数会被自动调用。

应用场景

在配置管理中,当配置项发生变化时,通过发射信号通知其他对象更新状态。

示例代码
// app_settings_example.h#include <QObject>
#include <QString>class AppSettings : public QObject {Q_OBJECT
public:explicit AppSettings(QObject* parent = nullptr) : QObject(parent) {}void changeSetting() {// 模拟配置变化emit settingChanged();}signals:void settingChanged();
};class SettingListener : public QObject {Q_OBJECT
public:explicit SettingListener(QObject* parent = nullptr) : QObject(parent) {}public slots:void onSettingChanged() {std::cout << "Setting has been changed." << std::endl;}
};

5. 常量定义

理论

常量定义用于定义程序中不会改变的值,使用 constconstexpr 关键字保证值的不可修改性,提高代码的安全性和可读性。

应用场景

在配置管理中,文件扩展名、子目录名称等固定值通常使用常量定义。

示例代码
// app_settings_example.h#include <iostream>class AppSettings {
public:static constexpr const char* parameterFileExtension = "params";static constexpr const char* parameterDirectory = "Parameters";
};

6. 工具函数

理论

工具函数是为了实现特定功能而封装的独立函数,提高代码的复用性和可维护性。

应用场景

在配置管理中,类型转换、状态标记等操作可以封装成工具函数。

示例代码
// app_settings_example.h#include <QVariant>
#include <QList>class AppSettings {
public:static QList<int> firstRunPromptsIdsVariantToList(const QVariant& firstRunPromptIds) {QList<int> ids;if (firstRunPromptIds.canConvert<QList<int>>()) {ids = firstRunPromptIds.value<QList<int>>();}return ids;}static QVariant firstRunPromptsIdsListToVariant(const QList<int>& rgIds) {return QVariant::fromValue(rgIds);}
};

7. 友元类设计

理论

友元类是一种特殊的类,它可以访问另一个类的私有成员,打破了类的封装性,增强了类之间的协作能力。

应用场景

在配置管理中,当某个类需要频繁访问另一个类的私有成员时,可以将其声明为友元类。

示例代码
// app_settings_example.hclass AppSettings {
private:int privateSetting = 0;friend class QGCApplication;
};class QGCApplication {
public:void accessPrivateSetting(AppSettings& settings) {// 可以访问 AppSettings 的私有成员settings.privateSetting = 10;}
};

完整示例整合

// main.cpp#include <iostream>
#include <QObject>
#include <QString>
#include <QVariant>
#include <QList>// 基类:SettingsGroup
class SettingsGroup {
public:SettingsGroup() = default;virtual ~SettingsGroup() = default;virtual void loadSettings() = 0;virtual void saveSettings() = 0;
};// 定义宏
#define DEFINE_SETTINGFACT(name) \
private: \std::string name##Value; \
public: \void set##name(const std::string& value) { name##Value = value; } \std::string get##name() const { return name##Value; }// 子类:AppSettings
class AppSettings : public SettingsGroup, public QObject {Q_OBJECTQ_PROPERTY(QString missionSavePath READ missionSavePath WRITE setMissionSavePath NOTIFY missionSavePathChanged)public:explicit AppSettings(QObject* parent = nullptr) : QObject(parent) {}void loadSettings() override {std::cout << "Loading app settings..." << std::endl;}void saveSettings() override {std::cout << "Saving app settings..." << std::endl;}DEFINE_SETTINGFACT(offlineEditingFirmwareClass)DEFINE_SETTINGFACT(offlineEditingVehicleClass)QString missionSavePath() const { return m_missionSavePath; }void setMissionSavePath(const QString& path) {if (m_missionSavePath != path) {m_missionSavePath = path;emit missionSavePathChanged();}}void changeSetting() {emit settingChanged();}static QList<int> firstRunPromptsIdsVariantToList(const QVariant& firstRunPromptIds) {QList<int> ids;if (firstRunPromptIds.canConvert<QList<int>>()) {ids = firstRunPromptIds.value<QList<int>>();}return ids;}static QVariant firstRunPromptsIdsListToVariant(const QList<int>& rgIds) {return QVariant::fromValue(rgIds);}static constexpr const char* parameterFileExtension = "params";static constexpr const char* parameterDirectory = "Parameters";private:int privateSetting = 0;QString m_missionSavePath;signals:void missionSavePathChanged();void settingChanged();friend class QGCApplication;
};class SettingListener : public QObject {Q_OBJECT
public:explicit SettingListener(QObject* parent = nullptr) : QObject(parent) {}public slots:void onSettingChanged() {std::cout << "Setting has been changed." << std::endl;}
};class QGCApplication {
public:void accessPrivateSetting(AppSettings& settings) {settings.privateSetting = 10;}
};#include "main.moc"int main() {AppSettings appSettings;SettingListener listener;QObject::connect(&appSettings, &AppSettings::settingChanged, &listener, &SettingListener::onSettingChanged);appSettings.loadSettings();appSettings.setofflineEditingFirmwareClass("FirmwareClass");std::cout << "Offline Editing Firmware Class: " << appSettings.getofflineEditingFirmwareClass() << std::endl;appSettings.setMissionSavePath("NewMissionSavePath");appSettings.changeSetting();appSettings.saveSettings();QList<int> ids = {1, 2, 3};QVariant variantIds = AppSettings::firstRunPromptsIdsListToVariant(ids);QList<int> convertedIds = AppSettings::firstRunPromptsIdsVariantToList(variantIds);for (int id : convertedIds) {std::cout << "ID: " << id << std::endl;}QGCApplication qgcApp;qgcApp.accessPrivateSetting(appSettings);return 0;
}

总结

通过上述分析和示例,我们了解了 QGC 配置管理的核心机制,包括宏定义的使用、SettingsGroup 类的实现以及如何创建和使用设置组与设置项。这种设计使得 QGC 的配置管理具有良好的可扩展性和可维护性,开发者可以方便地添加新的设置组和设置项。希望本文能帮助你更好地理解 QGC 配置管理,并在自己的项目中应用相关技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88670.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT,从规则到强化学习

要了解 ChatGPT&#xff08;Chat Generative Pre-training Transformer&#xff09;&#xff0c;我们不得不先看看 NLP 自然语言处理&#xff08;Natural Language Processing&#xff09;。因为 ChatGPT 属于 NLP 领域&#xff0c;而 NLP 则又是人工智能的一个分支。 那么什么…

【目标检测之Ultralytics预测框颜色修改】

在 Ultralytics YOLOv8 中修改预测框颜色为红色&#xff0c;以下是三种实用方案&#xff1a;方案 1&#xff1a;直接修改 plot() 方法的 colors 参数 在调用 results.plot() 时直接指定颜色参数&#xff1a; from ultralytics import YOLO# 加载模型 model YOLO("yolov8n…

让 VSCode 调试器像 PyCharm 一样显示 Tensor Shape、变量形状、变量长度、维度信息

文章目录&#x1f3af; 目标&#xff1a;在 VS Code 调试器中自动显示这些变量信息&#x1f50d; 原理简介⚠️ 其他方案的局限性❌ 方案一&#xff1a;重写 __repr__❌ 方案二&#xff1a;向 debugpy 注册自定义变量显示器&#xff08;StrPresentationProvider&#xff09;✅ …

pip国内镜像源一览

以下是2025年主流pip国内镜像源完整清单及配置指南&#xff0c;综合多个权威来源整理的最新数据&#xff1a;一、核心镜像源推荐&#xff08;2025年稳定可用&#xff09;‌阿里云镜像‌https://mirrors.aliyun.com/pypi/simple/优势&#xff1a;依托阿里云CDN&#xff0c;全国平…

当大模型遇见毫米波:用Wi-Fi信号做“透视”的室内语义SLAM实践——从CSI到神经辐射场的端到端开源方案

作者 | Blossom.118 2025-07-12 关键词&#xff1a;CSI-SLAM、神经辐射场、毫米波、Transformer、数字孪生、开源 ---- 1. 为什么要“无摄像头”语义SLAM&#xff1f; • 隐私红线&#xff1a;欧盟GDPR 2024修订版把“摄像头点云”列入高风险生物特征&#xff0c;落地成本高。…

脉冲神经网络膜电位泄漏系数学习:开启时空动态特征提取的新篇章

脉冲神经网络膜电位泄漏系数学习&#xff1a;开启时空动态特征提取的新篇章 摘要 脉冲神经网络&#xff08;Spiking Neural Networks, SNNs&#xff09;作为第三代神经网络模型&#xff0c;凭借其事件驱动、高生物逼真度和潜在的超低功耗特性&#xff0c;已成为类脑计算与高效人…

SSRF(ctfshow)

web351-358这部分的题目都是明文的&#xff0c;按照题目要求绕过就行了<?php error_reporting(0); highlight_file(__FILE__); $url$_POST[url]; $xparse_url($url); if($x[scheme]http||$x[scheme]https){ if(!preg_match(/localhost|127\.0\.|\。/i, $url)){ $chcurl_ini…

亚矩阵云手机:重构物流供应链,让跨境包裹“飞”得更快更准

在跨境电商“时效即生命”的竞争中&#xff0c;物流信息滞后、清关效率低下、成本居高不下已成为商家最头疼的“三座大山”。传统模式下&#xff0c;人工更新物流状态耗时易错&#xff0c;跨境包裹常因清关延误遭客户投诉&#xff0c;而高昂的物流成本更直接吞噬利润。亚矩阵云…

HTML(5) 代码规范

HTML(5) 代码规范 引言 HTML(HyperText Markup Language)是一种用于创建网页的标准标记语言。HTML5 作为最新的 HTML 标准,自 2014 年正式发布以来,已经成为了构建现代网页应用的基础。本文将详细介绍 HTML5 代码规范,包括结构、语法、属性以及最佳实践等内容,旨在帮助…

【PTA数据结构 | C语言版】顺序栈的3个操作

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将 n1 个整数顺序压入容量为 n 的栈&#xff0c;随后执行 n1 次取顶并出栈的操作。 输入格式&#xff1a; 输入首先在第一行给出正整数 n&#xff08;≤10^4 &#xff09;&a…

使用Pycharm集成开发工具远程调试部署在虚拟机上的flask项目:超级详细的完整指南

本文将详细介绍如何通过PyCharm Professional版远程调试部署在虚拟机(这里以Ubuntu为例)中的Flask项目。这种开发方式特别适合需要在接近生产环境调试的场景。 虚拟机网络配置 这里用到的是VMware的NAT&#xff0c;即网络地址转换模式&#xff0c;要保证你Linux虚拟机的IP&…

UE制作的 AI 交互数字人嵌入到 Vue 开发的信息系统中的方法和步骤

要将 UE(Unreal Engine,虚幻引擎)制作的 AI 交互数字人嵌入到 Vue 开发的信息系统首页中运行,可以参考以下方法步骤以及涉及的软件工具: 准备工作 软件工具 Unreal Engine:用于创建和编辑 AI 交互数字人,需要在 UE 中完成数字人的建模、绑定骨骼、添加 AI 交互逻辑等工…

基于elementUI的el-autocomplete组件的自动补全下拉框实践

<template><div :class"$options.name"><el-autocompletestyle"width: 100%"ref"autocomplete":popper-class"${$options.name}-el-autocomplete"v-model"inputSearchValue":placeholder"输入关键词...…

Gameplay - 独立游戏Celeste的Player源码

TGA2018最佳独立游戏《蔚蓝》的一些公开代码&#xff1b;主要是Player部分的代码&#xff1a;using System; using System.Collections; using System.Collections.Generic; using Monocle; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input;namespace Cel…

基于大模型的鼻咽癌全周期预测及诊疗优化研究报告

目录 一、引言 1.1 研究背景与意义 1.2 国内外研究现状 1.3 研究目标与创新点 二、大模型技术与鼻咽癌相关理论基础 2.1 大模型技术概述 2.2 鼻咽癌疾病知识 2.3 大模型在医学领域的应用 三、数据收集与预处理 3.1 数据来源 3.2 数据清洗 3.3 数据标注 3.4 数据标…

基于odoo17的设计模式详解---访问模式

大家好&#xff0c;我是你的Odoo技术伙伴。想象一下&#xff0c;我们有一个复杂的对象结构&#xff0c;比如一个由不同类型的订单行&#xff08;销售行、折扣行、备注行&#xff09;组成的销售订单。现在&#xff0c;我们需要对这个结构执行一些新的操作&#xff0c;比如&#…

使用langgraph 构建RAG 智能问答代理

RAG 智能问答代理&#xff1a; ✅ 支持用户持续提问 ✅ 根据模型判断是否需要查资料 ✅ 自动调用 PDF 检索工具查找内容 ✅ 自动引用内容回答 ✅ 可以输入 exit / quit 退出 下载需要的library pip install langchain-google-genai pip install langgraph pip install langchai…

零基础搭建监控系统:Grafana+InfluxDB 保姆级教程,5分钟可视化服务器性能!​

​​你是否遇到过这些问题&#xff1f;​​服务器突然卡顿&#xff0c;却找不到性能瓶颈需要手动查看日志&#xff0c;无法实时监控数据运维报表全靠截图拼接&#xff0c;领导直呼“太原始”​今天教你用GrafanaInfluxDB构建企业级监控系统&#xff0c;从此告别“盲人摸象”式运…

Java中的wait和notify、Condition接口的使用

Java中的wait和notify机制基础概念在Java中&#xff0c;wait()和notify()是Object类的原生方法&#xff0c;用于实现线程间的协作&#xff1a;wait()使当前线程释放对象锁并进入等待状态必须在synchronized代码块内调用语法&#xff1a;obj.wait() 或 obj.wait(long timeout)线…

【Modern C++ Part9】Prefer-alias-declarations-to-typedefs

条款9&#xff1a;优先使用声明别名而不是typedef 我有信心说&#xff0c;大家都同意使用STL容器是个好的想法&#xff0c;并且我希望&#xff0c;条款18可以说服你使用std::unique_ptr也是个好想法&#xff0c;但是我想绝对我们中间没有人喜欢写像这样std::unique_ptr<std:…