【无人机学习之QGroundControl】android端App初解3-ParameterEditorController

  • Post author:
  • Post category:其他




█ 【无人机学习之QGroundControl】android端 源码




█ 系列文章目录


提示:这里是收集了无人机的相关文章




█ 文章目录




█ 读前说明

  • 本文通过学习别人写demo,学习一些课件,参考一些博客,学习相关知识,如有涉及侵权请告知
  • 本文可能只简单罗列了一些相关的代码实现过程,复制了一些大神的高论,如内容有误请自行辨别
  • 涉及到的逻辑以及说明可能只做了简单的介绍,主要当做笔记,了解过程而已,如有不同看法,欢迎下方评论
  • 本文源码:

    https://github.com/mavlink/qgroundcontrol
  • 本文UI:

    https://github.com/mavlink/qgroundcontrol/blob/master/src/ui
  • QGC中UI设计的主要模式是用QML编写的UI页面,多次与用C ++编写的定制“控制器”进行通信。类似MVC的设计模式。


提示:QGroundControl是使用QT & c++ 编写的




█ 界面分析



1.参数界面

在这里插入图片描述

在这里插入图片描述



2.相关文件

在这里插入图片描述



3.ParameterEditor.qml


此文件在

qgroundcontrol-master\src\QmlControls\ParameterEditor.qml

 //---------------------------------------------
    //-- Header 头部搜索栏
    Row {
        id:             header
        anchors.left:   parent.left
        anchors.right:  parent.right
        spacing:        ScreenTools.defaultFontPixelWidth

        QGCLabel {
            anchors.verticalCenter: parent.verticalCenter
            text: qsTr("Search:")
        }

        QGCTextField {// 
            id:                 searchText
            text:               controller.searchText
            onDisplayTextChanged: controller.searchText = displayText
            anchors.verticalCenter: parent.verticalCenter
        }

        QGCButton {
            text: qsTr("Clear")// 清除按钮
            onClicked: {
                if(ScreenTools.isMobile) {
                    Qt.inputMethod.hide();
                }
                clearTimer.start()// 定时器自动清除
            }
            anchors.verticalCenter: parent.verticalCenter
        }

        QGCCheckBox {
            text:                   qsTr("Show modified only")
            anchors.verticalCenter: parent.verticalCenter
            checked:                controller.showModifiedOnly
            onClicked:              controller.showModifiedOnly = checked
            visible:                QGroundControl.multiVehicleManager.activeVehicle.px4Firmware
        }
    } // Row - Header
    QGCButton {
        anchors.top:    header.top
        anchors.bottom: header.bottom
        anchors.right:  parent.right
        text:           qsTr("Tools")
        visible:        !_searchFilter // _searchFilter=true: showing results of search
        onClicked:      toolsMenu.popup()
    }
    // Group buttons 左边菜单 折叠列表
    QGCFlickable {
        id :                groupScroll
        width:              ScreenTools.defaultFontPixelWidth * 25
        anchors.top:        header.bottom
        anchors.bottom:     parent.bottom
        clip:               true
        pixelAligned:       true
        contentHeight:      groupedViewCategoryColumn.height
        flickableDirection: Flickable.VerticalFlick
        visible:            !_searchFilter

        ColumnLayout {
            id:             groupedViewCategoryColumn
            anchors.left:   parent.left
            anchors.right:  parent.right
            spacing:        Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25)

            Repeater {// Repeater重复器 可以用于显示一个数组的数据,一级目录
                model: controller.categories

                Column {
                    Layout.fillWidth:   true
                    spacing:            Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25)


                    SectionHeader {
                        id:             categoryHeader
                        anchors.left:   parent.left
                        anchors.right:  parent.right
                        text:           object.name
                        checked:        object == controller.currentCategory// 一级目录,判断是否折叠
                        exclusiveGroup: sectionGroup

                        onCheckedChanged: {// 修改一级目录
                            if (checked) {
                                controller.currentCategory  = object
                            }
                        }
                    }

                    Repeater {// Repeater重复器 可以用于显示一个数组的数据,二级目录
                        model: categoryHeader.checked ? object.groups : 0

                        QGCButton {
                            width:          ScreenTools.defaultFontPixelWidth * 25
                            text:           object.name
                            height:         _rowHeight
                            checked:        object == controller.currentGroup // 判断当前选中的菜单
                            autoExclusive:  true

                            onClicked: {
                                if (!checked) _rowWidth = 10
                                checked = true
                                controller.currentGroup = object
                            }
                        }
                    }
                }
            }
        }
    }
    // Parameter list 右侧的参数列表(二级目录对应的参数列表)
    QGCListView {
        id:                 editorListView
        anchors.leftMargin: ScreenTools.defaultFontPixelWidth
        anchors.left:       _searchFilter ? parent.left : groupScroll.right// 有搜索时,全屏,遮盖groupScroll(QGCFlickable的id)
        anchors.right:      parent.right
        anchors.top:        header.bottom
        anchors.bottom:     parent.bottom
        orientation:        ListView.Vertical// 垂直列表
        model:              controller.parameters// 数据源
        cacheBuffer:        height > 0 ? height * 2 : 0
        clip:               true

        delegate: Rectangle {
            height: _rowHeight
            width:  _rowWidth
            color:  Qt.rgba(0,0,0,0)

            Row {
                id:     factRow
                spacing: Math.ceil(ScreenTools.defaultFontPixelWidth * 0.5)
                anchors.verticalCenter: parent.verticalCenter

                property Fact modelFact: object

                QGCLabel {// 参数名
                    id:     nameLabel
                    width:  ScreenTools.defaultFontPixelWidth  * 20
                    text:   factRow.modelFact.name
                    clip:   true
                }

                QGCLabel {// 参数值
                    id:     valueLabel
                    width:  ScreenTools.defaultFontPixelWidth  * 20
                    color:  factRow.modelFact.defaultValueAvailable ? (factRow.modelFact.valueEqualsDefault ? qgcPal.text : qgcPal.warningText) : qgcPal.text
                    text:   factRow.modelFact.enumStrings.length === 0 ? factRow.modelFact.valueString + " " + factRow.modelFact.units : factRow.modelFact.enumStringValue
                    clip:   true
                }

                QGCLabel {// 参数描述
                    text:   factRow.modelFact.shortDescription
                }

                Component.onCompleted: {
                    if(_rowWidth < factRow.width + ScreenTools.defaultFontPixelWidth) {
                        _rowWidth = factRow.width + ScreenTools.defaultFontPixelWidth
                    }
                }
            }

            MouseArea {// 点击事件
                anchors.fill:       parent
                acceptedButtons:    Qt.LeftButton
                onClicked: {
                    _editorDialogFact = factRow.modelFact
                    // 弹出参数编辑器对话框,对应对话框id是editorDialogComponent
                    mainWindow.showComponentDialog(editorDialogComponent, qsTr("Parameter Editor"), mainWindow.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Save)
                }
            }
        }
    }



