Qt中的自定义模型类
文章目錄
- 1 Qt中的通用模型類
- 1.1 Qt中的通用模型類
- 1.2 Qt中的變體類型QVariant
- 2 自定義模型類
- 2.1 自定義模型類設(shè)計分析
- 2.2 自定義模型類數(shù)據(jù)層、數(shù)據(jù)表示層、數(shù)據(jù)組織層實現(xiàn)
- 2.3 顯示層的實現(xiàn)
- 2.4 設(shè)計分析
1 Qt中的通用模型類
1.1 Qt中的通用模型類
QStandardItemModel是一個通用的模型類:
- 能夠以任意的方式組織數(shù)據(jù)(線性、非線性)。
- 數(shù)據(jù)組織的基本單位為數(shù)據(jù)項(QStandardItem)。
- 每一個數(shù)據(jù)項能夠存儲多個具體數(shù)據(jù)(附加數(shù)據(jù)角色)。
- 每一個數(shù)據(jù)項能夠?qū)?shù)據(jù)狀態(tài)進(jìn)行控制(可編輯、可選…)。
Qt中的通用模型類QStandardItemModel:
1.2 Qt中的變體類型QVariant
QVariant:
- QVariant是一個用于封裝的類型。
- QVariant能夠表示大多數(shù)常見的值類型。
- QVariant每次只能封裝(保存)單一類型的值。
- QVariant的意義在于能夠設(shè)計“返回類型可變的函數(shù)”。
變體類型QVariant中常用的成員函數(shù):
編程實驗:變體類型QVariant的應(yīng)用
2 自定義模型類
2.1 自定義模型類設(shè)計分析
工程中的常用模型設(shè)計:
- 解析數(shù)據(jù)源中的數(shù)據(jù)(數(shù)據(jù)庫、網(wǎng)絡(luò)、串口、等)。
- 將解析后的數(shù)據(jù)存入QStandardItem對象中。
- 根據(jù)數(shù)據(jù)間的關(guān)系在QStandardItemModel對象中組織數(shù)據(jù)項。
- 選擇合適的視圖顯示數(shù)據(jù)值。
實例分析:
- 在文件中以行的形式存儲了考試成績信息(ID、Name、Score)。
- 開發(fā)GUI程序顯示文件中的信息;
- 計算平均成績。
- 查找最好成績和最差成績。
- 可刷新顯示的內(nèi)容和刪除內(nèi)容。
系統(tǒng)架構(gòu)圖:
系統(tǒng)核心類圖:
系統(tǒng)架構(gòu)圖:
2.2 自定義模型類數(shù)據(jù)層、數(shù)據(jù)表示層、數(shù)據(jù)組織層實現(xiàn)
先了解如下概念:
- 工程中的架構(gòu)圖用于定義模塊接口。
- 工程中的類圖用于定義具體功能的接口。
- 工程圖中的流程圖用于定義類對象間的交互。
- 模塊實現(xiàn)結(jié)束后需要進(jìn)行單元測試。
DataSource類的設(shè)計與實現(xiàn):
- 設(shè)置數(shù)據(jù)源并讀取數(shù)據(jù)。
- 對數(shù)據(jù)進(jìn)行解析后生成數(shù)據(jù)對象。
ScoreInfo類的設(shè)計與實現(xiàn):
- 封裝數(shù)據(jù)源中的一組完成數(shù)據(jù)。
- 提供返回具體數(shù)據(jù)值的接口函數(shù)。
ScoreInfoModel類的設(shè)計與實現(xiàn):
- 使用標(biāo)準(zhǔn)模型類QStandardItemModel作為成員。
- 以ScoreInfo類對象為最小單位進(jìn)行數(shù)據(jù)組織。
數(shù)據(jù)交互流程圖:
主要代碼如下:
ScoreInfo.h:
#ifndef SCOREINFO_H #define SCOREINFO_H#include <QObject>class ScoreInfo : public QObject {Q_OBJECTQString m_id;QString m_name;int m_score; public:explicit ScoreInfo(QObject* parent = 0);explicit ScoreInfo(QString id, QString name, int score, QObject* parent = 0);ScoreInfo(const ScoreInfo& obj);ScoreInfo& operator= (const ScoreInfo& obj);QString id();QString name();int score();};#endif // SCOREINFO_HScoreInfo.cpp:
#include "ScoreInfo.h"ScoreInfo::ScoreInfo(QObject* parent) : QObject(parent) {m_id = "NULL";m_name = "NULL";m_score = -1; }ScoreInfo::ScoreInfo(QString id, QString name, int score, 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; }DataSource.h:
#ifndef DATASOURCE_H #define DATASOURCE_H#include <QObject> #include <QList> #include "ScoreInfo.h"class DataSource : public QObject {Q_OBJECTQList<ScoreInfo> m_data;bool parse(QString line, ScoreInfo& info); public:explicit DataSource(QObject* parent = 0);bool setDataPath(QString path);QList<ScoreInfo> fetchData();int count(); };#endif // DATASOURCE_HDataSource.cpp:
#include "DataSource.h" #include <QFile> #include <QTextStream> #include <QStringList>DataSource::DataSource(QObject *parent) : QObject(parent) { }bool DataSource::setDataPath(QString path) {bool ret = true;QFile file(path);if( file.open(QIODevice::ReadOnly | QIODevice::Text) ){QTextStream in(&file);while( !in.atEnd() ){ScoreInfo info;if( parse(in.readLine(), info) ){m_data.append(info);}}file.close();}else{ret = false;}return ret; }bool DataSource::parse(QString line, ScoreInfo& info) {bool ret = true;QStringList list = line.split(",", QString::SkipEmptyParts);if( list.count() == 3 ){QString id = list[0].trimmed();QString name = list[1].trimmed();QString score = list[2].trimmed();int value = score.toInt(&ret);if( ret && (0 <= value) && (value <=150) ){info = ScoreInfo(id, name, value);}else{ret = false;}}else{ret = false;}return ret; }QList<ScoreInfo> DataSource::fetchData() {QList<ScoreInfo> ret = m_data;m_data.clear();return ret; }int DataSource::count() {return m_data.count(); }ScoreInfoModel.h:
#ifndef SCOREINFOMODEL_H #define SCOREINFOMODEL_H#include <QObject> #include <QStandardItem> #include <QTableView> #include <QList> #include "ScoreInfo.h"class ScoreInfoModel : public QObject {Q_OBJECTQStandardItemModel m_model; public:explicit ScoreInfoModel(QObject *parent = 0);bool add(ScoreInfo info);bool add(QList<ScoreInfo> list);bool remove(int i);ScoreInfo getItem(int i);int count();void clear();void setView(QTableView& view); };#endif // SCOREINFOMODEL_HScoreInfoModel.cpp:
#include "ScoreInfoModel.h" #include <QStandardItem> #include <QVariant>ScoreInfoModel::ScoreInfoModel(QObject* parent) : QObject(parent) { }bool ScoreInfoModel::add(ScoreInfo info) {QStandardItem* root = m_model.invisibleRootItem();QStandardItem* item0 = new QStandardItem();QStandardItem* item1 = new QStandardItem();QStandardItem* item2 = new QStandardItem();bool ret = true;if( m_model.rowCount() == 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(info.id(), Qt::DisplayRole);item1->setData(info.name(), Qt::DisplayRole);item2->setData(info.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; }bool ScoreInfoModel::add(QList<ScoreInfo> list) {bool ret = true;for(int i=0; i<list.count(); i++){ret = ret && add(list[i]);}return ret; }bool ScoreInfoModel::remove(int i) {bool ret = true;if( (0 <= i) && (i < count()) ){m_model.removeRow(i);}else{ret = false;}return ret; }void ScoreInfoModel::clear() {m_model.clear(); }ScoreInfo ScoreInfoModel::getItem(int i) {ScoreInfo ret;if( (0 <= i) && (i < count()) ){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);ret = ScoreInfo(v0.toString(), v1.toString(), v2.toInt());}return ret; }int ScoreInfoModel::count() {return m_model.rowCount(); }void ScoreInfoModel::setView(QTableView& view) {view.setModel(&m_model); }Widget.h:
#ifndef WIDGET_H #define WIDGET_H#include <QtGui/QWidget>class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = 0);~Widget(); };#endif // WIDGET_HWidget.cpp:
#include "Widget.h" #include "ScoreInfo.h" #include "DataSource.h" #include "ScoreInfoModel.h" #include <QDebug>Widget::Widget(QWidget *parent) : QWidget(parent) {DataSource ds;ScoreInfoModel sim;if( ds.setDataPath("C:/Users/hp/Desktop/test.txt") ){qDebug() << "item cout: " << ds.count();QList<ScoreInfo> list = ds.fetchData();for(int i=0; i<list.count(); i++){qDebug() << list[i].id();qDebug() << list[i].name();qDebug() << list[i].score();qDebug() << endl;sim.add(list[i]);}qDebug() << "test model:" << endl;for(int i=0; i<sim.count(); i++){ScoreInfo info = sim.getItem(i);qDebug() << info.id();qDebug() << info.name();qDebug() << info.score();qDebug() << endl;}qDebug() << endl;}else{qDebug() << "data source read error";} }Widget::~Widget() {}main.cpp:
#include <QtGui/QApplication> #include "Widget.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); }2.3 顯示層的實現(xiàn)
界面效果如下:
界面設(shè)計:
右鍵上下文菜單的實現(xiàn):
- 定義菜單對象(QMenu)。
- 連接菜單中的QAction對象到槽函數(shù)。
- 定義事件過濾器,并處理ContextMenu事件。
- 在當(dāng)前鼠標(biāo)的位置打開菜單對象。
Widget.h:
#ifndef WIDGET_H #define WIDGET_H#include <QtGui/QWidget> #include <QTableView> #include <QPushButton> #include <QMenu> #include "ScoreInfoModel.h"class Widget : public QWidget {Q_OBJECTScoreInfoModel m_model;QTableView m_view;QPushButton m_refreshBtn;QPushButton m_clearBtn;QPushButton m_scoreBtn;QMenu m_menu; private slots:void onRefreshBtnClicked();void onClearBtnClicked();void onScoreBtnClicked();void onDeleteActionClicked(); public:Widget(QWidget* parent = 0);bool eventFilter(QObject* obj, QEvent* evt);~Widget(); };#endif // WIDGET_HWidget.cpp:
#include "Widget.h" #include "ScoreInfo.h" #include "DataSource.h" #include <QMessageBox> #include <QEvent> #include <QDebug>Widget::Widget(QWidget *parent) : QWidget(parent) {m_view.setParent(this);m_view.move(10, 10);m_view.resize(345, 180);m_view.installEventFilter(this);m_refreshBtn.setParent(this);m_refreshBtn.move(10, 200);m_refreshBtn.resize(95, 30);m_refreshBtn.setText("Refresh");m_clearBtn.setParent(this);m_clearBtn.move(135, 200);m_clearBtn.resize(95, 30);m_clearBtn.setText("Clear");m_scoreBtn.setParent(this);m_scoreBtn.move(260, 200);m_scoreBtn.resize(95, 30);m_scoreBtn.setText("Score");m_menu.addAction("Delete");m_model.setView(m_view);connect(&m_refreshBtn, SIGNAL(clicked()), this, SLOT(onRefreshBtnClicked()));connect(&m_clearBtn, SIGNAL(clicked()), this, SLOT(onClearBtnClicked()));connect(&m_scoreBtn, SIGNAL(clicked()), this, SLOT(onScoreBtnClicked()));connect(m_menu.actions()[0], SIGNAL(triggered()), this, SLOT(onDeleteActionClicked()));onRefreshBtnClicked(); }void Widget::onDeleteActionClicked() {m_model.remove(m_view.currentIndex().row()); }void Widget::onRefreshBtnClicked() {DataSource ds;m_model.clear();if( ds.setDataPath("C:/Users/hp/Desktop/test.txt") ){m_model.add(ds.fetchData());}else{QMessageBox::critical(this, "Error", "Data source read error!", QMessageBox::Ok);} }void Widget::onClearBtnClicked() {m_model.clear(); }void Widget::onScoreBtnClicked() {int min = 256;int max = 0;int average = 0;if( m_model.count() > 0 ){for(int i=0; i<m_model.count(); i++){ScoreInfo info = m_model.getItem(i);if( info.score() < min ){min = info.score();}if( info.score() > max ){max = info.score();}average += info.score();}average /= m_model.count();QMessageBox::information(this, "Statistic", QString().sprintf("Min: %d\nMax: %d\nAverage: %d", min, max, average), QMessageBox::Ok);}else{QMessageBox::information(this, "Statistic", "No data record!", QMessageBox::Ok);} }bool Widget::eventFilter(QObject* obj, QEvent* evt) {if( (obj == &m_view) && (evt->type() == QEvent::ContextMenu) ){m_menu.exec(cursor().pos());}return QWidget::eventFilter(obj, evt); }Widget::~Widget() {}2.4 設(shè)計分析
為什么DataSource類中獲取數(shù)據(jù)的方式是fetchData而不是getData?
答案:因為數(shù)據(jù)可能從文件中來,也可能從網(wǎng)絡(luò)、串口、USB中來,這么做就實現(xiàn)了各種方式的統(tǒng)一。
總結(jié)一下:
- 數(shù)據(jù)源類(DataSource)用于抽象表示數(shù)據(jù)的來源。
- 模型類(Model)用于從數(shù)據(jù)源獲取數(shù)據(jù)并組織。
- 視圖類(View)用于顯示模型中的數(shù)據(jù)。
- 數(shù)據(jù)應(yīng)用4層架構(gòu)設(shè)計非常易于擴展和維護(hù)。
參考資料:
總結(jié)
以上是生活随笔為你收集整理的Qt中的自定义模型类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何删除微粒贷记录 伴随个人贷款记录终生
- 下一篇: 海底捞的成功有哪些因素?火锅是一个良好的