【Qt】DataVisualization
- 电脑硬件
- 2025-08-31 15:45:03

三维数据可视化 三维柱状图三维图的创建程序截图示例代码 三维散点图三维图创建程序截图示例代码 三维曲面图三维图创建程序截图示例代码
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_DATAVISUALIZATION class 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 #endifQBar3DSeries 序列被创建后,就会自动创建数据代理,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_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT private: 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继承自QWindow graph3D->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(); //创建三维柱状图,会创建一个容器对象graphContainer QSplitter *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) { //显示背景网格 CheckBox graph3D->activeTheme()->setGridEnabled(checked); } void MainWindow::on_chkBoxSmooth_clicked(bool checked) { //光滑效果 CheckBox series->setMeshSmooth(checked); } void MainWindow::on_chkBoxReflection_clicked(bool checked) {//显示倒影 CheckBox graph3D->setReflection(checked); } void MainWindow::on_chkBoxAxisTitle_clicked(bool checked) {//显示轴标题 CheckBox graph3D->valueAxis()->setTitleVisible(checked); graph3D->rowAxis()->setTitleVisible(checked); graph3D->columnAxis()->setTitleVisible(checked); } void MainWindow::on_chkBoxAxisBackground_clicked(bool checked) {//显示轴标签背景 CheckBox graph3D->activeTheme()->setLabelBackgroundEnabled(checked); } void MainWindow::on_chkBoxReverse_clicked(bool checked) {//数值坐标轴反向 CheckBox graph3D->valueAxis()->setReversed(checked); } void MainWindow::on_chkBoxBackground_clicked(bool checked) {//显示背景 CheckBox 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) { //显示选中棒柱的标签 CheckBox series->setItemLabelFormat("value at (@rowLabel,@colLabel): %.1f"); series->setItemLabelVisible(checked); } void MainWindow::on_cBoxBarStyle_currentIndexChanged(int index) { //棒柱样式 ComboBox QAbstract3DSeries::Mesh aMesh; aMesh=QAbstract3DSeries::Mesh(index+1); //0=MeshUserDefined series->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_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT private: 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~1 int N=41; int itemCount=N*N; QScatterDataArray *dataArray = new QScatterDataArray(); dataArray->resize(itemCount); //设置数组大小 QScatterDataItem *ptrToDataArray = &dataArray->first(); //首地址 //墨西哥草帽,-10:0.5:10, N=41 float 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; else z=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) { //光滑效果 CheckBox series->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) { //散点形状 ComboBox QAbstract3DSeries::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) {//显示阴影 CheckBox if (checked) graph3D->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium); else graph3D->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_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT private: 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; else z=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) { //单点光滑效果 CheckBox series->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) { //显示单点标签 CheckBox series->setItemLabelVisible(checked); } void MainWindow::on_cBoxBarStyle_currentIndexChanged(int index) { //单点形状 ComboBox QAbstract3DSeries::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); else graph3D->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) {//曲面样式 ComboBox if (index==0) series->setDrawMode(QSurface3DSeries::DrawWireframe); else if (index==1) series->setDrawMode(QSurface3DSeries::DrawSurface); else series->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe); } void MainWindow::on_comboCamera_currentIndexChanged(int index) { graph3D->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPreset(index)); } void MainWindow::on_chkBoxFlatShading_clicked(bool checked) {//平面着色 CheckBox series->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() {//渐变颜色1 QLinearGradient 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() {//渐变颜色2 QLinearGradient 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】DataVisualization由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【Qt】DataVisualization”