Qt開發技術:Q3D圖表開發筆記(一):Q3DScatter三維散點圖介紹、Demo以及程式碼詳解

2023-03-14 15:01:58

前言

  qt提供了q3d進行三維開發,雖然這個框架沒有得到大量運用也不是那麼成功,效能上也有很大的欠缺,但是普通的點到為止的應用展示還是可以的。
  其中就包括華麗絢爛的三維圖表,資料量不大的時候是可以使用的。

 

Demo:Q3DScatter散點圖演示效果

  在這裡插入圖片描述
   在這裡插入圖片描述
  在這裡插入圖片描述

 

Q3D提供的三維圖表

  依賴QtDataVisualization。在安裝qt的時候要選擇安裝QtDataVisualization模組。

Q3DScatter散點圖

  Q3D的散點圖,效能大約支撐1000個點可以不卡頓,具體依賴pc,1000個點是什麼 概念,可以理解為:10x10x10的區域,每個區域一個資料點。
  在這裡插入圖片描述

Q3DBars柱狀圖

  Q3D的柱狀圖,效能跟散點圖類似。
  在這裡插入圖片描述

Q3DSurface平面凹凸圖,平面紋理圖

  Q3D的柱狀圖,效能跟散點圖類似。
  在這裡插入圖片描述

 

Q3DScatter散點圖

簡介

  Q3DScatter類提供了渲染3D散點圖的方法。能夠在3D中渲染散點圖,並通過自由旋轉場景來檢視散點圖。
  旋轉是通過按住滑鼠右鍵並移動滑鼠來完成的。縮放由滑鼠滾輪完成。如果啟用,則通過滑鼠左鍵進行選擇。通過單擊滑鼠滾輪,可以將場景重置為預設攝影機檢視。在觸控裝置中,旋轉是通過點選和移動完成的,選擇是通過點選並按住並縮放。
  如果沒有設定軸,將建立沒有標籤的臨時預設軸。這些預設軸可以通過軸存取器進行修改,但是一旦為方向明確設定了任何軸,該方向的預設軸就會被破壞。
  Q3DScatter支援同時顯示多個系列。

構造最小Q3DS散點圖

  首先,構建Q3DS散射器。由於在本例中我們將圖形作為頂級視窗執行,因此需要清除Qt::FramelessWindowHint標誌,該標誌預設設定為:

Q3DScatter scatter;
scatter.setFlags(scatter.flags() ^ Qt::FramelessWindowHint);

  現在Q3DScatter已準備好接收要渲染的資料。新增一系列3個QVector3D專案:

QScatter3DSeries *series = new QScatter3DSeries;
QScatterDataArray data;
data << QVector3D(0.5f, 0.5f, 0.5f) << QVector3D(-0.3f, -0.5f, -0.4f) << QVector3D(0.0f, -0.3f, 0.2f);
series->dataProxy()->addItems(data);
scatter.addSeries(series);

  最後,將其設定為可見:

scatter.show();

  建立和顯示此圖形所需的完整程式碼是:

#include <QtDataVisualization>

using namespace QtDataVisualization;

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);

    Q3DScatter scatter;
    scatter.setFlags(scatter.flags() ^ Qt::FramelessWindowHint);
    QScatter3DSeries *series = new QScatter3DSeries;
    QScatterDataArray data;
data << QVector3D(0.5f, 0.5f, 0.5f) 
            << QVector3D(-0.3f, -0.5f, -0.4f)
            << QVector3D(0.0f, -0.3f, 0.2f);
    series->dataProxy()->addItems(data);
    scatter.addSeries(series);
    scatter.show();

    return app.exec();
}

  執行效果:
  在這裡插入圖片描述
  場景可以被旋轉、放大,並且可以選擇一個專案來檢視其位置,但在這個最小的程式碼範例中不包括其他互動。通過熟悉所提供的範例(如散點範例)來了解更多資訊。

 

Q3Ddemo構建流程解析

步驟一:確認安裝QtDataVisualization模組

  如何確認,則是在幫助檔案中檢視是否有Q3dscatter類。一般是安裝了模組才會有對應的幫助檔案。沒有則重新安裝qt或者單獨安裝該模組。
  在這裡插入圖片描述

步驟二:工程組態檔中加入模組

  Q3d是在資料視覺化模組中,需要在pro或者pri組態檔中新增。

QT += datavisualization

  在這裡插入圖片描述

步驟三:新增使用到的標頭檔案

  使用到Q3DScatter相關類中新增標頭檔案,主要使用到Q3DScatter和QScatter3DSeries等等。

#include <Q3DScatter>
#include <Q3DTheme>
#include <QScatter3DSeries>
#include <QVector3D>

  在這裡插入圖片描述

