基于QT的运动参数提取与轨迹重现

基于QT的运动参数提取与轨迹重现

前言

本项目中的原始数据可通过GPS模块读取串口内容或者直接利用手机某些APP导出原始文件得到,大概数据图如下:

本项目的核心就是利用上述数据,重现出整个运动轨迹并显示一些运动信息(包括速度、方向、位置等)。

项目演示地址:https://www.bilibili.com/video/BV1KF411W7oF

下面过程中涉及到QT编写的程序大家最好复制到QT的界面中看,不然没有高亮啥的看的挺难受的。

GUI界面的设计

  1. 软件启动界面:

    软件启动界面由QT中的闪屏方法编写,在启动时会显示软件的名称以及加载中的字样。

  2. 软件运行主界面:

    软件运行主界面和主流软件主界面编写类似,是基于QT中的qmainwindow类编写的。从上到下依次有菜单栏、工具栏、主界面以及状态栏。菜单栏中文件菜单用于导入数据,调试菜单用于控制绘制过程的开始、暂停、结束,编辑菜单用于清除轨迹信息以及显示运动过程中的最大最小经纬度和最大最小高度数据。而工具栏中是常用的几个操作,只需点击对应的按钮即可执行相应的操作。状态栏用于显示实时的运动状态信息以及轨迹的绘制进程,并实时提示用户的操作。

  3. 软件运行悬浮界面:

    当按下工具栏中的显示轨迹信息按钮后,会启动一个可以由用户随意拖动的悬浮框界面。在这个界面中可以由柱形图看到运动速度和高度的直观变化,以及轨迹颜色与运动速度的对应关系。

  4. GUI界面的构造函数:

    gpsMainWindow::gpsMainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::gpsMainWindow)
    {
        ui->setupUi(this);
        setWindowTitle("GPS轨迹重现");//设置窗口名字
        resize(1440,900);// 设置初始窗口尺寸
        setWindowIcon(QIcon(":/images/main.png"));//设置主窗口图标
        menuBar();
        //菜单栏:淡绿色;下拉菜单:深天蓝
        this->setStyleSheet("QMenu::item:selected{background-color:#00BFFF;}\
                             QMenuBar{background-color:#90EE90;}");
        toolBar();
        myBar->setOrientation(Qt::Horizontal);//设置进度条为水平样式
        ui->statusbar->addPermanentWidget(myBar);//将进度条添加到永久显示即状态栏右下角
        m_webEngineView = new QWebEngineView();//为指针分配内存
        m_webChannel = new QWebChannel();
        /*向QWebChannel对象注册Qt对象,下面的注册的就是当前对象
        注意:这里注册时所用的名字“cppObject”,就是js获取qt对象时所使用的索引名
        将设置好的QWebChannel对象设置为当前页面的通道,即调用QWebEnginePage的setWebChannel方法
        如果没有正确注册webchannel,在js调用qt对象时会出现qt对象为undefined的情况
        覆盖默认的用户代理字符串,将其设置为HttpUserAgent_H5
        加载web视图,QUrl后跟自己的html文件的路径*/
        //m_webEngineView->setAttribute(Qt::WA_DeleteOnClose);//关闭子窗体时调用子窗体的析构函数
        m_webChannel->registerObject(QStringLiteral("cppObject"), this);
        m_webEngineView->page()->setWebChannel(m_webChannel);
        m_webEngineView->page()->profile()->setHttpUserAgent(HttpUserAgent_H5);
        m_webEngineView->load(QUrl("qrc:/leaflet/gaodeGPSdraw.html"));
        //m_webEngineView->show();//在新窗口显示web视图
        setCentralWidget(m_webEngineView);//在主窗口显示web视图
        myBar->setFixedSize(400,60);
    }
    
    gpsMainWindow::~gpsMainWindow()
    {
        delete ui;
        m_webChannel->deregisterObject(this);
    }
    

