QML访问C++对象驱动飞行仪表

引入

在上篇文章基于QML的飞行仪表中,从GitHub上搬下来几个基于QML的飞行仪表,但只是绘制出来,并不会动。今天通过C++对象访问的方式把他们驱动起来。
实际上,原项目也是这么做的,这里参考其C++类,并做了一些改动。

QML访问C++对象

在之前的文章中曾提到过QML与C++交互的方法,用到了将C++类名注册到QML,并在QML实体化然后使用的方式。今天我们换个方式实现,在C++中先定义类并实体化,然后使用QML访问C++对象。
C++类的定义其实没有什么区别,QML要想访问C++对象的方法,要使用Q_INVOKABLE、Q_PROPERTY宏等。
访问C++对象,需要先注册该对象
QQmlApplicationEngine::rootContext()->setContextProperty()
然后就可以在QML中直接访问C++对象了

C++类

在工程上右键添加类,类必须继承 QObject ,并包含QObject。直接上源码

#ifndef FLIGHTDATA_H
#define FLIGHTDATA_H

#include <QObject>
#include <QTimer>

class FlightData : public QObject
{
    Q_OBJECT
public:
    explicit FlightData(QObject *parent = nullptr);

    double roll() const;
    void setRoll(double newroll);

    double pitch() const;
    void setPitch(double newpitch);

    double heading() const;
    void setHeading(double newheading);

    double slipSkid() const;
    void setSlipSkid(double newslipskid);

    double turnRate() const;
    void setTurnRate(double newturnrate);

    double airspeed() const;
    void setAirspeed(double newairspeed);

    double pressure() const;
    void setPressure(double newpressure);

    double altitude() const;
    void setAltitude(double newaltitude);

    double climbRate() const;
    void setClimbRate(double newclimbrate);

    Q_INVOKABLE void anim_Start();
    Q_INVOKABLE void anim_Stop();


signals:
    void rollChanged();
    void pitchChanged();
    void headingChanged();
    void slipSkidChanged();
    void turnRateChanged();
    void airspeedChanged();
    void pressureChanged();
    void altitudeChanged();
    void climbRateChanged();

private:

    double m_roll;
    double m_pitch;
    double m_heading;
    double m_slipskid;
    double m_turnrate;
    double m_airspeed;
    double m_pressure;
    double m_altitude;
    double m_climbrate;

    QTimer mTimer;
    int frame;
    void update();

    Q_PROPERTY(double roll READ roll WRITE setRoll NOTIFY rollChanged);
    Q_PROPERTY(double pitch READ pitch WRITE setPitch NOTIFY pitchChanged);
    Q_PROPERTY(double heading READ heading WRITE setHeading NOTIFY headingChanged);
    Q_PROPERTY(double slipSkid READ slipSkid WRITE setSlipSkid NOTIFY slipSkidChanged);
    Q_PROPERTY(double turnRate READ turnRate WRITE setTurnRate NOTIFY turnRateChanged);
    Q_PROPERTY(double airspeed READ airspeed WRITE setAirspeed NOTIFY airspeedChanged);
    Q_PROPERTY(double pressure READ pressure WRITE setPressure NOTIFY pressureChanged);
    Q_PROPERTY(double altitude READ altitude WRITE setAltitude NOTIFY altitudeChanged);
    Q_PROPERTY(double climbRate READ climbRate WRITE setClimbRate NOTIFY climbRateChanged);


};

#endif // FLIGHTDATA_H

C++类的实现

#include "flightdata.h"
#include <cmath>

FlightData::FlightData(QObject *parent)
    : QObject{parent}
{
    m_roll = 0;
    m_pitch = 0;
    m_heading = 0;
    m_slipskid = 0;
    m_turnrate = 0;
    m_airspeed = 0;
    m_pressure = 0;
    m_altitude = 0;
    m_climbrate = 0;

    frame = 0;

    connect(&mTimer,&QTimer::timeout,this,&FlightData::update);

}

double FlightData::roll() const{
    return m_roll;
}

void FlightData::setRoll(double newroll){
    if (std::isnan(newroll) || qFuzzyCompare(m_roll, newroll))
           return;

    if (newroll < -180)
        newroll = -180;
    else if (newroll > 180)
        newroll = 180;

    m_roll = newroll;
    emit rollChanged();
}

double FlightData::pitch() const{
    return m_pitch;
}

void FlightData::setPitch(double newpitch){
    if (std::isnan(newpitch) || qFuzzyCompare(m_pitch, newpitch))
           return;

    if (newpitch < -90)
        newpitch = -90;
    else if (newpitch > 90)
        newpitch = 90;

    m_pitch = newpitch;
    emit pitchChanged();
}

double FlightData::heading() const{
    return m_heading;
}

