1.前言
ECharts開源來自百度商業前端資料可視化團隊,基于html5 Canvas,是一個純Javascript圖表庫,提供直覺,生動,可互動,可個性化定制的資料可視化圖表。創新的拖拽重計算、資料視圖、值域漫遊等特性大大增強了使用者體驗,賦予了使用者對資料進行挖掘、整合的能力。
在之前的blog中曾經就QT與echarts混合開發實作漂亮的圖表做了講解,參見《QT5中使用Echarts圖表元件》--連結位址:http://blog.csdn.net/liuyez123/article/details/50372123,但是QT與echarts混合開發還能打造豐富的動态圖表,例如:需要将分布在全國各地的系統使用者數量統計出來,以地圖的形式展示出每個地域的使用者數量,使用者點選全國地圖中的各個省區域時,能夠打開各省地圖,在各省地圖上的地市區域上以不同的顔色着色,顯示地域的使用者量情況。這個需求可以基于Echarts的地圖圖表功能實作,這是其他圖表工具很難做到的。先展示下最終實作效果。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN3gjM1gDN1EzNxEDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
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的對象,QT将Document對象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,
bool)
{
//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();
}
文中使用例子的代碼可以從此連結下載下傳:http://download.csdn.net/detail/liuyez123/9407361
注:例子中的provinces.json檔案隻包含了部分省份的省名群組成地市對照關系,運作例子時,有正确對應關系的省份會以不同着色顯示,沒有的省份則顯示為灰色,但在編造的地市資料檔案citygeo.json檔案中除了上海、天津、北京三個直轄市外基本資料是完整的,如果感興趣可以自行添加資料補充完整。如有不明白之處可以給我留言~