48、柱状图---------QChart

柱状图

柱状图

Qt 柱状图的核心组件

在 Qt Charts 中,绘制柱状图主要涉及以下核心组件:

●QBarSet:用于存储柱状图中的一组数据。

●QBarSeries:包含多个 QBarSet,代表一组柱状图。

●QChart:图表的核心对象,管理所有的系列、图例和标题。

●QChartView:用于在界面上显示 QChart 的视图组件,类似于 QGraphicsView。

●QValueAxis 和 QCategoryAxis:用于设置图表的值轴和类别轴。

示例场景

完成基本的柱状图

Jane, John,Axel对应的就是三个QBarSet,我们分别向三个QBarSet写入六组数据,对应的就是下面六个月分产生的纵轴的值。

解决思路

1 先在构造函数中添加柱状图要用到的成员

cpp 复制代码
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    // Qt Charts 成员
    QChartView *chartView;
    QChart *chart;
    QBarSeries *series;
    //三个集合,每个坐标对应三个柱子
    QBarSet *set0;
    QBarSet *set1;
    QBarSet *set2;
};

实现构造函数

cpp 复制代码
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    chartView(new QChartView(this))
    , chart(new QChart())
    , series(new QBarSeries())
    , set0(new QBarSet("Jane"))
    , set1(new QBarSet("John"))
    , set2(new QBarSet("Axel"))
{
    ui->setupUi(this);

    // 初始化数据
     *set0 << 1 << 2 << 3 << 4 << 5 << 6;
     *set1 << 5 << 0 << 0 << 4 << 0 << 7;
     *set2 << 3 << 5 << 8 << 13 << 8 << 5;

     // 添加 QBarSet 到 QBarSeries
     series->append(set0);
     series->append(set1);
     series->append(set2);

     // 添加系列到图表
     chart->addSeries(series);
     chart->setTitle("简单的柱状图示例");
     chart->setAnimationOptions(QChart::SeriesAnimations);

     // 设置类别轴
     QStringList categories;
     categories << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun";
     QBarCategoryAxis *axisX = new QBarCategoryAxis();
     axisX->append(categories);
     chart->addAxis(axisX, Qt::AlignBottom);
     series->attachAxis(axisX);

     // 设置值轴
     QValueAxis *axisY = new QValueAxis();
     axisY->setRange(0, 15);
     chart->addAxis(axisY, Qt::AlignLeft);
     series->attachAxis(axisY);

     // 设置图表到视图
     chartView->setChart(chart);
     chartView->setRenderHint(QPainter::Antialiasing);

     // 设置布局
     QVBoxLayout *layout = new QVBoxLayout();
     layout->addWidget(chartView);

     QWidget *widget = new QWidget();
     widget->setLayout(layout);
     setCentralWidget(widget);

     resize(800,600);
}

柱状图美化(扩展内容)

为了使柱状图更具美观性和可读性,可以进行多种定制和美化操作,包括:

  • 设置柱状条颜色
  • 添加标签和数值
  • 设置轴和标题
  • 设定图表主题
  • 添加动画效果
设置柱状条颜色

可以为不同的柱状条设置不同的颜色,以增强视觉效果。

cpp 复制代码
// 设置每个 QBarSet 的颜色
set0->setColor(Qt::red);
set1->setColor(Qt::green);
set2->setColor(Qt::blue);

将以上代码添加到 mainwindow.cpp// 添加 QBarSet 到 QBarSeries 之后。

添加标签和数值

为柱状条添加标签,以显示数值信息。

cpp 复制代码
// 显示每个柱状条的数值标签
set0->setLabelRotation(0);
set1->setLabelRotation(0);
set2->setLabelRotation(0);

set0->setLabelVisible(true);
set1->setLabelVisible(true);
set2->setLabelVisible(true);

将以上代码添加到柱状条颜色设置之后。

设置轴和标题

自定义轴的范围、标题等属性。

cpp 复制代码
// 设置 X 轴标题
axisX->setTitleText("月份");

// 设置 Y 轴标题
axisY->setTitleText("销售量");

将以上代码添加到轴设置之后。

设定图表主题

Qt Charts 提供了多种内置主题,可以快速改变图表的整体风格。

cpp 复制代码
chart->setTheme(QChart::ChartThemeQt);

可选主题列表包括:

  • ChartThemeLight
  • ChartThemeBlueCerulean
  • ChartThemeDark
  • ChartThemeBrownSand
  • ChartThemeBlueNcs
  • ChartThemeHighContrast
  • ChartThemeBlueIcy
  • ChartThemeQt(默认)

将以上代码添加到图表初始化部分。

添加动画效果

通过设置动画选项,使图表在数据变化时具有过渡效果。

cpp 复制代码
chart->setAnimationOptions(QChart::AllAnimations);

动态更新柱状图

在MainWindow的构造函数中我们将原来加载数据的方式改为先初始化三个QVector容器,里面存储我们将要用到的数据

cpp 复制代码
    //分别初始化三组数据,将来用作三个BarSet
    data0 = {1, 2, 3, 4, 5, 6};
    data1 = {5, 0, 0, 4, 0, 7};
    data2 = {3, 5, 8, 13, 8, 5};

因为三个QVector大小一致,我们可以通过一次遍历分别取出vector中的元素添加到不同的QBarSet中

cpp 复制代码
    //初始化三个BarSet
    for(int i = 0; i < data0.size(); i++){
        //依次向三个集合添加数据
        set0->append(data0[i]);
        set1->append(data1[i]);
        set2->append(data2[i]);
    }

