1. 程式人生 > >QT5.5或QT5.6與echarts實現動態圖表

QT5.5或QT5.6與echarts實現動態圖表

1.前言

ECharts開源來自百度商業前端資料視覺化團隊,基於html5 Canvas,是一個純Javascript圖表庫,提供直觀,生動,可互動,可個性化定製的資料視覺化圖表。創新的拖拽重計算、資料檢視、值域漫遊等特性大大增強了使用者體驗,賦予了使用者對資料進行挖掘、整合的能力。

在之前的blog中曾經就QT與echarts混合開發實現漂亮的圖表做了講解,參見《QT5中使用Echarts圖表元件》--連結地址:http://blog.csdn.net/liuyez123/article/details/50372123,但是QT與echarts混合開發還能打造豐富的動態圖表,例如:需要將分佈在全國各地的系統使用者數量統計出來,以地圖的形式展示出每個地域的使用者數量,使用者點選全國地圖中的各個省區域時,能夠開啟各省地圖,在各省地圖上的地市區域上以不同的顏色著色,顯示地域的使用者量情況。這個需求可以基於Echarts的地圖圖表功能實現,這是其他圖表工具很難做到的。先展示下最終實現效果。

2.實現思路

本例中右側的圖表是基於Echarts將分佈在全國各地的系統使用者數量統計出來,以地圖的形式展示出每個地域的使用者數量,使用者點選全國地圖中的各個省區域時,能夠開啟各省地圖,在各省地圖上的地市區域上以不同的顏色著色,顯示地域的使用者量情況,當滑鼠移到相應的地市上面還會顯示相應的資料。Echarts圖表還有更多複雜的功能大家可以參考百度Echarts的官方文件。

為了簡化演示程式,將使用者資料存放在檔案中,使用者資料以JSON格式存放。實際應用中可以將資料存放於資料庫。在本例中使用兩個JSON檔案,一個用於存放省和地市的對應資料,另一個用於存放各個地市的實際使用者資料。

在QtWebengine載入的Echarts圖表頁面中將點選選中的省份通過QtWebchannel傳至QT C++程式中,QT C++程式通過查詢存在JSON檔案中的資料,組成JSON格式的資料傳回Echart元件實現資料展現。QtWebchannel能夠實現QT C++和HTML頁中JS雙向資料互動,實現物件傳遞和基於QT的訊號和槽機制,具體的實現機制可以參看另一篇blog《

實現QT與HTML頁面通訊》—連結地址:http://blog.csdn.net/liuyez123/article/details/50509788

這種設計思路是基於:Echarts有著豐富的圖表展現功能,各種圖表的樣式是很容易在HTML和JS定製,而重要的資料來源(如圖中的各區域的使用者數量)是需要進行庫表的查詢、業務邏輯處理,最終進行展現資料的拼裝,這些處理工作不是Echarts的強項,但是QT卻很容易實現,所以可以將兩者結合進行混合開發,打造完美的應用。另外,Echarts是一款開源元件,可以流暢的執行在 PC 和移動裝置上,具有很強的跨平臺能力。

3.實現程式碼

Document物件是橋接QT C++

JS的物件,QTDocument物件receiveText槽函式開放給JS,當HTML頁面中使用者點選相應的省份時呼叫此方法接收選中的省份,並最終傳送sendText(constQByteArray&text)訊號給JS通知HTML頁面接受返回的處理結果。

document.h內容

#ifndefDOCUMENT_H

#defineDOCUMENT_H

#include<QObject>

#include<QString>

#include<QJsonArray>

#include<QJsonObject>

classDocument:publicQObject

{

Q_OBJECT

Q_PROPERTY(QStringtextMEMBERs_textNOTIFYsendText)

public:

explicitDocument(QObject*parent=nullptr);

voidsetSendTextText(constQString&text);

publicslots:

voidreceiveText(constQString&r_text);

signals:

voidsendText(constQByteArray&text);

private:

QJsonObjectprovinceJsonObj;

QJsonArraycityJsonData;

QStrings_text;

QStringrecieve_text;

};

#endif//DOCUMENT_H

document.cpp內容