4.参数编辑器Dialog

在这里插入图片描述

MouseArea {
    anchors.fill:       parent
    acceptedButtons:    Qt.LeftButton
    onClicked: {
        _editorDialogFact = factRow.modelFact
        // editorDialogComponent是对话框的id
        mainWindow.showComponentDialog(editorDialogComponent, qsTr("Parameter Editor"), mainWindow.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Save)
    }
}
    Component {
        id: editorDialogComponent

        ParameterEditorDialog {// 参数编辑器Dialog
            fact:           _editorDialogFact
            showRCToParam:  _showRCToParam
        }
    }
  • ParameterEditorDialog.qml
QGCViewDialog {
    id:     root
    focus:  true
    property Fact   fact
    ParameterEditorController { id: controller; }
    // Save 保存按钮
   function accept() {// 下一步需要查看的信息:参数编辑器Dialog
        if (bitmaskColumn.visible && !manualEntry.checked) {
            fact.value = bitmaskValue();
            fact.valueChanged(fact.value)
            valueChanged()
            hideDialog();
        } else if (factCombo.visible && !manualEntry.checked) {
            fact.enumIndex = factCombo.currentIndex
            valueChanged()
            hideDialog()
        } else {
            var errorString = fact.validate(valueField.text, forceSave.checked)
            if (errorString === "") {
                fact.value = valueField.text
                fact.valueChanged(fact.value)
                valueChanged()
                hideDialog()
            } else {
                validationError.text = errorString
                if (_allowForceSave) {
                    forceSave.visible = true
                }
            }
        }
    }
	// Cancel 取消按钮
    function reject() {
        fact.valueChanged(fact.value)
        hideDialog();
    }
    QGCFlickable {
        id:                 flickable
        anchors.fill:       parent
        contentHeight:      _column.y + _column.height
        flickableDirection: Flickable.VerticalFlick

        Column {
            id:             _column
            spacing:        globals.defaultTextHeight
            anchors.left:   parent.left
            anchors.right:  parent.right

            QGCLabel {
                id:         validationError
                width:      parent.width
                wrapMode:   Text.WordWrap
                color:      qgcPal.warningText
            }

            RowLayout {
                spacing:        ScreenTools.defaultFontPixelWidth
                anchors.left:   parent.left
                anchors.right:  parent.right

                QGCTextField {
                    id:                 valueField
                    text:               validate ? validateValue : fact.valueString
                    visible:            fact.enumStrings.length === 0 || validate || manualEntry.checked
                    unitsLabel:         fact.units
                    showUnits:          fact.units != ""
                    Layout.fillWidth:   true
                    focus:              setFocus
                    inputMethodHints:   (fact.typeIsString || ScreenTools.isiOS) ?
                                            Qt.ImhNone :                // iOS numeric keyboard has no done button, we can't use it
                                            Qt.ImhFormattedNumbersOnly  // Forces use of virtual numeric keyboard
                }

                QGCButton {
                    visible:    _allowDefaultReset
                    text:       qsTr("Reset to default")

                    onClicked: {
                        fact.value = fact.defaultValue
                        fact.valueChanged(fact.value)
                        hideDialog()
                    }
                }
            }
            
            QGCLabel {
                width:      parent.width
                wrapMode:   Text.WordWrap
                visible:    !longDescriptionLabel.visible
                text:       fact.shortDescription
            }

            QGCLabel {
                id:         longDescriptionLabel
                width:      parent.width
                wrapMode:   Text.WordWrap
                visible:    fact.longDescription != ""
                text:       fact.longDescription
            }
   
            QGCLabel {
                text:       qsTr("Parameter name: ") + fact.name
                visible:    fact.componentId > 0 // > 0 means it's a parameter fact
            }
  
            QGCLabel {
                width:      parent.width
                wrapMode:   Text.WordWrap
                text:       qsTr("Warning: Modifying values while vehicle is in flight can lead to vehicle instability and possible vehicle loss. ") +
                            qsTr("Make sure you know what you are doing and double-check your values before Save!")
                visible:    fact.componentId != -1
            }

        } // Column
    }
} // QGCViewDialog