运动数据的提取与格式转换

  • 通信协议:

    NMEA-0183是美国国家海洋电子协会(National Marine Electronics Association )为海用电子设备制定的标准格式。目前已成了GPS导航设备统一的RTCM(Radio Technical Commission for Maritime services)标准协议。

    本实验中,主要运用到了该协议中的“ G P R M C ” 和 “ GPRMC”和“ GPRMCGPGGA”两个命令,其格式分别如下:

    $GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*<13>

    <1> UTC(Coordinated Universal Time)时间,hhmmss(时分秒)格式

    <2> 定位状态,A=有效定位,V=无效定位

    <3> Latitude,纬度ddmm.mmmm(度分)格式(前导位数不足则补0)

    <4> 纬度半球N(北半球)或S(南半球)

    <5> Longitude,经度dddmm.mmmm(度分)格式(前导位数不足则补0)

    <6> 经度半球E(东经)或W(西经)

    <7> 地面速率(000.0~999.9节,Knot,前导位数不足则补0)

    <8> 地面航向(000.0~359.9度,以真北为参考基准,前导位数不足则补0)

    <9> UTC日期,ddmmyy(日月年)格式

    <10> Magnetic Variation,磁偏角(000.0~180.0度,前导位数不足则补0)

    <11> Declination,磁偏角方向,E(东)或W(西)

    <12> Mode Indicator,模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)

    <13> 校验和。

    $GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>

    <1> UTC时间,hhmmss(时分秒)格式

    <2> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)

    <3> 纬度半球N(北半球)或S(南半球)

    <4> 经度dddmm.mmmm(度分)格式(前面的0也将被传输)

    <5> 经度半球E(东经)或W(西经)

    <6> GPS状态:0=未定位,1=非差分定位,2=差分定位,6=正在估算

    <7> 正在使用解算位置的卫星数量(00~12)(前面的0也将被传输)

    <8> HDOP水平精度因子(0.5~99.9)

    <9> 海拔高度(-9999.9~99999.9)

    <10> 地球椭球面相对大地水准面的高度

    <11> 差分时间(从最近一次接收到差分信号开始的秒数,如果不是差分定位将为空)

    <12> 差分站ID号0000~1023(前面的0也将被传输,如果不是差分定位将为空)

  • 数据格式:

    如上图所示,在一个典型的存放数据的文本文件中,除去GPRMC和GPGGA格式的数据外还有很多其他格式的无用数据。而在有用的数据中,每个运动信息都是由逗号分隔开的。因此可以先将GPRMC和GPGGA格式的数据找出来存放到一个链表中,然后再对链表的每一项进行运动信息的提取。

  • 数据提取程序:

    数据提取程序中也涉及到了数据的导入操作,由于要求是由用户对话框导入(这样也符合用户操作的逻辑习惯),因此在导入前先对导入的数据的有效性进行了判断,防止由于用户的操作不当未导入数据或者导入无效数据导致程序卡死的情况出现。

    其中,函数**screenGPSMessage()**将文件打开,并读取和筛查数据。每次读取文本中的一行数据,使用字符串的方法“split(“,”)”,可将一行的数据按“,”为分隔符,将第一个逗号前的字符和两个逗号之间字符存入一个字符串列表中,然后判断第一个字符串,根据协议,当“GPRMC”和“​GPGGA”两个命令同时出现时,“​GPRMC”总是在前,所以先判断第一个字符串是否为“​GPRMC”,如果是就进行数据的校准和存储,这将会在接下来讨论,存储完毕后,依据协议,下一命令大概率是“​GPGGA”,所以在这个判断语句内,再次读取一行数据,并分割存储,然后判断第一个字符串是否为“GPGGA”,如果是,则执行“​GPGGA”对应的数据校准和存储程序,如果不是,结束判断。如果第一次读取命令首字符串不是“GPRMC”,那么继续继续判断是否为“$GPGGA”,如果是,则进行相应的数据校准和存储程序。重复执行,直至数据全部读取完成。

  • 数据格式的转换:

    首先我们需要了解常用的地图坐标系,总的来说有以下三种:
    (1)天地图:CGCS2000,2000国家大地坐标系;我们其实很多时候直接用WGS84的坐标来代替CGCS2000坐标。因为CGCS2000的定义与WGS84实质一样。采用的参考椭球非常接近。扁率差异引起椭球面上的纬度和高度变化最大达0.1mm当前测量精度范围内,可以忽略这点差异。可以说两者相容至cm级水平,但若一点的坐标精度达不到cm水平,则不认为CGCS2000和WGS84的坐标是相容的。

    (2)百度地图:bd09II坐标。首先了解一下火星坐标,它是在国际标准坐标WGS-84上进行的一次加密,由于国内的电子地图都要至少使用火星坐标进行一次加密,百度直接就任性一些,直接自己又研究了一套加密算法,来了个二次加密,这就是我们所熟知的百度坐标(BD-09)。

    (3)高德地图:gcj02坐标,也称为火星坐标。火星坐标是国家测绘局为了国家安全在原始坐标的基础上进行偏移得到的坐标,基本国内的电子地图、导航设备都是采用的这一坐标系或在这一坐标的基础上进行二次加密得到的。
    在本次实验中,考虑到为了更加清晰准确地重现运动轨迹,并可以实现大范围的轨迹重现,特选用高德离线地图。相比于在线地图,离线地图的加载更快,不受网络状况的干扰,也不会在运行过程中出现大范围的白边。从网上了解到WGS84ToGCJ02格式的数据转换公式后,得到转换函数为:

    此函数调用后将返回高德地图下的实际经纬度坐标,经验证,坐标定位地非常准确,轨迹基本和实际完全一致。