之后的逻辑和之前是一致的, 我们这里创建定时器

cpp 复制代码
     //初始化定时器
     timer = new QTimer(this);
     //连接定时器超时逻辑
     connect(timer, &QTimer::timeout,this, &MainWindow::updateChart);

     //每隔2s更新依次
     timer->start(2000);

定时器的回调函数

cpp 复制代码
void MainWindow::updateChart(){
    int maxVal = -1;
    // 模拟数据变化:随机增加或减少每个 QBarSet 的值
       for (int i = 0; i < set0->count(); ++i) {
           int change0 = QRandomGenerator::global()->bounded(-2, 3); // -2到+2
           int change1 = QRandomGenerator::global()->bounded(-2, 3);
           int change2 = QRandomGenerator::global()->bounded(-2, 3);

           data0[i] += change0;
           data1[i] += change1;
           data2[i] += change2;

           // 确保数据不为负数
           if (data0[i] < 0) data0[i] = 0;
           if (data1[i] < 0) data1[i] = 0;
           if (data2[i] < 0) data2[i] = 0;

           // 更新 QBarSet 的值
           set0->replace(i,data0[i]);
           set1->replace(i,data1[i]);
           set2->replace(i,data2[i]);

           //定义QList用来获取最大值
           QList<int> temp_list;
           temp_list << data0[i] << data1[i] << data2[i] << maxVal;
           //获取最大元素的迭代器
           auto max_iter = std::max_element(temp_list.begin(),temp_list.end());
           if(max_iter != temp_list.end()){
                maxVal = *max_iter;
           }

       }

       qDebug() << "maxval is " << maxVal;
       // 动态调整 Y 轴范围
       // 基类向子类转换用qobject_cast
       QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axisY());
       if (axisY) {
           axisY->setRange(0, maxVal + 5);
       }
}

再次启动程序,运行后会发现每隔两秒数据就更新一次。

捕获事件信号

想实现如下图的悬浮效果

QBarSet同样具有点击信号和悬浮信号

被点击时发出如下信号

cpp 复制代码
[signal] void QBarSet::clicked(int index)

悬浮时发出信号

cpp 复制代码
[signal] void QBarSet::hovered(bool status, int index)

在构造函数中连接点击信号

cpp 复制代码
     //连接点击信号
     for(auto & bar_set : series->barSets()){
         //连接点击事件
        connect(bar_set, &QBarSet::clicked,
                this,&MainWindow::handleBarClicked);
     }

实现点击函数,因为信号里只有一个index,我们需要通过sender()方法获取发送信号的对象,sender()返回的是QObject,进而转为具体的对象类型

cpp 复制代码
//处理点击事件
void MainWindow::handleBarClicked(int index)
{
    //此处获得信号的发送者
    QBarSet *barSet = qobject_cast<QBarSet*>(sender());
     if (barSet) {
         QMessageBox::information(this, "柱状条点击",
                QString("您点击了 %1, 数据为 %2").arg(barSet->label())
                                  .arg(barSet->at(index)));
     }
}

同样在构造函数中连接悬浮信号

cpp 复制代码
     //连接信号
     for(auto & bar_set : series->barSets()){
         //连接点击事件
        connect(bar_set, &QBarSet::clicked,
                this,&MainWindow::handleBarClicked);

        //连接悬浮信号
        connect(bar_set,&QBarSet::hovered,
                this,&MainWindow::handleBarHovered);
     }

实现悬浮事件回调函数

cpp 复制代码
//处理鼠标悬浮事件
void MainWindow::handleBarHovered(bool status, int index)
{
    //此处获得信号的发送者
    QBarSet *barSet = qobject_cast<QBarSet*>(sender());
    if(!barSet){
        return;
    }

    //处理未悬浮在数据的情况
    if (!status) {
        QToolTip::hideText();
     }

    auto label_str = barSet->label();
    auto num = barSet->at(index);

    QToolTip::showText(QCursor::pos(),
                       QString("%1:数据为%2").arg(label_str).arg(num));
}
相关推荐
Tanecious.2 小时前
蓝桥杯备赛:Day8-小苯的异或和
c++·蓝桥杯
王老师青少年编程2 小时前
csp信奥赛c++中的递归和递推研究
c++·算法·递归·递推·csp·信奥赛
样例过了就是过了2 小时前
LeetCode热题100 跳跃游戏
c++·算法·leetcode·贪心算法·动态规划
chen_ever2 小时前
从网络基础到吃透 Linux 高并发 I/O 核心(epoll+零拷贝 完整版)
linux·网络·c++·后端
无限进步_3 小时前
【C++&string】寻找字符串中第一个唯一字符:两种经典解法详解
开发语言·c++·git·算法·github·哈希算法·visual studio
小此方3 小时前
Re:思考·重建·记录 现代C++ C++11篇 (二) 右值引用与移动语义&引用折叠与完美转发
开发语言·c++·c++11·现代c++
深邃-3 小时前
【C语言】-数据在内存中的存储(1)
c语言·开发语言·数据结构·c++·算法
xiaoye-duck3 小时前
《算法题讲解指南:优选算法-字符串》--61.最长公共前缀,62.最长回文子串,63.二进制求和,64.字符串相乘
c++·算法·字符串
chh5633 小时前
从零开始学C++--类和对象
java·开发语言·c++·学习·算法