本示在Widget应用程序中使用Q3DBars绘制3D柱状图,显示芬兰奥卢和赫尔辛基的平均气温(2006-2013),并通过UI操作,对显示效果进行调整。展示了以下内容:
新建一个空的Qt Application Widget工程。
右击文件名,选择Remove,删除mainwindow.h和mainwindow.cpp文件。这里我们不需要主窗口。我们将在main函数中创建一个QWidget对象作为显示窗口。
在pro工程文件中,加入对Q3Dbars的支持:QT += datavisualization
#include
#include
//Q3DBars的支持
#include
#include
#include
using namespace QtDataVisualization;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Q3DBars *widgetgraph = new Q3DBars();
//所有的数据展示图表类对象,都必须装到容器使用。
QWidget *container = QWidget::createWindowContainer(widgetgraph);
if (!widgetgraph->hasContext()) {
QMessageBox msgBox;
msgBox.setText("Couldn't initialize the OpenGL context.");
msgBox.exec();
return -1;
}
//设置图形的大小规则
QSize screenSize = widgetgraph->screen()->size();
container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5));
container->setMaximumSize(screenSize);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
//widget通过选项卡和单击接受焦点
container->setFocusPolicy(Qt::StrongFocus);
QWidget *widget=new QWidget;
widget->setWindowTitle(QStringLiteral("芬兰奥卢和赫尔辛基的平均气温(2006-2013)"));
QHBoxLayout *hLayout = new QHBoxLayout(widget);
QVBoxLayout *vLayout = new QVBoxLayout();
hLayout->addWidget(container, 1);
hLayout->addLayout(vLayout);
widget->show();
return a.exec();
}
运行效果如下,可以看到一个没有数据的三维坐标,可以通过鼠标控制显示角度:
右边部分是一个vLayout布局,用于控制左边的显示效果。我们先将控件准备好。
//! [旋转控制Slider]
QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget);
rotationSliderX->setTickInterval(30);
rotationSliderX->setTickPosition(QSlider::TicksBelow);
rotationSliderX->setMinimum(-180);
rotationSliderX->setValue(0);
rotationSliderX->setMaximum(180);
QSlider *rotationSliderY = new QSlider(Qt::Horizontal, widget);
rotationSliderY->setTickInterval(15);
rotationSliderY->setTickPosition(QSlider::TicksAbove);
rotationSliderY->setMinimum(-90);
rotationSliderY->setValue(0);
rotationSliderY->setMaximum(90);
vLayout->addWidget(new QLabel(QStringLiteral("Rotate horizontally")));
vLayout->addWidget(rotationSliderX, 0, Qt::AlignTop);
vLayout->addWidget(new QLabel(QStringLiteral("Rotate vertically")));
vLayout->addWidget(rotationSliderY, 0, Qt::AlignTop);
//! [旋转控制Slider]
//! [效果切换按钮]
QPushButton *labelButton = new QPushButton(widget);
labelButton->setText(QStringLiteral("Change label style"));
QPushButton *cameraButton = new QPushButton(widget);
cameraButton->setText(QStringLiteral("Change camera preset"));
QPushButton *zoomToSelectedButton = new QPushButton(widget);
zoomToSelectedButton->setText(QStringLiteral("Zoom to selected bar"));
vLayout->addWidget(labelButton, 0, Qt::AlignTop);
vLayout->addWidget(cameraButton, 0, Qt::AlignTop);
vLayout->addWidget(zoomToSelectedButton, 0, Qt::AlignTop);
//! [效果切换按钮]
//! [显示效果开关复选框]
QCheckBox *backgroundCheckBox = new QCheckBox(widget);
backgroundCheckBox->setText(QStringLiteral("Show background"));
backgroundCheckBox->setChecked(false);
QCheckBox *gridCheckBox = new QCheckBox(widget);
gridCheckBox->setText(QStringLiteral("Show grid"));
gridCheckBox->setChecked(true);
QCheckBox *smoothCheckBox = new QCheckBox(widget);
smoothCheckBox->setText(QStringLiteral("Smooth bars"));
smoothCheckBox->setChecked(false);
QCheckBox *seriesCheckBox = new QCheckBox(widget);
seriesCheckBox->setText(QStringLiteral("Show second series"));
seriesCheckBox->setChecked(false);
QCheckBox *reverseValueAxisCheckBox = new QCheckBox(widget);
reverseValueAxisCheckBox->setText(QStringLiteral("Reverse value axis"));
reverseValueAxisCheckBox->setChecked(false);
QCheckBox *reflectionCheckBox = new QCheckBox(widget);
reflectionCheckBox->setText(QStringLiteral("Show reflections"));
reflectionCheckBox->setChecked(false);
QCheckBox *axisTitlesVisibleCB = new QCheckBox(widget);
axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible"));
axisTitlesVisibleCB->setChecked(true);
QCheckBox *axisTitlesFixedCB = new QCheckBox(widget);
axisTitlesFixedCB->setText(QStringLiteral("Axis titles fixed"));
axisTitlesFixedCB->setChecked(true);
vLayout->addWidget(backgroundCheckBox);
vLayout->addWidget(gridCheckBox);
vLayout->addWidget(smoothCheckBox);
vLayout->addWidget(reflectionCheckBox);
vLayout->addWidget(seriesCheckBox);
vLayout->addWidget(reverseValueAxisCheckBox);
vLayout->addWidget(axisTitlesVisibleCB);
vLayout->addWidget(axisTitlesFixedCB);
//! [显示效果开关复选框]
//! [下拉菜单]
QComboBox *themeList = new QComboBox(widget);
themeList->addItem(QStringLiteral("Qt"));
themeList->addItem(QStringLiteral("Primary Colors"));
themeList->addItem(QStringLiteral("Digia"));
themeList->addItem(QStringLiteral("Stone Moss"));
themeList->addItem(QStringLiteral("Army Blue"));
themeList->addItem(QStringLiteral("Retro"));
themeList->addItem(QStringLiteral("Ebony"));
themeList->addItem(QStringLiteral("Isabelle"));
themeList->setCurrentIndex(0);
QComboBox *barStyleList = new QComboBox(widget);
barStyleList->addItem(QStringLiteral("Bar"), int(QAbstract3DSeries::MeshBar));
barStyleList->addItem(QStringLiteral("Pyramid"), int(QAbstract3DSeries::MeshPyramid));
barStyleList->addItem(QStringLiteral("Cone"), int(QAbstract3DSeries::MeshCone));
barStyleList->addItem(QStringLiteral("Cylinder"), int(QAbstract3DSeries::MeshCylinder));
barStyleList->addItem(QStringLiteral("Bevel bar"), int(QAbstract3DSeries::MeshBevelBar));
barStyleList->addItem(QStringLiteral("Sphere"), int(QAbstract3DSeries::MeshSphere));
barStyleList->setCurrentIndex(4);
QComboBox *selectionModeList = new QComboBox(widget);
selectionModeList->addItem(QStringLiteral("None"),
int(QAbstract3DGraph::SelectionNone));
selectionModeList->addItem(QStringLiteral("Bar"),
int(QAbstract3DGraph::SelectionItem));
selectionModeList->addItem(QStringLiteral("Row"),
int(QAbstract3DGraph::SelectionRow));
selectionModeList->addItem(QStringLiteral("Bar and Row"),
int(QAbstract3DGraph::SelectionItemAndRow));
selectionModeList->addItem(QStringLiteral("Column"),
int(QAbstract3DGraph::SelectionColumn));
selectionModeList->addItem(QStringLiteral("Bar and Column"),
int(QAbstract3DGraph::SelectionItemAndColumn));
selectionModeList->addItem(QStringLiteral("Row and Column"),
int(QAbstract3DGraph::SelectionRowAndColumn));
selectionModeList->addItem(QStringLiteral("Bar, Row and Column"),
int(QAbstract3DGraph::SelectionItemRowAndColumn));
selectionModeList->addItem(QStringLiteral("Slice into Row"),
int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionRow));
selectionModeList->addItem(QStringLiteral("Slice into Row and Item"),
int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow));
selectionModeList->addItem(QStringLiteral("Slice into Column"),
int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionColumn));
selectionModeList->addItem(QStringLiteral("Slice into Column and Item"),
int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn));
selectionModeList->addItem(QStringLiteral("Multi: Bar, Row, Col"),
int(QAbstract3DGraph::SelectionItemRowAndColumn
| QAbstract3DGraph::SelectionMultiSeries));
selectionModeList->addItem(QStringLiteral("Multi, Slice: Row, Item"),
int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow
| QAbstract3DGraph::SelectionMultiSeries));
selectionModeList->addItem(QStringLiteral("Multi, Slice: Col, Item"),
int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn
| QAbstract3DGraph::SelectionMultiSeries));
selectionModeList->setCurrentIndex(1);
QFontComboBox *fontList = new QFontComboBox(widget);
fontList->setCurrentFont(QFont("Times New Roman"));
QComboBox *shadowQuality = new QComboBox(widget);
shadowQuality->addItem(QStringLiteral("None"));
shadowQuality->addItem(QStringLiteral("Low"));
shadowQuality->addItem(QStringLiteral("Medium"));
shadowQuality->addItem(QStringLiteral("High"));
shadowQuality->addItem(QStringLiteral("Low Soft"));
shadowQuality->addItem(QStringLiteral("Medium Soft"));
shadowQuality->addItem(QStringLiteral("High Soft"));
shadowQuality->setCurrentIndex(5);
QComboBox *rangeList = new QComboBox(widget);
rangeList->addItem(QStringLiteral("2006"));
rangeList->addItem(QStringLiteral("2007"));
rangeList->addItem(QStringLiteral("2008"));
rangeList->addItem(QStringLiteral("2009"));
rangeList->addItem(QStringLiteral("2010"));
rangeList->addItem(QStringLiteral("2011"));
rangeList->addItem(QStringLiteral("2012"));
rangeList->addItem(QStringLiteral("2013"));
rangeList->addItem(QStringLiteral("All"));
rangeList->setCurrentIndex(8);
vLayout->addWidget(new QLabel(QStringLiteral("Show year")));
vLayout->addWidget(rangeList);
vLayout->addWidget(new QLabel(QStringLiteral("Change bar style")));
vLayout->addWidget(barStyleList);
vLayout->addWidget(new QLabel(QStringLiteral("Change selection mode")));
vLayout->addWidget(selectionModeList);
vLayout->addWidget(new QLabel(QStringLiteral("Change theme")));
vLayout->addWidget(themeList);
vLayout->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")));
vLayout->addWidget(shadowQuality);
vLayout->addWidget(new QLabel(QStringLiteral("Change font")));
vLayout->addWidget(fontList);
//! [下拉菜单]
//! [文字显示slider]
QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, widget);
fontSizeSlider->setTickInterval(10);
fontSizeSlider->setTickPosition(QSlider::TicksBelow);
fontSizeSlider->setMinimum(1);
fontSizeSlider->setValue(30);
fontSizeSlider->setMaximum(100);
QSlider *axisLabelRotationSlider = new QSlider(Qt::Horizontal, widget);
axisLabelRotationSlider->setTickInterval(10);
axisLabelRotationSlider->setTickPosition(QSlider::TicksBelow);
axisLabelRotationSlider->setMinimum(0);
axisLabelRotationSlider->setValue(30);
axisLabelRotationSlider->setMaximum(90);
vLayout->addWidget(new QLabel(QStringLiteral("Adjust font size")));
vLayout->addWidget(fontSizeSlider);
vLayout->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
vLayout->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop);
//![文字显示slider]
显示效果如下(目前图形和控件还没有建立联系):
下面我们将添加GraphModifier类。设置图形效果,为图形添加数据。
//! [创建modifier并建立联系]
GraphModifier *modifier = new GraphModifier(widgetgraph);
QObject::connect(rotationSliderX, &QSlider::valueChanged, modifier, &GraphModifier::rotateX);
QObject::connect(rotationSliderY, &QSlider::valueChanged, modifier, &GraphModifier::rotateY);
QObject::connect(labelButton, &QPushButton::clicked, modifier,
&GraphModifier::changeLabelBackground);
QObject::connect(cameraButton, &QPushButton::clicked, modifier,
&GraphModifier::changePresetCamera);
QObject::connect(zoomToSelectedButton, &QPushButton::clicked, modifier,
&GraphModifier::zoomToSelectedBar);
QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setBackgroundEnabled);
QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setGridEnabled);
QObject::connect(smoothCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setSmoothBars);
QObject::connect(seriesCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setSeriesVisibility);
QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setReverseValueAxis);
QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier,
&GraphModifier::setReflection);
QObject::connect(modifier, &GraphModifier::backgroundEnabledChanged,
backgroundCheckBox, &QCheckBox::setChecked);
QObject::connect(modifier, &GraphModifier::gridEnabledChanged,
gridCheckBox, &QCheckBox::setChecked);
QObject::connect(rangeList, SIGNAL(currentIndexChanged(int)), modifier,
SLOT(changeRange(int)));
QObject::connect(barStyleList, SIGNAL(currentIndexChanged(int)), modifier,
SLOT(changeStyle(int)));
QObject::connect(selectionModeList, SIGNAL(currentIndexChanged(int)), modifier,
SLOT(changeSelectionMode(int)));
QObject::connect(themeList, SIGNAL(currentIndexChanged(int)), modifier,
SLOT(changeTheme(int)));
QObject::connect(shadowQuality, SIGNAL(currentIndexChanged(int)), modifier,
SLOT(changeShadowQuality(int)));
QObject::connect(modifier, &GraphModifier::shadowQualityChanged, shadowQuality,
&QComboBox::setCurrentIndex);
QObject::connect(widgetgraph, &Q3DBars::shadowQualityChanged, modifier,
&GraphModifier::shadowQualityUpdatedByVisual);
QObject::connect(fontSizeSlider, &QSlider::valueChanged, modifier,
&GraphModifier::changeFontSize);
QObject::connect(fontList, &QFontComboBox::currentFontChanged, modifier,
&GraphModifier::changeFont);
QObject::connect(modifier, &GraphModifier::fontSizeChanged, fontSizeSlider,
&QSlider::setValue);
QObject::connect(modifier, &GraphModifier::fontChanged, fontList,
&QFontComboBox::setCurrentFont);
QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier,
&GraphModifier::setAxisTitleVisibility);
QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier,
&GraphModifier::setAxisTitleFixed);
QObject::connect(axisLabelRotationSlider, &QSlider::valueChanged, modifier,
&GraphModifier::changeLabelRotation);
//! [创建modifier并建立联系]
#ifndef GRAPHMODIFIER_H
#define GRAPHMODIFIER_H
#include
#include
#include
#include
#include
#include
#include
#include
using namespace QtDataVisualization;
class GraphModifier : public QObject
{
Q_OBJECT
public:
explicit GraphModifier(Q3DBars *bargraph);
//该析构函数在本示例中可以省略
//~GraphModifier();
void resetTemperatureData();
void changePresetCamera();
void changeLabelBackground();
void changeFont(const QFont &font);
void changeFontSize(int fontsize);
void rotateX(int rotation);
void rotateY(int rotation);
void setBackgroundEnabled(int enabled);
void setGridEnabled(int enabled);
void setSmoothBars(int smooth);
void setSeriesVisibility(int enabled);
void setReverseValueAxis(int enabled);
void setReflection(bool enabled);
public Q_SLOTS:
void changeRange(int range);
void changeStyle(int style);
void changeSelectionMode(int selectionMode);
void changeTheme(int theme);
void changeShadowQuality(int quality);
void shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality shadowQuality);
void changeLabelRotation(int rotation);
void setAxisTitleVisibility(bool enabled);
void setAxisTitleFixed(bool enabled);
void zoomToSelectedBar();
Q_SIGNALS:
void shadowQualityChanged(int quality);
void backgroundEnabledChanged(bool enabled);
void gridEnabledChanged(bool enabled);
void fontChanged(QFont font);
void fontSizeChanged(int size);
private:
Q3DBars *m_graph;
float m_xRotation;
float m_yRotation;
int m_fontSize;
int m_segments;
int m_subSegments;
float m_minval;
float m_maxval;
QStringList m_months;
QStringList m_years;
QValue3DAxis *m_temperatureAxis;
QCategory3DAxis *m_yearAxis;
QCategory3DAxis *m_monthAxis;
QBar3DSeries *m_primarySeries;
QBar3DSeries *m_secondarySeries;
QAbstract3DSeries::Mesh m_barMesh;
bool m_smooth;
QPropertyAnimation m_animationCameraX;
QPropertyAnimation m_animationCameraY;
QPropertyAnimation m_animationCameraZoom;
QPropertyAnimation m_animationCameraTarget;
float m_defaultAngleX;
float m_defaultAngleY;
float m_defaultZoom;
QVector3D m_defaultTarget;
};
#endif
#include "graphmodifier.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace QtDataVisualization;
const QString celsiusString = QString(QChar(0xB0)) + "C";
//! [0]
GraphModifier::GraphModifier(Q3DBars *bargraph)
: m_graph(bargraph),
m_xRotation(0.0f),
m_yRotation(0.0f),
m_fontSize(30),
m_segments(4),
m_subSegments(3),
m_minval(-20.0f),
m_maxval(20.0f),
m_temperatureAxis(new QValue3DAxis),
m_yearAxis(new QCategory3DAxis),
m_monthAxis(new QCategory3DAxis),
m_primarySeries(new QBar3DSeries),
m_secondarySeries(new QBar3DSeries),
m_barMesh(QAbstract3DSeries::MeshBevelBar),
m_smooth(false)
{
m_months << "January" << "February" << "March" << "April" << "May" << "June" << "July" << "August" << "September" << "October" << "November" << "December";
m_years << "2006" << "2007" << "2008" << "2009" << "2010" << "2011" << "2012" << "2013";
//![轴设置]
//OpenGL:Y轴:m_temperatureAxis
m_temperatureAxis->setTitle("平均温度");
//要绘制的网格线的数量按以下公式计算:线段*子线段+1。预设默认值为5。
//这里分成4分,需要五根线
m_temperatureAxis->setSegmentCount(4);
//这里分成3份,需要两根线。因为已经有头尾两根线
m_temperatureAxis->setSubSegmentCount(3);
m_temperatureAxis->setRange(m_minval, m_maxval);//OpenGL:Y轴范围-20到20度
m_temperatureAxis->setLabelFormat(QString(QStringLiteral("%.1f ") + celsiusString));
//留相机角度更改时,标签可以自动旋转的最大角度。
m_temperatureAxis->setLabelAutoRotation(30.0f);
m_temperatureAxis->setTitleVisible(true);
//OpenGL:Z、X轴:m_yearAxis、m_monthAxis
m_yearAxis->setTitle("年");
m_yearAxis->setLabelAutoRotation(30.0f);
m_yearAxis->setTitleVisible(true);
m_monthAxis->setTitle("月");
m_monthAxis->setLabelAutoRotation(30.0f);
m_monthAxis->setTitleVisible(true);
m_graph->setValueAxis(m_temperatureAxis);
m_graph->setRowAxis(m_yearAxis);
m_graph->setColumnAxis(m_monthAxis);
//![轴设置]
//![设置图形的一些视觉效果]
m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftMedium);
m_graph->activeTheme()->setBackgroundEnabled(false);
m_graph->activeTheme()->setFont(QFont("Times New Roman", m_fontSize));
m_graph->activeTheme()->setLabelBackgroundEnabled(true);
//值根据一个数据序列决定缩放比例
//如果设置为true,则条间距将仅正确应用于Z轴。
m_graph->setMultiSeriesUniform(true);
//![设置图形的一些视觉效果]
//![数据设置]
m_primarySeries->setItemLabelFormat(QStringLiteral("奥卢 - @colLabel @rowLabel: @valueLabel"));
m_primarySeries->setMesh(QAbstract3DSeries::MeshBevelBar);
m_primarySeries->setMeshSmooth(false);
m_secondarySeries->setItemLabelFormat(QStringLiteral("赫尔辛基 - @colLabel @rowLabel: @valueLabel"));
m_secondarySeries->setMesh(QAbstract3DSeries::MeshBevelBar);
m_secondarySeries->setMeshSmooth(false);
//初始状态不显示
m_secondarySeries->setVisible(false);
m_graph->addSeries(m_primarySeries);
m_graph->addSeries(m_secondarySeries);
//![数据设置]
//设置相机角度:
//与UI中的camera angle change按钮方法一样
changePresetCamera();
resetTemperatureData();
// 设置用于缩放到选定栏的属性动画
//! [动画设置]
Q3DCamera *camera = m_graph->scene()->activeCamera();
m_defaultAngleX = camera->xRotation();
m_defaultAngleY = camera->yRotation();
m_defaultZoom = camera->zoomLevel();
m_defaultTarget = camera->target();
m_animationCameraX.setTargetObject(camera);
m_animationCameraY.setTargetObject(camera);
m_animationCameraZoom.setTargetObject(camera);
m_animationCameraTarget.setTargetObject(camera);
m_animationCameraX.setPropertyName("xRotation");
m_animationCameraY.setPropertyName("yRotation");
m_animationCameraZoom.setPropertyName("zoomLevel");
m_animationCameraTarget.setPropertyName("target");
int duration = 1700;
m_animationCameraX.setDuration(duration);
m_animationCameraY.setDuration(duration);
m_animationCameraZoom.setDuration(duration);
m_animationCameraTarget.setDuration(duration);
//动画除了起始状态,最终状态,还可以插入一个中间状态
// 缩放总是先缩小到图形上方,然后再放大
//在动画进行到30%的时候到达下面的状态
qreal zoomOutFraction = 0.3;
m_animationCameraX.setKeyValueAt(zoomOutFraction, QVariant::fromValue(0.0f));
//顶视图
m_animationCameraY.setKeyValueAt(zoomOutFraction, QVariant::fromValue(90.0f));
//缩小一半
m_animationCameraZoom.setKeyValueAt(zoomOutFraction, QVariant::fromValue(50.0f));
m_animationCameraTarget.setKeyValueAt(zoomOutFraction,
QVariant::fromValue(QVector3D(0.0f, 0.0f, 0.0f)));0.0f)));
//! [动画设置]
}
//! [0]
void GraphModifier::resetTemperatureData()
{
}
void GraphModifier::changeRange(int range)
{
}
void GraphModifier::changeStyle(int style)
{
}
void GraphModifier::changePresetCamera()
{
}
void GraphModifier::changeTheme(int theme)
{
}
void GraphModifier::changeLabelBackground()
{
}
void GraphModifier::changeSelectionMode(int selectionMode)
{
}
void GraphModifier::changeFont(const QFont &font)
{
}
void GraphModifier::changeFontSize(int fontsize)
{
}
void GraphModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq)
{
}
void GraphModifier::changeLabelRotation(int rotation)
{
}
void GraphModifier::setAxisTitleVisibility(bool enabled)
{
}
void GraphModifier::setAxisTitleFixed(bool enabled)
{
}
//! [11]
void GraphModifier::zoomToSelectedBar()
{
}
void GraphModifier::changeShadowQuality(int quality)
{
}
void GraphModifier::rotateX(int rotation)
{
}
void GraphModifier::rotateY(int rotation)
{
}
void GraphModifier::setBackgroundEnabled(int enabled)
{
}
void GraphModifier::setGridEnabled(int enabled)
{
}
void GraphModifier::setSmoothBars(int smooth)
{
}
void GraphModifier::setSeriesVisibility(int enabled)
{
}
void GraphModifier::setReverseValueAxis(int enabled)
{
}
void GraphModifier::setReflection(bool enabled)
{
}
void GraphModifier::changePresetCamera()
{
//如果当前有动画正在运行则停止播放
m_animationCameraX.stop();
m_animationCameraY.stop();
m_animationCameraZoom.stop();
m_animationCameraTarget.stop();
//如果动画改变了摄像机目标,则还原摄像机目标
m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
//当前活动的相机预设,从正前方看一个很好的位置和角度
//注意这里是static,配合下面if里的++preset,实现观察点的遍历
static int preset = Q3DCamera::CameraPresetFront;
m_graph->scene()->activeCamera()->setCameraPreset((Q3DCamera::CameraPreset)preset);
//枚举量的一个循环, CameraPresetFrontLow 的值是0,CameraPresetDirectlyBelow的值为最大值
if (++preset > Q3DCamera::CameraPresetDirectlyBelow)
preset = Q3DCamera::CameraPresetFrontLow;
}
该函数不但可以设置初始角度,还已经在main函数里设置了connection,现在可以点击按钮观察效果了。该函数的效果演示,我们在添加完数据后一起验收。
void GraphModifier::resetTemperatureData()
{
// 设置数据8年12个月
static const float tempOulu[8][12] = {
{
-6.7f, -11.7f, -9.7f, 3.3f, 9.2f, 14.0f, 16.3f, 17.8f, 10.2f, 2.1f, -2.6f, -0.3f}, // 2006
{
-6.8f, -13.3f, 0.2f, 1.5f, 7.9f, 13.4f, 16.1f, 15.5f, 8.2f, 5.4f, -2.6f, -0.8f}, // 2007
{
-4.2f, -4.0f, -4.6f, 1.9f, 7.3f, 12.5f, 15.0f, 12.8f, 7.6f, 5.1f, -0.9f, -1.3f}, // 2008
{
-7.8f, -8.8f, -4.2f, 0.7f, 9.3f, 13.2f, 15.8f, 15.5f, 11.2f, 0.6f, 0.7f, -8.4f}, // 2009
{
-14.4f, -12.1f, -7.0f, 2.3f, 11.0f, 12.6f, 18.8f, 13.8f, 9.4f, 3.9f, -5.6f, -13.0f}, // 2010
{
-9.0f, -15.2f, -3.8f, 2.6f, 8.3f, 15.9f, 18.6f, 14.9f, 11.1f, 5.3f, 1.8f, -0.2f}, // 2011
{
-8.7f, -11.3f, -2.3f, 0.4f, 7.5f, 12.2f, 16.4f, 14.1f, 9.2f, 3.1f, 0.3f, -12.1f}, // 2012
{
-7.9f, -5.3f, -9.1f, 0.8f, 11.6f, 16.6f, 15.9f, 15.5f, 11.2f, 4.0f, 0.1f, -1.9f} // 2013
};
static const float tempHelsinki[8][12] = {
{
-3.7f, -7.8f, -5.4f, 3.4f, 10.7f, 15.4f, 18.6f, 18.7f, 14.3f, 8.5f, 2.9f, 4.1f}, // 2006
{
-1.2f, -7.5f, 3.1f, 5.5f, 10.3f, 15.9f, 17.4f, 17.9f, 11.2f, 7.3f, 1.1f, 0.5f}, // 2007
{
-0.6f, 1.2f, 0.2f, 6.3f, 10.2f, 13.8f, 18.1f, 15.1f, 10.1f, 9.4f, 2.5f, 0.4f}, // 2008
{
-2.9f, -3.5f, -0.9f, 4.7f, 10.9f, 14.0f, 17.4f, 16.8f, 13.2f, 4.1f, 2.6f, -2.3f}, // 2009
{
-10.2f, -8.0f, -1.9f, 6.6f, 11.3f, 14.5f, 21.0f, 18.8f, 12.6f, 6.1f, -0.5f, -7.3f}, // 2010
{
-4.4f, -9.1f, -2.0f, 5.5f, 9.9f, 15.6f, 20.8f, 17.8f, 13.4f, 8.9f, 3.6f, 1.5f}, // 2011
{
-3.5f, -3.2f, -0.7f, 4.0f, 11.1f, 13.4f, 17.3f, 15.8f, 13.1f, 6.4f, 4.1f, -5.1f}, // 2012
{
-4.8f, -1.8f, -5.0f, 2.9f, 12.8f, 17.2f, 18.0f, 17.1f, 12.5f, 7.5f, 4.5f, 2.3f} // 2013
};
// 创建 QBarDataArray对象
QBarDataArray *dataSet = new QBarDataArray;
QBarDataArray *dataSet2 = new QBarDataArray;
QBarDataRow *dataRow;
QBarDataRow *dataRow2;
dataSet->reserve(m_years.size());
for (int year = 0; year < m_years.size(); year++) {
// 创建数据行
dataRow = new QBarDataRow(m_months.size());
dataRow2 = new QBarDataRow(m_months.size());
for (int month = 0; month < m_months.size(); month++) {
// 向行中添加数据
(*dataRow)[month].setValue(tempOulu[year][month]);
(*dataRow2)[month].setValue(tempHelsinki[year][month]);
}
// 将行添加到QBarDataArray对象
dataSet->append(dataRow);
dataSet2->append(dataRow2);
}
// 将数据添加到数据代理(数据代理取得它的所有权)
m_primarySeries->dataProxy()->resetArray(dataSet, m_years, m_months);
m_secondarySeries->dataProxy()->resetArray(dataSet2, m_years, m_months);
}
void GraphModifier::rotateX(int rotation)
{
m_xRotation = rotation;
m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation);
}
void GraphModifier::rotateY(int rotation)
{
m_yRotation = rotation;
m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation);
}
void GraphModifier::changeLabelBackground()
{
m_graph->activeTheme()->setLabelBackgroundEnabled(
!m_graph->activeTheme()->isLabelBackgroundEnabled());
}
//! [放大选中区域]
//1、设置动画初始状态
//2、设置动画结束状态(这部分是核心,需要更加观察目标找到合适的观察方位)
//3、启动动画
void GraphModifier::zoomToSelectedBar()
{
m_animationCameraX.stop();
m_animationCameraY.stop();
m_animationCameraZoom.stop();
m_animationCameraTarget.stop();
Q3DCamera *camera = m_graph->scene()->activeCamera();
float currentX = camera->xRotation();
float currentY = camera->yRotation();
float currentZoom = camera->zoomLevel();
QVector3D currentTarget = camera->target();
//! [动画初始状态]
m_animationCameraX.setStartValue(QVariant::fromValue(currentX));
m_animationCameraY.setStartValue(QVariant::fromValue(currentY));
m_animationCameraZoom.setStartValue(QVariant::fromValue(currentZoom));
m_animationCameraTarget.setStartValue(QVariant::fromValue(currentTarget));
//! [动画初始状态]
//selectedSeries返回所选栏归属的序列
QPoint selectedBar = m_graph->selectedSeries()
? m_graph->selectedSeries()->selectedBar()//返回选中的项目
: QBar3DSeries::invalidSelectionPosition();
if (selectedBar != QBar3DSeries::invalidSelectionPosition()) {
//! [在轴范围内标准化选定的条位置,以确定目标坐标]
//OpenGL的坐标系下面是Z和X周,上面是Y
QVector3D endTarget;
float xMin = m_graph->columnAxis()->min();
float xRange = m_graph->columnAxis()->max() - xMin;
float zMin = m_graph->rowAxis()->min();
float zRange = m_graph->rowAxis()->max() - zMin;
//标准化到-1到1之间
endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f);
endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f);
//! [在轴范围内标准化选定的条位置,以确定目标坐标]
//! [旋转相机使其始终指向图形中心]
//水平旋转
qreal endAngleX = 90.0 - qRadiansToDegrees(qAtan(qreal(endTarget.z() / endTarget.x())));
//角度为正是逆时针转置。所以如果x为负数,则正常旋转0-180度即可。
//如果x为正数,需要顺时针选择0-180度,否则到不了屏幕正前方。
if (endTarget.x() > 0.0f)
endAngleX -= 180.0f;
float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(selectedBar.x(),
selectedBar.y())->value();
//观察value的角度为30°,值为负数则为-30°,Rotate Vertically
float endAngleY = barValue >= 0.0f ? 30.0f : -30.0f;
if (m_graph->valueAxis()->reversed())
endAngleY *= -1.0f;
//! [旋转相机使其始终指向图形中心]
m_animationCameraX.setEndValue(QVariant::fromValue(float(endAngleX)));
m_animationCameraY.setEndValue(QVariant::fromValue(endAngleY));
//放大到默认的2.5倍
m_animationCameraZoom.setEndValue(QVariant::fromValue(250));
m_animationCameraTarget.setEndValue(QVariant::fromValue(endTarget));
} else {
// 没有选择栏,回到初始状态
m_animationCameraX.setEndValue(QVariant::fromValue(m_defaultAngleX));
m_animationCameraY.setEndValue(QVariant::fromValue(m_defaultAngleY));
m_animationCameraZoom.setEndValue(QVariant::fromValue(m_defaultZoom));
m_animationCameraTarget.setEndValue(QVariant::fromValue(m_defaultTarget));
}
m_animationCameraX.start();
m_animationCameraY.start();
m_animationCameraZoom.start();
m_animationCameraTarget.start();
}
//! [放大选中区域]
这部分代码只是简单的设置特效的开关,把程序运行起来对照观察即可,这里就不详细描述了。
void GraphModifier::setBackgroundEnabled(int enabled)
{
m_graph->activeTheme()->setBackgroundEnabled(bool(enabled));
}
void GraphModifier::setGridEnabled(int enabled)
{
m_graph->activeTheme()->setGridEnabled(bool(enabled));
}
void GraphModifier::setSmoothBars(int smooth)
{
m_smooth = bool(smooth);
m_primarySeries->setMeshSmooth(m_smooth);
m_secondarySeries->setMeshSmooth(m_smooth);
}
void GraphModifier::setSeriesVisibility(int enabled)
{
m_secondarySeries->setVisible(bool(enabled));
}
void GraphModifier::setReverseValueAxis(int enabled)
{
m_graph->valueAxis()->setReversed(enabled);
}
void GraphModifier::setReflection(bool enabled)
{
m_graph->setReflection(enabled);
}
void GraphModifier::setAxisTitleVisibility(bool enabled)
{
m_temperatureAxis->setTitleVisible(enabled);
m_monthAxis->setTitleVisible(enabled);
m_yearAxis->setTitleVisible(enabled);
}
void GraphModifier::setAxisTitleFixed(bool enabled)
{
m_temperatureAxis->setTitleFixed(enabled);
m_monthAxis->setTitleFixed(enabled);
m_yearAxis->setTitleFixed(enabled);
}
//show year
void GraphModifier::changeRange(int range)
{
if (range >= m_years.count())
m_yearAxis->setRange(0, m_years.count() - 1);
else
m_yearAxis->setRange(range, range);
}
//change bar style
void GraphModifier::changeStyle(int style)
{
//返回指向发送信号的对象的指针,没有则返回nullptr。
QComboBox *comboBox = qobject_cast<QComboBox *>(sender());
if (comboBox) {
//mesh:由数据(通常指的是顶点数据)构成的一个三维模型
//为数据选择一个形状(内置的mesh类型)
m_barMesh = QAbstract3DSeries::Mesh(comboBox->itemData(style).toInt());
m_primarySeries->setMesh(m_barMesh);
m_secondarySeries->setMesh(m_barMesh);
}
}
//选择模式,选择一个bar?还是一行?等等
void GraphModifier::changeSelectionMode(int selectionMode)
{
QComboBox *comboBox = qobject_cast<QComboBox *>(sender());
if (comboBox) {
int flags = comboBox->itemData(selectionMode).toInt();
m_graph->setSelectionMode(QAbstract3DGraph::SelectionFlags(flags));
}
}
void GraphModifier::changeTheme(int theme)
{
Q3DTheme *currentTheme = m_graph->activeTheme();
currentTheme->setType(Q3DTheme::Theme(theme));
emit backgroundEnabledChanged(currentTheme->isBackgroundEnabled());
emit gridEnabledChanged(currentTheme->isGridEnabled());
emit fontChanged(currentTheme->font());
emit fontSizeChanged(currentTheme->font().pointSize());
}
void GraphModifier::changeShadowQuality(int quality)
{
QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality);
m_graph->setShadowQuality(sq);
emit shadowQualityChanged(quality);
}
void GraphModifier::changeFont(const QFont &font)
{
QFont newFont = font;
m_graph->activeTheme()->setFont(newFont);
}
void GraphModifier::changeFontSize(int fontsize)
{
m_fontSize = fontsize;
QFont font = m_graph->activeTheme()->font();
font.setPointSize(m_fontSize);
m_graph->activeTheme()->setFont(font);
}
void GraphModifier::changeLabelRotation(int rotation)
{
m_temperatureAxis->setLabelAutoRotation(float(rotation));
m_monthAxis->setLabelAutoRotation(float(rotation));
m_yearAxis->setLabelAutoRotation(float(rotation));
}
//这里好像没有用到,保持效果与控件的显示一致。
//具体实现方法查看main函数
void GraphModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq)
{
int quality = int(sq);
emit shadowQualityChanged(quality);
}
该示例并不完整,还有需要改良的地方。例如旋转的控件与显示效果不一致,没有联动。在改变选择模式后,zoom to selected bar功能效果不对。理解了前面的内容,这些都可以自己实现。例如可以在main中加入以下代码,关联xRotate和yRotate控件和显示效果:
//也可以直接将显示效果与控件绑定,但我们这样更加灵活。
//示例中的阴影效果设置,也是用的同样的方法。
QObject::connect(widgetgraph->scene()->activeCamera(), &Q3DCamera::xRotationChanged,
modifier, &GraphModifier::xRotateChanged);
QObject::connect(modifier, &GraphModifier::xRotateChanged,
rotationSliderX,&QSlider::setValue);
QObject::connect(widgetgraph->scene()->activeCamera(), &Q3DCamera::xRotationChanged,
modifier, &GraphModifier::xRotateChanged);
QObject::connect(modifier, &GraphModifier::xRotateChanged,
rotationSliderX,&QSlider::setValue);
必须事先在GraphModifier类的头文件中给出xRotateChanged和yRotateChanged的声明,Q_SIGNALS无需给出实现代码。只是用来传递参数。
Q_SIGNALS:
void xRotateChanged(int xRotate);
void yRotateChanged(int yRotate);
这样我们就可以在需要改变选择值的时候,通过emit发出可。
注意:在本示例中,modifier对象的构造函数改变了旋转角度,旋转发生在connect建立之前。需要通过其他手段实现效果的一致,例如通过timer,一段时间后emit一个信号,执行一次后关闭timer。