目录
1、QGC 地面站的显示机制
1.1、C++类注册为 qml 可访问类型
1.2、注册属性或者方法
1.1.1、 属性声明
1.1.2、方法声明
2、QGC 地面站界面初步修改
3、QGC 地面站菜单和 LOGO 修改
3.1、地面站菜单
3.2、Logo 修改
3.2.1、 Windows Logo
3.2.2、 Linux Logo
4、QGC 地面站菜单功能裁剪
4.1、File 菜单
4.1.1、 日志回放
4.1.2、 退出
4.2、Widget 菜单项
4.3、五大页面
QGC 显示入口是 MainWindow Widget,加载了 MainWindowHybrid.qml,开始了整个 qml 文件调用系统, 再 MainWindowHybrid.qml 通过 Loader 器加载了 MainWindowInner.qml 在该文件系统中,该文件中完成了主页面的布局。 MainWindowInner.qml 中 MainToolBar 控件位于页面的顶部, 包含了5 大主页面的切换按钮,当鼠标点击时,对 onClicked 事件处理,通过Loader 来加载相应的 qml 文件,页面和 qml 文件的对应关系参看下图:QGC 地面站显示逻辑
QGC 将 QGroundControlQmlGlobal 类构造为单例,在 qml 声明 importQGroundControl 1.0 来引入相关的内容,之后通过 QGroundControl 来访问相应的属性和方法。
为方便大家的使用,在此我给大家介绍一下 Qt 中 C++属性和方法的定义方式。对于 C++中自定义的类如果想要在 qml 中被访问需要满足下面的条件:1. C++类继承自 QObject 或者 Qobject 的父类 2. 添加 Q_OBJECT 字段
我们可以通过下面的两种方式将 C++的类声明为 qml 中可以访问的类型:
1. qmlRegisterUncreatableType 声明的类型可以被 qml 通过属性方式访问,却无法qml 中直接构造。 使用示例如下:
qmlRegisterUncreatableType .MultiVehicleManager", 1, 0, "MultiVehicleManager", "Reference only"); |
QGroundControlQmlGlobal.h 中将 MultiVehicleManager 通过下面的语句 声明为它的属性: Q_PROPERTY(MultiVehicleManager* multiVehicleManager READ multiVehicleManager CONSTANT) |
Qml 中课题通过 QGroundControl.multiVehicleManager 来访问其中的属 性和方法 |
2. qmlRegisterType 声明的 C++类型可以在 qml 文件中直接构造,在 qml 中通过 id来访问属性和方法。
qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PlanMasterController"); |
PlanView.qml 中: PlanMasterController { id: masterController ……} |
通过上面的方式我们将一个 C++类注册入 Qt 的元对象系统中 ,在 C++类中我们可以通过Q_PROPERTY 注册属性,同时可以通过在函数声明前添加Q_INVOKABLE 来声明 qml 可以调用的方法。
主要存在以下几种形式(示例取自 Vehicle 类,大家在实践过程中可以参考其中的实现进行):
1. Q_PROPERTY(int id READ idCONSTANT)当该属性的值一旦被赋值,在整个程序运行期间,在我们的 C++承租中不会修改该参数值时,我们采用这样的表达式。
2. Q_PROPERTY(QGeoCoordinate coordinate READ coordinate NOTIFY coordinateChanged)
当该属性只会在 C++中改变该属性的值的时候,我们会采用这样的表达式,当我们在 C++中改变该参数值时,发射 coordinateChanged 信号, 来通知 qml 相关的属性有更改 i 进而实现相关的状态更改。
3. Q_PROPERTY(bool armed READ armed WRITE setArmed NOTIFY armedChanged)当该属性在 C++和 qml 都会改变该属性的值的时候,我们会采用这样的表达式,当我们在 C++中改变该参数值时,发射 armedChanged 信号,来通知 qml 相关的属性有更改进而实现相关的状态更改。 同样地在 qml 中我们通过 id.armed = true,此时会出发 C++中的 setArmed 函数,来实现相关状态的更改。
方法声明只需要我们在函数定义前面添加 Q_INVOKABLE 即可。如: Q_INVOKABLE void emergencyStop(void);
这一节我给以示例的形式介绍一下在飞行页面的 Fly 那一列下方添加一个矩形框,在鼠标点击触发 QGC 发送发送向飞机请求全参指令,然后再 QGC 的模拟飞控 Mock 连接中将接收到的 QGC 的请求信息打印出来,进而完成整个回路的验证
1. 添加一个矩形框,在FlightDisplayView.qml 中, Fly 操作栏对应的组件 id 是 toolStrip
Rectangle{ //为控件设置层级,层级低的可能会被层级高的遮挡而无法看到 |
2. 在 Vehicle 中添加函数实现
Vehicle.h 中添加函数定义: Q_INVOKABLE void requestAllParameters(void); |
Vehicle.cc 中完成函数的实现: void Vehicle::requestAllParameters() { mavlink_message_t msg; mavlink_msg_param_request_list_pack_chan( _mavlink->getSystemId(), _mavlink->getComponentId(), priorityLink()->mavlinkChannel(), &msg,_id,MAV_COMP_ID_ALL); sendMessageOnLink(priorityLink(), msg); qDebug() << "==============send Vehicle::requestAllParameters ===============" << _id << MAV_COMP_ID_ALL; } |
3. Mock 回路验证代码:
void MockLink::_handleParamRequestList(const mavlink_message_t& |
4. 验证结果:
下面我们以在 QGC 地面站菜单栏添加 options 菜单,并在该下单下添加室内外模式切换为例来进行介绍:打开 MainWindow.ui
首先添加 options 菜单,直接双击输入名字,之后再 Mainwindow 的_showAdvancedUIChanged 函数中添加 menuBar()->addMenu(_ui.menuoptions);形成下面这样的代码
void MainWindow::_showAdvancedUIChanged(bool advanced) { if (advanced) { menuBar()->addMenu(_ui.menuFile); menuBar()->addMenu(_ui.menuWidgets); menuBar()->addMenu(_ui.menuoptions); } else { menuBar()->clear(); } } |
在MainWindow.cc 的添加函数定义执行相关修改:
void MainWindow::selectIndoorMode(bool in) { qgcApp()->_loadCurrentStyleSheet(); QGCPalette::setGlobalTheme(in ? QGCPalette::Dark : QGCPalette::Light); } |
在MainWindow.h 添加槽函数:
protected slots: |
void selectIndoorMode(bool in); |
MainWindow.cc 构造函数中将点击触发的 triggered 和自定义的操函数绑定:
connect(_ui.indoor, &QAction::triggered, this, &MainWindow::selectIndoorMode); |
QGC 中 windows 加载 Logo 在 qgroundcontrol.pro 中
WindowsBuild { RC_ICONS = resources/icons/qgroundcontrol.ico } |
QGC 中 Linux 加载 Logo 在 main.cc 中
#ifdef Q_OS_LINUX QApplication::setWindowIcon(QIcon(":/res/resources/icons/qgroundcon trol.ico")); #endif |
选择 Logo 图片,添加进 qrc 资源文件中,更改对应的 logo 文件路径。 如果你的图片过大可以利用该网址 http://www.uupoop.com/ico/?action=make 进行转换。 选好 ico 后添加到资源文件中。
QGC 的菜单栏包含 File 和 Widget 两项,下面对这两项进行一个详细介绍
在 MainWindow.ui 总添加了 id 分别为 actionStatusBar(日志回放)actionExit(退出)两个按钮:
在 MainWindow 的构造函数中通过connect(_ui.actionStatusBar, &QAction::triggered, this,&MainWindow::showStatusBarCallback);将用户点击的动作和 showStatusBarCallback 函数绑定,来执行相关的操作。
Widget 下拉子菜单时通过 MainWindow 的 C++代码中实现的,如果想要对菜单项裁剪只要 rgDockWidgetNames 相应的项注释掉即可。
static const char *rgDockWidgetNames[] = { for (int i = 0, end = ARRAY_SIZE(rgDockWidgetNames); i < end; action->setData(i); |
QGC 默认进入的时 Fly 页面,用户可以通过菜单栏下方的额五个按钮实现不同页面的切换, MainToolBar.qml 实现了五大按钮的横向布局,并监听鼠标的点击 onClicked 事件,发射出相应的信号,如 showSettingsView 等,在MainWindowInner.qml 中对相应的信号处理,加载不同的页面。按钮的对应关系在 MainToolBar.qml 参看下文。
Row { //Analyze 页面 |
如果您想要裁剪某个页面只需要将对应的 QGCToolBarButton 控件的实现代码注释或者删除即可。