5.下拉框spinner(对应QT – QGCMenu )

在这里插入图片描述

    QGCButton {
        anchors.top:    header.top
        anchors.bottom: header.bottom
        anchors.right:  parent.right
        text:           qsTr("Tools")
        visible:        !_searchFilter
        onClicked:      toolsMenu.popup()// toolsMenu是下拉框spinner的id
    }
 QGCMenu {
        id:                 toolsMenu
        QGCMenuItem {// 下一步需要查看的信息:刷新参数
            text:           qsTr("Refresh")
            onTriggered:	controller.refresh()
        }
        QGCMenuItem {
            text:           qsTr("Reset all to firmware's defaults")
            onTriggered:    mainWindow.showComponentDialog(resetToDefaultConfirmComponent, qsTr("Reset All"), mainWindow.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Reset)
        }
        QGCMenuItem {
            text:           qsTr("Reset to vehicle's configuration defaults")
            visible:        !_activeVehicle.apmFirmware
            onTriggered:    mainWindow.showComponentDialog(resetToVehicleConfigurationConfirmComponent, qsTr("Reset All"), mainWindow.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Reset)
        }
        QGCMenuSeparator { }
        QGCMenuItem {
            text:           qsTr("Load from file...")
            onTriggered: {
                fileDialog.title =          qsTr("Load Parameters")
                fileDialog.selectExisting = true
                fileDialog.openForLoad()
            }
        }
        QGCMenuItem {
            text:           qsTr("Save to file...")
            onTriggered: {
                fileDialog.title =          qsTr("Save Parameters")
                fileDialog.selectExisting = false
                fileDialog.openForSave()
            }
        }
        QGCMenuSeparator { visible: _showRCToParam }
        QGCMenuItem {
            text:           qsTr("Clear all RC to Param")
            onTriggered:	_activeVehicle.clearAllParamMapRC()
            visible:        _showRCToParam
        }
        QGCMenuSeparator { }
        QGCMenuItem {
            text:           qsTr("Reboot Vehicle")
            onTriggered:    mainWindow.showComponentDialog(rebootVehicleConfirmComponent, qsTr("Reboot Vehicle"), mainWindow.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Ok)
        }
    }



