• Qt开发技术:Q3D图表开发笔记(三):Q3DSurface三维曲面图介绍、Demo以及代码详解


    前言

      qt提供了q3d进行三维开发,虽然这个框架没有得到大量运用也不是那么成功,性能上也有很大的欠缺,但是普通的点到为止的应用展示还是可以的。
      其中就包括华丽绚烂的三维图表,数据量不大的时候是可以使用的。
      前面介绍了基础的q3d散点图、柱状图,本篇介绍基础的三维曲面图。

     

    Demo:Q3DSurface散点图演示效果

      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

     

    Q3D提供的三维图表

      依赖QtDataVisualization。在安装qt的时候要选择安装QtDataVisualization模块。

    Q3DScatter散点图

      Q3D的散点图,性能大约支撑1000个点可以不卡顿,具体依赖pc,1000个点是什么 概念,可以理解为:10x10x10的区域,每个区域一个数据点。
      在这里插入图片描述

    Q3DBars柱状图

      Q3D的柱状图,性能跟散点图类似。
       在这里插入图片描述

    Q3DSurface平面凹凸图,平面纹理图,平面曲线图

      Q3D的柱状图,性能跟散点图类似。
      在这里插入图片描述

     

    Q3DSurface平面曲线图

    简介

      Q3DSurface类提供了渲染3D曲面图的方法。该类使开发人员能够渲染3D表面图,并通过自由旋转场景来查看它们。可以通过QSurface3DSeries控制曲面的视觉财产,例如绘制模式和着色。
      Q3DSurface通过在用户用鼠标左键点击的数据点上显示高亮显示的球(当使用默认输入处理程序时)或通过QSurface3DSeries进行选择来支持选择。选择指针附带一个标签,在默认情况下,该标签显示数据点的值和点的坐标。
    轴上显示的值范围和标签格式可以通过QValue3DAxis进行控制。
      要旋转图形,请按住鼠标右键并移动鼠标。缩放是使用鼠标滚轮完成的。两者都假设默认的输入处理程序正在使用中。
      如果没有将任何轴明确设置为Q3DSurface,则会创建不带标签的临时默认轴。这些默认轴可以通过轴访问器进行修改,但只要明确设置了方向的任何轴,该方向的默认轴就会被破坏。

    构造最小Q3D平面曲线图

      首先,构造Q3D曲面。由于在本例中,我们将图形作为顶级窗口运行,因此需要清除Qt::FramelessWindowHint标志,该标志在默认情况下设置:

    Q3DSurface surface; 
    surface.setFlags(surface.flags() ^ Qt::FramelessWindowHint);
    

      现在Q3DSurface已准备好接收要渲染的数据。创建数据元素以接收值:

    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();
    

      创建和显示此图所需的完整代码为:

    #include 
    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();
    }
    

      运行效果:
      在这里插入图片描述

      场景可以被旋转、放大,并且可以选择一个项目来查看其位置,但在这个最小的代码示例中不包括其他交互。

     

    Q3Ddemo构建流程解析

    步骤一:确认安装QtDataVisualization模块

      如何确认,则是在帮助文件中查看是否有Q3dscatter类。一般是安装了模块才会有对应的帮助文件。没有则重新安装qt或者单独安装该模块。
      在这里插入图片描述

    步骤二:工程配置文件中加入模块

      Q3d是在数据可视化模块中,需要在pro或者pri配置文件中添加。

    QT += datavisualization
    

      在这里插入图片描述

    步骤三:添加使用到的头文件

      使用到Q3DBar相关类中添加头文件,主要使用到Q3DBar、QBar3DSeries、QBarDataRow等等。

    #include 
    #include 
    #include 
    #include 
    

      在这里插入图片描述

    步骤四:添加命名空间

      这时候还是无法使用对应的类,需要添加命名空间才行:

    using namespace QtDataVisualization;
    

      在这里插入图片描述

    步骤五:Q3D的图标基础构建框架

      下面是包含注释的Q3DSurface基础构建流程(注意轴的显示,查看末尾“入坑一”,注意数据的成面规则,查看“入坑二”

    _pQ3DSurface = new Q3DSurface();
    _pContainer = QWidget::createWindowContainer(_pQ3DSurface, this);
    // 设置轴文本
    {
        // 注意笛卡尔坐标
        _pQ3DSurface->axisX()->setTitle("经度(°)");
        _pQ3DSurface->axisX()->setTitleVisible(true);
        _pQ3DSurface->axisY()->setTitle("高度(m)");
        _pQ3DSurface->axisY()->setTitleVisible(true);
        _pQ3DSurface->axisZ()->setTitle("纬度(°)");
        _pQ3DSurface->axisZ()->setTitleVisible(true);
    }
    // 设置轴范围
    {
        // 注意笛卡尔坐标
        _pQ3DSurface->axisX()->setRange(0, 359);
        _pQ3DSurface->axisY()->setRange(0, 100);
        _pQ3DSurface->axisZ()->setRange(0, 359);
    }
    
    // 生成一个曲线
    _pSurface3DSeries = new QSurface3DSeries(_pQ3DSurface);
    // 设置渲染平滑
    _pSurface3DSeries->setMeshSmooth(true);
    // 设置渲染模式
    //   DrawWireframe           : 绘制栅格
    //   DrawSurface             : 绘制表面
    //   DrawSurfaceAndWireframe : 绘制栅格和图表面
    _pSurface3DSeries->setDrawMode(QSurface3DSeries::DrawSurface);
    
    // 视图添加该曲线
    _pQ3DSurface->addSeries(_pSurface3DSeries);
    // 设置阴影质量
    _pQ3DSurface->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftLow);
    // 设置视角
    _pQ3DSurface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricLeft);
    // 设置子网格
    _pQ3DSurface->activeTheme()->setGridEnabled(true);
    
    #if 1
    // 添加模拟数据
    QSurfaceDataArray *pSurfaceDataArray = new QSurfaceDataArray;
    #if 1
    
    #if 1
    // 这是 z 纬度
    for(int n = 0; n < 360; n++)
    {
        QSurfaceDataRow *pSurfaceDataRow  = new QSurfaceDataRow;
        // 这是 x 经度
        for(int m = 0; m < 360; m++)
        {
           // 注意与笛卡尔坐标进行映射
           *pSurfaceDataRow << QVector3D(m, n / 7 + m / 7, n);
        }
        *pSurfaceDataArray << pSurfaceDataRow;
    }
    #else
    for(int n = 0; n < 360; n++)
    {
        QSurfaceDataRow *pSurfaceDataRow  = new QSurfaceDataRow;
        // 这是 x 经度
        for(int m = 0; m < 360; m++)
        {
           // 注意与笛卡尔坐标进行映射
           *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;
    // 行与行之间,要形成一个四点成面
    *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
    // 添加数据(自动冲掉之前的数据)
    _pSurface3DSeries->dataProxy()->resetArray(pSurfaceDataArray);
    #endif
    _pQ3DSurface->addSeries(_pSurface3DSeries);
    _pQ3DSurface->show();
    
     

    Demo源码

    Q3dSurfaceWidget.h

    #ifndef Q3DSURFACEWIDGET_H
    #define Q3DSURFACEWIDGET_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    using namespace QtDataVisualization;
    
    namespace Ui {
    class Q3dSurfaceWidget;
    }
    
    class Q3dSurfaceWidget : public QWidget
    {
        Q_OBJECT
    
    public:
        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柱状图数据
    };
    
    #endif // Q3DSURFACEWIDGET_H
    

    Q3dSurfaceWidget.cpp

    #include "Q3dSurfaceWidget.h"
    #include "ui_Q3dSurfaceWidget.h"
    #include 
    
    
    #include 
    #include 
    //#define LOG qDebug()<<__FILE__<<__LINE__
    //#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
    //#define LOG qDebug()<<__FILE__<<__LINE__<
    //#define LOG qDebug()<<__FILE__<<__LINE__<
    #define LOG qDebug()<<__FILE__<<__LINE__<"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);
    
        // 设置轴文本
        {
            // 注意笛卡尔坐标
            _pQ3DSurface->axisX()->setTitle("经度(°)");
            _pQ3DSurface->axisX()->setTitleVisible(true);
            _pQ3DSurface->axisY()->setTitle("高度(m)");
            _pQ3DSurface->axisY()->setTitleVisible(true);
            _pQ3DSurface->axisZ()->setTitle("纬度(°)");
            _pQ3DSurface->axisZ()->setTitleVisible(true);
        }
        // 设置轴范围
        {
            // 注意笛卡尔坐标
            _pQ3DSurface->axisX()->setRange(0, 359);
            _pQ3DSurface->axisY()->setRange(0, 100);
            _pQ3DSurface->axisZ()->setRange(0, 359);
        }
    
        // 生成一个曲线
        _pSurface3DSeries = new QSurface3DSeries(_pQ3DSurface);
        // 设置渲染平滑
        _pSurface3DSeries->setMeshSmooth(true);
        // 设置渲染模式
        //   DrawWireframe           : 绘制栅格
        //   DrawSurface             : 绘制表面
        //   DrawSurfaceAndWireframe : 绘制栅格和图表面
        _pSurface3DSeries->setDrawMode(QSurface3DSeries::DrawSurface);
    
        // 视图添加该曲线
        _pQ3DSurface->addSeries(_pSurface3DSeries);
        // 设置阴影质量
        _pQ3DSurface->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftLow);
        // 设置视角
        _pQ3DSurface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricLeft);
        // 设置子网格
        _pQ3DSurface->activeTheme()->setGridEnabled(true);
    
    #if 1
        // 添加模拟数据
        QSurfaceDataArray *pSurfaceDataArray = new QSurfaceDataArray;
    #if 1
    
    #if 1
        // 这是 z 纬度
        for(int n = 0; n < 360; n++)
        {
            QSurfaceDataRow *pSurfaceDataRow  = new QSurfaceDataRow;
            // 这是 x 经度
            for(int m = 0; m < 360; m++)
            {
               // 注意与笛卡尔坐标进行映射
               *pSurfaceDataRow << QVector3D(m, n / 7 + m / 7, n);
            }
            *pSurfaceDataArray << pSurfaceDataRow;
        }
    #else
        for(int n = 0; n < 360; n++)
        {
            QSurfaceDataRow *pSurfaceDataRow  = new QSurfaceDataRow;
            // 这是 x 经度
            for(int m = 0; m < 360; m++)
            {
    
               // 注意与笛卡尔坐标进行映射
               *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;
        // 行与行之间,要形成一个四点成面
        *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
    
        // 添加数据(自动冲掉之前的数据)
        _pSurface3DSeries->dataProxy()->resetArray(pSurfaceDataArray);
    
    #endif
        _pQ3DSurface->addSeries(_pSurface3DSeries);
        _pQ3DSurface->show();
    
    }
    
    void Q3dSurfaceWidget::resizeEvent(QResizeEvent *event)
    {
        if(_pContainer)
        {
            _pContainer->setGeometry(rect());
        }
    }
    
     

    工程模板v1.2.0

      在这里插入图片描述

     

    入坑

    入坑一:xyz坐标系不对

    问题

      x精度,y维度,z高度(海拔高度)映射错误
      在这里插入图片描述

    原因

      x,y,z实际是遵循笛卡尔坐标集

    解决

      先理解坐标,然后z轴方向,数据也要替换(按照x,y,z来排列,改为x,z,y)
     &emso;在这里插入图片描述

    入坑二:曲面显示不对

    问题

      数据显示映射错误

    原因

      点成面,需要遵循4点成面的规则,和opengl相关3点成面和4点成面的原理类似。
      在这里插入图片描述

      在这里插入图片描述
      

    解决

      相邻行与行之间,要形成面,修改后展示如下:

      在这里插入图片描述
      在这里插入图片描述

  • 相关阅读:
    Linux执行jps命令的时候报错:-bash: jps: command not found
    AcWing 93. 递归实现组合型枚举
    关于QT6实现翻金币小程序的避坑指南
    问题 A: 求最长公共子串(串)
    AAD基础知识(identity/token/PRT)
    第一位女性商业程序员玛丽库姆斯去世,享年 93 岁
    Sleuth+Zipkin链路追踪
    【RabbitMQ】——入门&安装
    从一道面试题来谈谈Golang中的 ==
    OpenHarmony SystemUI开发记录
  • 原文地址:https://www.cnblogs.com/qq21497936/p/17336634.html