窗口右侧是用 Q3DBars 和 QBar3DSeries 绘制的三维柱状图,这个图只有一个QBar3DSeries序列,数据是按行存储的,可以有多行。水平方向是行坐标轴和列坐标轴,使用OCategory3DAxis 坐标轴类;垂直方向是数值坐标轴,使用 QValue3DAxis 坐标轴类。在图上点击一个棒柱时,可以在图上显示其行标签、列标签和数值,状态栏上还会显示其行编号、列编号和数值。无须额外编程或设置,在图上按住鼠标右键并上下左右拖动鼠标可以进行水平和垂直方向的旋转,滚动鼠标滚轮可以进行缩放。 窗口工具栏上的按钮用于修改棒柱的基本颜色,修改选中棒柱的数值,添加、插入或删除行等。 窗口左侧是操作面板,分为多个分组框。
效果图:
private:
QWidget* graphContainer; //三维图的容器
Q3DBars* graph3D; //三维图
QBar3DSeries* series; //三维条形图中的数据序列
void mainwindow::iniGraph3D()
{
graph3D = new Q3DBars(); //创建三维图
graphContainer = QWidget::createWindowContainer(graph3D, this);//Q3DBars继承自QWindow
graph3D->scene()->activeCamera() //设置视角
->setCameraPreset(Q3DCamera::CameraPresetFrontHigh);
//设置 Z坐标轴
QValue3DAxis* axisV = new QValue3DAxis; //数值坐标
axisV->setTitle(QString::fromLocal8Bit("销量"));
axisV->setTitleVisible(true);
axisV->setLabelFormat("%d");
graph3D->setValueAxis(axisV); //设置数值坐标轴
//设置 X坐标轴
QCategory3DAxis* axisRow = new QCategory3DAxis;
axisRow->setTitle("row axis");
axisRow->setTitleVisible(true);
graph3D->setRowAxis(axisRow); //设置行坐标轴
//设置 Y坐标轴
QCategory3DAxis* axisCol = new QCategory3DAxis;
axisCol->setTitle("column axis");
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 = rand()%15+1; //随机整数[1,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);
}
如果没有为该系列显式设置数据代理,则该系列将创建默认代理。设置另一个代理将销毁现有代理以及添加到其中的所有数据。
标签格式 | 注释 |
---|---|
@rowTitle | 来自行轴的标题 |
@colTitle | 列轴的标题 |
@valueTitle | 来自值轴的标题 |
@rowIdx | 可见行索引,使用图形区域设置进行了本地化 |
@colIdx | 可见列索引,使用图形区域设置进行了本地化 |
@rowLabel | 来自行轴的标签 |
@colLabel | 来自列轴的标签 |
@valueLabel | 使用附加到图形的值轴格式格式化的项值 |
@seriesName | 系列的名称 |
%<format spec> | 指定格式的项值。使用与QValue3DAxis::abelFormat相同的规则进行格式化 |
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::fromLocal8Bit("选中的棒柱,Row=%1, Column=%2, Value=%3")
.arg(position.x()).arg(position.y()).arg(bar->value());
ui->statusBar->showMessage(info);
}
void mainwindow::on_actSeries_BaseColor_triggered()
{
//设置序列基本颜色
QColor color = series->baseColor();
color = QColorDialog::getColor(color);
if (color.isValid())
{
series->setBaseColor(color);
}
}
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 = rand() % (15 - 5 + 1) + 5;
QBarDataItem* dataItem = new QBarDataItem; //数据项
dataItem->setValue(value);
dataRow->append(*dataItem);
}
QString rowStr = QString::fromLocal8Bit("第%1周").arg(i + 1);
dataProxy->addRow(dataRow, rowStr); //添加行数据和标签
}
QStringList colLabs = series->dataProxy()->columnLabels(); //原来的列坐标轴标签
dataProxy->setColumnLabels(colLabs);
series->dataProxy()->resetArray(); //清除数据代理和坐标轴标签,必须清除,否则不能重新设置坐标轴标签
series->setDataProxy(dataProxy); //重新设置数据代理,会删除之前的数据代理
}
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, QString::fromLocal8Bit("输入数值"),
QString::fromLocal8Bit("更改棒柱数值"), value, 0, 50, 1, &ok);
if (ok)
{
bar.setValue(value);
series->dataProxy()->setItem(position, bar);
}
}
void mainwindow::on_actData_Add_triggered()
{
//添加行
const QString rowLabel = QInputDialog::getText(this,
QString::fromLocal8Bit("输入字符串"),
QString::fromLocal8Bit("请输入行标签"));
if (rowLabel.isEmpty())
return;
QBarDataRow* dataRow = new QBarDataRow; //棒柱数据行
for (int j = 1; j <= 5; j++) //固定5列
{
quint32 value = rand()%(25 - 15 + 1) + 15;
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, QString::fromLocal8Bit("输入字符串"),
QString::fromLocal8Bit("请输入行标签"));
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 = rand() % (30 - 20) + 20;
QBarDataItem* dataItem = new QBarDataItem;
dataItem->setValue(value);
dataRow->append(*dataItem);
}
series->dataProxy()->insertRow(index, dataRow); //插入棒柱数据行
}
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_actQiut_triggered()
{
QApplication::quit();
}
void mainwindow::on_comboCamera_currentIndexChanged(int index)
{
Q3DCamera::CameraPreset cameraPos = Q3DCamera::CameraPreset(index);
graph3D->scene()->activeCamera()->setCameraPreset(cameraPos);
}
预设视角类型参考 enum Q3DCamera::CameraPreset
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_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);
}
void mainwindow::on_btnResetCamera_clicked()
{
graph3D->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFrontHigh);
void mainwindow::on_cBoxTheme_currentIndexChanged(int index)
{
Q3DTheme* currentTheme = graph3D->activeTheme();
currentTheme->setType(Q3DTheme::Theme(index));
}
void mainwindow::on_spinFontSize_valueChanged(int arg1)
{
QFont font = graph3D->activeTheme()->font();
font.setPointSize(arg1);
graph3D->activeTheme()->setFont(font);
}
void mainwindow::on_cBoxSelectionMode_currentIndexChanged(int index)
{
graph3D->setSelectionMode(QAbstract3DGraph::SelectionFlags(index));
}
void mainwindow::on_chkBoxBackground_clicked(bool checked)
{
graph3D->activeTheme()->setBackgroundEnabled(checked);
}
enum QAbstract3DGraph::SelectionFlag
枚举值 | 含义 |
---|---|
QAbstract3DGraph::SelectionNone | 不允许选择 |
QAbstract3DGraph::SelectionItem | 选择并且高亮显示一个项 |
QAbstract3DGraph::SelectionRow | 选择并且高亮显示一行 |
QAbstract3DGraph::SelectionItemAndRow | 选择一个项和一行,用不同颜色高亮显示 |
QAbstract3DGraph::SelectionColumn | 选择并且高亮显示一列 |
QAbstract3DGraph::SelectionItemAndColumn | 选择一个项和一列,用不同颜色高亮显示 |
QAbstract3DGraph::SelectionRowAndColumn | 选择交叉的一行和一列 |
QAbstract3DGraph::SelectionItemRowAndColumn | 选择交叉的一行和一列,用不同颜色高亮显示 |
QAbstract3DGraph::SelectionSlice | 切片选择,需要与 SelectionRow 或 SelectionColumn 结合使用 |
QAbstract3DGraph::SelectionMultiSeries | 选中同一个位置处的多个序列的项 |
void mainwindow::on_chkBoxGrid_clicked(bool checked)
{
graph3D->activeTheme()->setGridEnabled(checked);
}
void mainwindow::on_chkBoxReflection_clicked(bool checked)
{
graph3D->setReflection(checked);
}
void mainwindow::on_chkBoxReverse_clicked(bool checked)
{
graph3D->valueAxis()->setReversed(checked);
}
void mainwindow::on_chkBoxAxisTitle_clicked(bool checked)
{
graph3D->valueAxis()->setTitleVisible(checked);
graph3D->rowAxis()->setTitleVisible(checked);
graph3D->columnAxis()->setTitleVisible(checked);
}
void mainwindow::on_chkBoxAxisBackground_clicked(bool checked)
{
graph3D->activeTheme()->setLabelBackgroundEnabled(checked);
}
void mainwindow::on_cBoxBarStyle_currentIndexChanged(int index)
{
QAbstract3DSeries::Mesh aMesh;
aMesh = QAbstract3DSeries::Mesh(index + 1); //0=MeshUserDefined
series->setMesh(aMesh);
}
enum QAbstract3DSeries::Mesh
并非所有样式都可用于所有可视化类型。
常量 | 值 | 注释 |
---|---|---|
QAbstract3DSeries::MeshUserDefined | 0 | 用户定义网格,通过QAbstract3DSeries::userDefinedMesh属性设置 |
QAbstract3DSeries::MeshBar | 1 | 基本矩形条 |
QAbstract3DSeries::MeshCube | 2 | 基本多维数据集 |
QAbstract3DSeries::MeshPyramid | 3 | 四边形金字塔 |
QAbstract3DSeries::MeshCone | 4 | 基本圆锥体 |
QAbstract3DSeries::MeshCylinder | 5 | 基本气缸 |
QAbstract3DSeries::MeshBevelBar | 6 | 略微倾斜(圆形)的矩形钢筋 |
QAbstract3DSeries::MeshBevelCube | 7 | 略微倾斜(圆形)的立方体 |
QAbstract3DSeries::MeshSphere | 8 | 球体 |
QAbstract3DSeries::MeshMinimal | 9 | 最小的三维网格:一个三角锥 |
QAbstract3DSeries::MeshArrow | 10 | 向上的箭头 |
QAbstract3DSeries::MeshPoint | 11 | 2D点 |
void mainwindow::on_chkBoxSmooth_clicked(bool checked)
{
series->setMeshSmooth(checked);
}
void mainwindow::on_chkBoxItemLabel_clicked(bool checked)
{
series->setItemLabelFormat("value at (@rowLabel,@colLabel): %.1f");
series->setItemLabelVisible(checked);
}
函数 | 功能 |
---|---|
int addRow() | 添加一行数据,一行数据是 QBarDataRow 类型对象 |
void insertRow() | 在某行之前插入一行数据 |
void removeRows() | 从某行开始,移除指定行数的数据 |
void setRow () | 为某一行重新设置一行数据 |
QBarDataRow *rowAt() | 返回某一行的数据 |
void setItem() | 根据行号和列号,设置某个棒柱数据项 |
QBarDataltem *itemAt() | 根据行号和列号,返回单个棒柱数据项 |
void resetArray() | 若不带任何参数,就清除数据代理所有的数据和标签;若使用一个 QBarDataArray类型的对象作为输入参数,就重新设置棒柱数据数组 |
int rowCount() | 返回数据代理的行数 |
void setRowLabels() | 用一个字符串列表设置行坐标轴的标签 |
StringList rowLabels() | 返回行坐标轴的所有标签 |
void setColumnLabels() | 用一个字符串列表设置列坐标轴的标签 |
QStringList columnLabels() | 返回列坐标轴的所有标签 |