█ 通信说明(ParameterManager.cc)



1.过程


1. 刚进入,通过MAV_COMP_ID_AUTOPILOT1来读取左侧的折叠列表:系统飞行控制器组件的所有参数;

2.当修改参数后,则通过MAV_COMP_ID_ALL来更新所有的参数:接收系统的所有组件;

在这里插入图片描述

序号 字段 说明
#20 PARAM_REQUEST_READ 请求一个参数。接收者使用PARAM_VALUE广播指定的参数值。
#21 PARAM_REQUEST_LIST 请求所有参数。接收者使用PARAM_VALUE广播所有参数值。
#22 PARAM_VALUE 参数的当前值,响应于获取一个或多个参数(PARAM_REQUEST_READ,PARAM_REQUEST_LIST)的请求而广播,或者在参数被设置(PARAM_SET)或更改时进行广播。
#23 PARAM_SET 发送命令以将指定参数设置为值。设置值后(无论成功与否),接收者应使用PARAM_VALUE广播当前值。



2.发送参数

void ParameterManager::_sendParamSetToVehicle(int componentId, const QString& paramName, FactMetaData::ValueType_t valueType, const QVariant& value)
{
    WeakLinkInterfacePtr weakLink = _vehicle->vehicleLinkManager()->primaryLink();

    if (!weakLink.expired()) {
        mavlink_param_set_t     p;// 参数设置消息(mavlink_param_set_t)
        mavlink_param_union_t   union_value;// 所有的参数都以浮点型的mavlink_param_union_t值发送
        SharedLinkInterfacePtr  sharedLink = weakLink.lock();

        memset(&p, 0, sizeof(p));
		// MAV_PARAM_TYPE_UINT8/MAV_PARAM_TYPE_INT8/MAV_PARAM_TYPE_UINT16
		// MAV_PARAM_TYPE_INT16/MAV_PARAM_TYPE_UINT32
		// MAV_PARAM_TYPE_UINT64/MAV_PARAM_TYPE_INT64/MAV_PARAM_TYPE_REAL32/MAV_PARAM_TYPE_REAL64
        p.param_type = factTypeToMavType(valueType);

        switch (valueType) {
        case FactMetaData::valueTypeUint8:
            union_value.param_uint8 = (uint8_t)value.toUInt();
            break;

        case FactMetaData::valueTypeInt8:
            union_value.param_int8 = (int8_t)value.toInt();
            break;

        case FactMetaData::valueTypeUint16:
            union_value.param_uint16 = (uint16_t)value.toUInt();
            break;

        case FactMetaData::valueTypeInt16:
            union_value.param_int16 = (int16_t)value.toInt();
            break;

        case FactMetaData::valueTypeUint32:
            union_value.param_uint32 = (uint32_t)value.toUInt();
            break;

        case FactMetaData::valueTypeFloat:
            union_value.param_float = value.toFloat();
            break;

        default:
            qCritical() << "Unsupported fact falue type" << valueType;
            // fall through

        case FactMetaData::valueTypeInt32:
            union_value.param_int32 = (int32_t)value.toInt();
            break;
        }

        p.param_value = union_value.param_float;
        p.target_system = (uint8_t)_vehicle->id();
        p.target_component = (uint8_t)componentId;

        strncpy(p.param_id, paramName.toStdString().c_str(), sizeof(p.param_id));

        mavlink_message_t msg;
        mavlink_msg_param_set_encode_chan(_mavlink->getSystemId(),
                                          _mavlink->getComponentId(),
                                          sharedLink->mavlinkChannel(),
                                          &msg,
                                          &p);
        _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
    }
}



3.读取参数