导入地图和绘制轨迹

  1. leaflet的基本使用:

    Leaflet 是一个为建设移动设备友好的互动地图,而开发的现代的、开源的 JavaScript库 。虽然前端地图框架不少,但leaflet凭借简单、易用、上手快、拓展多、轻量级等优点,让我选择了它。此外,leaflet的官网还有丰富的教程以及帮助文档,大大降低了我们的开发难度。而它在移动端应用开发的优势,更是完美契合本次实验的要求。综合考虑后,决定使用leaflet作为地图框架。

    在官网下载leaflet的js包解压后即可使用leaflet库了,在使用前要在程序开头加上下面两句:

    <link rel="stylesheet" href="./leaflet.css"/>
    <script src="./leaflet.js"></script>
    
  2. 利用leaflet库绘制轨迹:

绘制轨迹其实就是利用两点确定一条直线,依据得到的数据每两点画一条线后得到 总轨迹。因此查阅leaflet的帮助文档后可知利用polyline函数可以实现直线的绘制

此外,为保证绘制的轨迹会随着地图的移动而实时移动,我们需要确保我们绘制点的坐标一直保持在地图的中心,为此利用leaflet中的panto函数,使得轨迹会随着绘制的进行而移动。在绘制过程中,速度会不断地发生变化,因此要设置不同的速度区间用不同颜色的轨迹标识,并在速度低时地图等级高,速度高时地图等级低,利用setzoom函数实现不同等级的设定。

