【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_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
#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_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);
}