QString ParameterManager::readParametersFromStream(QTextStream& stream)
{
    QString missingErrors;
    QString typeErrors;

    while (!stream.atEnd()) {
        QString line = stream.readLine();
        if (!line.startsWith("#")) {
            QStringList wpParams = line.split("\t");
            int lineMavId = wpParams.at(0).toInt();
            if (wpParams.size() == 5) {
                if (_vehicle->id() != lineMavId) {
                    return QString("The parameters in the stream have been saved from System Id %1, but the current vehicle has the System Id %2.").arg(lineMavId).arg(_vehicle->id());
                }

                int     componentId = wpParams.at(1).toInt();
                QString paramName = wpParams.at(2);
                QString valStr = wpParams.at(3);
                uint    mavType = wpParams.at(4).toUInt();

                if (!parameterExists(componentId, paramName)) {
                    QString error;
                    error += QStringLiteral("%1:%2 ").arg(componentId).arg(paramName);
                    missingErrors += error;
                    qCDebug(ParameterManagerLog) << QStringLiteral("Skipped due to missing: %1").arg(error);
                    continue;
                }

                Fact* fact = getParameter(componentId, paramName);
                if (fact->type() != mavTypeToFactType((MAV_PARAM_TYPE)mavType)) {
                    QString error;
                    error  = QStringLiteral("%1:%2 ").arg(componentId).arg(paramName);
                    typeErrors += error;
                    qCDebug(ParameterManagerLog) << QStringLiteral("Skipped due to type mismatch: %1").arg(error);
                    continue;
                }

                qCDebug(ParameterManagerLog) << "Updating parameter" << componentId << paramName << valStr;
                fact->setRawValue(valStr);
            }
        }
    }

    QString errors;

    if (!missingErrors.isEmpty()) {
        errors = tr("Parameters not loaded since they are not currently on the vehicle: %1\n").arg(missingErrors);
    }

    if (!typeErrors.isEmpty()) {
        errors += tr("Parameters not loaded due to type mismatch: %1").arg(typeErrors);
    }

    return errors;
}



█ 事件分析



1.搜索文本框

在这里插入图片描述

QGCTextField变化
controller.searchText变化 ,即cc中_searchText变化
回调cc中 searchTextChanged (QString searchText),即_searchTextChanged(void)
  • ParameterEditor.qml
    ParameterEditorController {
        id: controller
    }
    QGCTextField {
        id:                 searchText// QGCTextField的id
        text:               controller.searchText// cc文件的成员
        onDisplayTextChanged: controller.searchText = displayText
        anchors.verticalCenter: parent.verticalCenter
    }
  • ParameterEditorController.h
class ParameterEditorController : public FactPanelController
{
    Q_OBJECT

public:
    ParameterEditorController(void);
    ~ParameterEditorController();
	// 成员_searchText 和 qml的controller.searchText绑定,变化时,回调 searchTextChanged
    Q_PROPERTY(QString              searchText          MEMBER _searchText                                          NOTIFY searchTextChanged)
    // 表明searchParameters可以被qml调用
    Q_INVOKABLE QStringList searchParameters(const QString& searchText, bool searchInName=true, bool searchInDescriptions=true);
signals:// 信号
    void searchTextChanged              (QString searchText);
private slots://void _searchTextChanged     (void);
private:
    QString                     _searchText;// 成员_searchText
    QmlObjectListModel          _searchParameters;
};
  • ParameterEditorController.cc
ParameterEditorController::ParameterEditorController(void)
    : _parameterMgr(_vehicle->parameterManager())
{
    _buildLists();
    // 一个信号可以连接一个槽,在发射这个信号的时候,会以不确定的顺序一个接一个的调用这些槽。
    connect(this, &ParameterEditorController::searchTextChanged,        this, &ParameterEditorController::_searchTextChanged);
}

