Qt開發(fā)技術(shù):Q3D圖表開發(fā)筆記(三):Q3DSurface三維曲面圖介紹、Demo以及代碼詳解
前言
??qt提供了q3d進(jìn)行三維開發(fā),雖然這個(gè)框架沒有得到大量運(yùn)用也不是那么成功,性能上也有很大的欠缺,但是普通的點(diǎn)到為止的應(yīng)用展示還是可以的。
??其中就包括華麗絢爛的三維圖表,數(shù)據(jù)量不大的時(shí)候是可以使用的。
??前面介紹了基礎(chǔ)的q3d散點(diǎn)圖、柱狀圖,本篇介紹基礎(chǔ)的三維曲面圖。
Demo:Q3DSurface散點(diǎn)圖演示效果
??

??

??

Q3D提供的三維圖表
??依賴QtDataVisualization。在安裝qt的時(shí)候要選擇安裝QtDataVisualization模塊。
Q3DScatter散點(diǎn)圖
??Q3D的散點(diǎn)圖,性能大約支撐1000個(gè)點(diǎn)可以不卡頓,具體依賴pc,1000個(gè)點(diǎn)是什么 概念,可以理解為:10x10x10的區(qū)域,每個(gè)區(qū)域一個(gè)數(shù)據(jù)點(diǎn)。
??

Q3DBars柱狀圖
??Q3D的柱狀圖,性能跟散點(diǎn)圖類似。
???

Q3DSurface平面凹凸圖,平面紋理圖,平面曲線圖
??Q3D的柱狀圖,性能跟散點(diǎn)圖類似。
??

Q3DSurface平面曲線圖
簡介
??Q3DSurface類提供了渲染3D曲面圖的方法。該類使開發(fā)人員能夠渲染3D表面圖,并通過自由旋轉(zhuǎn)場景來查看它們??梢酝ㄟ^QSurface3DSeries控制曲面的視覺財(cái)產(chǎn),例如繪制模式和著色。
??Q3DSurface通過在用戶用鼠標(biāo)左鍵點(diǎn)擊的數(shù)據(jù)點(diǎn)上顯示高亮顯示的球(當(dāng)使用默認(rèn)輸入處理程序時(shí))或通過QSurface3DSeries進(jìn)行選擇來支持選擇。選擇指針附帶一個(gè)標(biāo)簽,在默認(rèn)情況下,該標(biāo)簽顯示數(shù)據(jù)點(diǎn)的值和點(diǎn)的坐標(biāo)。
軸上顯示的值范圍和標(biāo)簽格式可以通過QValue3DAxis進(jìn)行控制。
??要旋轉(zhuǎn)圖形,請(qǐng)按住鼠標(biāo)右鍵并移動(dòng)鼠標(biāo)。縮放是使用鼠標(biāo)滾輪完成的。兩者都假設(shè)默認(rèn)的輸入處理程序正在使用中。
??如果沒有將任何軸明確設(shè)置為Q3DSurface,則會(huì)創(chuàng)建不帶標(biāo)簽的臨時(shí)默認(rèn)軸。這些默認(rèn)軸可以通過軸訪問器進(jìn)行修改,但只要明確設(shè)置了方向的任何軸,該方向的默認(rèn)軸就會(huì)被破壞。
構(gòu)造最小Q3D平面曲線圖
??首先,構(gòu)造Q3D曲面。由于在本例中,我們將圖形作為頂級(jí)窗口運(yùn)行,因此需要清除Qt::FramelessWindowHint標(biāo)志,該標(biāo)志在默認(rèn)情況下設(shè)置:
Q3DSurface surface; surface.setFlags(surface.flags() ^ Qt::FramelessWindowHint);
??現(xiàn)在Q3DSurface已準(zhǔn)備好接收要渲染的數(shù)據(jù)。創(chuàng)建數(shù)據(jù)元素以接收值:
QSurfaceDataArray *data = new QSurfaceDataArray;QSurfaceDataRow *dataRow1 = new QSurfaceDataRow;QSurfaceDataRow *dataRow2 = new QSurfaceDataRow;
??首先將數(shù)據(jù)喂給行元素,然后將它們的指針添加到數(shù)據(jù)元素:
*dataRow1 << QVector3D(0.0f, 0.1f, 0.5f) << QVector3D(1.0f, 0.5f, 0.5f);*dataRow2 << QVector3D(0.0f, 1.8f, 1.0f) << QVector3D(1.0f, 1.2f, 1.0f);*data << dataRow1 << dataRow2;、
??創(chuàng)建新系列并為其設(shè)置數(shù)據(jù):
QSurface3DSeries *series = new QSurface3DSeries;series->dataProxy()->resetArray(data); ? surface.addSeries(series);
??最后,設(shè)置為可見:
surface.show();
??創(chuàng)建和顯示此圖所需的完整代碼為:
#include <QtDataVisualization>using namespace QtDataVisualization;int main(int argc, char **argv){
? ?QGuiApplication app(argc, argv);
? ?Q3DSurface surface;
? ?surface.setFlags(surface.flags() ^ Qt::FramelessWindowHint);
? ?QSurfaceDataArray *data = new QSurfaceDataArray;
? ?QSurfaceDataRow *dataRow1 = new QSurfaceDataRow;
? ?QSurfaceDataRow *dataRow2 = new QSurfaceDataRow;
? ?*dataRow1 << QVector3D(0.0f, 0.1f, 0.5f) << QVector3D(1.0f, 0.5f, 0.5f);
? ?*dataRow2 << QVector3D(0.0f, 1.8f, 1.0f) << QVector3D(1.0f, 1.2f, 1.0f);
? ?*data << dataRow1 << dataRow2;
? ?QSurface3DSeries *series = new QSurface3DSeries;
? ?series->dataProxy()->resetArray(data);
? ?surface.addSeries(series);
? ?surface.show();
? ?return app.exec();}
??運(yùn)行效果:
??

