示例学QT系列二——Bars Example

Bars Example (可视化的柱状图数据)

1、功能描述:

左侧:数据的展示窗口(3D的,右键可改变观察的camera位置),有三个维度(右手坐标系):z方向表示平均温度、x方向表示月份、y方向年份。

右侧:是对左侧3D柱状图属性的一些设置。    

2、代码实现:

布局:整体上看分左右两块(水平分布),右边又有一堆垂直分布的控件;因此外层我们使用可以让控件左右分布的布局类QHBOXLayout:F1进入该类的说明文档,继承关系:QHBOXLayout:QBoxLayout:QLayout:QObject, QLayoutItem

头文件:#include<QHBOXLayout>(QT中类名与文件名一致,这是比较方便的地方)

构造函数:QHBoxLayout()
                         QHBoxLayout(QWidget *parent),QT中几乎所有的类的构造函数都会有parent参数。该参数通常是QObject 或者是 QWidget 的。此参数有很多用处:指定组件的父组件(有时子组件有调用父组件接口的需要);此外,每层的子控件都有其对应的parent指针,当最外层的对象析构时,可以确保子控件自动删除。

常见接口:addWidget(QWidget *, int stretch = 0, Qt::Alignment alignment = Qt::Alignment());
        QWidget* :需要添加的widget;
        stretch :拉伸因子,这个比较抽象 可以自己试一试
        alignment :对齐方式,如Qt::AlignLeft | Qt::AlignTop(水平居左 垂直居上),

 addLayout,按布局可水平插入控件(有顺序)

按照上面的布局需要,代码可以写成如下:

QWidget *widget = new QWidget;
QHBoxLayout *hLayout = new QHBoxLayout(widget);
QVBoxLayout *vLayout = new QVBoxLayout();
hLayout->addWidget(container, 1);
hLayout->addLayout(vLayout);

大体的布局如下 

右侧控件垂直分布,于是就有了以下代码

    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);
    //! [5]
    vLayout->addWidget(labelButton, 0, Qt::AlignTop);
    vLayout->addWidget(cameraButton, 0, Qt::AlignTop);
    vLayout->addWidget(zoomToSelectedButton, 0, Qt::AlignTop);
    vLayout->addWidget(backgroundCheckBox);
    vLayout->addWidget(gridCheckBox);
    vLayout->addWidget(smoothCheckBox);
    vLayout->addWidget(reflectionCheckBox);
    vLayout->addWidget(seriesCheckBox);
    vLayout->addWidget(reverseValueAxisCheckBox);
    vLayout->addWidget(axisTitlesVisibleCB);
    vLayout->addWidget(axisTitlesFixedCB);
    //略....

 关键的类:本示例用到了一个比较酷炫的3D可视化的类 Q3DBars,该类提供了一些方法用于render 3D的柱状图

                           1、初始化:Q3DBars对象的初始化放在了GraphModifier的构造函数中

//截取与Q3DBars有关代码
GraphModifier::GraphModifier(Q3DBars *bargraph)
    : m_graph(bargraph)  //使用初始化列表少了一次调用默认构造函数的过程
{
    //![设置图形的一些视觉效果]
    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);
    m_graph->setMultiSeriesUniform(true);
}

  坐标轴与数据设置

    m_months << "January" << "February" << "March" << "April" << "May" << "June" << "July" << "August" << "September" << "October" << "November" << "December";
    m_years << "2006" << "2007" << "2008" << "2009" << "2010" << "2011" << "2012" << "2013";

    //! [3]
    m_temperatureAxis->setTitle("Average temperature");
    m_temperatureAxis->setSegmentCount(m_segments);
    m_temperatureAxis->setSubSegmentCount(m_subSegments);
    m_temperatureAxis->setRange(m_minval, m_maxval);
    m_temperatureAxis->setLabelFormat(QString(QStringLiteral("%.1f ") + celsiusString));
    m_temperatureAxis->setLabelAutoRotation(30.0f);
    m_temperatureAxis->setTitleVisible(true);

    m_yearAxis->setTitle("Year");
    m_yearAxis->setLabelAutoRotation(30.0f);
    m_yearAxis->setTitleVisible(true);
    m_monthAxis->setTitle("Month");
    m_monthAxis->setLabelAutoRotation(30.0f);
    m_monthAxis->setTitleVisible(true);

    m_graph->setValueAxis(m_temperatureAxis);
    m_graph->setRowAxis(m_yearAxis);
    m_graph->setColumnAxis(m_monthAxis);
    //! [3]

 每个柱状图的属性和备注设置

    m_primarySeries->setItemLabelFormat(QStringLiteral("Oulu - @colLabel @rowLabel: @valueLabel"));                                               //备注
    m_primarySeries->setMesh(QAbstract3DSeries::MeshBevelBar);//网格图
    m_primarySeries->setMeshSmooth(false);                    //网格平滑未开启

    m_secondarySeries->setItemLabelFormat(QStringLiteral("Helsinki - @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);                   //Series每次只能显示一种,可切换

我们关注一些槽函数

//按功能从上到下
void rotateX(int rotation);  //水平旋转
void GraphModifier::rotateY(int rotation); //垂直旋转
void GraphModifier::changeLabelBackground(); //改变label的style
changePresetCamera();  //改变camera观察的位置
zoomToSelectedBar();   //缩放到选定栏的属性动画
setGridEnabled();      //设置是否显示网格
setSmoothBars();       //设置是否平滑处理
setReflection(bool enabled); //是否加光反射
changeRange(int range); //改变数据年份,改了显示的数据
changeFontSize(int fontsize); //改变字体大小
...

这些都是Q3DBars对象接口的炫技,眼花缭乱,有用的时候再看示例也未尝不可

本示例中用到了自定义信号,如是写

Q_SIGNALS:
    void shadowQualityChanged(int quality);
    void backgroundEnabledChanged(bool enabled);
    void gridEnabledChanged(bool enabled);
    void fontChanged(QFont font);
    void fontSizeChanged(int size);

只要在自己需要的地方,将信息广播出去即可(似C#事件,需要加emit关键字)

void GraphModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq)
{
    int quality = int(sq);
    // Updates the UI component to show correct shadow quality
    emit shadowQualityChanged(quality);
}

当然也要有注册该信号 处理该信号的地方(这样就实现了两个类之间的低耦合通信

    QObject::connect(modifier, &GraphModifier::shadowQualityChanged, shadowQuality,
                     &QComboBox::setCurrentIndex);
    QObject::connect(widgetgraph, &Q3DBars::shadowQualityChanged, modifier,
                     &GraphModifier::shadowQualityUpdatedByVisual);

3、其他

如果将数据来源是从数据库或者excel中读取,而不是写死;我们就可以完成一个炫酷的数据可视化工具了~

遗留:怎么理解QT中3D的坐标系?怎么样熟练操控activeCamera?这是一个问题

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页