【Qt】 Data Visualization
三维数据可视化
- 三维柱状图
- 三维图的创建
- 程序截图
- 示例代码
- 三维散点图
- 三维图创建
- 程序截图
- 示例代码
- 三维曲面图
- 三维图创建
- 程序截图
- 示例代码
Data Visualization 是 Qt 中的一个三维数据可视化模块,可用于绘制三维柱状图、三维散点图和三维曲面。与 Charts 模块类似,Data Visualization 模块也是基于图形/视图架构的。DataVisualization 的功能虽然不能和一些专业的三维图形类库(如 VTK)的相提并论,但是它操作简单、易用,对于简单的三维数据显示是比较实用的。
三维柱状图
三维图的创建
- 创建Q3DBars对象后,必须为其创建一个QWidget对象作为容器,程序中的代码是:
graph3D = new Q3DBars();
graphContainer = QWidget::createWindowContainer(graph3D, this); //创建三维图的容器
这里使用了QWidget的静态函数createWindowContainer(),这个函数的原型定义如下:
QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags())
window 是一个QWindow类对象,parent 是父容器对象,flags是窗口标志。函数的返回值是一个QWidget对象指针。
这个函数的功能是为 QWindow 类型的对象创建特殊的 QWidget 对象来作为容器,因为
QWindow 类型的对象不能直接放在普通的QWidget对象上。Q3DBars 的上层父类是QWindow,所以必须用这个静态函数为其创建一个容器。
- 场景和相机。Q3DBars的父类QAbstract3DGraph定义了三维图的一些基本元素和属性,包括场景、主题、选择模式等。QAbstract3DGraph 的函数scene()返回一个 Q3DScene 对象,它是三维图的场景对象。在一个三维场景里必须有相机(camera)和光源(light),创建三维图时会自动创建默认的相机和光源。
- 函数Q3DScene::activeCamera()返回场景当前的相机对象,是Q3DCamera类型。相机类似于人的眼睛,通过对相机位置的控制可以实现图形的旋转、缩放和平移。
- 函数Q3DScene::activeLight()返回场景当前的光源对象,是Q3DLight类型。
- 坐标轴。Q3DBars用3个函数分别设置3个坐标轴,这3个函数原型定义如下:
void Q3DBars::setValueAxis(QValue3DAxis *axis) //设置数值坐标轴
void Q3DBars::setRowAxis(QCategory3DAxis *axis) //设置行坐标轴
void Q3DBars::setColumnAxis(QCategory3DAxis *axis) //设置列坐标轴
函数setValueAxis()设置一个 QValue3DAxis 类型的对象作为数值坐标轴,也就是垂直方向的坐标轴。
函数setRowAxis()和 setColumnAxis()分别设置一个 QCategory3DAxis 类型的对象作为行坐标轴和列坐标轴,也就是水平方向的两个坐标轴。
- QBar3DSeries 序列。与Q3DBars 结合使用的序列类是QBar3DSeries,程序创建了序列对象series,设置棒柱形状和标签格式后添加到三维图中。QBar3DSeries的函数setItemLabelFormat()用于设置棒柱被点击后标签显示的格式,程序中的设置语句为:
series->setItemLabelFormat("(@rowLabel,@colLabel): %d");
- 数据代理。与 QBar3DSeries 配套的数据代理类是 QBarDataProxy,它用于存储和管理在QBar3DSeries 序列中显示的数据。在三维柱状图中,每一个棒柱都要用一个QBarDataItem对象表示。
QBarDataProxy 在添加、插入或删除棒柱时是以行为单位管理的。一行的棒柱数据用一个QBarDataRow 类型管理,所有行的棒柱数据用QBarDataArray类型管理。这两个类型其实不是类,而是列表类型,在Qt C++的源代码中,它们的定义如下:
typedef QList<QBarDataItem> QBarDataRow;
typedef QList<QBarDataRow *> QBarDataArray;
QBarDataRow 就是 QBarDataItem 对象的列表,QBarDataArray 就是 QBarDataRow 对象指针列表。程序中每创建一个QBarDataItem类型的棒柱数据项dataItem,就将dataItem先添加到棒柱数据行dataRow里;创建完一行棒柱数据后,再将dataRow添加到棒柱数据数组dataArray里。
- QBarDataItem 代码
#ifndef QBARDATAITEM_H
#define QBARDATAITEM_H#include <QtDataVisualization/qdatavisualizationglobal.h>QT_BEGIN_NAMESPACE_DATAVISUALIZATIONclass QBarDataItemPrivate;class QT_DATAVISUALIZATION_EXPORT QBarDataItem
{
public:QBarDataItem();QBarDataItem(float value);QBarDataItem(float value, float angle);QBarDataItem(const QBarDataItem &other);~QBarDataItem();QBarDataItem &operator=(const QBarDataItem &other);inline void setValue(float val) { m_value = val; }inline float value() const { return m_value; }inline void setRotation(float angle) { m_angle = angle; }inline float rotation() const { return m_angle; }protected:void createExtraData();QBarDataItemPrivate *d_ptr;private:float m_value;float m_angle;
};QT_END_NAMESPACE_DATAVISUALIZATION#endif
QBar3DSeries 序列被创建后,就会自动创建数据代理,QBar3DSeries::dataProxy()返回当前的QBarDataProxy 数据代理对象。QBarDataProxy 有接口函数用于设置行坐标标签、列坐标标签和棒柱数据数组,这3个函数的原型定义如下:
void QBarDataProxy::setRowLabels(const QStringList &labels) //设置行坐标标签
void QBarDataProxy::setColumnLabels(const QStringList &labels) //设置列坐标标签
void QBarDataProxy::resetArray(QBarDataArray *newArray) //设置棒柱数据数组
在数据代理里设置的行坐标标签与每一行数据是对应的,如果删除了某一行的数据,这个行坐标标签也会被删除。在QBarDataProxy里无法删除一列棒柱数据项或单独删除某个棒柱数据项。
- QBar3DSeries 的 selectedBarChanged()信号。在三维柱状图上选择的当前棒柱变化时,序列对象会发射selectedBarChanged()信号,其函数原型定义如下:
void QBar3DSeries::selectedBarChanged(const QPoint &position)
参数position 表示当前选中棒柱的坐标,position.x()表示棒柱的行号,position.y()表示列号。 为此信号关联自定义槽函数do_barSelected()后,当点击一个棒柱时会在状态栏里显示其信息。有棒柱被选中时,通过QBarDataProxy 的itemAt()函数可以获取这个棒柱关联的QBarDataItem 对象,再通过函数QBarDataItem::value()可以获取棒柱的数值
程序截图
示例代码
- h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include <QtDataVisualization>
#include "q3dbars.h"
#include "qbar3dseries.h"using namespace QtDataVisualization;QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTprivate:QWidget *graphContainer; //三维图的容器Q3DBars *graph3D; //三维图QBar3DSeries *series; //序列void iniGraph3D(); //创建三维图public:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void do_barSelected(const QPoint &position);//void on_chkBoxGrid_clicked(bool checked);void on_chkBoxSmooth_clicked(bool checked);void on_chkBoxReflection_clicked(bool checked);void on_chkBoxAxisTitle_clicked(bool checked);void on_chkBoxAxisBackground_clicked(bool checked);void on_chkBoxReverse_clicked(bool checked);void on_chkBoxBackground_clicked(bool checked);void on_spinFontSize_valueChanged(int arg1);void on_cBoxTheme_currentIndexChanged(int index);// void on_btnBarColor_clicked();void on_chkBoxItemLabel_clicked(bool checked);void on_cBoxBarStyle_currentIndexChanged(int index);void on_cBoxSelectionMode_currentIndexChanged(int index);void on_comboCamera_currentIndexChanged(int index);void on_sliderH_valueChanged(int value);void on_sliderV_valueChanged(int value);void on_sliderZoom_valueChanged(int value);void on_actBar_ChangeValue_triggered();void on_actSeries_BaseColor_triggered();void on_actData_Add_triggered();void on_actData_Insert_triggered();void on_actData_Delete_triggered();void on_btnMoveDown_clicked();void on_btnMoveUp_clicked();void on_btnMoveLeft_clicked();void on_btnMoveRight_clicked();void on_btnResetCamera_clicked();void on_actRedraw_triggered();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
- cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"#include <QSplitter>#include <QColorDialog>
#include <QInputDialog>
#include <QVector3D>void MainWindow::iniGraph3D()
{graph3D = new Q3DBars();graphContainer = QWidget::createWindowContainer(graph3D, this); //Q3DBars继承自QWindowgraph3D->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFrontHigh); //设置视角//创建坐标轴QValue3DAxis *axisV=new QValue3DAxis; //数值坐标axisV->setTitle("销量");axisV->setTitleVisible(true);axisV->setLabelFormat("%d");graph3D->setValueAxis(axisV); //设置数值坐标轴QCategory3DAxis *axisRow=new QCategory3DAxis;axisRow->setTitle("row axis");// QStringList rowLabs; //行坐标标签// rowLabs << "Week1" << "Week2"<<"Week3";// axisRow->setLabels(rowLabs);axisRow->setTitleVisible(true);graph3D->setRowAxis(axisRow); //设置行坐标轴QCategory3DAxis *axisCol=new QCategory3DAxis;axisCol->setTitle("column axis");// QStringList colLabs; //列坐标标签// colLabs << "Mon" << "Tue" << "Wed" << "Thur"<<"Fri";// axisCol->setLabels(colLabs);axisCol->setTitleVisible(true);graph3D->setColumnAxis(axisCol); //设置列坐标轴//创建序列series = new QBar3DSeries;series->setMesh(QAbstract3DSeries::MeshCylinder); //棒柱形状series->setItemLabelFormat("(@rowLabel,@colLabel): %d"); //项的标签显示格式graph3D->addSeries(series);//设置数据代理的数据QBarDataArray *dataArray = new QBarDataArray; //棒柱数据数组,typedef QList<QBarDataRow *> QBarDataArray;for (int i=0; i<3; i++) //行{QBarDataRow *dataRow= new QBarDataRow; //棒柱数据行, typedef QList<QBarDataItem> QBarDataRow;for(int j=1; j<=5; j++) //列{quint32 value=QRandomGenerator::global()->bounded(5,15); //随机整数[5,15)QBarDataItem *dataItem= new QBarDataItem; //创建棒柱数据对象dataItem->setValue(value); //设置棒柱的数据dataRow->append(*dataItem); //添加到棒柱数据行}dataArray->append(dataRow); //棒柱数据数组添加一个棒柱数据行}QStringList rowLabs; //行坐标标签rowLabs << "Week1" << "Week2"<<"Week3";series->dataProxy()->setRowLabels(rowLabs); //设置数据代理的行标签QStringList colLabs; //列坐标标签colLabs << "Mon" << "Tue" << "Wed" << "Thur"<<"Fri";series->dataProxy()->setColumnLabels(colLabs);//设置数据代理的列标签series->dataProxy()->resetArray(dataArray); //重设数据代理的数据connect(series,&QBar3DSeries::selectedBarChanged,this,&MainWindow::do_barSelected);
}//void MainWindow::resetData()
//{
// QBarDataProxy *dataProxy=new QBarDataProxy; //新建数据代理
// int rowCount= series->dataProxy()->rowCount(); //数据代理的行数
// for (int i=0; i<rowCount; i++) //行
// {
// QBarDataRow *dataRow= new QBarDataRow; //typedef QList<QBarDataItem> QBarDataRow;
// for(int j=1; j<=5; j++) //列
// {
// quint32 value=QRandomGenerator::global()->bounded(5,15);
// QBarDataItem *dataItem= new QBarDataItem; //数据项
// dataItem->setValue(value);
// dataRow->append(*dataItem);
// }
// QString rowStr=QString("第%1周").arg(i+1);
// dataProxy->addRow(dataRow, rowStr); //添加行数据和标签
// }// QStringList colLabs=series->dataProxy()->columnLabels(); //原来的列坐标轴标签
// dataProxy->setColumnLabels(colLabs);// series->dataProxy()->resetArray(); //清除数据代理和坐标轴标签,必须清除,否则不能重新设置坐标轴标签
// series->setDataProxy(dataProxy); //重新设置数据代理,会删除之前的数据代理
//}//void MainWindow::redrawGraph3D()
//{
// QBarDataProxy *dataProxy=series->dataProxy();
// int rowCount=dataProxy->rowCount();
// int colCount=dataProxy->columnLabels().count();
// for (int i=0; i<rowCount; i++) //行
// {
// for(int j=0; j<colCount; j++) //列
// {
// QBarDataItem dataItem= *(dataProxy->itemAt(i,j));
// quint32 value=QRandomGenerator::global()->bounded(5,15);
// dataItem.setValue(value);
// dataProxy->setItem(i,j,dataItem);
// }
// }
//}MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);ui->sliderZoom->setRange(10,500); //设置slider的数据范围ui->sliderH->setRange(-180,180); //水平旋转角度范围ui->sliderV->setRange(-180,180); //垂直旋转角度范围iniGraph3D(); //创建三维柱状图,会创建一个容器对象graphContainerQSplitter *splitter=new QSplitter(Qt::Horizontal);splitter->addWidget(ui->frameSetup); //左侧控制面板splitter->addWidget(graphContainer); //右侧三维图this->setCentralWidget(splitter); //设置主窗口中心组建
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::do_barSelected(const QPoint &position)
{if (position.x()<0 || position.y()<0) //必须加此判断{ui->actBar_ChangeValue->setEnabled(false);return;}ui->actBar_ChangeValue->setEnabled(true);const QBarDataItem *bar =series->dataProxy()->itemAt(position);QString info=QString::asprintf("选中的棒柱,Row=%d, Column=%d, Value=%.1f",position.x(),position.y(),bar->value());ui->statusBar->showMessage(info);
}void MainWindow::on_chkBoxGrid_clicked(bool checked)
{ //显示背景网格 CheckBoxgraph3D->activeTheme()->setGridEnabled(checked);
}void MainWindow::on_chkBoxSmooth_clicked(bool checked)
{ //光滑效果 CheckBoxseries->setMeshSmooth(checked);
}void MainWindow::on_chkBoxReflection_clicked(bool checked)
{//显示倒影 CheckBoxgraph3D->setReflection(checked);
}void MainWindow::on_chkBoxAxisTitle_clicked(bool checked)
{//显示轴标题 CheckBoxgraph3D->valueAxis()->setTitleVisible(checked);graph3D->rowAxis()->setTitleVisible(checked);graph3D->columnAxis()->setTitleVisible(checked);
}void MainWindow::on_chkBoxAxisBackground_clicked(bool checked)
{//显示轴标签背景 CheckBoxgraph3D->activeTheme()->setLabelBackgroundEnabled(checked);
}void MainWindow::on_chkBoxReverse_clicked(bool checked)
{//数值坐标轴反向 CheckBoxgraph3D->valueAxis()->setReversed(checked);
}void MainWindow::on_chkBoxBackground_clicked(bool checked)
{//显示背景 CheckBoxgraph3D->activeTheme()->setBackgroundEnabled(checked);
}void MainWindow::on_spinFontSize_valueChanged(int arg1)
{//轴标签字体大小QFont font = graph3D->activeTheme()->font();font.setPointSize(arg1);graph3D->activeTheme()->setFont(font);
}void MainWindow::on_cBoxTheme_currentIndexChanged(int index)
{//三维图主题Q3DTheme *currentTheme = graph3D->activeTheme();currentTheme->setType(Q3DTheme::Theme(index));
}void MainWindow::on_chkBoxItemLabel_clicked(bool checked)
{ //显示选中棒柱的标签 CheckBoxseries->setItemLabelFormat("value at (@rowLabel,@colLabel): %.1f");series->setItemLabelVisible(checked);
}void MainWindow::on_cBoxBarStyle_currentIndexChanged(int index)
{ //棒柱样式 ComboBoxQAbstract3DSeries::Mesh aMesh;aMesh=QAbstract3DSeries::Mesh(index+1); //0=MeshUserDefinedseries->setMesh(aMesh);
}void MainWindow::on_cBoxSelectionMode_currentIndexChanged(int index)
{//选择模式graph3D->setSelectionMode(QAbstract3DGraph::SelectionFlags(index));
}void MainWindow::on_comboCamera_currentIndexChanged(int index)
{ //预设视角Q3DCamera::CameraPreset cameraPos=Q3DCamera::CameraPreset(index);graph3D->scene()->activeCamera()->setCameraPreset(cameraPos);
}void MainWindow::on_sliderH_valueChanged(int value)
{//水平旋转graph3D->scene()->activeCamera()->setXRotation(value);
}void MainWindow::on_sliderV_valueChanged(int value)
{//垂直旋转graph3D->scene()->activeCamera()->setYRotation(value);
}void MainWindow::on_sliderZoom_valueChanged(int value)
{//缩放graph3D->scene()->activeCamera()->setZoomLevel(value);
}void MainWindow::on_actBar_ChangeValue_triggered()
{QPoint position=series->selectedBar();if (position.x()<0 || position.y()<0) //必须加此判断return;QBarDataItem bar=*(series->dataProxy()->itemAt(position));qreal value=bar.value(); //原来的值bool ok;value = QInputDialog::getInt(this, "输入数值","更改棒柱数值" , value, 0, 50, 1, &ok);if (ok){bar.setValue(value);series->dataProxy()->setItem(position, bar);}
}void MainWindow::on_actSeries_BaseColor_triggered()
{//设置序列基本颜色QColor color=series->baseColor();color=QColorDialog::getColor(color);if (color.isValid())series->setBaseColor(color);
}void MainWindow::on_actData_Add_triggered()
{//添加行QString rowLabel = QInputDialog::getText(this, "输入字符串","请输入行标签");if (rowLabel.isEmpty())return;QBarDataRow *dataRow= new QBarDataRow; //棒柱数据行for(int j=1; j<=5; j++) //固定5列{quint32 value=QRandomGenerator::global()->bounded(15,25);QBarDataItem *dataItem= new QBarDataItem;dataItem->setValue(value);dataRow->append(*dataItem);}series->dataProxy()->addRow(dataRow, rowLabel); //添加棒柱数据行和标签
}void MainWindow::on_actData_Insert_triggered()
{ //插入行QString rowLabel = QInputDialog::getText(this, "输入字符串","请输入行标签");if (rowLabel.isEmpty())return;QPoint position=series->selectedBar();int index=position.x(); //当前行号if (index<0)index=0;QBarDataRow *dataRow= new QBarDataRow; //棒柱数据行for(int j=1; j<=5; j++) //固定5列{quint32 value=QRandomGenerator::global()->bounded(20,30);QBarDataItem *dataItem= new QBarDataItem;dataItem->setValue(value);dataRow->append(*dataItem);}series->dataProxy()->insertRow(index, dataRow, rowLabel); //插入棒柱数据行和标签
}//删除行
void MainWindow::on_actData_Delete_triggered()
{QPoint position=series->selectedBar();if (position.x()<0 || position.y()<0)return;int rowIndex=position.x(); //当前行号int removeCount=1; //删除的行数int removeLabels=true; //是否删除行标签series->dataProxy()->removeRows(rowIndex, removeCount, removeLabels);
}void MainWindow::on_btnMoveDown_clicked()
{//下移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal z=target3D.z();target3D.setZ(z+0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveUp_clicked()
{//上移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal z=target3D.z();target3D.setZ(z-0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveLeft_clicked()
{//左移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal x=target3D.x();target3D.setX(x+0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveRight_clicked()
{//右移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal x=target3D.x();target3D.setX(x-0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}// 复位到FrontHigh视角
void MainWindow::on_btnResetCamera_clicked()
{graph3D->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFrontHigh);
}void MainWindow::on_actRedraw_triggered()
{QBarDataProxy *dataProxy=new QBarDataProxy; //新建数据代理int rowCount= series->dataProxy()->rowCount(); //数据代理的行数for (int i=0; i<rowCount; i++) //行{QBarDataRow *dataRow= new QBarDataRow; //棒柱数据行for(int j=1; j<=5; j++) //列{quint32 value=QRandomGenerator::global()->bounded(5,15);QBarDataItem *dataItem= new QBarDataItem; //数据项dataItem->setValue(value);dataRow->append(*dataItem);}QString rowStr=QString("第%1周").arg(i+1);dataProxy->addRow(dataRow, rowStr); //添加行数据和标签}QStringList colLabs=series->dataProxy()->columnLabels(); //原来的列坐标轴标签dataProxy->setColumnLabels(colLabs);series->dataProxy()->resetArray(); //清除数据代理和坐标轴标签,必须清除,否则不能重新设置坐标轴标签series->setDataProxy(dataProxy); //重新设置数据代理,会删除之前的数据代理
}
三维散点图
三维图创建
数据代理类 QScatterDataProxy。与三维散点图序列 QScatter3DSeries 配套的数据代理类是QScatterDataProxy,它存储和管理的基本元素是QScatterDataItem对象。序列中每个散点是一个QScatterDataItem 对象,它存储了空间点的三维坐标和旋转角度。
QScatterDataProxy 的函数 resetArray()用于重设数据内容,其函数原型定义如下:
void QScatterDataProxy::resetArray(QScatterDataArray *newArray)
参数newArray 是 QScatterDataArray 类型指针,而 QScatterDataArray 就是 QScatterDataItem对象的列表,Qt源程序中对其的定义如下:
typedef QList<QScatterDataItem> QScatterDataArray;
所以,QScatterDataProxy 存储的就是一系列QScatterDataItem 散点对象。在本示例中,“墨西哥草帽”图的数据点在水平面上是均匀分布的,相当于在水平面上做了网格划分,每个网格里面都有一个数据点。但是散点图并不要求数据点规则分布,只是根据每个散点的三维坐标和旋转方向绘图。
程序截图
示例代码
- .h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include <QtDataVisualization>
using namespace QtDataVisualization;QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTprivate:QWidget *graphContainer; //图表的容器Q3DScatter *graph3D; //散点图QScatter3DSeries *series; //散点序列void iniGraph3D(); //初始化绘图public:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void do_itemSelected(int index); //与 series 的selectedItemChanged信号关联void on_chkBoxGrid_clicked(bool checked);void on_chkBoxSmooth_clicked(bool checked);void on_chkBoxAxisTitle_clicked(bool checked);void on_chkBoxAxisBackground_clicked(bool checked);void on_chkBoxReverse_clicked(bool checked);void on_chkBoxBackground_clicked(bool checked);void on_spinFontSize_valueChanged(int arg1);void on_cBoxTheme_currentIndexChanged(int index);// void on_btnBarColor_clicked();void on_chkBoxItemLabel_clicked(bool checked);void on_cBoxBarStyle_currentIndexChanged(int index);void on_cBoxSelectionMode_currentIndexChanged(int index);void on_spinItemSize_valueChanged(double arg1);void on_chkBoxShadow_clicked(bool checked);// void on_comboBox_currentIndexChanged(int index);void on_sliderH_valueChanged(int value);void on_sliderV_valueChanged(int value);// void on_btnUp_clicked();void on_sliderZoom_valueChanged(int value);void on_comboCamera_currentIndexChanged(int index);void on_actSeries_BaseColor_triggered();void on_actPoint_ChangeValue_triggered();void on_actData_Delete_triggered();void on_actData_Add_triggered();void on_btnResetCamera_clicked();void on_btnMoveLeft_clicked();void on_btnMoveRight_clicked();void on_btnMoveUp_clicked();void on_btnMoveDown_clicked();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
- .cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"#include <QSplitter>
#include <QInputDialog>
#include <QColorDialog>
#include <QMessageBox>void MainWindow::iniGraph3D()
{graph3D = new Q3DScatter();graphContainer = QWidget::createWindowContainer(graph3D,this); //Q3DBars继承自QWindow,必须如此创建QScatterDataProxy *proxy = new QScatterDataProxy(); //数据代理series = new QScatter3DSeries(proxy); //创建序列series->setItemLabelFormat("(@xLabel, @zLabel, @yLabel)"); //散点标签的格式series->setMeshSmooth(true);series->setBaseColor(Qt::yellow);graph3D->addSeries(series);//使用内置的坐标轴graph3D->axisX()->setTitle("axis X");graph3D->axisX()->setLabelFormat("%.2f");graph3D->axisX()->setTitleVisible(true);graph3D->axisY()->setTitle("axis Y");graph3D->axisY()->setLabelFormat("%.2f");graph3D->axisY()->setTitleVisible(true);graph3D->axisZ()->setTitle("axis Z");graph3D->axisZ()->setLabelFormat("%.2f");graph3D->axisZ()->setTitleVisible(true);graph3D->activeTheme()->setLabelBackgroundEnabled(false); //不显示轴标签背景series->setMesh(QAbstract3DSeries::MeshSphere); //设置散点形状series->setItemSize(0.2); //设置散点大小,默认值为0,取值范围 0~1int N=41;int itemCount=N*N;QScatterDataArray *dataArray = new QScatterDataArray();dataArray->resize(itemCount); //设置数组大小QScatterDataItem *ptrToDataArray = &dataArray->first(); //首地址//墨西哥草帽,-10:0.5:10, N=41float x,y,z;x=-10;for (int i=1 ; i <=N; i++){y=-10;for ( int j =1; j <=N; j++){z=qSqrt(x*x+y*y);if (z!=0)z=10*qSin(z)/z;elsez=10;ptrToDataArray->setPosition(QVector3D(x,z,y));ptrToDataArray++;y+=0.5;}x+=0.5;}series->dataProxy()->resetArray(dataArray);connect(series, &QScatter3DSeries::selectedItemChanged,this, &MainWindow::do_itemSelected);
}MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);ui->sliderZoom->setRange(10,500); //设置slider的数据范围ui->sliderH->setRange(-180,180);ui->sliderV->setRange(-180,180);iniGraph3D(); //创建图表QSplitter *splitter=new QSplitter(Qt::Horizontal);splitter->addWidget(ui->frameSetup);splitter->addWidget(graphContainer);this->setCentralWidget(splitter);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::do_itemSelected(int index)
{ui->actPoint_ChangeValue->setEnabled(index>=0);ui->actData_Delete->setEnabled(index>=0);if(index>=0){QScatterDataItem item= *(series->dataProxy()->itemAt(index));QString str=QString::asprintf("选中点的坐标(x,z,y)=(%.2f, %.2f, %.2f)",item.x(), item.z(), item.y());ui->statusBar->showMessage(str);}
}void MainWindow::on_chkBoxGrid_clicked(bool checked)
{ //图表的网格graph3D->activeTheme()->setGridEnabled(checked);
}void MainWindow::on_chkBoxSmooth_clicked(bool checked)
{ //光滑效果 CheckBoxseries->setMeshSmooth(checked);
}void MainWindow::on_chkBoxAxisTitle_clicked(bool checked)
{//轴标题graph3D->axisX()->setTitleVisible(checked);graph3D->axisY()->setTitleVisible(checked);graph3D->axisZ()->setTitleVisible(checked);
}void MainWindow::on_chkBoxAxisBackground_clicked(bool checked)
{//轴标题背景graph3D->activeTheme()->setLabelBackgroundEnabled(checked);
}void MainWindow::on_chkBoxReverse_clicked(bool checked)
{//Z轴反向graph3D->axisY()->setReversed(checked);
}void MainWindow::on_chkBoxBackground_clicked(bool checked)
{//图表的背景graph3D->activeTheme()->setBackgroundEnabled(checked);
}void MainWindow::on_spinFontSize_valueChanged(int arg1)
{//轴标签字体大小QFont font = graph3D->activeTheme()->font();font.setPointSize(arg1);graph3D->activeTheme()->setFont(font);
}void MainWindow::on_cBoxTheme_currentIndexChanged(int index)
{//设置主题Q3DTheme *currentTheme = graph3D->activeTheme();currentTheme->setType(Q3DTheme::Theme(index));
}void MainWindow::on_chkBoxItemLabel_clicked(bool checked)
{ //显示选中散点的标签// series->setItemLabelFormat("value at (@rowLabel,@colLabel): %.1f");series->setItemLabelVisible(checked);
}void MainWindow::on_cBoxBarStyle_currentIndexChanged(int index)
{ //散点形状 ComboBoxQAbstract3DSeries::Mesh aMesh;aMesh=QAbstract3DSeries::Mesh(index+1);series->setMesh(aMesh);
}void MainWindow::on_cBoxSelectionMode_currentIndexChanged(int index)
{//选择模式graph3D->setSelectionMode(QAbstract3DGraph::SelectionFlags(index));
}void MainWindow::on_spinItemSize_valueChanged(double arg1)
{//散点大小series->setItemSize(arg1);//default 0. value 0~1
}void MainWindow::on_chkBoxShadow_clicked(bool checked)
{//显示阴影 CheckBoxif (checked)graph3D->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium);elsegraph3D->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
}void MainWindow::on_sliderH_valueChanged(int value)
{//水平旋转graph3D->scene()->activeCamera()->setXRotation(value);
}void MainWindow::on_sliderV_valueChanged(int value)
{//垂直旋转graph3D->scene()->activeCamera()->setYRotation(value);
}void MainWindow::on_sliderZoom_valueChanged(int value)
{//缩放graph3D->scene()->activeCamera()->setZoomLevel(value);
}void MainWindow::on_comboCamera_currentIndexChanged(int index)
{//预设视角Q3DCamera::CameraPreset cameraPos=Q3DCamera::CameraPreset(index);graph3D->scene()->activeCamera()->setCameraPreset(cameraPos);
}void MainWindow::on_actSeries_BaseColor_triggered()
{QColor color=series->baseColor();color=QColorDialog::getColor(color);if (color.isValid())series->setBaseColor(color);
}void MainWindow::on_actPoint_ChangeValue_triggered()
{//修改当前散点的坐标int index=series->selectedItem(); //当前选中点的序号if (index<0)return;QScatterDataItem item =*(series->dataProxy()->itemAt(index));QString coord=QString::asprintf("%.2f, %.2f, %.2f",item.x(),item.z(),item.y());bool ok=false;QString newText = QInputDialog::getText(this,"修改点坐标","按格式输入点的坐标(x,z,y)",QLineEdit::Normal,coord,&ok);if (!ok)return;newText =newText.simplified(); //去除前后和中间的空格QStringList xzy=newText.split(QLatin1Char(','),Qt::SkipEmptyParts); //按逗号分割if(xzy.size() != 3){QMessageBox::critical(this,"错误","输入坐标数据格式错误");return;}item.setX(xzy[0].toFloat()); //设置散点坐标item.setZ(xzy[1].toFloat());item.setY(xzy[2].toFloat());series->dataProxy()->setItem(index,item); //重新设置散点数据项
}void MainWindow::on_actData_Delete_triggered()
{//删除当前点int index=series->selectedItem(); //当前选中点的序号if (index<0)return;int removeCount=1; //删除点个数series->dataProxy()->removeItems(index,removeCount);
}void MainWindow::on_actData_Add_triggered()
{//添加随机点int x=QRandomGenerator::global()->bounded(-10,10);int z=QRandomGenerator::global()->bounded(-10,10);int y=QRandomGenerator::global()->bounded(5,10);QScatterDataItem item;item.setX(x);item.setY(y);item.setZ(z);series->dataProxy()->addItem(item);
}void MainWindow::on_btnResetCamera_clicked()
{//复位到FrontHigh视角graph3D->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFrontHigh);
}void MainWindow::on_btnMoveLeft_clicked()
{//左移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal x=target3D.x();target3D.setX(x+0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveRight_clicked()
{//右移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal x=target3D.x();target3D.setX(x-0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveUp_clicked()
{//上移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal z=target3D.z();target3D.setZ(z-0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveDown_clicked()
{//下移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal z=target3D.z();target3D.setZ(z+0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}
三维曲面图
三维图创建
绘制三维曲面图需要使用Q3DSurface图形类和QSurface3DSeries序列类,根据使用的数据代理类不同,可以绘制两种三维曲面图。
- QHeightMapSurfaceDataProxy 数据代理类,根据一张图片的数据绘制三维曲面,典型的如三维地形图
- QSurfaceDataProxy 数据代理类,根据空间点的三维坐标绘制曲面,例如一般的三维函数曲面
三维曲面实际上是由三维空间中的点确定的,曲面的基本数据是空间中的点坐标数据,然后由图形类的底层将这些点连线、划分为基本的三角形,再渲染成曲面。在确定三维曲面的数据点时,一般有两个轴的坐标是均匀划分的。 与三维曲面序列QSurface3DSeries配套的数据代理类是QSurfaceDataProxy,它用二维索引管理数据,与三维柱状图的数据代理类QBarDataProxy的管理方式类似。
空间中每个点是一个QSurfaceDataItem对象,我们称之为点数据项,它存储了点的空间坐标。QSurfaceDataRow 是 QSurfaceDataItem 对象的列表,它存储了一行点的数据;QSurfaceDataArray 是QSurfaceDataRow 对象指针的列表,它按行存储了所有点的数据。它们在Qt源代码中的定义如下:
typedef QList<QSurfaceDataItem> QSurfaceDataRow;
typedef QList<QSurfaceDataRow *> QSurfaceDataArray;
程序截图
示例代码
- .h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include <QtDataVisualization>
using namespace QtDataVisualization;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTprivate:QWidget *graphContainer; //图表的容器Q3DSurface *graph3D; //三维图表QSurface3DSeries *series; //序列// QSurfaceDataProxy *dataProxy; //数据代理void iniGraph3D(); //初始化绘制三维图表public:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void do_pointSelected(const QPoint &position);void on_chkBoxGrid_clicked(bool checked);void on_chkBoxSmooth_clicked(bool checked);void on_chkBoxReflection_clicked(bool checked);void on_chkBoxAxisTitle_clicked(bool checked);void on_chkBoxAxisBackground_clicked(bool checked);void on_chkBoxReverse_clicked(bool checked);void on_chkBoxBackground_clicked(bool checked);void on_spinFontSize_valueChanged(int arg1);void on_cBoxTheme_currentIndexChanged(int index);// void on_btnBarColor_clicked();void on_chkBoxItemLabel_clicked(bool checked);void on_cBoxBarStyle_currentIndexChanged(int index);void on_cBoxSelectionMode_currentIndexChanged(int index);// void on_spinItemSize_valueChanged(double arg1);void on_chkBoxShadow_clicked(bool checked);// void on_comboBox_currentIndexChanged(int index);void on_sliderH_valueChanged(int value);void on_sliderV_valueChanged(int value);void on_comboDrawMode_currentIndexChanged(int index);// void on_btnGrad1_clicked();// void on_btnGrad2_clicked();// void on_pushButton_clicked();// void on_pushButton_clicked();void on_sliderZoom_valueChanged(int value);void on_comboCamera_currentIndexChanged(int index);void on_chkBoxFlatShading_clicked(bool checked);void on_actSurf_Color_triggered();void on_actSurf_GradColor1_triggered();void on_actSurf_GradColor2_triggered();// void on_actSurf_Texture_triggered();void on_actPoint_Modify_triggered();void on_actPoint_DeleteRow_triggered();// void on_cBoxSelectionMode_2_currentIndexChanged(int index);void on_btnResetCamera_clicked();void on_btnMoveUp_clicked();void on_btnMoveLeft_clicked();void on_btnMoveRight_clicked();void on_btnMoveDown_clicked();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
- .cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"#include <QSplitter>#include <QColorDialog>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>void MainWindow::iniGraph3D()
{graph3D = new Q3DSurface();graphContainer = QWidget::createWindowContainer(graph3D,this); //Q3DBars继承自QWindow,必须如此创建//创建坐标轴QValue3DAxis *axisX=new QValue3DAxis;axisX->setTitle("Axis X");axisX->setTitleVisible(true);axisX->setLabelFormat("%.2f");axisX->setRange(-11,11);// axisX->setAutoAdjustRange(true);graph3D->setAxisX(axisX);QValue3DAxis *axisY=new QValue3DAxis;axisY->setTitle("Axis Y");axisY->setTitleVisible(true);axisY->setLabelFormat("%.2f");// axisY->setRange(-10,10);axisY->setAutoAdjustRange(true); //自动调整范围graph3D->setAxisY(axisY);QValue3DAxis *axisZ=new QValue3DAxis;axisZ->setTitle("Axis Z");axisZ->setTitleVisible(true);axisZ->setLabelFormat("%.2f");axisZ->setRange(-11,11);// axisZ->setAutoAdjustRange(true);graph3D->setAxisZ(axisZ);//创建数据代理QSurfaceDataProxy *dataProxy = new QSurfaceDataProxy();series = new QSurface3DSeries(dataProxy);series->setItemLabelFormat("(@xLabel,@zLabel,@yLabel)");series->setMeshSmooth(true);series->setBaseColor(Qt::cyan);series->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe);series->setFlatShadingEnabled(false);graph3D->addSeries(series);graph3D->activeTheme()->setLabelBackgroundEnabled(false);//创建数据, 墨西哥草帽int N=41; //-10:0.5:10, N个数据点QSurfaceDataArray *dataArray = new QSurfaceDataArray; //数组dataArray->reserve(N);float x=-10,y,z;for (int i =1 ; i <=N; i++){QSurfaceDataRow *newRow = new QSurfaceDataRow(N); //一行的数据y=-10;int index=0;for (int j = 1; j <=N; j++){z=qSqrt(x*x+y*y);if (z!=0)z=10*qSin(z)/z;elsez=10;(*newRow)[index++].setPosition(QVector3D(x, z, y));y += 0.5;}x += 0.5;// *dataArray << newRow;dataArray->append(newRow); //添加一行数据}dataProxy->resetArray(dataArray);connect(series, &QSurface3DSeries::selectedPointChanged,this, &MainWindow::do_pointSelected);
}MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);ui->sliderZoom->setRange(10,500); //设置slider的数据范围ui->sliderH->setRange(-180,180);ui->sliderV->setRange(-180,180);iniGraph3D();QSplitter *splitter=new QSplitter(Qt::Horizontal);splitter->addWidget(ui->frameSetup);splitter->addWidget(graphContainer);this->setCentralWidget(splitter);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::do_pointSelected(const QPoint &position)
{if((position.x()<0) || (position.y()<0)){ui->actPoint_Modify->setEnabled(false);ui->actPoint_DeleteRow->setEnabled(false);return;}ui->actPoint_Modify->setEnabled(true);ui->actPoint_DeleteRow->setEnabled(true);QSurfaceDataItem item= *(series->dataProxy()->itemAt(position));QString str=QString::asprintf("选中点的坐标(x,z,y)=(%.2f, %.2f, %.2f)",item.x(), item.z(), item.y());ui->statusBar->showMessage(str);
}void MainWindow::on_chkBoxGrid_clicked(bool checked)
{ //显示背景网格graph3D->activeTheme()->setGridEnabled(checked);
}void MainWindow::on_chkBoxSmooth_clicked(bool checked)
{ //单点光滑效果 CheckBoxseries->setMeshSmooth(checked);
}void MainWindow::on_chkBoxReflection_clicked(bool checked)
{//反射graph3D->setReflection(checked);
}void MainWindow::on_chkBoxAxisTitle_clicked(bool checked)
{//显示轴标题graph3D->axisX()->setTitleVisible(checked);graph3D->axisY()->setTitleVisible(checked);graph3D->axisZ()->setTitleVisible(checked);
}void MainWindow::on_chkBoxAxisBackground_clicked(bool checked)
{//轴标题背景graph3D->activeTheme()->setLabelBackgroundEnabled(checked);
}void MainWindow::on_chkBoxReverse_clicked(bool checked)
{//Y轴(垂直)反向graph3D->axisY()->setReversed(checked);
}void MainWindow::on_chkBoxBackground_clicked(bool checked)
{//图表的背景graph3D->activeTheme()->setBackgroundEnabled(checked);
}void MainWindow::on_spinFontSize_valueChanged(int arg1)
{//轴标签字体大小QFont font = graph3D->activeTheme()->font();font.setPointSize(arg1);graph3D->activeTheme()->setFont(font);
}void MainWindow::on_cBoxTheme_currentIndexChanged(int index)
{//设置主题Q3DTheme *currentTheme = graph3D->activeTheme();currentTheme->setType(Q3DTheme::Theme(index));
}void MainWindow::on_chkBoxItemLabel_clicked(bool checked)
{ //显示单点标签 CheckBoxseries->setItemLabelVisible(checked);
}void MainWindow::on_cBoxBarStyle_currentIndexChanged(int index)
{ //单点形状 ComboBoxQAbstract3DSeries::Mesh aMesh;aMesh=QAbstract3DSeries::Mesh(index+1);series->setMesh(aMesh);
}void MainWindow::on_cBoxSelectionMode_currentIndexChanged(int index)
{//选择模式switch(index){case 0:graph3D->setSelectionMode(QAbstract3DGraph::SelectionNone);break;case 1:graph3D->setSelectionMode(QAbstract3DGraph::SelectionItem);break;case 2:graph3D->setSelectionMode(QAbstract3DGraph::SelectionItemAndRow| QAbstract3DGraph::SelectionSlice);break;case 3:graph3D->setSelectionMode(QAbstract3DGraph::SelectionItemAndColumn| QAbstract3DGraph::SelectionSlice);default:break;}
}void MainWindow::on_chkBoxShadow_clicked(bool checked)
{//显示阴影if (checked)graph3D->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium);elsegraph3D->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
}void MainWindow::on_sliderH_valueChanged(int value)
{//水平旋转graph3D->scene()->activeCamera()->setXRotation(value);
}void MainWindow::on_sliderV_valueChanged(int value)
{//垂直旋转graph3D->scene()->activeCamera()->setYRotation(value);
}void MainWindow::on_sliderZoom_valueChanged(int value)
{//缩放graph3D->scene()->activeCamera()->setZoomLevel(value);
}void MainWindow::on_comboDrawMode_currentIndexChanged(int index)
{//曲面样式 ComboBoxif (index==0)series->setDrawMode(QSurface3DSeries::DrawWireframe);else if (index==1)series->setDrawMode(QSurface3DSeries::DrawSurface);elseseries->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe);
}void MainWindow::on_comboCamera_currentIndexChanged(int index)
{graph3D->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPreset(index));
}void MainWindow::on_chkBoxFlatShading_clicked(bool checked)
{//平面着色 CheckBoxseries->setFlatShadingEnabled(checked);
}void MainWindow::on_actSurf_Color_triggered()
{//设置曲面颜色QColor color=series->baseColor();color=QColorDialog::getColor(color);if (color.isValid()){series->setBaseColor(color);series->setColorStyle(Q3DTheme::ColorStyleUniform); //单一颜色}
}void MainWindow::on_actSurf_GradColor1_triggered()
{//渐变颜色1QLinearGradient gr;gr.setColorAt(0.0, Qt::blue);// gr.setColorAt(0.33, Qt::blue);// gr.setColorAt(0.67, Qt::red);gr.setColorAt(1.0, Qt::yellow);// gr.setColorAt(0.0, Qt::black);// gr.setColorAt(0.33, Qt::blue);// gr.setColorAt(0.67, Qt::red);// gr.setColorAt(1.0, Qt::yellow);series->setBaseGradient(gr);series->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
}void MainWindow::on_actSurf_GradColor2_triggered()
{//渐变颜色2QLinearGradient grGtoR;// grGtoR.setColorAt(0.5, Qt::yellow);// grGtoR.setColorAt(0.2, Qt::red);grGtoR.setColorAt(0.0, Qt::cyan);grGtoR.setColorAt(1.0, Qt::red);// grGtoR.setColorAt(1.0, Qt::darkGreen);// grGtoR.setColorAt(0.5, Qt::yellow);// grGtoR.setColorAt(0.2, Qt::red);// grGtoR.setColorAt(0.0, Qt::darkRed);series->setBaseGradient(grGtoR);series->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
}void MainWindow::on_actPoint_Modify_triggered()
{//修改点坐标QPoint point=series->selectedPoint();if ((point.x()<0) || point.y()<0)return;QSurfaceDataItem item =*(series->dataProxy()->itemAt(point));QString coord=QString::asprintf("%.2f, %.2f, %.2f",item.x(),item.z(),item.y());bool ok=false;QString newText = QInputDialog::getText(this,"修改点坐标","按格式输入点的坐标(x,z,y)",QLineEdit::Normal,coord,&ok);if (!ok)return;newText =newText.simplified(); //去除前后和中间的空格QStringList xzy=newText.split(QLatin1Char(','),Qt::SkipEmptyParts); //按逗号分割if(xzy.size() != 3){QMessageBox::critical(this,"错误","输入坐标数据格式错误");return;}item.setX(xzy[0].toFloat());item.setZ(xzy[1].toFloat());item.setY(xzy[2].toFloat());series->dataProxy()->setItem(point,item);
}void MainWindow::on_actPoint_DeleteRow_triggered()
{//删除行QPoint point=series->selectedPoint();if ((point.x()<0) || point.y()<0)return;int removeCount=1; //删除行的个数series->dataProxy()->removeRows(point.x(),removeCount);
}void MainWindow::on_btnResetCamera_clicked()
{//复位到FrontHigh视角graph3D->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFrontHigh);
}void MainWindow::on_btnMoveUp_clicked()
{//上移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal z=target3D.z();target3D.setZ(z-0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveLeft_clicked()
{//左移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal x=target3D.x();target3D.setX(x+0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveRight_clicked()
{//右移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal x=target3D.x();target3D.setX(x-0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}void MainWindow::on_btnMoveDown_clicked()
{//下移QVector3D target3D=graph3D->scene()->activeCamera()->target();qreal z=target3D.z();target3D.setZ(z+0.1);graph3D->scene()->activeCamera()->setTarget(target3D);
}
相关文章:
【Qt】 Data Visualization
三维数据可视化 三维柱状图三维图的创建程序截图示例代码 三维散点图三维图创建程序截图示例代码 三维曲面图三维图创建程序截图示例代码 Data Visualization 是 Qt 中的一个三维数据可视化模块,可用于绘制三维柱状图、三维散点图和三维曲面。与 Charts 模块类似&am…...
python基础语法
文章目录 字面量定义分类 注释定义分类单行注释多行注释 变量定义 数据类型类型转换定义 案例 标识符定义命名规则内容限定大小写敏感不可使用关键字 命名规范变量的命名规范 运算符数学运算符赋值运算符复合赋值运算符 定义字符串定义方式 字符串拼接语法 字符串格式化语法1字…...
【C++游戏开发-五子棋】
使用C开发五子棋游戏的详细实现方案,涵盖核心逻辑、界面设计和AI对战功能: 1. 项目结构 FiveChess/ ├── include/ │ ├── Board.h // 棋盘类 │ ├── Player.h // 玩家类 │ ├── AI.h // AI类 │ └── Game.h // 游戏主逻辑 ├── src/ …...
C/C++ | 每日一练 (2)
💢欢迎来到张胤尘的技术站 💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥 文章目录 C/C | 每日一练 (2)题目参考答案封装继承多态虚函数底…...
如何在 VS Code 中快速使用 Copilot 来辅助开发
在日常开发中,编写代码往往是最耗时的环节之一。而 GitHub Copilot,作为一款 AI 编码助手,可以帮助开发者 自动补全代码、生成代码片段,甚至直接编写完整的函数,大幅提升编码效率。那么,如何在 VS Code 中快…...
FFmpeg源码:av_strlcpy函数分析
一、引言 在C/C编程中经常会用到strcpy这个字符串复制函数。strcpy是C/C中的一个标准函数,可以把含有\0结束符的字符串复制到另一个地址空间。但是strcpy不会检查目标数组dst的大小是否足以容纳源字符串src,如果目标数组太小,将会导致缓冲区…...
【生产变更】- 集群中配置SCAN ip的不同端口应用
【生产变更】- 集群中配置SCAN ip的不同端口应用 一、概述二、操作步骤三、故障解决 一、概述 使用非默认端口(1521)监听scan ip。 二、操作步骤 1、添加11521端口 srvctl add listener -l lis11521 -o /opt/grid/products/11.2.0 -p 11521 srvctl st…...
RabbitMQ 3.12.2:单节点与集群部署实战指南
前言:在当今的分布式系统架构中,消息队列已经成为不可或缺的组件之一。它不仅能够实现服务之间的解耦,还能有效提升系统的可扩展性和可靠性。RabbitMQ 作为一款功能强大且广泛使用的开源消息中间件,凭借其高可用性、灵活的路由策略…...
Node.js技术原理分析系列——如何在Node.js中新增一个内置模块
本文由体验技术团队曹杨毅原创。 Node.js 是一个开源的、跨平台的JavaScript运行时环境,它允许开发者在服务器端运行JavaScript代码。Node.js 是基于Chrome V8引擎构建的,专为高性能、高并发的网络应用而设计,广泛应用于构建服务器端应用程序…...
从低清到4K的魔法:FlashVideo突破高分辨率视频生成计算瓶颈(港大港中文字节)
论文链接:https://arxiv.org/pdf/2502.05179 项目链接:https://github.com/FoundationVision/FlashVideo 亮点直击 提出了 FlashVideo,一种将视频生成解耦为两个目标的方法:提示匹配度和视觉质量。通过在两个阶段分别调整模型规模…...
康耐视CAM-CIC-10MR-10-GC工业相机
康耐视(COGNEX)的工业相机CAM-CIC-10MR-10-GC是CAM-CIC-10MR系列中的一款型号,主要应用于工业自动化检测和高精度视觉系统 基本参数与特性 分辨率与帧率: CAM-CIC-10MR-10-GC属于康耐视CIC系列,具备10MP(1000万像素)的分辨能力,帧率为10fps。该系列相机支持卷帘快门(R…...
解惑Python:一文解决osgeo库安装失败问题
Osgeo(Open Source Geospatial Foundation)是一个支持开源地理空间数据处理的基金会,我们可以在python中使用“osgeo”库来访问其提供的高效地理空间数据。例如,我们使用该模块提供的GDAL处理栅格数据,使用OGR处理矢量…...
3、树莓派5 安装VNC查看器 开启VNC服务器
在前序文章中( 2、树莓派5第一次开机),可以使用三种方式开机,其中使用网线及wifi的方式均需要使用到VNC查看器进行远程桌面控制,本文将介绍如何下载安装并配置及使用VNC查看器及服务器,对前序文章做一些补充…...
Django 创建表时 “__str__ ”方法的使用
在 Django 模型中,__str__ 方法是一个 Python 特殊方法(也称为“魔术方法”),用于定义对象的字符串表示形式。它的作用是控制当对象被转换为字符串时,应该返回什么样的内容。 示例: 我在初学ModelForm时尝…...
STM32 CAN过滤器配置和应用方法介绍
目录 概述 一、CAN过滤器核心概念 二、过滤器配置步骤(以标准ID为例) 三、不同模式的配置示例 四、高级配置技巧 五、调试与问题排查 六、关键计算公式 总结 概述 在STM32微控制器中,CAN过滤器可以配置为标识符屏蔽模式和标识符列表模…...
【第1章:深度学习概览——1.3 深度学习的核心组件与概念解析之神经网络基础】
大家好!今天咱们一头扎进深度学习的神秘领域,好好探索一下其最重要的基石 —— 神经网络。不管你是深度学习的新手小白,还是已经接触过一些基础概念,这篇文章都能助力你更透彻地理解神经网络的原理和运作机制。咱们从最基础的知识入手,一步步揭开神经网络的神秘面纱! 一、…...
Python中如何进行数据库连接?
在 Python 中进行数据库连接,不同的数据库需要使用不同的库。下面分别介绍几种常见数据库(SQLite、MySQL、PostgreSQL)的连接方法。 1. 连接 SQLite 数据库 SQLite 是一种轻量级的嵌入式数据库,Python 标准库中自带了sqlite3模块…...
解析 WebGPU 中 device.createBuffer 的参数意义
在 WebGPU 开发里,device.createBuffer 方法扮演着至关重要的角色,它用于创建一个 GPU 缓冲区对象,这个对象能够存储顶点数据、索引数据、统一数据等。下面我们就来详细剖析该方法各个参数的意义。 1. size:决定缓冲区容量 size …...
PLC的集成RAM,存储器卡,用户程序存储空间,数据存储容量分别指的什么,有什么关联?
1. 集成RAM 定义:集成RAM(随机存取存储器)是PLC内部的高速易失性存储器,用于临时存储运行时的数据(如输入/输出状态、中间变量、计数器/定时器的当前值等)。 特点: 易失性:断电后数…...
BFS-FloodFill 算法 解决最短路问题 多源 解决拓扑排序
文章目录 一、FloodFill 算法[733. 图像渲染](https://leetcode.cn/problems/flood-fill/description/)2.思路3.代码 [200. 岛屿数量](https://leetcode.cn/problems/number-of-islands/description/)2.思路3.代码 [LCR 105. 岛屿的最大面积](https://leetcode.cn/problems/ZL6…...
USB2.03.0接口区分usb top工具使用
一. USB2.0 & 3.0接口支持区分 1.1. 颜色判断 USB接口的颜色并不是判断版本的可靠标准,但根据行业常见规范分析如下: USB接口颜色与版本对照表: 接口颜色常见版本内部触点数量传输速度黑色USB2.04触点480 Mbps (60 MB/s)白色USB2.0(多…...
2025百度快排技术分析:模拟点击与发包算法的背后原理
一晃做SEO已经15年了,2025年还有人问我如何做百度快速排名,我能给出的答案就是:做好内容的前提下,多刷刷吧!百度的SEO排名算法一直是众多SEO从业者研究的重点,模拟算法、点击算法和发包算法是百度快速排名的…...
idea 2019.3常用插件
idea 2019.3常用插件 文档 idea 2019.3常用插件idea 2023.3.7常用插件 idea 2019.3常用插件 插件名称插件版本说明1AceJump3.5.9AceJump允许您快速将插入符号导航到编辑器中可见的任何位置。只需按“ctrl;”,键入一个字符,然后在Ace Jump…...
【Python 学习 / 5】函数详解(定义、参数、作用域、lambda、内置函数)
文章目录 一、函数1. 定义函数1.1 基本函数定义1.2 带参数的函数1.3 带返回值的函数 2. 参数传递2.1 位置参数2.2 默认参数2.3 可变参数2.3.1 使用*args2.3.2 使用**kwargs 2.4 参数的混合使用 3. 作用域3.1 局部和全局变量3.2 global 关键字输出: 3.3 nonlocal关键…...
WPF7-数据绑定基础
1. WPF数据绑定试验 1.1. 数据绑定的核心实现1.2. {Binding}语法1.3. 理解 DataContext 1. WPF数据绑定试验 以下是一个简单的 WPF 数据绑定示例,使用两个TextBox控件分别表示Name和Age来进行进行数据绑定试验。 数据模型类 创建一个 Person 类,包含…...
http 与 https 的区别?
HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)是互联网通信的基础协议。随着网络技术的发展和安全需求的提升,HTTPS变得越来越重要。本文将深入探讨HTTP与HTTPS之间的区别,包括其工作原理、安全性、性能、应用场景及未来发展等。 1. HTTP与HTTPS的基本概念 1.1 HT…...
大数据学习(49) - Flink按键分区状态(Keyed State)
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦ᾑ…...
IP 路由基础 | 路由条目生成 / 路由表内信息获取
注:本文为 “IP 路由” 相关文章合辑。 未整理去重。 IP 路由基础 秦同学学学已于 2022-04-09 18:44:20 修改 一. IP 路由产生背景 我们都知道 IP 地址可以标识网络中的一个节点,并且每个 IP 地址都有自己的网段,各个网段并不相同…...
COBOL语言的移动应用开发
COBOL语言的移动应用开发探讨 引言 在信息技术快速发展的今天,移动应用开发已成为各行各业不可或缺的一部分。许多企业和开发者纷纷转向使用新兴的编程语言和开发工具,以满足不断变化的用户需求。然而,作为一种历史悠久的编程语言ÿ…...
TCP协议(Transmission Control Protocol)
TCP协议,即传输控制协议,其最大的特征就是对传输的数据进行可靠、高效的控制,其段格式如下: 源端口和目的端口号表示数据从哪个进程来,到哪个进程去,四位报头长度表示的是TCP头部有多少个4字节,…...
C语言数组之二维数组
C语言 主要内容 数组 二维数组 数组 二维数组 定义 二维数组本质上是一个行列式的组合,也就是说二维数组由行和列两部分组成,属于多维数组。二维数组数据是通过行列进行解读。二维数组可被视为一个特殊的一维数组,相当于二维数组又是一…...
计算机专业知识【软件开发中的常用图表:E - R图、HIPO、DFD、N - S、PAD详解】
在软件开发过程中,有许多种图表工具被用于不同阶段的设计和分析,帮助开发者更清晰地理解系统结构、数据流程和算法逻辑。下面将详细介绍E - R图、HIPO图、DFD图、N - S图和PAD图,包括它们的样子和用途。 一、E - R图(实体 - 联系…...
多人协同开发 —— Git Aoneflow工作流
一、Aoneflow工作流核心架构 #mermaid-svg-rwTOe9qYwzG3wkdy {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-rwTOe9qYwzG3wkdy .error-icon{fill:#552222;}#mermaid-svg-rwTOe9qYwzG3wkdy .error-text{fill:#552222…...
VSCode运行Go程序报错:Unable to process `evaluate`: debuggee is running
如果使用默认的VSCode的服务器来运行Go程序,那么使用fmt.Scan函数输入数据的时候就会报错,我们需要修改launch.json文件,将Go程序运行在shell终端上。 main.go package mainimport "fmt"func main() {var n intfmt.Scan(&n)v…...
Mybatis高级(动态SQL)
目录 一、动态SQL 1.1 数据准备: 1.2 <if>标签 1.3<trim> 标签 1.4<where>标签 1.5<set>标签 1.6 <foreach>标签 1.7<include> 标签 一、动态SQL 动态SQL是Mybatis的强⼤特性之⼀,能够完成不同条件下不同…...
在 Vue 3 中使用 Lottie 动画:实现一个加载动画
在现代前端开发中,动画是提升用户体验的重要元素之一。Lottie 是一个流行的动画库,它允许我们使用 JSON 文件来渲染高质量的动画。本文将介绍如何在 Vue 3 项目中集成 Lottie 动画,并实现一个加载动画效果。 如果对你有帮助请帮忙点个&#x…...
建筑行业安全技能竞赛流程方案
一、比赛时间: 6月23日8:30分准时到场;9:00-10:00理论考试;10:10-12:00现场隐患答疑;12:00-13:30午餐;下午13:30-15:30现场…...
Mybatisplus自定义sql
文章目录 引言流程 引言 mybatisplus最擅长的将where里面的语句给简便化,而不用我们自己写标签来实现条件查询 但是很多公司规范我们将sql写在mapper层中,不能写在service中 而且一些语句查询的不同select count(*) xxx from xxx 也难以用mp来实现 如何…...
情书网源码 情书大全帝国cms7.5模板
源码介绍 帝国cms7.5仿《情书网》模板源码,同步生成带手机站带采集。适合改改做文学类的网站。 效果预览 源码获取 情书网源码 情书大全帝国cms7.5模板...
基于Unity引擎的网络通信架构深度解析——以NetworkConnectionController为例
一、架构概览与设计理念 本文将以重构后的NetworkConnectionController为核心,深入分析基于Unity引擎的MMO网络通信架构设计。该模块采用分层设计思想,通过连接池管理、流量控制、心跳监测等多维度技术手段,构建了一个高性能、可扩展的网络通…...
C#学习之DataGridView控件
目录 一、DataGridView控件常用属性、方法、事件汇总表 1. 常用方法、属性和事件汇总 二、DataGridView 控件的常用方法调用 1. DataBind() 方法 2. Clear() 方法 3. Refresh() 方法 4. Sort() 方法 5. ClearSelection() 方法 6. BeginEdit() 方法 7. EndEdit() 方法…...
midjourney 一 prompt 提示词
midjourney 不需要自然语言的描述,它只需要关键词即可。 一个完整的Midjourney prompt通常包括三个部分 图片提示(Image Prompts)、文本提示(Text Prompt)和参数(Parameters)。 1、图片提示(…...
谈谈 wait 和 notify
目录 1 wait()方法 2 notify()⽅法 3 wait 和 sleep 的区别 多线程调度是随机的, 很多时候希望多个线程能够按照咱们规定的顺序来执行. 完成线程之间的配合工作. wait和notify就是一个用来协调线程顺序的重要工具. 这俩方法都是 Object 提供的方法. 随便找个对象࿰…...
250214-java类集框架
引言 类集框架本质上相当于是容器,容器装什么东西由程序员指定 1.单列集合 单列集合是list和set,list的实现类有ArrayList和LinkedList,前者是数组实现,后者是链表实现。list和set,前者有序、可重复,后者…...
Python学习心得异常处理
有些代码在操作的过程当中,如果不注意其所限定的条件,可能在输入函数值时引发一些程序的报错,这样为了让代码自己能做到抛除异常操作的情况,就得让代码具有排除异常的能力。下面的一些操作就使得代码具有该功能,处理异…...
【机器学习】线性回归 多项式线性回归
【机器学习系列】 KNN算法 KNN算法原理简介及要点 特征归一化的重要性及方式线性回归算法 线性回归与一元线性回归 线性回归模型的损失函数 多元线性回归 多项式线性回归 多项式线性回归 V1.0多项式回归多项式回归的公式 特征代换超越函数作为特征向量维度 V1.0 多项式回归 …...
链表和list
链表和list 算法题中的经典操作:用空间代替时间 双链表头插顺序: 1.先修改新结点的左右指针 2.然后修改结点y的左指针 3.最后修改哨兵位的右指针 双链表在任意位置(p)之后插入…...
vscode的一些实用操作
1. 焦点切换(比如主要用到使用快捷键在编辑区和终端区进行切换操作) 2. 跳转行号 使用ctrl g,然后输入指定的文件内容,即可跳转到相应位置。 使用ctrl p,然后输入指定的行号,回车即可跳转到相应行号位置。...
sass中@import升级@use的使用区别与案例
在 Sass 中,import 和 use 都用于模块化代码,但二者有显著区别。以下是主要差异和具体案例说明: 核心区别对比 特性 import (旧版) use (新版) 作用域 全局作用域(变量/混合易冲突) 局部作用域(需通过…...
基于单片机ht7038 demo
单片机与ht7038 demo,三相电能表,电量数据包括电流电压功能,采用免校准方法 列表 ht7038模块/CORE/core_cm3.c , 17273 ht7038模块/CORE/core_cm3.h , 85714 ht7038模块/CORE/startup_stm32f10x_hd.s , 15503 ht7038模块/CORE/startup_stm32…...