??場景可以被旋轉(zhuǎn)、放大,并且可以選擇一個(gè)項(xiàng)目來查看其位置,但在這個(gè)最小的代碼示例中不包括其他交互。
Q3Ddemo構(gòu)建流程解析
步驟一:確認(rèn)安裝QtDataVisualization模塊
??如何確認(rèn),則是在幫助文件中查看是否有Q3dscatter類。一般是安裝了模塊才會(huì)有對(duì)應(yīng)的幫助文件。沒有則重新安裝qt或者單獨(dú)安裝該模塊。
??

步驟二:工程配置文件中加入模塊
??Q3d是在數(shù)據(jù)可視化模塊中,需要在pro或者pri配置文件中添加。
QT += datavisualization
??

步驟三:添加使用到的頭文件
??使用到Q3DBar相關(guān)類中添加頭文件,主要使用到Q3DBar、QBar3DSeries、QBarDataRow等等。
#include <Q3DBars>#include <Q3DTheme>#include <QBar3DSeries>#include <QVector3D>
??

步驟四:添加命名空間
??這時(shí)候還是無法使用對(duì)應(yīng)的類,需要添加命名空間才行:
using namespace QtDataVisualization;
??

步驟五:Q3D的圖標(biāo)基礎(chǔ)構(gòu)建框架
??下面是包含注釋的Q3DSurface基礎(chǔ)構(gòu)建流程(注意軸的顯示,查看末尾“入坑一”,注意數(shù)據(jù)的成面規(guī)則,查看“入坑二”
_pQ3DSurface = new Q3DSurface();_pContainer = QWidget::createWindowContainer(_pQ3DSurface, this);// 設(shè)置軸文本{
? ?// 注意笛卡爾坐標(biāo)
? ?_pQ3DSurface->axisX()->setTitle("經(jīng)度(°)");
? ?_pQ3DSurface->axisX()->setTitleVisible(true);
? ?_pQ3DSurface->axisY()->setTitle("高度(m)");
? ?_pQ3DSurface->axisY()->setTitleVisible(true);
? ?_pQ3DSurface->axisZ()->setTitle("緯度(°)");
? ?_pQ3DSurface->axisZ()->setTitleVisible(true);}// 設(shè)置軸范圍{
? ?// 注意笛卡爾坐標(biāo)
? ?_pQ3DSurface->axisX()->setRange(0, 359);
? ?_pQ3DSurface->axisY()->setRange(0, 100);
? ?_pQ3DSurface->axisZ()->setRange(0, 359);}// 生成一個(gè)曲線_pSurface3DSeries = new QSurface3DSeries(_pQ3DSurface);// 設(shè)置渲染平滑_pSurface3DSeries->setMeshSmooth(true);// 設(shè)置渲染模式// ? DrawWireframe ? ? ? ? ? : 繪制柵格// ? DrawSurface ? ? ? ? ? ? : 繪制表面// ? DrawSurfaceAndWireframe : 繪制柵格和圖表面_pSurface3DSeries->setDrawMode(QSurface3DSeries::DrawSurface);// 視圖添加該曲線_pQ3DSurface->addSeries(_pSurface3DSeries);// 設(shè)置陰影質(zhì)量_pQ3DSurface->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftLow);// 設(shè)置視角_pQ3DSurface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricLeft);// 設(shè)置子網(wǎng)格_pQ3DSurface->activeTheme()->setGridEnabled(true);#if 1// 添加模擬數(shù)據(jù)QSurfaceDataArray *pSurfaceDataArray = new QSurfaceDataArray;#if 1#if 1// 這是 z 緯度for(int n = 0; n < 360; n++){
? ?QSurfaceDataRow *pSurfaceDataRow ?= new QSurfaceDataRow;
? ?// 這是 x 經(jīng)度
? ?for(int m = 0; m < 360; m++)
? ?{
? ? ? // 注意與笛卡爾坐標(biāo)進(jìn)行映射
? ? ? *pSurfaceDataRow << QVector3D(m, n / 7 + m / 7, n);
? ?}
? ?*pSurfaceDataArray << pSurfaceDataRow;}#elsefor(int n = 0; n < 360; n++){
? ?QSurfaceDataRow *pSurfaceDataRow ?= new QSurfaceDataRow;
? ?// 這是 x 經(jīng)度
? ?for(int m = 0; m < 360; m++)
? ?{
? ? ? // 注意與笛卡爾坐標(biāo)進(jìn)行映射
? ? ? *pSurfaceDataRow << QVector3D(m, qrand() % 100, n);
? ? ? LOG << n << m;
? ?}
? ?*pSurfaceDataArray << pSurfaceDataRow;}#endif#elseQSurfaceDataRow *pSurfaceDataRow1 ?= new QSurfaceDataRow;QSurfaceDataRow *pSurfaceDataRow2 ?= new QSurfaceDataRow;QSurfaceDataRow *pSurfaceDataRow3 ?= new QSurfaceDataRow;// 行與行之間,要形成一個(gè)四點(diǎn)成面*pSurfaceDataRow1 << QVector3D(0, 0, 0) ?<< QVector3D(359, 20, 0);*pSurfaceDataRow2 << QVector3D(50, 20, 179) ?<< QVector3D(359, 40, 179);*pSurfaceDataRow3 << QVector3D(100, 80, 359) ?<< QVector3D(359, 100, 359);*pSurfaceDataArray << pSurfaceDataRow1 << pSurfaceDataRow2 << pSurfaceDataRow3;#endif// 添加數(shù)據(jù)(自動(dòng)沖掉之前的數(shù)據(jù))_pSurface3DSeries->dataProxy()->resetArray(pSurfaceDataArray);#endif_pQ3DSurface->addSeries(_pSurface3DSeries);_pQ3DSurface->show();
Demo源碼
Q3dSurfaceWidget.h
#ifndef Q3DSURFACEWIDGET_H#define Q3DSURFACEWIDGET_H#include <QWidget>#include <Q3DSurface>#include <Q3DTheme>#include <QSurface3DSeries>#include <QVector3D>using namespace QtDataVisualization;namespace Ui {class Q3dSurfaceWidget;}class Q3dSurfaceWidget : public QWidget{
? ?Q_OBJECTpublic:
? ?explicit Q3dSurfaceWidget(QWidget *parent = 0);
? ?~Q3dSurfaceWidget();protected:
? ?void initControl();protected:
? ?void resizeEvent(QResizeEvent *event);private:
? ?Ui::Q3dSurfaceWidget *ui;private:
? ?Q3DSurface *_pQ3DSurface; ? ? ? ? ?// q3d平面曲線圖
? ?QWidget *_pContainer; ? ? ? ? ? // q3d窗口容器
? ?QSurface3DSeries ?*_pSurface3DSeries ; ? ?// q3d柱狀圖數(shù)據(jù)};#endif // Q3DSURFACEWIDGET_H
Q3dSurfaceWidget.cpp
#include "Q3dSurfaceWidget.h"#include "ui_Q3dSurfaceWidget.h"#include <Q3DTheme>#include <QDebug>#include <QDateTime>//#define LOG qDebug()<<__FILE__<<__LINE__//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")Q3dSurfaceWidget::Q3dSurfaceWidget(QWidget *parent) :
? ?QWidget(parent),
? ?ui(new Ui::Q3dSurfaceWidget),
? ?_pQ3DSurface(0),
? ?_pContainer(0),
? ?_pSurface3DSeries(0){
? ?ui->setupUi(this);
? ?QString version = "v1.0.0";
? ?initControl();}Q3dSurfaceWidget::~Q3dSurfaceWidget(){
? ?delete ui;}void Q3dSurfaceWidget::initControl(){
? ?_pQ3DSurface = new Q3DSurface();
? ?_pContainer = QWidget::createWindowContainer(_pQ3DSurface, this);
? ?// 設(shè)置軸文本
? ?{
? ? ? ?// 注意笛卡爾坐標(biāo)
? ? ? ?_pQ3DSurface->axisX()->setTitle("經(jīng)度(°)");
? ? ? ?_pQ3DSurface->axisX()->setTitleVisible(true);
? ? ? ?_pQ3DSurface->axisY()->setTitle("高度(m)");
? ? ? ?_pQ3DSurface->axisY()->setTitleVisible(true);
? ? ? ?_pQ3DSurface->axisZ()->setTitle("緯度(°)");
? ? ? ?_pQ3DSurface->axisZ()->setTitleVisible(true);
? ?}
? ?// 設(shè)置軸范圍
? ?{
? ? ? ?// 注意笛卡爾坐標(biāo)
? ? ? ?_pQ3DSurface->axisX()->setRange(0, 359);
? ? ? ?_pQ3DSurface->axisY()->setRange(0, 100);
? ? ? ?_pQ3DSurface->axisZ()->setRange(0, 359);
? ?}
? ?// 生成一個(gè)曲線
? ?_pSurface3DSeries = new QSurface3DSeries(_pQ3DSurface);
? ?// 設(shè)置渲染平滑
? ?_pSurface3DSeries->setMeshSmooth(true);
? ?// 設(shè)置渲染模式
? ?// ? DrawWireframe ? ? ? ? ? : 繪制柵格
? ?// ? DrawSurface ? ? ? ? ? ? : 繪制表面
? ?// ? DrawSurfaceAndWireframe : 繪制柵格和圖表面
? ?_pSurface3DSeries->setDrawMode(QSurface3DSeries::DrawSurface);
? ?// 視圖添加該曲線
? ?_pQ3DSurface->addSeries(_pSurface3DSeries);
? ?// 設(shè)置陰影質(zhì)量
? ?_pQ3DSurface->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftLow);
? ?// 設(shè)置視角
? ?_pQ3DSurface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricLeft);
? ?// 設(shè)置子網(wǎng)格
? ?_pQ3DSurface->activeTheme()->setGridEnabled(true);#if 1
? ?// 添加模擬數(shù)據(jù)
? ?QSurfaceDataArray *pSurfaceDataArray = new QSurfaceDataArray;#if 1#if 1
? ?// 這是 z 緯度
? ?for(int n = 0; n < 360; n++)
? ?{
? ? ? ?QSurfaceDataRow *pSurfaceDataRow ?= new QSurfaceDataRow;
? ? ? ?// 這是 x 經(jīng)度
? ? ? ?for(int m = 0; m < 360; m++)
? ? ? ?{
? ? ? ? ? // 注意與笛卡爾坐標(biāo)進(jìn)行映射
? ? ? ? ? *pSurfaceDataRow << QVector3D(m, n / 7 + m / 7, n);
? ? ? ?}
? ? ? ?*pSurfaceDataArray << pSurfaceDataRow;
? ?}#else
? ?for(int n = 0; n < 360; n++)
? ?{
? ? ? ?QSurfaceDataRow *pSurfaceDataRow ?= new QSurfaceDataRow;
? ? ? ?// 這是 x 經(jīng)度
? ? ? ?for(int m = 0; m < 360; m++)
? ? ? ?{
? ? ? ? ? // 注意與笛卡爾坐標(biāo)進(jìn)行映射
? ? ? ? ? *pSurfaceDataRow << QVector3D(m, qrand() % 100, n);
? ? ? ? ? LOG << n << m;
? ? ? ?}
? ? ? ?*pSurfaceDataArray << pSurfaceDataRow;
? ?}#endif#else
? ?QSurfaceDataRow *pSurfaceDataRow1 ?= new QSurfaceDataRow;
? ?QSurfaceDataRow *pSurfaceDataRow2 ?= new QSurfaceDataRow;
? ?QSurfaceDataRow *pSurfaceDataRow3 ?= new QSurfaceDataRow;
? ?// 行與行之間,要形成一個(gè)四點(diǎn)成面
? ?*pSurfaceDataRow1 << QVector3D(0, 0, 0) ?<< QVector3D(359, 20, 0);
? ?*pSurfaceDataRow2 << QVector3D(50, 20, 179) ?<< QVector3D(359, 40, 179);
? ?*pSurfaceDataRow3 << QVector3D(100, 80, 359) ?<< QVector3D(359, 100, 359);
? ?*pSurfaceDataArray << pSurfaceDataRow1 << pSurfaceDataRow2 << pSurfaceDataRow3;#endif
? ?// 添加數(shù)據(jù)(自動(dòng)沖掉之前的數(shù)據(jù))
? ?_pSurface3DSeries->dataProxy()->resetArray(pSurfaceDataArray);#endif
? ?_pQ3DSurface->addSeries(_pSurface3DSeries);
? ?_pQ3DSurface->show();}void Q3dSurfaceWidget::resizeEvent(QResizeEvent *event){
? ?if(_pContainer)
? ?{
? ? ? ?_pContainer->setGeometry(rect());
? ?}}
工程模板v1.2.0
??

入坑
入坑一:xyz坐標(biāo)系不對(duì)
問題
??x精度,y維度,z高度(海拔高度)映射錯(cuò)誤
??

原因
??x,y,z實(shí)際是遵循笛卡爾坐標(biāo)集
解決
??先理解坐標(biāo),然后z軸方向,數(shù)據(jù)也要替換(按照x,y,z來排列,改為x,z,y)
?&emso;

入坑二:曲面顯示不對(duì)
問題
??數(shù)據(jù)顯示映射錯(cuò)誤
原因
??點(diǎn)成面,需要遵循4點(diǎn)成面的規(guī)則,和opengl相關(guān)3點(diǎn)成面和4點(diǎn)成面的原理類似。
??

??

??
解決
??相鄰行與行之間,要形成面,修改后展示如下:
??

??