步驟四:新增名稱空間

  這時候還是無法使用對應的類,需要新增名稱空間才行,檢視最後「入坑一」:

using namespace QtDataVisualization;

  在這裡插入圖片描述

步驟五:Q3D的圖示基礎構建框架

  下面是包含註釋的Q3DScatter基礎構建流程,其他兩種圖類似:

_pQ3DScatter = new Q3DScatter();
_pContainer = QWidget::createWindowContainer(_pQ3DScatter, this);

// 設定軸文字
{
    _pQ3DScatter->axisX()->setTitle("X");
    _pQ3DScatter->axisY()->setTitle("Y");
    _pQ3DScatter->axisZ()->setTitle("Z");
}
// 設定軸範圍
{
//        _pQ3DScatter->axisX()->setRange(0, 10);
//        _pQ3DScatter->axisY()->setRange(0, 10);
//        _pQ3DScatter->axisZ()->setRange(0, 10);
}

// 生成一個曲線
_pScatter3DSeries = new QScatter3DSeries(_pQ3DScatter);
// 設定渲染平滑
_pScatter3DSeries->setMeshSmooth(true);

// 檢視新增該曲線
_pQ3DScatter->addSeries(_pScatter3DSeries);

// 設定陰影質量
_pQ3DScatter->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftLow);
// 設定視角
_pQ3DScatter->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricLeft);
// 設定子網格
_pQ3DScatter->activeTheme()->setGridEnabled(true);

#if 1
// 新增模擬資料
QScatterDataArray data;
data << QVector3D(1, 1,1) << QVector3D(1, 1, 2) << QVector3D(1, 1, 3)
     << QVector3D(1, 2,1) << QVector3D(1, 2, 2) << QVector3D(1, 2, 3)
     << QVector3D(1, 3,1) << QVector3D(1, 3, 2) << QVector3D(1, 3, 3);
// 新增資料(自動沖掉之前的資料)
_pScatter3DSeries->dataProxy()->addItems(data);
#endif

#if 1
// 模擬
QList<QVector3D> listVector3D;
#if 0
listVector3D <<  QVector3D(5, 1,1) << QVector3D(5, 1, 2) << QVector3D(5, 1, 3)
             << QVector3D(5, 2,1) << QVector3D(5, 2, 2) << QVector3D(5, 2, 3)
             << QVector3D(5, 3,1) << QVector3D(5, 3, 2) << QVector3D(5, 3, 3);
#else
listVector3D << QVector3D(1, 1,1) << QVector3D(1, 1, 2) << QVector3D(1, 1, 3)
             << QVector3D(1, 2,1) << QVector3D(1, 2, 2) << QVector3D(1, 2, 3)
             << QVector3D(1, 3,1) << QVector3D(1, 3, 2) << QVector3D(1, 3, 3);
#endif
 

Demo原始碼

Q3dScatterWidget.h

#ifndef Q3DSCATTERWIDGET_H
#define Q3DSCATTERWIDGET_H

#include <QWidget>
#include <Q3DScatter>
#include <Q3DTheme>
#include <QScatter3DSeries>
#include <QVector3D>


using namespace QtDataVisualization;

namespace Ui {
class Q3dScatterWidget;
}

class Q3dScatterWidget : public QWidget
{
    Q_OBJECT

public:
    explicit Q3dScatterWidget(QWidget *parent = 0);
    ~Q3dScatterWidget();

public:
    void setData(QList<QVector3D> listVector3D);

protected:
    void initControl();


protected:
    void resizeEvent(QResizeEvent *event);

private:
    Ui::Q3dScatterWidget *ui;

private:
    Q3DScatter *_pQ3DScatter;               // q3d散點檢視
    QWidget *_pContainer;                   // q3d視窗容器
    QScatter3DSeries *_pScatter3DSeries;    // q3d散點圖資料
};

#endif // Q3DSCATTERWIDGET_H

Q3dScatterWidget.cpp

#include "Q3dScatterWidget.h"
#include "ui_Q3dScatterWidget.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")

Q3dScatterWidget::Q3dScatterWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Q3dScatterWidget),
    _pQ3DScatter(0),
    _pContainer(0),
    _pScatter3DSeries(0)
{
    ui->setupUi(this);

    QString version = "v1.0.0";
    setWindowTitle(QString("q3d散點圖範例 %1(作者:長沙紅胖子 QQ:21497936 WX:15173255813 www.hpzwl.com").arg(version));

    initControl();
}

Q3dScatterWidget::~Q3dScatterWidget()
{
    delete ui;
}

