柱状图
柱状图
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);
可选主题列表包括:
ChartThemeLightChartThemeBlueCeruleanChartThemeDarkChartThemeBrownSandChartThemeBlueNcsChartThemeHighContrastChartThemeBlueIcyChartThemeQt(默认)
将以上代码添加到图表初始化部分。
添加动画效果
通过设置动画选项,使图表在数据变化时具有过渡效果。
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));
}