/*
* Function name :drawPoint
* Description   : 画点
* Parameter     : 无
* Return        :无
*/
function drawPoint() {
    
    
    if (drawPointNum >= num_list.length-1) {
    
    
        clearInterval(interval);
        alert("stop", drawPointNum);
        drawPointNum = 0;
        return;
    }
    var wgs84togcj02 = tracelist[drawPointNum];
    var wgs84togcj022 = tracelist[drawPointNum + 1];
    var speed = speedlist[drawPointNum];
    var latlngs = [
        [wgs84togcj02[1], wgs84togcj02[0]],
        [wgs84togcj022[1], wgs84togcj022[0]],
    ];
    if(spdMax<30)
    {
    
    
        map.setZoom(18);
    }
    else if(wgs84togcj02[1]>30.08&&wgs84togcj02[1]          <30.80&&wgs84togcj02[0]>103.09&&wgs84togcj02[0]<104.76&&speed<120)
    {
    
    
        map.setZoom(16);
    }
    else if(wgs84togcj02[1]>26.05&&wgs84togcj02[1]<34.3&&wgs84togcj02[0]>97.35&&wgs84togcj02[0]<108.55&&speed<300)
    {
    
    
        map.setZoom(14);
    }
    else
    {
    
    
        map.setZoom(12);
    }
    
 if(speed>=0&&speed<30)
 {
    
    
   L.polyline(latlngs, {
    
     color: "yellow", weight: 4, opacity: 100 }).addTo(map);
 }
 else if(speed>=30&&speed<60)
 {
    
    
   L.polyline(latlngs, {
    
     color: "purple", weight: 4, opacity: 100 }).addTo(map);
 }
 else if(speed>=60&&speed<90)
 {
    
    
    L.polyline(latlngs, {
    
     color: "blue", weight: 4, opacity: 100 }).addTo(map);
 }
 else if(speed>=90&&speed<120)
 {
    
    
    L.polyline(latlngs, {
    
     color: "green", weight: 4, opacity: 100 }).addTo(map);
 }
 else if(speed>=120&&speed<200)
 {
    
    
    L.polyline(latlngs, {
    
     color: "black", weight: 4, opacity: 100 }).addTo(map);
 }
 else if(speed>=200&&speed<350)
 {
    
    
    L.polyline(latlngs, {
    
     color: "red", weight: 4, opacity: 100 }).addTo(map);
 }
 else
 {
    
    
    L.polyline(latlngs, {
    
     color: "white", weight: 4, opacity: 100 }).addTo(map);
 }
    //drawPointNum++;
    drawPointNum = drawPointNum + 2;  //加快轨迹绘制速度
    map.panTo([wgs84togcj022[1], wgs84togcj022[0]]);
    fromWebToCpp();
}
  1. QT与JS的交互:

由于程序运行主界面是基于QT开发的,因此用户事件的输入其实是在QT中实现的,而绘图功能的实现却是在JS或者说是一个网页实现的。因此必须有一个桥梁建立起QT与JS之间的关系,实现二者的消息互通。幸运的是,QT内置了很多的类如QWebEngineView、QWebChannel、QJsonObject等来实现这一功能。我们只需要先向QWebChannel对象注册QT对象,获得js获取qt对象时所使用的索引名,并将设置好的QWebChannel对象设置为当前页面的通道,最后加载web视图即可。
而在JS端,首先要将qwebchannel.js文件引入在内,然后在页面初始化时创建一个QWebChannel对象,里面的第一个参数是qt.webChannelTransport,只有Qt端正确地向页面设置了webchannel才能取到,否则会是undefined。 QWebChannel对象里的channel.objects对应了Qt里实现向webchannel注册的对象表,channel.objects.cppObject即为从表中取出名为"cppObject"的对象,然后赋给window的bridge对象。
QT端主要程序:

void gpsMainWindow::JsDraw()
{
    QJsonArray num_json, num2_json, num3_json;//声明QJsonArray
    QJsonDocument num_document, num2_document, num3_document;  //将QJSonArray改为QJsonDocument类
    QByteArray num_byteArray, num2_byteArray, num3_byteArray;
    current = head;
    double *pos;//pos[0]——纬度  pos[1]——经度
    while(current->next != NULL)//将数组传入num_json
    {
        pos = WGS84ToGCJ02(current->getGprmc()->getLantitude(),current->getGprmc()->getLongitude());
        double x =pos[1];
        double y = pos[0];
        double s = current->getGprmc()->getLandSpeed();
        if(x<=137.8347&&x>=72.004&&y<=55.8271&&y>=0.8293)
        {
            //qDebug()<<"经度"<<x;
            //qDebug()<<"纬度"<<y;
            //在数组末尾插入值
            num_json.append(x);
            num2_json.append(y);
            num3_json.append(s);
        }
        current = current->next->next;
    }
    /*
    设置array作为文档中的主对象
    将QJsonDocument转换为UTF-8编码的format格式的JSON文档
    Indented:按照json风格生成字符串,自动缩进
    Compact:紧凑格式,直接生成一堆字符串,就是不换行,但是占用空间小
    */
    num_document.setArray(num_json);
    num2_document.setArray(num2_json);
    num3_document.setArray(num3_json);
    num_byteArray = num_document.toJson(QJsonDocument::Compact);
    num2_byteArray = num2_document.toJson(QJsonDocument::Compact);
    num3_byteArray = num3_document.toJson(QJsonDocument::Compact);

    QString numJson(num_byteArray);//再转为QString
    QString num2Json(num2_byteArray);
    QString num3Json(num3_byteArray);
    //每个arg()将替换编号最小的未替换位置
    QString cmd = QString("getPathData(\"%1\",\"%2\",\"%3\")").arg(numJson).arg(num2Json).arg(num3Json);
    m_webEngineView->page()->runJavaScript(cmd);//运行JS代码
}