void Q3dScatterWidget::setData(QList<QVector3D> listVector3D)
{
    double xMin, xMax, yMin, yMax, zMin, zMax;
    QScatterDataArray data;
    for(int index = 0; index < listVector3D.size(); index++)
    {
        // 新增模擬資料
        data << listVector3D.at(index);
        // 計算範圍
        if(index == 0)
        {
            xMin = listVector3D.at(index).x();
            xMax = listVector3D.at(index).x();
            yMin = listVector3D.at(index).y();
            yMax = listVector3D.at(index).y();
            zMin = listVector3D.at(index).z();
            zMax = listVector3D.at(index).z();
        }else {
            if(xMin > listVector3D.at(index).x() + 1e-8)
            {
                xMin = listVector3D.at(index).x();
            }
            if(xMax < listVector3D.at(index).x() - 1e-8)
            {
                xMax = listVector3D.at(index).x();
            }
            if(yMin > listVector3D.at(index).y() + 1e-8)
            {
                yMin = listVector3D.at(index).y();
            }
            if(yMax < listVector3D.at(index).y() - 1e-8)
            {
                yMax = listVector3D.at(index).y();
            }
            if(zMin > listVector3D.at(index).z() + 1e-8)
            {
                zMin = listVector3D.at(index).z();
            }
            if(zMax < listVector3D.at(index).z() - 1e-8)
            {
                zMax = listVector3D.at(index).z();
            }
        }

    }
    // 新增資料(自動沖掉之前的資料)
    _pScatter3DSeries->dataProxy()->addItems(data);
    // 計算範圍 x軸範圍要大於等於y軸
    if(xMax - xMin < yMax - yMin)
    {
        xMax = xMin + (yMax - yMin);
    }

    _pQ3DScatter->axisX()->setRange(xMin, xMax);
    _pQ3DScatter->axisY()->setRange(yMin, yMax);
    _pQ3DScatter->axisZ()->setRange(zMin, zMax);
}

void Q3dScatterWidget::initControl()
{
    _pQ3DScatter = new Q3DScatter();
    _pContainer = QWidget::createWindowContainer(_pQ3DScatter, this);

    // 設定軸文字
    {
        _pQ3DScatter->axisX()->setTitle("X");
        _pQ3DScatter->axisY()->setTitle("Y");
        _pQ3DScatter->axisZ()->setTitle("Z");
    }
    // 設定軸範圍
    {
//        _pQ3DScatter->axisX()->setRange(0, 10);
//        _pQ3DScatter->axisY()->setRange(0, 10);
//        _pQ3DScatter->axisZ()->setRange(0, 10);
    }

    // 生成一個曲線
    _pScatter3DSeries = new QScatter3DSeries(_pQ3DScatter);
    // 設定渲染平滑
    _pScatter3DSeries->setMeshSmooth(true);

    // 檢視新增該曲線
    _pQ3DScatter->addSeries(_pScatter3DSeries);

    // 設定陰影質量
    _pQ3DScatter->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftLow);
    // 設定視角
    _pQ3DScatter->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricLeft);
    // 設定子網格
    _pQ3DScatter->activeTheme()->setGridEnabled(true);

#if 1
    // 新增模擬資料
    QScatterDataArray data;
    data << QVector3D(1, 1,1) << QVector3D(1, 1, 2) << QVector3D(1, 1, 3)
         << QVector3D(1, 2,1) << QVector3D(1, 2, 2) << QVector3D(1, 2, 3)
         << QVector3D(1, 3,1) << QVector3D(1, 3, 2) << QVector3D(1, 3, 3);
    // 新增資料(自動沖掉之前的資料)
    _pScatter3DSeries->dataProxy()->addItems(data);
#endif

#if 1
    // 模擬
    QList<QVector3D> listVector3D;
#if 0
    listVector3D <<  QVector3D(5, 1,1) << QVector3D(5, 1, 2) << QVector3D(5, 1, 3)
                 << QVector3D(5, 2,1) << QVector3D(5, 2, 2) << QVector3D(5, 2, 3)
                 << QVector3D(5, 3,1) << QVector3D(5, 3, 2) << QVector3D(5, 3, 3);
#else
    listVector3D << QVector3D(1, 1,1) << QVector3D(1, 1, 2) << QVector3D(1, 1, 3)
                 << QVector3D(1, 2,1) << QVector3D(1, 2, 2) << QVector3D(1, 2, 3)
                 << QVector3D(1, 3,1) << QVector3D(1, 3, 2) << QVector3D(1, 3, 3);
#endif
    // 新增資料
    setData(listVector3D);
#endif
}

void Q3dScatterWidget::resizeEvent(QResizeEvent *event)
{
    if(_pContainer)
    {
        _pContainer->setGeometry(rect());
    }
}
 

工程模板

  在這裡插入圖片描述

 

入坑

入坑一:找不到Q3DScatter類

問題

  在這裡插入圖片描述

原因

  有名稱空間。

解決

using namespace QtDataVisualization;