void FlightData::setHeading(double newheading){
    if (std::isnan(newheading) || qFuzzyCompare(m_heading, newheading))
           return;

    while (newheading < 0.0)
        newheading += 360.0;
    while (newheading > 360.0)
        newheading -= 360.0;

    m_heading = newheading;
    emit headingChanged();
}

double FlightData::slipSkid() const{
    return m_slipskid;
}

void FlightData::setSlipSkid(double newslipskid){
    if (std::isnan(newslipskid) || qFuzzyCompare(m_slipskid, newslipskid))
           return;
    if (newslipskid < -15)
        newslipskid = -15;
    else if (newslipskid > 15)
        newslipskid = 15;

    m_slipskid = newslipskid;
    emit slipSkidChanged();
}

double FlightData::turnRate() const{
    return m_turnrate;
}

void FlightData::setTurnRate(double newturnrate){
    if (std::isnan(newturnrate) || qFuzzyCompare(m_turnrate, newturnrate))
           return;
    if (newturnrate < -6)
         newturnrate = -6;
     else if (newturnrate > 6)
         newturnrate = 6;

    m_turnrate = newturnrate;
    emit turnRateChanged();
}

double FlightData::airspeed() const{
    return m_airspeed;
}

void FlightData::setAirspeed(double newairspeed){
    if (std::isnan(newairspeed) || qFuzzyCompare(m_airspeed, newairspeed))
           return;

    if (newairspeed < 0)
        newairspeed = 0;
    else if (newairspeed > 9999)
        newairspeed = 9999;

    m_airspeed = newairspeed;
    emit airspeedChanged();
}

double FlightData::pressure() const{
    return m_pressure;
}

void FlightData::setPressure(double newpressure){
    if (std::isnan(newpressure) || qFuzzyCompare(m_pressure, newpressure))
           return;

    if (newpressure < 0)
        newpressure = 0;
    else if (newpressure > 2000)
        newpressure = 2000;

    m_pressure = newpressure;
    emit pressureChanged();
}

double FlightData::altitude() const{
    return m_altitude;
}

void FlightData::setAltitude(double newaltitude){
    if (std::isnan(newaltitude) || qFuzzyCompare(m_altitude, newaltitude))
           return;
    if (newaltitude < 0)
        newaltitude = 0;
    else if (newaltitude > 99999)
        newaltitude = 99999;

    m_altitude = newaltitude;
    emit altitudeChanged();
}

double FlightData::climbRate() const{
    return m_climbrate;
}

void FlightData::setClimbRate(double newclimbrate){
    if (std::isnan(newclimbrate) || qFuzzyCompare(m_altitude, newclimbrate))
           return;

    if (newclimbrate > 20)
        newclimbrate = 20;
    else if (newclimbrate < -20)
        newclimbrate = -20;

    m_climbrate = newclimbrate;
    emit climbRateChanged();
}

void FlightData::anim_Start(){

    mTimer.start(100);
    frame = 0;
}

void FlightData::anim_Stop(){

    if(mTimer.isActive())
        mTimer.stop();

    frame = 0;
}

void FlightData::update(){

    double newroll = 90.0 * sin(M_PI*frame*0.01);
    double newpitch = 60.0 * sin(0.5*M_PI*frame*0.01);
    double newheading = 180.0 * sin(M_PI*frame*0.01) ;
    double newslipskid = 15.0 * sin(2*M_PI*frame*0.01);
    double newturnrate =  6.0 * sin(1*M_PI*frame*0.01 + M_PI/2.0);
    double newairspeed = 120 * sin(M_PI*frame*0.01) + 120.0;
    double newmpressure = 1.75 * sin(M_PI*frame*0.01) + 29.75;
    double newaltitude = 15000 * sin(0.5*M_PI*frame*0.01) + 15000;
    double newclimbrate = 20.0 * sin(2*M_PI*frame*0.01) ;
    frame++;
    setRoll(newroll);
    setPitch(newpitch);
    setHeading(newheading);
    setTurnRate(newturnrate);
    setSlipSkid(newslipskid);
    setAirspeed(newairspeed);
    setPressure(newmpressure);
    setAltitude(newaltitude);
    setClimbRate(newclimbrate);

}


使用了一个定时器,定时触发更新飞行数据。

QML文件

加入访问C++对象的部分,这里访问C++对象的几个函数。

import "sixbasic"
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0