网页端主要程序:

/* Function name :getPathData
* Description   : 得到经纬度数据和速度数据
* Parameter     : 纬度列表,经度列表,速度列表
* Return        :无
*/
function getPathData(numlist, num2list,num3list) {
    
    
    num_list.length = 0;
    num2_list.length = 0;
    num3_list.length = 0;
    tracelist.length = 0;
    speedlist.length = 0;
    drawPointNum = 0;
    /* substring(indexStart[, indexEnd]) 方法返回一个字符串在开始索引到结束索引之间的一个子集,或从开始索引直到字符串的末尾的一个子集
      参数  indexStart:需要截取的第一个字符的索引,该索引位置的字符作为返回的字符串的首字母
            indexEnd:可选。一个 0 到字符串长度之间的整数,以该数字为索引的字符不包含在截取的字符串内
      split() 方法使用指定的分隔符字符串将一个String对象分割成子字符串数组,以一个指定的分割字串来决定每个拆分的位置
      */
    num_list = numlist.substring(1, numlist.length - 1);
    num2_list = num2list.substring(1, num2list.length - 1);
    num3_list = num3list.substring(1, num3list.length - 1);
    num_list = num_list.split(",");
    num2_list = num2_list.split(",");
    num3_list = num3_list.split(",");
    for (i = 0; i < num_list.length; i++) {
    
    
        point = [num_list[i], num2_list[i]];
        tracelist.push(point); //将每个点压入数组
        if(Number(num3_list[i])>Number(spdMax))
        {
    
    
            spdMax = num3_list[i];
        }
        if(Number(num3_list[i])<Number(spdMin))
        {
    
    
            spdMin = num3_list[i];
        }
        if(Number(num_list[i])>Number(logMax))
        {
    
    
            logMax = num_list[i];
        }
        if(Number(num_list[i])<(logMin))
        {
    
    
            logMin = num_list[i];
        }
        if(Number(num2_list[i])>(latMax))
        {
    
    
            latMax = num2_list[i];
        }
        if(Number(num2_list[i])<(latMin))
        {
    
    
            latMin = num2_list[i];
        }
        speedlist.push(num3_list[i]);
    }
    startDrawPoint();
}


//window.onload的功能是为了在页面加载完成后立即调用函数
window.onload = function () {
    
    
    if (navigator.userAgent.indexOf("Toon-pc") !== -1)
    {
    
    
        // 初始化时创建了一个QWebChannel对象,里面的第一个参数qt.webChannelTransport,只有Qt端正确地向页面设置了webchannel才能取到,否则会是undefined
        // QWebChannel对象里的channel.objects对应了Qt里实现向webchannel注册的对象表,channel.objects.cppObject即为从表中取出名为"cppObject"的对象,然后赋给window的bridge对象
        // window作为全局变量,代表了脚本正在运行的窗口
        new QWebChannel(window.qt.webChannelTransport, function (channel) {
    
    
            window.bridge = channel.objects.cppObject;
        });
    }
};

