15-数据准备
9.3.2 数据准备
图9-10左上方的数据采用Model/View结构,在主窗口类MainWindow里定义了数据模型,并在主窗口的构造函数里初始化数据。下面是MainWindow的类定义(省略了部分窗口界面组件的槽函数定义)。
#define fixedColumnCount 5 //数据模型的列数
#define iniDataRowCount 6 //学生个数
#define colNoName 0 //姓名的列编号
#define colNoMath 1 //数学的列编号
#define colNoChinese 2 //语文的列编号
#define colNoEnglish 3 //英语的列编号
#define colNoAverage 4 //平均分的列编号
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QStandardItemModel *theModel; //数据模型
void iniData(); //初始化数据
void surveyData(); //统计数据
void iniBarChart(); //柱状图初始化
void buildBarChart(); //构建柱状图
void iniPiewChart(); //饼图
void buildPieChart();
void iniStackedBar(); //堆叠图
void buildStackedBar();
void iniPercentBar(); //百分比柱状图
void buildPercentBar();
void iniScatterChart(); //散点图
void buildScatterChart();
private slots:
//三个分数列的数据发生变化,重新计算平均分
void on_itemChanged(QStandardItem *item);
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
在头文件中首先定义了一些常量,包括数据模型的列数fixedColumnCount和学生人数iniData RowCount。定义为常量后便于修改,例如现在学生人数定义为6个,后续为了增大数据样本,可以将学生人数定义为30人。头文件里还定义了数据模型中5个列的编号,在后续的程序里使用常量。
MainWindow类中定义了数据模型类变量theModel,函数iniData()用于随机初始化数据,surveyData()用于对数据进行统计。
每个图表有两个函数,一个用于初始化,一个用于根据数据构建图表。如iniBarChart()用于初始化柱状图,buildBarChart()用于根据数据构建柱状图。
数据模型中每名学生的平均分根据3门课的成绩自动计算,希望在界面修改一个分数时,自动计算其平均分。这里可以利用QStandardItemModel的itemChanged()信号,在某个数据项被修改后会发射此信号,定义了一个私有槽函数on_itemChanged()用于响应此信号,实现平均分的自动计算。
下面是MainWindow的构造函数的实现代码。
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(); //柱状图初始化
iniPiewChart();//饼图初始化
iniStackedBar();//堆叠图初始化
iniPercentBar();//百分比柱状图初始化
iniScatterChart(); //散点图初始化
}
在构造函数中,创建数据模型theModel,然后初始化数据、统计数据,将数据模型的itemChanged()信号与自定义槽函数on_itemChanged()关联起来。然后对5个图表调用函数进行初始化。
下面是iniData()函数的实现代码,用于对数据模型进行初始化。
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); //学生列,设置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));
aItem->setTextAlignment(Qt::AlignHCenter);
theModel->setItem(i,j,aItem); //设置Item
}
aItem=new QStandardItem(QString::asprintf("%.1f",avgScore/3));//平均分
aItem->setTextAlignment(Qt::AlignHCenter);
aItem->setFlags(aItem->flags() & (!Qt::ItemIsEditable));//不允许编辑
theModel->setItem(i,colNoAverage,aItem);
}
}
数据初始化会设置表头标题,数学、语文、英语分数采用随机数生成,然后计算平均分。其中,平均分一列是不允许编辑的。
数据统计函数surveyData()用于对数据模型里的数据按照分数段进行统计,然后显示在QTreeWidget组件里。数据统计是简单的数学计算,对QTreeWidget的操作在4.7节有介绍,所以surveyData()函数的代码不再具体显示。
自定义槽函数on_itemChanged()用于在分数修改后自动计算平均分,下面是槽函数on_itemChanged()的代码。
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));
}
传递的参数item是被修改的数据项。程序首先判断该数据项是不是3个基本分数,如果不是就退出。通过item->row()获取当前数据的行号,然后提取3个基本分数计算平均分,再更新数据模型中的平均分数据项。