QStringList ParameterEditorController::searchParameters(const QString& searchText, bool searchInName, bool searchInDescriptions)
{
    QStringList list;

    for(const QString &paramName: _parameterMgr->parameterNames(_vehicle->defaultComponentId())) {
        if (searchText.isEmpty()) {
            list += paramName;
        } else {
            Fact* fact = _parameterMgr->getParameter(_vehicle->defaultComponentId(), paramName);

            if (searchInName && fact->name().contains(searchText, Qt::CaseInsensitive)) {
                list += paramName;
            } else if (searchInDescriptions && (fact->shortDescription().contains(searchText, Qt::CaseInsensitive) || fact->longDescription().contains(searchText, Qt::CaseInsensitive))) {
                list += paramName;
            }
        }
    }
    list.sort();

    return list;
}
void ParameterEditorController::_searchTextChanged(void)
{
    QObjectList newParameterList;

#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
    QStringList rgSearchStrings = _searchText.split(' ', QString::SkipEmptyParts);
#else
    QStringList rgSearchStrings = _searchText.split(' ', Qt::SkipEmptyParts);
#endif


    if (rgSearchStrings.isEmpty() && !_showModifiedOnly) {// 空字符串,显示全部
        ParameterEditorCategory* category = _categories.count() ? _categories.value<ParameterEditorCategory*>(0) : nullptr;
        setCurrentCategory(category);
        _searchParameters.clear();
    } else {
        _searchParameters.beginReset();
        _searchParameters.clear();
		// ParameterManager _parameterMgr 获取全部参数树 名称
        for (const QString &paraName: _parameterMgr->parameterNames(_vehicle->defaultComponentId())) {
        	// 获取具体的参数对象
            Fact* fact = _parameterMgr->getParameter(_vehicle->defaultComponentId(), paraName);
            bool matched = _shouldShow(fact);
            // All of the search items must match in order for the parameter to be added to the list
            if (matched) {
                for (const auto& searchItem : rgSearchStrings) {
                    if (!fact->name().contains(searchItem, Qt::CaseInsensitive) &&
                            !fact->shortDescription().contains(searchItem, Qt::CaseInsensitive) &&
                            !fact->longDescription().contains(searchItem, Qt::CaseInsensitive)) {
                        matched = false;
                    }
                }
            }
            if (matched) {// 参数匹配,就加入搜索的参数
                _searchParameters.append(fact);
            }
        }

        _searchParameters.endReset();

        if (_parameters != &_searchParameters) {
            _parameters = &_searchParameters;
            emit parametersChanged();

            _currentCategory    = nullptr;
            _currentGroup       = nullptr;
        }
    }
}

bool ParameterEditorController::_shouldShow(Fact* fact)
{
    bool show = _showModifiedOnly ? (fact->defaultValueAvailable() ? (fact->valueEqualsDefault() ? false : true) : false) : true;
    return show;
}
  • ParameterManager.cc
  QMap<int /* comp id */, QMap<QString /* parameter name */, Fact*>> _mapCompId2FactMap;
// 根据参数名获取参数对象
Fact* ParameterManager::getParameter(int componentId, const QString& paramName)
{
    componentId = _actualComponentId(componentId);

    QString mappedParamName = _remapParamNameToVersion(paramName);
    if (!_mapCompId2FactMap.contains(componentId) || !_mapCompId2FactMap[componentId].contains(mappedParamName)) {
        qgcApp()->reportMissingParameter(componentId, mappedParamName);
        return &_defaultFact;
    }

    return _mapCompId2FactMap[componentId][mappedParamName];
}
// 根据组件id获取所有的参数名数组
QStringList ParameterManager::parameterNames(int componentId)
{
    QStringList names;

    for(const QString &paramName: _mapCompId2FactMap[_actualComponentId(componentId)].keys()) {
        names << paramName;
    }

    return names;
}



2.修改参数(参数编辑对话框)

  • ParameterEditor.qml
    Component {
        id: editorDialogComponent

        ParameterEditorDialog {// 下一步需要查看的信息:参数编辑器Dialog
            fact:           _editorDialogFact
            showRCToParam:  _showRCToParam
        }
    }
  • ParameterEditorDialog.qml