Window {
    minimumWidth: 800
    minimumHeight: 700
    title: "Basic Six"
    color: "#ffffff"
    visible: true
    id:root

    GridLayout{
        columns: 3
        id:meter
        width: parent.width*0.9

        anchors
        {
            fill:parent

            margins: 16
        }

        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true
            id: it_asi

            AirspeedIndicator {
                radius: 0.5 * Math.min(parent.width, parent.height)
                id:myasi
                airspeed: pfd.airspeed

            }
        }

        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true

            AttitudeIndicator{
                radius: 0.5 * Math.min(parent.width,parent.height)
                id: myai

                roll: pfd.roll
                pitch: pfd.pitch
            }
        }

        Item {
            Layout.fillHeight: true
            Layout.fillWidth: true
            id:it_alt

        Altimeter{
            radius: 0.5 * Math.min(parent.width, parent.height)
            id: myalt
            altitude: pfd.altitude
            pressure: pfd.pressure
            }
        }

        Item{
            Layout.fillWidth: true
            Layout.fillHeight: true

            TurnCoordinator {
                radius: 0.5 * Math.min(parent.width, parent.height)
                id: mytc
                turnRate: pfd.turnRate
                slipSkid: pfd.slipSkid
            }
        }


        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true

            HeadingIndicator {
                radius: 0.5 * Math.min(parent.width, parent.height)
                id: myheading
                heading: pfd.heading
            }
        }

        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true

            VerticalSpeedIndicator {
                radius: 0.5 * Math.min(parent.width, parent.height)
                id: myvsi
                climbRate: pfd.climbRate
            }
        }

//       Slider{
//           id: airspeed_ctl
//           anchors.left: myasi.it_asi
//           width: myasi.width
//           from: 0
//           to: 240
//           stepSize: 0.1
           Connections{
               target:myai
               onValueChanged:{
                   myai.airspeed = airspeed_ctl.value
               }
//           onValueChanged: {
//               myasi.airspeed = value
//           }

//       }

//       Slider{
//           id: roll_ctl
//           anchors.left: myai.left
//           width: myai.width
//           from: -180
//           to: 180
//           stepSize: 1
           onValueChanged: {
               myai.roll = value
           }
//       }

//       Slider{
//           id: pitch_ctl
//           anchors.left: myalt.left
//           width: myalt.width

//           from: -30
//           to: 30
//           stepSize: 0.1
           onValueChanged: {
               myai.pitch = value
           }
//       }

//       Slider{
//           id: altitude_ctl
//           anchors.left: myasi.it_asi
//           width: myasi.width

//           from: 0
//           to: 10000
//           stepSize: 1
           onValueChanged: {
               myalt.altitude = value
           }
//       }

//       Slider{
//           id: pressure_ctl
//           anchors.left: myai.left
//           width: myai.width

//           from: 28
//           to: 31.6
//           stepSize: 0.1
           onValueChanged: {
               myalt.pressure = value
           }
//       }

//       Slider{
//           id: heading_ctl
//           anchors.left: myalt.left
//           width: myalt.width
//           from: 0
//           to: 360
//           stepSize: 0.1
//           onValueChanged: {
//               myheading.heading = value
//           }
//       }


//       Slider{
//           id: turnrate_ctl
//           anchors.left: myasi.it_asi
//           width: myasi.width

//           from: -6
//           to: 6
//           stepSize: 0.1
//           onValueChanged: {
//               mytc.turnRate = value
//           }
//       }

//       Slider{
//           id: slipskid_ctl
//           anchors.left: myai.left
//           width: myai.width
//           from: -15
//           to: 15
//           stepSize: 0.1
//           onValueChanged: {
//               mytc.slipSkid = value
//           }
//       }

//       Slider{
//           id: verticalspd_ctl
//           anchors.left: myalt.left
//           width: myalt.width

//           from: -20
//           to: 20
//           stepSize: 0.1
           onValueChanged: {
               myvsi.climbRate = value
           }
//       }


            Button{
            id:btn
            text: qsTr("Sim_Start")
            font{
                pixelSize: 16
                family: "Times"
                bold: true
            }

            background:Rectangle{
                id: btnbg
                color: "green"
                }


            Layout.fillWidth: false
            Layout.fillHeight: false
            Layout.columnSpan: 3
            Layout.rowSpan: 1
            Layout.column:0
            Layout.row:2

            Layout.alignment: Qt.AlignHCenter

            onClicked: {
               if(btn.text == "Sim_Start")
               {
                   pfd.anim_Start()
                   btn.text = "Sim_Stop"
                   btnbg.color = "orange"
               }
               else if(btn.text == "Sim_Stop")
               {
                   pfd.anim_Stop()
                   btn.text = "Sim_Start"
                   btnbg.color = "green"
               }
             }
          }



//       Button{
//          width: parent.width*0.4
          anchors.left: start.left + 500
//          visible: false
//       }



//       Button{
//           text: qsTr("Anim_Stop")
//           width: root.width*0.3
//           Layout.columnSpan: 3
//           Layout.column:1
//           Layout.row:3

//           onClicked: {
//               pfd.anim_Stop()
//           }
//       }

    }

}

控件布局还不理想,还需要研究优化😟

Main函数

将C++对象注册到QML即可。

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "flightdata.h"

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    FlightData *pfd = new FlightData();

    engine.rootContext()->setContextProperty("pfd", pfd);
    engine.load(url);

    return app.exec();
}

运行结果

在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值