目录
1.7 自定义模型类 QAbstractTableModel
1、模型视图模式
1.1 基本概括
模型视图模式的工作机制 :模型(数据)与视图(显示)相分离 。当数据发生改变时 ,模型发出信号通知视图;当用户与视图进行交互时 ,视图发出信号提供交互信息
Qt中的模型视图
-模型用于组织数据源,不关心数据的显示方式 ,视图用于定义数据的显示方式,不关心数据的组织方式
-QAbstractItemModel的各种子类代表着各种组织数据的结构的模型,QAbstractItemView的各种子类代表着各种数据显示方式的视图
在Qt中,模型必须为每一个数据提供独一无二的模型索引;视图通过模型索引访问模型中的具体数据
-模型创建模型索引: QModelIndex QAbstractItemModel::createIndex(int row, int column, void *ptr = Q_NULLPTR) const
-模型访问模型索引关联的数据:QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const
模型定义标准接口(成员函数)对数据进行访问 ,视图通过标准接口获取数据并定义显示方式
模型与视图都是QT对象,模型使用信号与槽的机制通知视图数据变化 ,模型中的数据都是以层次结构表示的
1.2 模型中的索引
模型索引是数据与视图分离的重要机制 ,模型中的数据使用唯一的模型索引来访问
QModelIndex是Qt中的模型索引类,包含具体数据的访问途径 ,含一个指向模型的指针
索引是访问模型中具体数据的约定方式 ,获取索引的通用形式为三元组(row, column, parent)
索引在需要时由模型实时创建 ,使用空索引作为父节点表示顶层数据元素
特殊的模型可以自定义特殊的索引获取方式
模型中的通用树形结构
Root为虚拟节点 用于统一所有数据到同一棵树中 ,同一节点的子节点以递增的方式编号 ,通过(index, parent)的方式确定节点
模型中数据索引的通用方式 :三元组:(row、column、parent)
理解模型索引
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPlainTextEdit>
#include <QFileSystemModel>
class Widget : public QWidget
{
Q_OBJECT
QPlainTextEdit m_edit;
QFileSystemModel m_fsm;
protected slots:
void onDirectoryLoaded(const QString& path);
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
#include "Widget.h"
#include <QDir>
#include <QModelIndex>
#include <QByteArray>
#include <QBuffer>
#include <QTextStream>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_edit.setParent(this);
m_edit.move(10, 10);
m_edit.resize(500, 300);
connect(&m_fsm, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded(QString)));
//This signal is emitted when the gatherer thread has finished to load the path.
m_fsm.setRootPath(QDir::currentPath()); // 为模型设置数据源
// 有感:所谓模型其实不过是一系列数据操作的封装
}
//目录加载完毕后槽函数调用
void Widget::onDirectoryLoaded(const QString& path)
{
QModelIndex root = m_fsm.index(path, 0); //Returns the model index for the given path and column.
QByteArray array;
QBuffer buffer(&array);
if( buffer.open(QIODevice::WriteOnly) )
{
QTextStream out(&buffer);
out << m_fsm.isDir(root) << endl;
out << m_fsm.data(root).toString() << endl; // 模型通过模型索引拿数据
out << root.data().toString() << endl; // 可以直接通过索引取数据
out << &m_fsm << endl;
out << root.model() << endl; // Returns a pointer to the model containing the item that this index refers to.
out << m_fsm.filePath(root) << endl; // Returns the path of the item stored in the model under the index given
out << m_fsm.fileName(root) << endl; // Returns the path of the item stored in the model under the index given.
out << endl;
// Returns the number of rows under the given parent
for(int i=0; i<m_fsm.rowCount(root); i++)
{
QModelIndex ci = m_fsm.index(i, 0, root); // 第i行第0列数据的索引
out << ci.data().toString() << endl; // 索引关联数据
}
out.flush();
buffer.close();
}
if( buffer.open(QIODevice::ReadOnly) )
{
QTextStream in(&buffer);
m_edit.insertPlainText(in.readAll());
buffer.close();
}
}
Widget::~Widget()
{
}
1.3 QFileSystemModel
文件系统模型用于组织文件夹数据,当目录发生变化时模型发出信号通知视图 ,于是此树状视图就重新呈现数据
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFileSystemModel>
#include <QTreeView>
#include <QPushButton>
class Widget : public QWidget
{
Q_OBJECT
QFileSystemModel m_fsModel; // 定义文件系统模型,用于组织文件夹数据
QTreeView m_treeView; // 定义树形显示视图
QPushButton newFolderBtn;
QPushButton rmFolderBtn;
public:
Widget(QWidget *parent = 0);
~Widget();
protected slots:
void onNewFolderBtn();
void onRmFolderBtn();
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QDir>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(800, 800);
m_treeView.setParent(this); // QAbstractItemView 子类不过就是个组件,父组件为Widget对象
m_treeView.move(0, 0); // 绝对定位组件位置,大小
m_treeView.resize(800, 300);
newFolderBtn.setParent(this);
newFolderBtn.setText("New Folder");
newFolderBtn.resize(100, 40);
newFolderBtn.move(40, 400);
rmFolderBtn.setParent(this);
rmFolderBtn.setText("remove Folder");
rmFolderBtn.resize(160, 40);
rmFolderBtn.move(400, 400);
m_fsModel.setRootPath(QDir::currentPath()); // 将模型监视的目录设置为当前工作目录。对该目录中的文件和目录的任何更改都将反映在模型中
m_treeView.setModel(&m_fsModel); // 为视图设置一个模型
m_treeView.setRootIndex(m_fsModel.index(QDir::currentPath()));
//模型索引从模型的index()函数得到
//为树形显示视图根部设置索引,指明数据从根开始呈现
connect(&newFolderBtn, SIGNAL(clicked()), this, SLOT(onNewFolderBtn()));
connect(&rmFolderBtn, SIGNAL(clicked()), this, SLOT(onRmFolderBtn()));
}
void Widget::onNewFolderBtn()
{
QString dirName = "";
dirName = QInputDialog::getText(this, "tip", "please input dir name");
if(dirName == "")
return;
QModelIndex curIndex = m_treeView.currentIndex();
qDebug() << "(" << curIndex.row() << "," << curIndex.column() << ")" << endl;
qDebug() << curIndex.data() << endl;
curIndex = curIndex.parent();
if(!curIndex.isValid())
return;
// Create a directory with the name in the parent model index.
if( !(m_fsModel.mkdir(curIndex, dirName).isValid()) )
QMessageBox::information(this, "New Folder", "failed to new Folder");
}
void Widget::onRmFolderBtn()
{
QModelIndex curIndex = m_treeView.currentIndex();
if (!curIndex.isValid())
return;
bool ok;
if (m_fsModel.fileInfo(curIndex).isDir())
ok = m_fsModel.rmdir(curIndex);
else
ok = m_fsModel.remove(curIndex);
if (!ok) {
QMessageBox::information(this, "Remove", QString("Failed to remove %1").arg(m_fsModel.fileName(curIndex)));
}
}
Widget::~Widget()
{
}
1.4 QStringListModel
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QStringListModel>
#include <QListView>
#include <QPushButton>
class Widget : public QWidget
{
Q_OBJECT
QStringListModel* strListModel;
QListView* listView;
QPushButton* addBtn;
QPushButton* delBtn;
public:
Widget(QWidget *parent = 0);
~Widget();
protected slots:
void onAddBtn();
void onDelBtn();
void onTest(const QModelIndex &index);
};
#endif // WIDGET_H
#include "Widget.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(500, 500);
QStringList list;
list << "a" << "b" << "c";
strListModel = new QStringListModel(this);
listView = new QListView(this);
addBtn = new QPushButton(this);
delBtn = new QPushButton(this);
listView->resize(width(), 200);
addBtn->setText("add");
addBtn->resize(60 ,40);
addBtn->move(10, 300);
delBtn->setText("del");
delBtn->resize(60 ,40);
delBtn->move(100, 300);
listView->setModel(strListModel); // 关联模型视图
strListModel->setStringList(list); // 设置模型数据源:
//QStringListModel其实就是封装了QStringList, QStringListModel内部有一个QStringList lst成员
qDebug() << strListModel->stringList() << endl; // ("a", "b", "c")
qDebug() << strListModel->rowCount() << endl; // 3
connect(addBtn, SIGNAL(clicked()), this, SLOT(onAddBtn()));
connect(delBtn, SIGNAL(clicked()), this, SLOT(onDelBtn()));
connect(listView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(onTest(const QModelIndex&)));
}
void Widget::onTest(const QModelIndex &index)
{
int row = index.row();
qDebug() << "(" << row << "," << index.column() << ", " << ")" << endl;
qDebug() << index.data() << endl;
}
void Widget::onAddBtn()
{
QString text = "";
text = QInputDialog::getText(this, "tip", "please input data");
if(text != "")
{
// Returns the model index of the current item.
QModelIndex curIndex = listView->currentIndex();
int row = curIndex.row();
if(row != -1) // QModelIndex 有isValid()
{
// Inserts count rows into the model, beginning at the given row
strListModel->insertRows(row, 1); // 在指定的行位置插入1行
// Sets the data for the specified role in the item with the given index in the model, to the provided value.
strListModel->setData(curIndex, text); // 模型通过逆向索引设置数据
}else{
strListModel->insertRows(0, 1);
strListModel->setData(strListModel->index(0), text);
}
}
}
void Widget::onDelBtn()
{
if (strListModel->rowCount() > 0) {
strListModel->removeRows(listView->currentIndex().row(), 1);
}
}
Widget::~Widget()
{
}
1.5 标准模型与数据角色
标准模型定义 :QStandardItemModel是一种基于数据项的模型。 数据项由QStandardItem表示
数据角色的概念
-模型中的数据有附加的角色属性 ,数据角色定义了数据显示的标准方式
-模型中的数据在视图中的用途(显示方式)可能不同,模型必须为数据设置特定数据角色(数据属性)
-数据角色用于提示视图数据的作用 ,数据角色是不同视图以统一风格显示数据的标准
数据角色的意义 :定义了数据在特定系统下的标准用途 ,不同的视图可以通过相同标准显示数据
注意: 数据角色只是一个附加的属性,这个属性代表推荐的数据显示方式。 不同的视图完全可以自由解析或者忽略数据的角色信息
Qt中的数据角色定义
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTableView>
#include <QListView>
#include <QTreeView>
#include <QStandardItemModel>
class Widget : public QWidget
{
Q_OBJECT
QStandardItemModel m_model;
QTableView m_tableView;
QListView m_listView;
QTreeView m_treeView;
void initModel();
void initView();
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include <QStandardItem>
Widget::Widget(QWidget *parent)
: QWidget(parent, Qt::WindowContextHelpButtonHint)
{
setFixedSize(600, 600);
initModel();
initView();
m_tableView.setModel(&m_model); // 多个视图使用一个模型
m_listView.setModel(&m_model);
m_treeView.setModel(&m_model);
}
void Widget::initModel()
{
//Returns the model's invisible root item. 不就是树形模型的统一管理嘛
QStandardItem* root = m_model.invisibleRootItem();
//The QStandardItem class provides an item for use with the QStandardItemModel class.
QStandardItem* itemA = new QStandardItem();
QStandardItem* itemB = new QStandardItem();
QStandardItem* itemC = new QStandardItem();
QStandardItem* itemChild = new QStandardItem();
//Sets the item's data for the given role to the specified value.
itemA->setData("A", Qt::DisplayRole);
itemA->setData("Tip A", Qt::ToolTipRole);
itemA->setData("Help A", Qt::WhatsThisRole);
itemB->setData("B", Qt::DisplayRole);
itemB->setData("Tip B", Qt::ToolTipRole);
itemC->setData("C", Qt::DisplayRole);
itemC->setData("Tip C", Qt::ToolTipRole);
itemC->setData("Help C", Qt::WhatsThisRole);
itemChild->setData("Child", Qt::DisplayRole);
itemChild->setData("Tip Child", Qt::ToolTipRole);
itemChild->setData("Help Child", Qt::WhatsThisRole);
itemC->setChild(0, 0, itemChild);
root->setChild(0, 0, itemA);
root->setChild(0, 1, itemB);
root->setChild(1, 0, itemC);
}
void Widget::initView()
{
m_tableView.setParent(this);
m_tableView.move(0, 0);
m_tableView.resize(width(), height()/3);
m_listView.setParent(this);
m_listView.move(0, height()/3);
m_listView.resize(width(), height()/3);
m_treeView.setParent(this);
m_treeView.move(0, height()/3 * 2);
m_treeView.resize(width(), height()/3);
}
Widget::~Widget()
{
}
1.6 自定义模型类 QStandardltemModel
QStandardltemModel是一个通用的模型类
-能够以任意的方式组织数据(线性,非线性) ,数据组织的基本单位为数据项(QStandardltem)
-每一个数据项能够存储多个具体数据(附加数据角色) ,每一个数据项能够对数据状态进行控制(可编辑,可选,... )
Qt中的通用模型类QStandardltemModel
注意其中的聚合关系
工程中的常用模型设计
-解析数据源中的数据(数据库,网络,串口,等) ,将解析后的数据存入QStandardltem对象中
-根据数据间的关系在QStandardltemModel对象中组织数据项 ,选择合适的视图显示数据值
实例分析:
-在文件中以行的形式存储了考试成绩信息(ID, Name, Score)
-开发GUI程序显示文件中的信息
✔ 计算平均成绩
✔ 查找最好成绩和最差成绩
✔ 可刷新显示的内容和删除内容
系统架构图
-工程中常用数据应用架构为4层结构: 数据层,数据表示层,数据组织层,数据显示层
-数据应用4层架构设计非常易于扩展和维护
-数据源类(DataSource)用于抽象表示数据的来源
-模型类(Model)用于从数据源获取数据并组织
-视图类(View)用于显示模型中的数据
系统核心类图
DataSource读取文件后得到一组ScoreInfo(id,name,score)对象,这一组对象数据使用标准通用模型组织数据,QTableView显示数据
DataSource:设置数据源并读取数据 ,对数据进行解析后生成数据对象
Scorelnfo(实体类):封装数据源中的一组完整数据 ,提供返回具体数据值的接口函数
ScorelnfoModel(模型类):使用标准模型类QStandardltemModel作为成员 ,以Scorelnfo类对象为最小单位进行数据组织
数据交互流程图
界面设计
右键上下文菜单的实现
-定义菜单对象(QMenu) ,连接菜单中的QAction对象到槽函数
-定义事件过滤器,并处理ContextMenu事件
-在当前鼠标的位置打开菜单对象
数据应用的实现 DataAppDemo.pro
ScoreInfo.h
#ifndef SCOREINFO_H
#define SCOREINFO_H
#include <QObject>
class ScoreInfo : public QObject
{
Q_OBJECT
QString 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); // QObject中的拷贝构造和赋值操作符为私有成员
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)
{
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_OBJECT
QList<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_H
DataSource.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_OBJECT
QStandardItemModel 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_H
ScoreInfoModel.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); //使用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()); // 获得i行三个数据索引
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>
#include <QTableView>
#include <QPushButton>
#include <QMenu>
#include "ScoreInfoModel.h"
class Widget : public QWidget
{
Q_OBJECT
ScoreInfoModel 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_H
Widget.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/wss/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()
{
}
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();
}
1.7 自定义模型类 QAbstractTableModel
QAbstractTableModel类提供了一个抽象模型,可以通过子类化创建表模型
QAbstractTableModel实现了通用的index() 和 parent() 函数
QAbstractTableModel的子类必须实现 rowCount()、columnCount()和data(),尽量实现headerData()。
若想可编辑、可调整大小等模型还应实现更多接口,参见帮助文档
#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H
#include <QObject>
#include <QAbstractTableModel>
#include <QMap>
class MyTableModel : public QAbstractTableModel
{
QStringList headList; // 表头数据
QStringList nameList; // 第0列数据
QVector<int> subVec; // 第1列真实数据
QMap<int, QString> m_map; // 第1列显示数据
public:
MyTableModel(QObject* parent = 0);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
};
#endif // MYTABLEMODEL_H
#include "MyTableModel.h"
MyTableModel::MyTableModel(QObject *parent)
{
headList << "姓名" << "学科";
nameList << "张三" << "李四" << "王五" << "赵六" << "田七";
subVec << 1 << 2 << 3 << 4 << 5;
m_map.insert(1, "C++");
m_map.insert(2, "Java");
m_map.insert(3, "Python");
m_map.insert(4, "PHP");
m_map.insert(5, "JavaScrip");
}
// 实现
int MyTableModel::rowCount(const QModelIndex &parent) const
{
return m_map.count();
}
int MyTableModel::columnCount(const QModelIndex &parent) const
{
return 2;
}
QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(role == Qt::DisplayRole)
{
switch (index.column())
{
case 0:
return nameList[index.row()];
break;
case 1:
return m_map[subVec[index.row()]];
break;
default:
return QVariant();
break;
}
}
return QVariant();
}
QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
return headList[section];
return QAbstractTableModel::headerData(section, orientation, role);
}
#include "MyTableModel.h"
#include <QTableView>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyTableModel model;
QTableView view;
view.setModel(&model);
view.setAlternatingRowColors(true);
view.setWindowTitle(QObject::tr("Student Table"));
view.resize(400, 400);
view.show();
return a.exec();
}