QGCViewDialog {
    id:     root
    focus:  true
    property Fact   fact
    ParameterEditorController { id: controller; }
    // Save 保存按钮
   function accept() {// 下一步需要查看的信息:参数编辑器Dialog
        if (bitmaskColumn.visible && !manualEntry.checked) {
            fact.value = bitmaskValue();
            fact.valueChanged(fact.value)
            valueChanged()
            hideDialog();
        } else if (factCombo.visible && !manualEntry.checked) {
            fact.enumIndex = factCombo.currentIndex
            valueChanged()
            hideDialog()
        } else {// 在这边进行修改参数
            var errorString = fact.validate(valueField.text, forceSave.checked)
            if (errorString === "") {// 判断是否合法
                fact.value = valueField.text
                // This signal is only meant for use by the QT property system.
                // It should not be connected to by client code.
                fact.valueChanged(fact.value)// 库函数 
                valueChanged()
                hideDialog()
            } else {// 非法参数,直接提示
                validationError.text = errorString
                if (_allowForceSave) {
                    forceSave.visible = true
                }
            }
        }
    }
	// Cancel 取消按钮
    function reject() {
        fact.valueChanged(fact.value)
        hideDialog();
    }
} // QGCViewDialog
  • QGCViewDialogContainer.qml
// This is the main dialog panel
    Item {
        id:                 _dialogPanel
        anchors.fill:       parent
        Rectangle {
            id:             _header
            width:          parent.width
            height:         _acceptButton.visible ? _acceptButton.height : _rejectButton.height
            color:          qgcPal.windowShade
            QGCLabel {
                x:                  _defaultTextWidth
                text:               dialogTitle
                height:             parent.height
                verticalAlignment:	Text.AlignVCenter
            }
            QGCButton {// 取消按钮
                id:                 _rejectButton
                anchors.right:      _acceptButton.visible ?  _acceptButton.left : parent.right
                anchors.bottom:     parent.bottom
                onClicked:          _dialogComponentLoader.item.reject()
            }
            QGCButton {// 保存按钮
                id:                 _acceptButton
                anchors.right:      parent.right
                anchors.bottom:     parent.bottom
                primary:            true
                onClicked:          _dialogComponentLoader.item.accept()
            }
        }
       
    }



2.刷新参数(Tools -> Refresh)

mavlink_msg_param_request_list_pack_chan(SysId,CmpId,mavlinkChannel(),&msg,vehicleid, MAV_COMP_ID_ALL);
_vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
  • ParameterEditor.qml
    property var    _controller:        controller

    ParameterEditorController {
        id: controller
    }
	QGCMenuItem {
        text:           qsTr("Refresh")
        onTriggered:	controller.refresh()// 下一步需要查看的信息:刷新参数
   }

  • ParameterEditorController.h
class ParameterEditorController : public FactPanelController
{
    Q_OBJECT

public:
    ParameterEditorController(void);
    ~ParameterEditorController();

   。。。。。。
    Q_INVOKABLE void saveToFile                     (const QString& filename);
    Q_INVOKABLE bool buildDiffFromFile              (const QString& filename);
    Q_INVOKABLE void clearDiff                      (void);
    Q_INVOKABLE void sendDiff                       (void);
    Q_INVOKABLE void refresh                        (void);// 下一步需要查看的信息:刷新参数
  。。。。。。
private:
    ParameterManager*           _parameterMgr           = nullptr;
    QString                     _searchText;
    ParameterEditorCategory*    _currentCategory        = nullptr;
    ParameterEditorGroup*       _currentGroup           = nullptr;
  。。。。。。
};
  • ParameterManager.cc
void ParameterEditorController::refresh(void)
{
    _parameterMgr->refreshAllParameters();// 下一步需要查看的信息:刷新参数
}
  • ParameterManager.h
class ParameterManager : public QObject
{
    Q_OBJECT

    friend class ParameterEditorController;

public:
    Vehicle*            _vehicle;
    MAVLinkProtocol*    _mavlink;
    // Re-request the full set of parameters from the autopilot 
    // 刷新全部参数树:MAV_COMP_ID_ALL = 0
    void refreshAllParameters(uint8_t componentID = MAV_COMP_ID_ALL);// 下一步需要查看的信息
};
  • ParameterEditorController.cc
