QT Charts绘图-柱状图、折线图的绘画
图表的类型由数据序列决定,除了折线图,QT charts还提供了柱状图、饼图、百分比图等常见图表。本程序主要目的时演示柱状图的绘制方法,以此深入了解QT Charts的基本用法。
程序实现的功能
在运行界面的左上方 “初始化数据“ 按钮实现了随机生成若干个学生的数学、语文、英语分数。平均分是自动计算的,左下方是根据分数统计的结果,右方是图表的页面。当我们修改学生数据时,themodel会发出itemChanged()信号,相应的on_itemChanged()槽函数自动跟新图表,和统计结果。运行界面如下:
界面分析
窗口左上方是一个QTableView组件,使用Model/View结构提供数据编辑功能。左下角是一个QTreeWidget组件,用于显示统计数据结果。窗口右方是一个QTabWidget组件,用于显示柱状图。图表的显示用一个QgraphicsView组件升级为QChartsView。
mainwindow.h的实现
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QtCharts>
#define fixedColumnCount 5 //数据模型的列数
#define iniDataRowCount 10 //学生个数
#define colNoName 0 //姓名的列编号
#define colNoMath 1 //数学的列编号
#define colNoChinese 2 //语文的列编号
#define colNoEnglish 3 //英语的列编号
#define colNoAverage 4 //平均分的列编号
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QStandardItemModel *theModel;//数据模型
void iniData();//初始化数据
void surveyData();//统计数据
void iniBarChart(); //柱状图初始化
void buildBarChart();//构建柱状图
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_actGenData_triggered(); //初始化数据
void on_actTongJi_triggered(); //统计结果
//当数据发生变化时 更新图表
void on_itemChanged(QStandardItem *item);
void on_btnBuildBarChart_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp的实现
#include "mainwindow.h"
#include "ui_mainwindow.h"
void MainWindow::iniData()
{
//数据初始化
QStringList headerList;
headerList<<"姓名"<<"数学"<<"语文"<<"英语"<<"平均分";
theModel->setHorizontalHeaderLabels(headerList); //设置表头文字
qsrand(QTime::currentTime().second());//随机数种子
for (int i=0;i<theModel->rowCount();i++)
{
QString studName=QString::asprintf("学生%2d",i+1);
QStandardItem* aItem=new QStandardItem(studName);//创建item
aItem->setTextAlignment(Qt::AlignHCenter);
theModel->setItem(i,colNoName,aItem); //i行,学生列,设置item
qreal avgScore=0;
for (int j=colNoMath;j<=colNoEnglish;j++) //数学,语文,英语
{ //不包含最后一列
qreal score=50.0+(qrand() % 50);//随机数
avgScore+=score;
aItem=new QStandardItem(QString::asprintf("%.0f",score));//创建 item
aItem->setTextAlignment(Qt::AlignHCenter);
theModel->setItem(i,j,aItem); //为模型的某个行列位置设置Item
}
aItem=new QStandardItem(QString::asprintf("%.1f",avgScore/3));//创建平均分item
aItem->setTextAlignment(Qt::AlignHCenter);
aItem->setFlags(aItem->flags() & (!Qt::ItemIsEditable));//平均分不允许编辑
theModel->setItem(i,colNoAverage,aItem); //平均分
}
}
void MainWindow::surveyData()
{
//数据统计
int cnt50,cnt60,cnt70,cnt80,cnt90;
qreal sumV,minV,maxV;
qreal val;
QTreeWidgetItem *item; //节点
int i,j;
for(i=colNoMath;i<=colNoAverage;i++)
{
sumV=0;
cnt50=0;
cnt60=0;
cnt70=0;
cnt80=0;
cnt90=0;
for(j=0;j<theModel->rowCount();j++)
{
val=theModel->item(j,i)->text().toDouble();
if (j==0)
{
minV=val;
maxV=val;
}
if (val<minV)
minV=val;
if (val>maxV)
maxV=val;
//
sumV+=val;
if (val<60)
cnt50++;
else if ((val>=60) && (val<70))
cnt60++;
else if ((val>=70) && (val<80))
cnt70++;
else if ((val>=80) && (val<90))
cnt80++;
else
cnt90++;
}
//
item=ui->treeWidget->topLevelItem(0); //<60
item->setText(i,QString::number(cnt50));
item->setTextAlignment(i,Qt::AlignHCenter);
item=ui->treeWidget->topLevelItem(1); //60
item->setText(i,QString::number(cnt60));
item->setTextAlignment(i,Qt::AlignHCenter);
item=ui->treeWidget->topLevelItem(2); //70
item->setText(i,QString::number(cnt70));
item->setTextAlignment(i,Qt::AlignHCenter);
item=ui->treeWidget->topLevelItem(3); //80
item->setText(i,QString::number(cnt80));
item->setTextAlignment(i,Qt::AlignHCenter);
item=ui->treeWidget->topLevelItem(4); //90
item->setText(i,QString::number(cnt90));
item->setTextAlignment(i,Qt::AlignHCenter);
item=ui->treeWidget->topLevelItem(5); //average
item->setText(i,QString::number(sumV/theModel->rowCount()));
item->setTextAlignment(i,Qt::AlignHCenter);
item=ui->treeWidget->topLevelItem(6); //max
item->setText(i,QString::number(maxV));
item->setTextAlignment(i,Qt::AlignHCenter);
item=ui->treeWidget->topLevelItem(7); //min
item->setText(i,QString::number(minV));
item->setTextAlignment(i,Qt::AlignHCenter);
}
}
void MainWindow::iniBarChart()
{
//柱状图初始化
QChart*chart=new QChart();
chart->setTitle("BarChar 演示");
chart->setAnimationOptions(QChart::SeriesAnimations);
ui->chartViewBar->setChart(chart);
ui->chartViewBar->setRenderHint(QPainter::Antialiasing);
}
void MainWindow::buildBarChart()
{
//构造柱状图
QChart *chart =ui->chartViewBar->chart(); //获取ChartView关联的chart
chart->removeAllSeries(); //删除所有序列
chart->removeAxis(chart->axisX()); //删除坐标轴
chart->removeAxis(chart->axisY()); //删除坐标轴
//创建三个QBarSet数据集,从数据模型的表头获取Name
QBarSet *setMath = new QBarSet(theModel->horizontalHeaderItem(colNoMath)->text());
QBarSet *setChinese = new QBarSet(theModel->horizontalHeaderItem(colNoChinese)->text());
QBarSet *setEnglish= new QBarSet(theModel->horizontalHeaderItem(colNoEnglish)->text());
QLineSeries *Line = new QLineSeries(); //创建一个QLineSeries序列用于显示平均分
Line->setName(theModel->horizontalHeaderItem(colNoAverage)->text());
QPen pen;
pen.setColor(Qt::red);
pen.setWidth(2);
Line->setPen(pen);
// lineseries->setPointLabelsVisible(true);
// lineseries->setPointLabelsFormat("@yPoint");
for(int i=0;i<theModel->rowCount();i++)
{//从数据模型获取数据
setMath->append(theModel->item(i,colNoMath)->text().toInt()); //数学
setChinese->append(theModel->item(i,colNoChinese)->text().toInt()); //语文
setEnglish->append(theModel->item(i,colNoEnglish)->text().toInt()); //英语
Line->append(QPointF(i,theModel->item(i,colNoAverage)->text().toFloat())); //平均分
}
//创建一个柱状图序列 QBarSeries, 并添加三个数据集
QBarSeries *series = new QBarSeries();
series->append(setMath);
series->append(setChinese);
series->append(setEnglish);
// series->setLabelsVisible(true);
// series->setLabelsPosition(QAbstractBarSeries::LabelsOutsideEnd);// LabelsCenter
// series->setLabelsPosition(QAbstractBarSeries::LabelsCenter);// LabelsCenter
// lineseries->setPointLabelsVisible(true);
// lineseries->setPointLabelsFormat("@yPoint");
chart->addSeries(series); //添加柱状图序列
chart->addSeries(Line); //添加折线图序列
//用于横坐标在字符串列表,即学生姓名
QStringList categories;
for (int i=0;i<theModel->rowCount();i++)
categories <<theModel->item(i,colNoName)->text();
//用于柱状图的坐标轴
QBarCategoryAxis *axisX = new QBarCategoryAxis();
axisX->append(categories); //添加横坐标文字列表
// chart->createDefaultAxes();
chart->setAxisX(axisX, series); //设置横坐标
chart->setAxisX(axisX, Line);//设置横坐标
axisX->setRange(categories.at(0), categories.at(categories.count()-1)); //这只坐标轴范围
//数值型坐标作为纵轴
QValueAxis *axisY = new QValueAxis;
axisY->setRange(0, 100);
axisY->setTitleText("分数");
axisY->setTickCount(6);//11
axisY->setLabelFormat("%.0f"); //标签格式
// axisY->setGridLineVisible(false);
// axisY->setMinorTickCount(4);
chart->setAxisY(axisY, series); //为
chart->setAxisY(axisY, Line);
// lineseries->setPointLabelsVisible(true);
// lineseries->setPointLabelsFormat("@yPoint");
chart->legend()->setVisible(true); //显示图例
chart->legend()->setAlignment(Qt::AlignBottom); //图例显示在下方
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
theModel=new QStandardItemModel(iniDataRowCount,fixedColumnCount,this);
iniData();
surveyData();
//数据模块的 itemChanged信号与自定义的槽函数关联,用于自动计算平均分
connect(theModel,SIGNAL(itemChanged(QStandardItem *)),
this,SLOT(on_itemChanged(QStandardItem *)));
ui->tableView->setModel(theModel);
iniBarChart();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actGenData_triggered()
{
iniData();
surveyData();
}
void MainWindow::on_actTongJi_triggered()
{
surveyData();
}
void MainWindow::on_itemChanged(QStandardItem *item)
{
//自动计算平均分
if ((item->column()<colNoMath) || (item->column()>colNoEnglish))
return; //如果被修改的item不是数学、语文、英语,就退出
int rowNo=item->row(); //获取数据的行编号
qreal avg=0;
QStandardItem *aItem;
for (int i=colNoMath;i<=colNoEnglish;i++)
{ //获取三个分数的和
aItem=theModel->item(rowNo,i);
avg+=aItem->text().toDouble();
}
avg=avg/3; //计算平均分
aItem=theModel->item(rowNo,colNoAverage); //获取平均分数据的item
aItem->setText(QString::asprintf("%.1f",avg)); //更新平均分数据
//当数据发生变化时,更新柱状图和重新统计学生分数情况
on_btnBuildBarChart_clicked();
surveyData();
}
void MainWindow::on_btnBuildBarChart_clicked()
{//创建柱状图
buildBarChart();
}
一个柱状图有多个数据集组成,在此创建3个QBarSet数据集,分别对应数学、语文、英语3门课的分数。柱状图的数据集类是QBarSet,创建对象时使用了数据模型的列标题作为数据集的名称,这个名称会显示在图例中。显示平均分使用QLineSeries序列,即折线序列。
创建3个QBarSet数据集和折线序列后,遍历数据模型将数据添加到数据集或折线序列。如数学数据的添加
setMath->append(theModel->item(i,colNoMath)->text().toInt());
为3个QBarSet数据集添加完数据后创建一个QBarSeries序列,并将3个数据集添加到这个QBarSeries序列
//创建一个柱状图序列 QBarSeries, 并添加三个数据集
QBarSeries *series = new QBarSeries();
series->append(setMath);
series->append(setChinese);
series->append(setEnglish);
柱状图相关的主要类介绍
QBarSet 用于创建柱状图的数据集
QBarSeries 柱状图序列,一个柱状图序列一般包含多个QBarSet数据集
QBarCategoryAxis 柱状图分类坐标,以文字标签形式表示的坐标
下面时以上三个类的主要函数接口
QBarSet
QBarSeries
QBarCategoryAxis