#include"document.h"
#include<QDebug>
#include<QJsonArray>
#include<QtCore/QFile>
#include<QtCore/QTextStream>
#include<QJsonDocument>
Document::Document(QObject*parent):QObject(parent)
{
QFileproJsonFile(":/provinces.json");
if(!proJsonFile.open(QIODevice::ReadOnly)){
qWarning("Couldn'topenproincesjsonfile.");
return;
}
QTextStreaminProData(&proJsonFile);
//將文字流讀取到字串中:
QStringprovinceDat=inProData.readAll();
//關閉文字流:
proJsonFile.close();
QJsonDocumentloadDoc(QJsonDocument::fromJson(provinceDat.toUtf8()));
provinceJsonObj=loadDoc.object();
QFilecityJsonFile(":/citygeo.json");
cityJsonFile.open(QIODevice::ReadOnly);
QTextStreaminData(&cityJsonFile);
//將文字流讀取到字串中:
QStringdat=inData.readAll();
//關閉文字流:
cityJsonFile.close();
QJsonDocumentdoc=QJsonDocument::fromJson(dat.toUtf8());
cityJsonData=doc.array();
}
voidDocument::setSendTextText(constQString&text)
{
QJsonArraycityArray=provinceJsonObj[text.toUtf8()].toArray();
QJsonArrayreturnArray;
for(intcityIndex=0;cityIndex<cityArray.size();++cityIndex)
{
QStringcity=cityArray[cityIndex].toString();
  for(intvalueIndex=0;valueIndex<cityJsonData.size();++valueIndex)
{
QJsonObjectvalueObject=cityJsonData[valueIndex].toObject();
if(valueObject["name"].toString()==city)
{
returnArray.append(valueObject);
}
}
}
QJsonDocumentreturnDoc;
returnDoc.setArray(returnArray);
emitsendText(returnDoc.toJson());
}
/*!
ThisslotisinvokedfromtheHTMLclientsideandthetextdisplayedontheserverside.
*/
voidDocument::receiveText(constQString&r_text)
{
setSendTextText(r_text);
}
MainWidget物件負責主介面的顯示。

mainwidget.h內容

#ifndefMAINWIDGET_H

#defineMAINWIDGET_H

#include"document.h"

#include<QWidget>

#include<QString>

namespaceUi{

classMainWidget;

}

classMainWidget:publicQWidget

{

Q_OBJECT

public:

explicitMainWidget(QWidget*parent=0);

~MainWidget();

private:

Ui::MainWidget*ui;

Documentm_content;

};

#endif//MAINWIDGET_H

mainwidget.cpp內容

#include"mainwidget.h"
#include"ui_mainwidget.h"
#include"previewpage.h"
#include"document.h"
#include<QFile>
#include<QWebChannel>
MainWidget::MainWidget(QWidget*parent):
QWidget(parent),
ui(newUi::MainWidget)
{
ui->setupUi(this);
PreviewPage*page=newPreviewPage(this);
ui->preview->setPage(page);
QWebChannel*channel=newQWebChannel(this);
channel->registerObject(QStringLiteral("content"),&m_content);
page->setWebChannel(channel);
ui->preview->setUrl(QUrl("qrc:/index.html"));
}
MainWidget::~MainWidget()
{
deleteui;
}

previewpage.h內容

PreviewPage物件用於封裝展現Echarts元件的HTML頁面。

#ifndefPREVIEWPAGE_H
#definePREVIEWPAGE_H
#include<QWebEnginePage>
classPreviewPage:publicQWebEnginePage
{
Q_OBJECT
public:
explicitPreviewPage(QObject*parent=nullptr):QWebEnginePage(parent){}
protected:
boolacceptNavigationRequest(constQUrl&url,NavigationTypetype,boolisMainFrame);
};
#endif//PREVIEWPAGE_H

previewpage.cpp內容

#include"previewpage.h"

#include<QDesktopServices>

boolPreviewPage::acceptNavigationRequest(constQUrl&url,

QWebEnginePage::NavigationType/*type*/,

bool/*isMainFrame*/)

{

//Onlyallowqrc:/index.html.

if(url.scheme()==QString("qrc"))

returntrue;

QDesktopServices::openUrl(url);

returnfalse;

}

main.cpp內容

#include"document.h"

#include"mainwidget.h"

#include<QApplication>

intmain(intargc,char*argv[])

{

QApplicationa(argc,argv);

MainWidgetw;

w.show();

returna.exec();

}

注:例子中的provinces.json檔案只包含了部分省份的省名和組成地市對照關係,執行例子時,有正確對應關係的省份會以不同著色顯示,沒有的省份則顯示為灰色,但在編造的地市資料檔案citygeo.json檔案中除了上海、天津、北京三個直轄市外基本資料是完整的,如果感興趣可以自行新增資料補充完整。如有不明白之處可以給我留言~