最近有個接了一個小型項目,內容很簡單,就是解析北斗GPS的串口數據而後輸出經緯度,但接過來以爲太簡單,就發揮了主觀能動性,增長了百度地圖API,不但能實時定位,還能在地圖上標識出位置信息,用的QT5.5。上位機運行圖片如圖所示:總體運行比較流暢。html
原理就是界面上集成一個WebKits/WebView,讓Qt和Javascript進行交互。但須要注意Qt5.6以上版本取消了WebView的模塊,換成了webenginewidgets,看上去配置好麻煩,甚至還要本身編譯什麼的,雖然性能可能有指數性的提高,但對於我這個作嵌入式軟件和硬件,上位機會個基礎的就算是很好的人來講,仍是webkits好一點。c++
2017/07/22 更新:git
在本文後續版本已經適配了Qt 5.6 以上版本的QWebEngine版本,摒棄了QWebKits組件,後續如有經費的話,將繼續更新支持QWebChannel通訊通道。兩個版本均可以在本文尾部的附件中下載,歡迎學習討論。web
本設計開發主要涉及三個方面:windows
串口開發不用說了,請參考我前幾篇有個藍牙的博客,上面有源碼,Qt on Android 藍牙開發,本設計中的串口部分就是基於那個串口開發的。串口開發自動檢測鏈接設備,不須要進入管理器和找到COM口是多少,自動和串口進行鏈接。api
串口數據粘包和數據不連續很頭疼,進入一個串口接收槽函數QString rxArray.append(serialPort->readAll() ); 接收數據不完整,或者說會分好幾回進行接收,並且分好幾回接收長度沒有規律,因此沒法直接使用接收的數據。緩存
1S鍾GPS發送一次數據爲:網絡
/* $GNRMC,114821.880,V,3957.378130,N,11620.848015,E,0.000,0.000,230417,,E,N*23 $GNGGA,114821.880,3957.378130,N,11620.848015,E,0,00,127.000,100.800,M,0,M,,*6D $GNGLL,3957.378130,N,11620.848015,E,114821.880,V,N*52 $GNGSA,A,1,,,,,,,,,,,,,127.000,127.000,127.000*2A $GNGSA,A,1,,,,,,,,,,,,,127.000,127.000,127.000*2A $GPGSV,1,1,4,17,57,315,21,22,35,67,,28,75,176,,30,12,204,*74 */
數據量比較巨大,因此這裏增長處理機制,儘可能保存完整數據。app
// 接收數據槽函數 void Widget::RxData(){ QString rxString; rxArray.append(serialPort->readAll()); //qDebug() << QString(rxArray); if( serialRead == true ){ // 數據對齊,若是上次數據是一半,拋棄數據,從新接受 times++; rxString = QString(rxArray); //qDebug() << "rec:" << rxString; ui->textBrowser->append(tr("--------------------------------------------------------------------------")); ui->textBrowser->append("從北斗GPS傳感器第("+QString::number(times)+")次接受數據:"); ui->textBrowser->append(tr("--------------------------------------------------------------------------")); ui->textBrowser->append(QString(rxArray)); gpsDatasProcessing( rxArray ); rxArray.clear(); serialRead = false; if( times%50 == 0 ) { ui->textBrowser->clear(); } ui->textBrowser->append(tr("--------------------------------------------------------------------------\r")); }else{ return; } // 解析數據 } void Widget::gpsDatasProcessing(QByteArray GPSBuffer) { QString GPSBufferString = QString( GPSBuffer ); int error_pos = 0; QString GNRMC_String = NULL; QString GPGGA_String = NULL; QString GPGSV_String = NULL; QString GPRMC_String = NULL; QString GPGLL_String = NULL; QString GNGGA_String = NULL; bool latiflag = false; bool atiflag = false; bool utcflag = false; bool speedflag = false; bool longtiflag = false; QList<QString> gpsStringList = GPSBufferString.split('\n'); // 因爲定時間隔,數據包發生黏連,糾正數據。 if( gpsStringList.at(0).at(0) != '$' ) { QString ErrorString = gpsStringList.at(gpsStringList.length()-1) + gpsStringList.at(0); error_pos = 1; if( ErrorString.contains("$GNRMC") ){ GNRMC_String = ErrorString; }else if( ErrorString.contains("$GPGGA") ) { GPGGA_String = ErrorString; }else if( ErrorString.contains("$GPGSV") ) { GPGSV_String = ErrorString; }else if( ErrorString.contains("$GPRMC") ) { GPRMC_String = ErrorString; }else if( ErrorString.contains("$GPGLL") ) { GPGLL_String = ErrorString; }else if( ErrorString.contains("$GNGGA") ) { GNGGA_String = ErrorString; } }else{ error_pos = 0; } // 從QList中獲得數據 for( int i = error_pos; i < gpsStringList.length()- error_pos; i++ ) { if( gpsStringList.at(i).contains("$GNRMC") ){ GNRMC_String = gpsStringList.at(i); }else if( gpsStringList.at(i).contains("$GPGGA") ) { GPGGA_String = gpsStringList.at(i); }else if( gpsStringList.at(i).contains("$GPGSV") ) { GPGSV_String = gpsStringList.at(i); }else if( gpsStringList.at(i).contains("$GPRMC") ) { GPRMC_String = gpsStringList.at(i); }else if( gpsStringList.at(i).contains("$GPGLL") ) { GPGLL_String = gpsStringList.at(i); }else if( gpsStringList.at(i).contains("$GNGGA") ) { GNGGA_String = gpsStringList.at(i); } } if( !GPGGA_String.isNull() ) { QList<QString> gpggaStrList = GPGGA_String.split(","); QString utcstr = gpggaStrList.at(1); ui->lineEdit_UTC->setText("格林威治時間:"+utcstr.mid(0,2)+":"+utcstr.mid(2,2)+":"+utcstr.mid(4,2)); QString latistr = gpggaStrList.at(2); ui->lineEdit_latitude->setText("北緯"+latistr.mid(0,2)+"度"+latistr.mid(2,7)+"分"); QString altistr = gpggaStrList.at(4); ui->lineEdit_longitude->setText("西經"+altistr.mid(0,3)+"度"+altistr.mid(3,7)+"分"); utcflag = true; latiflag = true; atiflag = true; } if( !GNGGA_String.isNull() ) { if( !latiflag ) { QList<QString> gnggaStrList = GNGGA_String.split(","); QString utcstr = gnggaStrList.at(1); UTC2BTC(&utcstr); ui->lineEdit_UTC->setText("北京時間:"+utcstr.mid(0,2)+":"+utcstr.mid(2,2)+":"+utcstr.mid(4,2)); QString latistr = gnggaStrList.at(2); ui->lineEdit_latitude->setText("北緯"+latistr.mid(0,2)+"°"+latistr.mid(2,9)+"'"); double double_lati = latistr.mid(0,2).toDouble()+(latistr.mid(2,7).toDouble()+0.25)/60; QString altistr = gnggaStrList.at(4); ui->lineEdit_longitude->setText("西經"+altistr.mid(0,3)+"°"+altistr.mid(3,9)+"'"); double double_alti = altistr.mid(0,3).toDouble()+(altistr.mid(3,7).toDouble()+0.25)/60; setCoordinate(QString::number(double_alti),QString::number(double_lati)); //setCoordinate(QString::number(108.886119),QString::number(34.223921)); qDebug()<< "緯度:"<<QString::number(double_alti)<<"|"<<"經度:"<< QString::number(double_lati) << "\n"; QString longtistr = gnggaStrList.at(9); ui->lineEdit_altitude->setText(longtistr+"m "); ui->lineEdit_speed->setText("無效PPS"); utcflag = true; latiflag = true; atiflag = true; } } } void Widget::slotSerialTimerOut() { if( serialRead == false ){ serialRead = true; } }
百度地圖API我找了不少資料,參考資料本文附錄,很是感謝博客名「燦哥哥」,「我是大壞蛋」的整理,「燦哥哥」在文章中不但提供了離線地圖和方法,還提供了對於地圖的基本介紹,對於地圖上面的操做,請參考燦哥哥的博客。但就我開發我想提出兩點:函數
下面代碼能夠看到Qt和Javascript如何互動的。
void Widget::getCoordinate(QString lon,QString lat) { QString tempLon="鼠標經度:"+lon+"°"; QString tempLat="鼠標緯度:"+lat+"°"; ui->labelMouseLongitude->setText(tempLon); ui->labelMouseLatitude->setText(tempLat); } void Widget::setCoordinate(QString lon,QString lat) { QWebFrame *webFrame = ui->webView->page()->mainFrame(); QString cmd = QString("showAddress(\"%1\",\"%2\")").arg(lon).arg(lat); webFrame->evaluateJavaScript(cmd); } void Widget::on_pushButtonStreetMap_clicked() { QWebFrame *frame = ui->webView->page()->mainFrame(); QString cmd = QString("showStreetMap()"); frame->evaluateJavaScript(cmd); ui->pushButtonSatelliteMap->setEnabled(true); ui->pushButtonStreetMap->setEnabled(false); } void Widget::on_pushButtonSatelliteMap_clicked() { QWebFrame *frame = ui->webView->page()->mainFrame(); QString cmd = QString("showSatelliteMap()"); frame->evaluateJavaScript(cmd); ui->pushButtonSatelliteMap->setEnabled(false); ui->pushButtonStreetMap->setEnabled(true); } void Widget::slotPopulateJavaScriptWindowObject() { ui->webView->page()->mainFrame()->addToJavaScriptWindowObject("ReinforcePC", this); }
作完這個程序以後呢,我開始研究如何給程序打包,終於在兩個小時內搞定了。
Step1:把所須要的dll文件集成出來。
Step2:用工具把dll文件exe文件和其餘文件封裝起來,作成msi或者exe文件。
工具就是一個Qt自帶的windeployqt工具,另外一個是Advanced installer安裝包打包程序。
1)在開始菜單找到Qt文件夾,裏面有個像是cmd命令行同樣的東西,個人是MinGW的。反正運行出來這個樣子:
2)進入Qt的工程文件夾,在release或者debug裏面(看你用的是release編譯仍是debug編譯了),找到生成的exe文件夾,把這個exe文件複製到一個方便找,方便輸入路徑的地方。我放在了D:\setup文件裏了。
3)在剛纔出那個命令行裏面輸入: cd /d D:\setup 切換到這個文件夾。
4)輸入命令: windeployqt xxx.exe xxx.exe就是你剛纔編譯出exe的名字。
而後你會發現Qt把全部這個exe文件須要的.dll文件和其餘支持庫文件都放在這個文件夾了。實際上到了這步你能夠打成壓縮包而後發佈到任何電腦,解壓直接運行
我仍是比較喜歡把他弄成安裝包,這樣更方便,更正式。Advanced Installer這個工具簡直太讚了,打包的程序安裝界面十分的正式,一點都不山寨,還有不少安裝包的皮膚可供選擇。如圖爲打好包的圖標:
運行後的效果如圖:
裏面還提供了導入註冊表、安裝後建立快捷方式等等方便的配置。
** 下載地址:http://down7.pc6.com/gm1/Advanced%20Installer.zip **
使用教程,仍是參考後面的參考文獻中的【4】,這裏不在贅述了。
[1] 本程序舊版安裝包下載地址以下:百度雲盤地址 提取碼:41hx (2017-04-29)
[2] 本程序新版安裝包下載地址以下:百度雲盤地址 提取碼:o59r (2017-07-22更新)
[1] 我是大壞蛋,gps定位Qt界面百度地圖api的介紹,CSDN博客,2014-08-24
[2] 燦哥哥,Qt加載百度離線地圖,CSDN博客,2016-03-30
[3] winland0704,Qt官方開發環境生成的exe發佈方式--使用windeployqt,Qt百度貼吧,2015-04-28
[4] Prodesire,Windows安裝包製做指南-Advanced Installer的使用,cnBlogs博客,2016-08-18
[5] jwq2011的專欄,GPS數據包格式+數據解析,CSDN博客,2016-12-15
1· 本文爲MULTIBEANS團隊研發跟隨文章,未經容許不得轉載。
2· 文中涉及的內容如有侵權行爲,請與本人聯繫,本人會及時刪除。
3· 尊重成果,本文將用的參考文獻所有給出,向無私的工程師,愛好者致敬。