QStandardItemModel 是一个通用的模型类
能够以任意的方式组织数据
数据组织的基本单位为数据项 (QStandardItem)
每一个数据项能够存储多个具体数据 (附加数据角色)
每一个数据项能够对数据状态进行控制 (可编辑,可选,...)
Qt 中通用的模型类 QStandardItemModel
Qt 中的变体类型 QVariant
QVariant 是一个用于封装的类型
QVariant 能够表示大多数常见的值类型
QVariant 每次只能封装 (保存) 单一类型的值
QVariant 的意义在于能够设计 "返回类型可变的函数"
变体类型 QVariant 中的常用成员函数
变体类型 QVariant 的应用
#include <QCoreApplication>
#include <QVariant>
#include <QPoint>
#include <QDebug>
QVariant func()
{
QVariant v(10);
return v;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QVariant v1(1);
QVariant v2(3.14);
QVariant v3("520");
QVariant v4(QPoint(3, 4));
QVariant v5;
qDebug() << v1.type();
qDebug() << v1.typeName();
qDebug() << v1.toInt();
qDebug() << v1.value<int>();
qDebug() << endl;
qDebug() << v2.type();
qDebug() << v2.typeName();
qDebug() << v2.toInt();
qDebug() << v2.toDouble();
qDebug() << v2.value<double>();
qDebug() << endl;
qDebug() << v3.type();
qDebug() << v3.typeName();
qDebug() << v3.toInt();
qDebug() << v3.value<QString>();
qDebug() << endl;
bool ok = true;
qDebug() << v4.type();
qDebug() << v4.typeName();
qDebug() << v4.toPoint();
qDebug() << v4.value<QPoint>();
qDebug() << v4.toInt(&ok);
qDebug() << ok;
qDebug() << endl;
qDebug() << v5.isValid();
return a.exec();
}
程序运行结果如下所示:
工程中的常用模型设计
解析数据源中的数据 (数据库,网络,串口,等)
将解析后的数据存入 QStandardItem 对象中
根据数据间的关系在 QStandardItemModel 对象中组织数据项
选择合适的视图显示数据值
实例分析:
在文件中以行的形式存储了考试存储信息 (ID,Name,Score)
开发 GUI 程序显示文件中的信息
- 计算平均成绩
- 查找最好成绩和最差成绩
- 可刷新显示的内容和删除内容
系统框架图
系统核心类图
系统架构图
DataSource 类的设计与实现
设置数据源并读取数据
对数据进行解析后生产数据对象
ScoreInfo 类的设计与实现
封装数据源中的一组完整数据
提供返回具体数值的接口函数
ScoreInfoModel 类的设计与实现
使用标准模型类 QStandardItemModel 作为成员
以 ScoreInfo 类对象为最小单位进行数据组织
数据交互流程图
界面设计
右键上下文菜单的实现
定义菜单对象 (QMenu)
连接菜单中的 QAction 对象到槽函数
定义事件过滤器,并处理 ContextMenu 事件
在当前鼠标的位置打开菜单对象
编程实验
DataSource.h
#ifndef DATASOURCE_H
#define DATASOURCE_H
#include <QObject>
#include <QList>
#include "ScoreInfo.h"
class DataSource : public QObject
{
Q_OBJECT
private:
QList<ScoreInfo> m_data;
bool parse(QString line, ScoreInfo &obj);
public:
explicit DataSource(QObject* parent = nullptr);
bool setDataPath(const QString path);
QList<ScoreInfo> fetchData();
int count() const;
};
#endif // DATASOURCE_H
DataSource.cpp
#include "DataSource.h"
#include <QFile>
#include <QTextStream>
DataSource::DataSource(QObject* parent) : QObject(parent)
{
}
bool DataSource::parse(QString line, ScoreInfo& obj)
{
bool ret = true;
QStringList list = line.split(",", QString::SkipEmptyParts);
if(list.count() == 3)
{
QString id = list[0].trimmed();
QString name = list[1].trimmed();
QString sscore = list[2].trimmed();
int score = sscore.toInt(&ret);
if((ret) && (score >= 0) && (score <= 150))
{
obj = ScoreInfo(id, name, score);
}
else
{
ret = false;
}
}
else
{
ret = false;
}
return ret;
}
bool DataSource::setDataPath(const QString path)
{
bool ret = true;
QFile file(path);
if(file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream in(&file);
while(!in.atEnd())
{
QString line = in.readLine();
ScoreInfo info;
if(parse(line, info))
{
m_data.append(info);
}
}
file.close();
}
else
{
ret = false;
}
return ret;
}
QList<ScoreInfo> DataSource::fetchData()
{
QList<ScoreInfo> ret = m_data;
m_data.clear();
return ret;
}
int DataSource::count() const
{
return m_data.count();
}
ScoreInfo.h
#ifndef SCOREINFO_H
#define SCOREINFO_H
#include <QObject>
class ScoreInfo : public QObject
{
Q_OBJECT
private:
QString m_id;
QString m_name;
int m_score;
public:
explicit ScoreInfo(QObject* parent = nullptr);
explicit ScoreInfo(QString id, QString name, int score, QObject* parent = nullptr);
ScoreInfo(const ScoreInfo& obj);
ScoreInfo& operator= (const ScoreInfo& obj);
QString id();
QString name();
int score();
};
#endif // SCOREINFO_H
ScoreInfo.cpp
#include "ScoreInfo.h"
ScoreInfo::ScoreInfo(QObject *parent) : QObject(parent)
{
}
ScoreInfo::ScoreInfo(QString id, QString name, int score, QObject* parent) : QObject(parent)
{
m_id = id;
m_name = name;
m_score = score;
}
ScoreInfo::ScoreInfo(const ScoreInfo& obj)
{
m_id = obj.m_id;
m_name = obj.m_name;
m_score = obj.m_score;
}
ScoreInfo& ScoreInfo::operator= (const ScoreInfo& obj)
{
if(this != &obj)
{
m_id = obj.m_id;
m_name = obj.m_name;
m_score = obj.m_score;
}
return *this;
}
QString ScoreInfo::id()
{
return m_id;
}
QString ScoreInfo::name()
{
return m_name;
}
int ScoreInfo::score()
{
return m_score;
}
ScoreInfoModel.h
#ifndef SCOREINFOMODEL_H
#define SCOREINFOMODEL_H
#include <QObject>
#include <QStandardItemModel>
#include <QTableView>
#include "ScoreInfo.h"
class ScoreInfoModel
{
private:
QStandardItemModel m_model;
public:
ScoreInfoModel();
bool add(ScoreInfo obj);
void add(QList<ScoreInfo> obj);
bool remove(int i);
ScoreInfo getItem(int i);
int count();
void clear();
void setView(QTableView& view);
};
#endif // SCOREINFOMODEL_H
ScoreInfoModel.cpp
#include "ScoreInfoModel.h"
#include <QStandardItem>
#include <QModelIndex>
#include <QVariant>
ScoreInfoModel::ScoreInfoModel()
{
}
bool ScoreInfoModel::add(ScoreInfo obj)
{
bool ret = true;
QStandardItem *root = m_model.invisibleRootItem();
QStandardItem *item0 = new QStandardItem();
QStandardItem *item1 = new QStandardItem();
QStandardItem *item2 = new QStandardItem();;
if(count() == 0)
{
QStringList list;
list.append("ID");
list.append("NAME");
list.append("SCORE");
m_model.setHorizontalHeaderLabels(list);
}
if((root != NULL) && (item0 != NULL) && (item1 != NULL) && (item2 != NULL))
{
item0->setData(obj.id(), Qt::DisplayRole);
item1->setData(obj.name(), Qt::DisplayRole);
item2->setData(obj.score(), Qt::DisplayRole);
item0->setEditable(false);
item1->setEditable(false);
item2->setEditable(false);
int newRow = count();
root->setChild(newRow, 0, item0);
root->setChild(newRow, 1, item1);
root->setChild(newRow, 2, item2);
}
else
{
ret = false;
}
return ret;
}
void ScoreInfoModel::add(QList<ScoreInfo> obj)
{
for(int i = 0; i < obj.count(); i++)
{
add(obj[i]);
}
}
bool ScoreInfoModel::remove(int i)
{
bool ret = ((i >= 0) && (i < count()));
if(ret)
{
m_model.removeRows(i, 1);
}
return ret;
}
ScoreInfo ScoreInfoModel::getItem(int i)
{
ScoreInfo info;
bool ret = ((i >= 0) && (i < count()));
if(ret)
{
QModelIndex index0 = m_model.index(i, 0, QModelIndex());
QModelIndex index1 = m_model.index(i, 1, QModelIndex());
QModelIndex index2 = m_model.index(i, 2, QModelIndex());
QVariant v0 = index0.data(Qt::DisplayRole);
QVariant v1 = index1.data(Qt::DisplayRole);
QVariant v2 = index2.data(Qt::DisplayRole);
info = ScoreInfo(v0.toString(), v1.toString(), v2.toInt());
}
return info;
}
int ScoreInfoModel::count()
{
return m_model.rowCount(QModelIndex());
}
void ScoreInfoModel::clear()
{
m_model.clear();
}
void ScoreInfoModel::setView(QTableView& view)
{
view.setModel(&m_model);
}
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTableView>
#include <QPushButton>
#include <QMenu>
#include "ScoreInfoModel.h"
class Widget : public QWidget
{
Q_OBJECT
private:
ScoreInfoModel m_model;
QTableView m_view;
QMenu m_menu;
QPushButton m_refreshBtn;
QPushButton m_clearBtn;
QPushButton m_scoreBtn;
private slots:
void onRefreshBtnClicked();
void onClearBtnClicked();
void onScoreBtnClicked();
void onDeleteActionClicked();
public:
Widget(QWidget* parent = nullptr);
bool eventFilter(QObject* obj, QEvent* e);
~Widget();
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include "DataSource.h"
#include "ScoreInfo.h"
#include "ScoreInfoModel.h"
#include <QDebug>
#include <QMessageBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_view.setParent(this);
m_view.move(10, 10);
m_view.resize(380, 300);
m_view.installEventFilter(this);
m_refreshBtn.setParent(this);
m_refreshBtn.move(10, 320);
m_refreshBtn.resize(100, 30);
m_refreshBtn.setText("Refresh");
m_clearBtn.setParent(this);
m_clearBtn.move(150, 320);
m_clearBtn.resize(100, 30);
m_clearBtn.setText("Clear");
m_scoreBtn.setParent(this);
m_scoreBtn.move(290, 320);
m_scoreBtn.resize(100, 30);
m_scoreBtn.setText("Score");
m_menu.addAction("Delete");
connect(m_menu.actions()[0], &QAction::triggered, this, &Widget::onDeleteActionClicked);
connect(&m_refreshBtn, &QPushButton::clicked, this, &Widget::onRefreshBtnClicked);
connect(&m_clearBtn, &QPushButton::clicked, this, &Widget::onClearBtnClicked);
connect(&m_scoreBtn, &QPushButton::clicked, this, &Widget::onScoreBtnClicked);
m_model.setView(m_view);
onRefreshBtnClicked();
}
void Widget::onDeleteActionClicked()
{
m_model.remove(m_view.currentIndex().row());
}
void Widget::onRefreshBtnClicked()
{
m_model.clear();
DataSource ds;
if(ds.setDataPath("C:/Users/Administrator/Desktop/test.txt"))
{
m_model.add(ds.fetchData());
}
else
{
QMessageBox::critical(this, "error", "Open file error", QMessageBox::Ok);
}
}
void Widget::onClearBtnClicked()
{
m_model.clear();
}
void Widget::onScoreBtnClicked()
{
int max = 0;
int min = 250;
int avarage = 0;
if(m_model.count() != 0)
{
for(int i = 0; i < m_model.count(); i++)
{
ScoreInfo info;
info = m_model.getItem(i);
if(max < info.score())
{
max = info.score();
}
if(min > info.score())
{
min = info.score();
}
avarage += info.score();
}
avarage /= m_model.count();
QMessageBox::information(this, "information", QString().sprintf("Max : %d\nMin : %d\nAvarage : %d", max, min, avarage), QMessageBox::Ok);
}
else
{
QMessageBox::information(this, "information", "No data recorded", QMessageBox::Ok);
}
}
bool Widget::eventFilter(QObject* obj, QEvent* e)
{
if((obj == &m_view) && (e->type() == QEvent::ContextMenu))
{
m_menu.exec(cursor().pos());
}
return QWidget::eventFilter(obj, e);
}
Widget::~Widget()
{
}