界面的美化与功能的完善

  • 菜单栏、工具栏、状态栏的编写:

    void gpsMainWindow::menuBar()
    {
        //新建菜单
        QMenu *toolButtonMenu_1 = new QMenu(this);
        toolButtonMenu_1->setTitle("文件(F)");
        QMenu *toolButtonMenu_2 = new QMenu(this);
        toolButtonMenu_2->setTitle("调试(D)");
        QMenu *toolButtonMenu_3 = new QMenu(this);
        toolButtonMenu_3->setTitle("编辑(E)");
    
        //新建行为
        QAction *toolButtonAction1 = new QAction(this);
        QAction *toolButtonAction2 = new QAction(this);
        QAction *toolButtonAction3 = new QAction(this);
        QAction *toolButtonAction4 = new QAction(this);
        QAction *toolButtonAction5 = new QAction(this);
        QAction *toolButtonAction6 = new QAction(this);
        QAction *toolButtonAction7 = new QAction(this);
    
        //命名行为定位
        toolButtonAction1->setText("导入文件");
        toolButtonAction2->setText("开始");
        toolButtonAction3->setText("暂停");
        toolButtonAction4->setText("继续");
        toolButtonAction5->setText("清除");
        toolButtonAction6->setText("速度信息");
        toolButtonAction7->setText("位置信息");
    
        //为行为定位添加图标
        toolButtonAction1->setIcon(QIcon(":/images/input.png"));
        toolButtonAction2->setIcon(QIcon(":/images/start.png"));
        toolButtonAction3->setIcon(QIcon(":/images/mpause.png"));
        toolButtonAction4->setIcon(QIcon(":/images/mcontinue.png"));
        toolButtonAction5->setIcon(QIcon(":/images/refresh.png"));
        toolButtonAction6->setIcon(QIcon(":/images/speed.png"));
        toolButtonAction7->setIcon(QIcon(":/images/loglat.png"));
        //添加定位槽
        connect(toolButtonAction1,SIGNAL(triggered()),this,SLOT(fileIn()));
        connect(toolButtonAction2,SIGNAL(triggered()),this,SLOT(start()));
        connect(toolButtonAction3,SIGNAL(triggered()),this,SLOT(pause()));
        connect(toolButtonAction4,SIGNAL(triggered()),this,SLOT(myContinue()));
        connect(toolButtonAction5,SIGNAL(triggered()),this,SLOT(clearPath()));
        connect(toolButtonAction6,SIGNAL(triggered()),this,SLOT(spdInfo()));
        connect(toolButtonAction7,SIGNAL(triggered()),this,SLOT(posInfo()));
    
        //添加行为
        toolButtonMenu_1->addAction(toolButtonAction1);
        toolButtonMenu_2->addAction(toolButtonAction2);
        toolButtonMenu_2->addAction(toolButtonAction3);
        toolButtonMenu_2->addAction(toolButtonAction4);
        toolButtonMenu_3->addAction(toolButtonAction5);
        toolButtonMenu_3->addAction(toolButtonAction6);
        toolButtonMenu_3->addAction(toolButtonAction7);
    
        //向菜单栏中添加工具栏
        ui->menubar->addMenu(toolButtonMenu_1);
        ui->menubar->addMenu(toolButtonMenu_2);
        ui->menubar->addMenu(toolButtonMenu_3);
    
    }
    
    void gpsMainWindow::toolBar()
    {
        ui->toolBar->setMovable(false);//设置工具栏不可移动
    
        toolBtn1->setText(tr("导入GPS数据"));
        toolBtn2->setText(tr("GPS轨迹绘制"));
        toolBtn3->setText(tr("暂停"));
        toolBtn4->setText(tr("继续"));
        toolBtn5->setText(tr("清除轨迹"));
        toolBtn6->setText("显示轨迹信息");
    
        //工具栏按钮图标
        toolBtn1->setIcon(QIcon(":/images/data.png"));
        toolBtn2->setIcon(QIcon(":/images/draw.png"));
        toolBtn3->setIcon(QIcon(":/images/pause.png"));
        toolBtn4->setIcon(QIcon(":/images/continue.png"));
        toolBtn5->setIcon(QIcon(":/images/clear.png"));
        toolBtn6->setIcon(QIcon(":/images/information.png"));
    
        //设置文字在图标之后
        toolBtn1->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        toolBtn2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        toolBtn3->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        toolBtn4->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        toolBtn5->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        toolBtn6->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    
        //添加
        connect(toolBtn1,SIGNAL(clicked()),this,SLOT(fileIn()));
        connect(toolBtn2,SIGNAL(clicked()),this,SLOT(start()));
        connect(toolBtn3,SIGNAL(clicked()),this,SLOT(pause()));
        connect(toolBtn4,SIGNAL(clicked()),this,SLOT(myContinue()));
        connect(toolBtn5,SIGNAL(clicked()),this,SLOT(clearPath()));
        connect(toolBtn6,SIGNAL(clicked()),this,SLOT(dispInfo()));
    
        // 设置弹出模式
        toolBtn1->setPopupMode(QToolButton::DelayedPopup);
        toolBtn2->setPopupMode(QToolButton::DelayedPopup);
        toolBtn3->setPopupMode(QToolButton::DelayedPopup);
        toolBtn4->setPopupMode(QToolButton::DelayedPopup);
        toolBtn5->setPopupMode(QToolButton::DelayedPopup);
        toolBtn6->setPopupMode(QToolButton::DelayedPopup);
    
        //添加按钮至工具栏
        ui->toolBar->addWidget(toolBtn1);
        ui->toolBar->addWidget(toolBtn2);
        ui->toolBar->addWidget(toolBtn3);
        ui->toolBar->addWidget(toolBtn4);
        ui->toolBar->addWidget(toolBtn5);
        ui->toolBar->addWidget(toolBtn6);
    
        //添加状态至工具栏
        checkBox->setText(QString("离线"));
        checkBox->setCheckState(Qt::Checked);
        ui->toolBar->addWidget(checkBox);
    
    }
    
    void gpsMainWindow::setMessage()
    {
        if(current->next == NULL)
        {
            return;
        }
        QString status;
        //status = current->getGprmc()->getPositionStatus();
        QString latMessage;
        QString lonMessage;
        QString altMessage;
        QString speMessage;
        QString drcMessage;
        QString drawProgess;
        double *pos = WGS84ToGCJ02(current->getGprmc()->getLantitude(),current->getGprmc()->getLongitude());
        double x =pos[1];
        double y = pos[0];
        if(x<=137.8347&&x>=72.004&&y<=55.8271&&y>=0.8293)
        {
            latMessage =  "纬度:"+QString::number(y)+" "+current->getGprmc()->getLatHalfBall();
            lonMessage =  "经度:"+QString::number(x)+" "+current->getGprmc()->getLogHalfBall();
            altMessage =  "海拔:"+QString::number(current->getGpgga()->getAltitude())+"m ";
            speMessage =  "速度:"+QString::number(current->getGprmc()->getLandSpeed())+"km/h ";
            drcMessage =  "地面航向:"+QString::number(current->getGprmc()->getLandDirection());
            drawProgess =  "进度:"+QString::number(current->getGprmc()->getNums()/nums*100);
            ui->statusbar->showMessage(latMessage+" "+lonMessage+" "+altMessage+speMessage+drcMessage+"° "+drawProgess+"%");
            logTB->setText(lonMessage);
            lanTB->setText(latMessage);
            spdTB->setText(speMessage);
            altTB->setText(altMessage);
            myBar->setValue(int(current->getGprmc()->getNums()/nums*100));//设置进度条
            speedSlider->setValue(int(current->getGprmc()->getLandSpeed()));//设置速度滑块
            heightSlider->setValue(int(current->getGpgga()->getAltitude()));//设置高度滑块
        }
        current = current->next;
    }
    
  • 运动信息直观显示窗口的编写:

    void gpsMainWindow::dispInfo()
    {
        QDockWidget *dock = new QDockWidget("运动状态直观显示",this);//添加停靠窗口
        dock->setFeatures(QDockWidget::DockWidgetFloatable);//设置为可悬浮
    
        heightSlider->setMinimum(0);
        speedSlider->setMinimum(0);
        heightSlider->setMaximum(8000);
        speedSlider->setMaximum(1000);
        speedSlider->setStyleSheet(spdStyle);
        heightSlider->setStyleSheet(altStyle);
    
        QHBoxLayout *HLay1=new QHBoxLayout;
        HLay1->addWidget(heightSlider);
        HLay1->addWidget(speedSlider);
    
        QFont *tbFont = new QFont;
        tbFont->setPointSize(14);
    
        logTB->setFixedSize(400,70);
        lanTB->setFixedSize(400,70);
        spdTB->setFixedSize(400,70);
        altTB->setFixedSize(400,70);
        logTB->setFont(*tbFont);
        lanTB->setFont(*tbFont);
        spdTB->setFont(*tbFont);
        altTB->setFont(*tbFont);
    
        QVBoxLayout *VLay=new QVBoxLayout;
        VLay->addWidget(logTB);
        VLay->addWidget(lanTB);
        VLay->addWidget(spdTB);
        VLay->addWidget(altTB);
    
        colorLabel->setFixedSize(300,800);
        QPixmap color(":/images/color.png");
        colorLabel->setPixmap(color);
    
        QHBoxLayout *HLay2=new QHBoxLayout;
        HLay2->addLayout(HLay1);
        HLay2->addLayout(VLay);
        HLay2->addWidget(colorLabel);
    
        sWidget->setLayout(HLay2);
        dock->setWidget(sWidget);
        addDockWidget(Qt::RightDockWidgetArea,dock);
    
    }
    
  • 功能函数的编写:

    void gpsMainWindow::start()
    {
        if(pathEmpty == 0){
            QString warning = "警告:还未导入数据,请先导入数据后再进行绘制轨迹";
            ui->statusbar->showMessage(warning);
            return;
        }
        JsDraw();
        current = head;
    }
    
    void gpsMainWindow::fileIn()
    {
        filePath = QFileDialog::getOpenFileName(
                    this, "Please choose a path file",
                    "../gps/data",
                    "Txt Files(*.txt );;All(*.*)");
        if (filePath.isEmpty()==false)
        {
            pathEmpty = 1;
        }
        else
        {
            ui->statusbar->showMessage("警告:路径为空或文件不存在");
            pathEmpty = 0;
            return;
        }
        screenGPSMessage();
    }
    
    void gpsMainWindow::pause()
    {
        QString cmd = QString("pauseDraw()");
        m_webEngineView->page()->runJavaScript(cmd);//运行JS代码
    }
    
    void gpsMainWindow::myContinue()
    {
        QString cmd = QString("continueDraw()");
        m_webEngineView->page()->runJavaScript(cmd);//运行JS代码
    }
    
    void gpsMainWindow::clearPath()
    {
        m_webEngineView->reload();
        ui->statusbar->clearMessage();
        myBar->setValue(0);
        QString cmd = QString("clearPath()");
        m_webEngineView->page()->runJavaScript(cmd);//运行JS代码
    }
    
    void gpsMainWindow::spdInfo()
    {
        QString cmd = QString("dispSpdMaxMin()");
        m_webEngineView->page()->runJavaScript(cmd);//运行JS代码
    }
    
    void gpsMainWindow::posInfo()
    {
        QString cmd = QString("dispLogLat()");
        m_webEngineView->page()->runJavaScript(cmd);//运行JS代码
    }
    
    
    function pauseDraw()
    {
          
          
        clearInterval(interval);
        alert("已暂停");
    }
    
    function continueDraw()
    {
          
          
        alert("已继续");
        interval = setInterval(drawPoint, 10);
    }
    
    function clearPath()
    {
          
          
        alert("已清除");//清除操作在Qt中执行,这里只是弹出网页提示
    }
    
    function dispSpdMaxMin()
    {
          
          
        alert("本次行程的速度最大值为:"+spdMax+"\n最小值为:"+spdMin);//显示速度最大最小值
    }
    
    function dispLogLat()
    {
          
          
        alert("本次行程经度的最大值为:"+logMax+"最小值为:"+logMin+"\n本次行程纬度的最大值为:"+latMax+"最小值为:"+latMin);
    }
    

结果演示

详见基于QT的运动参数提取与轨迹重现

猜你喜欢

转载自blog.csdn.net/weixin_51426754/article/details/122679032