void ParameterManager::refreshAllParameters(uint8_t componentId)
{
    。。。。。。
    // Reset index wait lists
    for (int cid: _paramCountMap.keys()) {
        // Add/Update all indices to the wait list, parameter index is 0-based
        if(componentId != MAV_COMP_ID_ALL && componentId != cid)
            continue;
        for (int waitingIndex = 0; waitingIndex < _paramCountMap[cid]; waitingIndex++) {
            // This will add a new waiting index if needed and set the retry count for that index to 0
            _waitingReadParamIndexMap[cid][waitingIndex] = 0;
        }
    }

    MAVLinkProtocol*        mavlink = qgcApp()->toolbox()->mavlinkProtocol();// 从工具栏获取相关配置信息
    mavlink_message_t       msg;// 找不到对应的文件,可能是库函数
    SharedLinkInterfacePtr  sharedLink = weakLink.lock();
	// 找不到对应的详情方法,可能是库函数,将消息打包
	// MAVLinkProtocol将字节转换为MAVLink消息
    mavlink_msg_param_request_list_pack_chan(mavlink->getSystemId(),// 飞控id
                                             mavlink->getComponentId(),// 组件id
                                             sharedLink->mavlinkChannel(),
                                             &msg,
                                             _vehicle->id(),
                                             componentId);// MAV_COMP_ID_ALL
    _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);// 下一步需要查看的信息:发送消息

}
  • Vehicle.h
class Vehicle : public FactGroup
{
    Q_OBJECT

public:
    Vehicle(LinkInterface*          link,
            int                     vehicleId,
            int                     defaultComponentId,
            MAV_AUTOPILOT           firmwareType,
            MAV_TYPE                vehicleType,
            FirmwarePluginManager*  firmwarePluginManager,
            JoystickManager*        joystickManager);
    /// Sends a message to the specified link
    /// @return true: message sent, false: Link no longer connected
    bool sendMessageOnLinkThreadSafe(LinkInterface* link, mavlink_message_t message);// 下一步需要查看的信息:发送消息

  • Vehicle.cc
bool Vehicle::sendMessageOnLinkThreadSafe(LinkInterface* link, mavlink_message_t message)
{
    if (!link->isConnected()) {
        return false;
    }

    // Give the plugin a chance to adjust
    _firmwarePlugin->adjustOutgoingMavlinkMessageThreadSafe(this, link, &message);

    // Write message into buffer, prepending start sign
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];// 打包为Buff
    int len = mavlink_msg_to_send_buffer(buffer, &message);

    link->writeBytesThreadSafe((const char*)buffer, len);// 下一步需要查看的信息:发送消息
    _messagesSent++;
    emit messagesSentChanged();

    return true;
}
  • LinkInterface.h
/**
* @brief The link interface defines the interface for all links used to communicate
* with the ground station application.
**/
class LinkInterface : public QThread
{
    Q_OBJECT

    friend class LinkManager;

public:    
    virtual ~LinkInterface();
    Q_INVOKABLE virtual void    disconnect  (void) = 0;
    virtual bool isConnected    (void) const = 0;
    bool    decodedFirstMavlinkPacket   (void) const { return _decodedFirstMavlinkPacket; }
    bool    setDecodedFirstMavlinkPacket(bool decodedFirstMavlinkPacket) { return _decodedFirstMavlinkPacket = decodedFirstMavlinkPacket; }
    void    writeBytesThreadSafe        (const char *bytes, int length);// 下一步需要查看的信息:发送消息
}
  • LinkInterface.cc
void LinkInterface::writeBytesThreadSafe(const char *bytes, int length)
{
    QByteArray byteArray(bytes, length);
    _writeBytesMutex.lock();
    _writeBytes(byteArray);
    _writeBytesMutex.unlock();
}



█ 相关资料


提示:这里是参考的相关文章


  1. 2018-03-08 QGC 连接功能 底层执行逻辑_/* */-CSDN博客

  2. 2019-01-15 QT Qml 的qmlRegisterUncreatableType()函数_小马哔哔-CSDN博客

  3. 2018-03-04 QGroundControl 开发人员指南_/* */-CSDN博客_qgroundcontrol

  4. 2019-05-11 Qt学习笔记:多语言文件.qm的生成和使用_chase_hung的博客-CSDN博客

    :生成ts文件,修改ts文件,生成qm文件,加载qm语言包

  5. 2016-12-17 QT开发(五十三)———QML基本元素-生命不息,奋斗不止-51CTO博客

  6. 2019-02-15 qml和cpp 交互_Owen li的博客-CSDN博客



█ 免责声明

博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持!


提示:转载请注明出处:

https://blog.csdn.net/ljb568838953/article/details/112904481



版权声明:本文为ljb568838953原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。