本文通过一个三维柱状图的实例来说明使用Data Visualization的类绘制三维图形的基本原理。实例使用Q3DBars绘制一个三维柱状图,程序运行效果如图所示。
无需额外编程或设置,程序运行时在图表单击左键可以选择图表中的某个项、某个列或行(与选择方式的设置有关),按住鼠标右键时上下、左右移动可以进行水平和垂直方向的旋转,鼠标滚轮可以进行缩放。
主窗口设计实例主窗口继承自QWidget的应用程序,可视化创建主窗体。
由于三维图形类Q3DBars、Q3DScatter和Q3DSurface都是从QWindow继承而来的,不能简单使用QWidget组件作为Q3DBars组件的容器,也就是不能在主窗口上放置一个QWidget组件,然后作为Q3DBars组件的容器,而是需要使用QWidget::createWindowContainer()动态创建QWidget作为Q3DBars的容器。
主窗口类的头文件widget.h中的定义如下:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QWidget *graphContainer; //图表的容器
Q3DBars *graph3D; //图表
QBar3DSeries *series; //图表的序列
void iniGraph3D(); //创建图表
};
在Widget类中,定义了私有变量graphContainer作为三维图表的容器,它在的构造函数中创建。还定义三维图表和序列的私有变量graph3D和series,是为了在程序里便于直接引用。iniGraph3D()函数用于初始化创建图表,在构造函数里调用。
三维柱状图的创建在主窗口的构造函数里创建界面和三维图表,构造函数代码如下:
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
//创建图表
graph3D = new Q3DBars();
graphContainer = QWidget::createWindowContainer(graph3D);
QLayout *layout=new QVBoxLayout(this);
layout->addWidget(graphContainer);
this->setLayout(layout);
//创建坐标轴
QStringList rowLabs;
QStringList colLabs;
rowLabs<<"row1"<<"row2"<<"row3";
colLabs<<"col1"<<"col2"<<"col3"<<"col4"<<"col5";
QValue3DAxis *axisV=new QValue3DAxis; //数值坐标轴
axisV->setTitle("value");
axisV->setTitleVisible(true);
QCategory3DAxis *axisRow=new QCategory3DAxis; //行坐标轴
axisRow->setTitle("row axis");
axisRow->setLabels(rowLabs);
axisRow->setTitleVisible(true);
QCategory3DAxis *axisCol=new QCategory3DAxis; //列坐标轴
axisCol->setTitle ("column axis");
axisCol->setLabels(colLabs);
axisCol->setTitleVisible(true);
graph3D->setValueAxis(axisV);
graph3D->setRowAxis(axisRow);
graph3D->setColumnAxis(axisCol);
//创建序列
series = new QBar3DSeries;
series->setMesh(QAbstract3DSeries::MeshCylinder); //棒图形状
series->setItemLabelFormat("(@rowLabel,@colLabel) : %.1f");//标签格式
graph3D->addSeries(series);
//添加数据
QBarDataArray *dataSet = new QBarDataArray; //数据数组
dataSet->reserve(rowLabs.count());
QBarDataRow *dataRow = new QBarDataRow;
*dataRow << 1 << 2<< 3<< 4<<5; //第 1 行数据,有 5 列
dataSet->append(dataRow);
QBarDataRow *dataRow2 = new QBarDataRow;
*dataRow2 << 5<< 5<< 5<< 5<<5; //第 2 行数据,有 5 列
dataSet->append(dataRow2);
QBarDataRow *dataRow3 = new QBarDataRow;
*dataRow3 << 1<< 5<< 9<< 5<<1; //第 3 行数据,有 5 列
dataSet->append(dataRow3);
series->dataProxy()->resetArray(dataSet);
}
程序首先创建Q3DBars类的实例graph3D,然后创建一个QWidget实例graphContainer作为其容器。创建graphContainer时使用了QWidget的静态函数createWindowContainer(),这样创建的QWidget实例才可以作为QWindow及其子类组件的容器。
三维柱状图的水平面的两个轴采用QCategory3DAxis类型的坐标轴,是文字型标签坐标轴,分别为其设置轴标题和轴标签。垂直方向表示项的数值大小,所以使用QValue3DAxis类型的坐标轴。创建坐标轴后,分别设置为柱状图行坐标轴、列坐标轴和数值坐标轴:
graph3D->setValueAxis(axisV);
graph3D->setRowAxis(axisRow);
graph3D->setColumnAxis(axisCol);
与Q3DBars对应使用的序列是QBar3DSeries类,创建序列实例对象series,设置棒图性质和标签格式后添加到图表。
然后创建QBarDataArray数组dataSet,其元素个数等于三维柱状图的行数,也就是dataSet的每个元素是一个行的数据。
一个行的数据是QBarDataRow类型,例如,创建第1行的数据并添加到柱状图的数据数组里的代码如下:
QBarDataRow *dataRow = new QBarDataRow;
*dataRow << 1 << 2<< 3<< 4<<5; //第 1 行数据,有 5 列
dataSet->append(dataRow);
实际上,QBarDataRow和QBarDataArray都不是类,而是类型定义,在Qt的源代码中,它们的定义如下:
typedef QVector<QBarDataItem> QBarDataRow;
typedef QList<QBarDataRow *> QBarDataArray;
所以,QBarDataRow是QBarDataItem的向量,QBarDataArray是QBarDataRow的列表。数据的基本元素是QBarDataItem,也就是柱状图中的一个项,QBarDataItem只有value和rotation两个属性,用于存储项的数值和旋转角度。
在图中,柱状图的数据有3行5列,添加数据时,每次添加一行的数据,但是属于一个序列。
设置完数据后,为序列的数据代理设置数据,语句如下:
series->dataProxy()->resetArray(dataSet);
程序里没有为序列先创建QBarDataProxy类型的数据代理,然后再添加到序列里。调用series->dataProxy()使用内部缺省的数据代理,resetArray(dataSet)将清除数据代理原有的数据,并使用dataSet作为数据源。
————————————————
觉得有用的话请关注点赞,谢谢您的支持!
对于本系列文章相关示例完整代码有需要的朋友,可关注并在评论区留言!
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved