Qt 中 的 模 型 / 视 图 架 构 用 来 实 现 大 量 的 数 据 存 储 、 处 理 及 显 示 。
MVC(Model-View-Controller)包括了 3 个组件:模型(Model)是应用对象,用来表示数据;视图(View)是模型的用户界面,用来显示数据;控制(Controller)定义了用户界面对用户输入的反应方式。委托(Delegate)用于定制数据的渲染和编辑方式。
所有的模型都基于 QAbstractItemModel 类,该类提供了十分灵活的接口来处理各种视图,这些视图可以将数据的表现形式为表格(table)、列表(list)、树(tree)。
Qt 提供了一些现成的模型来处理数据项:
QStringListModel 存储简单的 QString 项目列表;
QStandardItemModel 管理复杂的属性结构数据项,每一个数据项可以包含任意的数据;
QFileSystemModel 提供了本地文件系统中文件和目录信息;
QSqlQueryModel、QSqlTableModel 和 QSqlRelationTableModel 用来访问数据库。
标准模型还无法满足需要时,可子类化 QAbstractItemModel、QAbstractListModel 或
QAbstractTableModel 来创建自定义的模型。
为确保数据的表示与数据的获取相分离,Qt 引入了模型索引的概念,输入和委托均可通过模型索引来请求数据并显示。只有模型需要知道怎样获取数据,被模型管理的数据类型可以被广泛的定义。模型索引包含一个指针,指向创建他们的模型,使用多个模型时可避免混淆。模型索引 QModelIndex 类提供对一块数据的临时引用,用来修改或检索模型中的数据,获取一个数据项的模型索引必须指定模型的 3 个属性:行号、列号和父项的模型索引。
Qt 提供了 QListView、QTableView 视图、QTreeView 视图分别实现列表、表格与树视图效果。QListView 将数据项显示为一个列表;QTableView 将模型中的数据显示在一个表格中;QTreeView 将模型中的数据项显示在具有层次的列表中。QTableView 和 QTreeView 在显示项目的时候同时还可以显示标头,通过 QHeaderView 类实现。自定义视图类是基于
QAbstractItemView 抽象基类,如实现条形图,饼状图等特殊显示方式。
在模型/视图框架中,QAbstractItemDelegate 是委托类的抽象基类,Qt 默认的委托实现由 QStyledItemDelegate 类 提 供 , 这 也 被 用 作 Qt 标 准 视 图 的 默 认 委 托 , 选 择
QStyledItemDelegate 或 QItemDelegate 中其一来为视图中的项目绘制和提供编辑器。不同的是 QStyledItemDelegate 使用当前的样式来绘制项目,实现自定义委托建议使用
QStyledItemDelegate 作为基类。
Qt 提供了项目试图的便捷类,这些类底层通过模型/视图框架实现。这些部件分别是
QListWidget 提供一个项目列表,QTreeWidget 显示一个多层次的树结构,QTableWidget
提供了一个以项目作为单元的表格。它们每一个类都继承了 QAbstractItemView 类的行为。
之所以成为便捷因其用起来比较简单,使用于少量的数据的存储和显示。因没有将视图与模型分离,所以没有视图类灵活,不能和任意的模型一起使用。
#include
#include
#include
#include
#include
#include
#include
#include
#includeint main(int argc, char *argv[])
{QApplication app(argc,argv);//创建模型QDirModel model;//创建树视图,列表视图,表格视图QTreeView tree;QListView list;QTableView table;//视图设置模型tree.setModel(&model);list.setModel(&model);table.setModel(&model);//设置视图对象的选择方式为多选tree.setSelectionMode(QAbstractItemView::MultiSelection);list.setSelectionMode(tree.selectionMode());table.setSelectionMode(tree.selectionMode());//树图示双击信号发射后,列表及表格视图刷新响应QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),&list,SLOT(setRootIndex(QModelIndex)));QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),&table,SLOT(setRootIndex(QModelIndex)));QSplitter *splitter = new QSplitter;splitter->addWidget(&tree);splitter->addWidget(&list);splitter->addWidget(&table);splitter->setWindowTitle(QString("视图/模型"));splitter->show();return app.exec();
}
#include
#include
#include
#includeint main(int argc,char* argv[]){QApplication app(argc,argv);//创建标准项模型QStandardItemModel model;//获取标准项模型的根项,根项不可见QStandardItem *parentItem = model.invisibleRootItem();//创建标准型item0,设置文本,设置图标,工具提示QStandardItem *item0 = new QStandardItem;item0->setText("A");QPixmap pixmap0(50,50);pixmap0.fill(Qt::red);item0->setIcon(QIcon(pixmap0));item0->setToolTip(QString("A项提示"));//将item0作为父项的子项parentItem->appendRow(item0);parentItem = item0;//创建item0的子项QStandardItem *item1 = new QStandardItem;item1->setText("B");QPixmap pixmap1(50,50);pixmap1.fill(Qt::yellow);item1->setIcon(QIcon(pixmap1));item1->setToolTip(QString("B的提示"));parentItem->appendRow(item1);QStandardItem *item2 = new QStandardItem;QPixmap pixmap2(50,50);pixmap2.fill(Qt::green);item2->setData("C",Qt::EditRole);item2->setData("index C",Qt::ToolTipRole);item2->setData(QIcon(pixmap2),Qt::DecorationRole);parentItem->appendRow(item2);//树视图中显示数据QTreeView view;view.setModel(&model);view.show();QModelIndex indexA = model.index(0,0,QModelIndex());qDebug() << "indexA row coutn"<
#ifndef WEAPONMOEDL_H
#define WEAPONMOEDL_H
#includeclass WeaponMoedl : public QAbstractTableModel
{
public:WeaponMoedl(QObject *parent = 0);virtual int rowCount(const QModelIndex &parent = QModelIndex())const;virtual int columnCount(const QModelIndex &parent = QModelIndex())const;QVariant data(const QModelIndex &index,int role)const;QVariant headerData(int section,Qt::Orientation orientation,int role)const;private:QVector army; //军队QVector weaponType; //武器类型QMap armyMap; //军队映射QMap weaponMap;//武器映射QStringList weapon;//武器QStringList header;//表头void populateModel();//表格数据的初始化
};#endif // WEAPONMOEDL_H
#include "weaponmoedl.h"WeaponMoedl::WeaponMoedl(QObject *parent):QAbstractTableModel(parent)
{armyMap[1] = QString("陆军");armyMap[2] = QString("空军");armyMap[3] = QString("海军");armyMap[4] = QString("海军陆战队");weaponMap[1] = QString("轰炸机");weaponMap[2] = QString("战斗机");weaponMap[3] = QString("坦克");weaponMap[4] = QString("直升机");weaponMap[5] = QString("航空母舰");weaponMap[6] = QString("潜水战车");weaponMap[7] = QString("核潜艇");weaponMap[8] = QString("超音速");populateModel();
}int WeaponMoedl::rowCount(const QModelIndex &parent) const
{return army.size();
}int WeaponMoedl::columnCount(const QModelIndex &parent) const
{return 3;
}//放回指定索引的数据,将数值映射成文字
QVariant WeaponMoedl::data(const QModelIndex &index, int role) const
{if(!index.isValid()) return QVariant();if(role == Qt::DisplayRole){switch (index.column()) {case 0:return armyMap[army[index.row()]];break;case 1:return weaponMap[weaponType[index.row()]];break;case 2:return weapon[index.row()];break;default:return QVariant();break;}}return QVariant();}QVariant WeaponMoedl::headerData(int section, Qt::Orientation orientation, int role) const
{if(role == Qt::DisplayRole && orientation == Qt::Horizontal)return header[section];return QAbstractTableModel::headerData(section,orientation,role);
}void WeaponMoedl::populateModel()
{header << QString("军种")<< QString("种类")<< QString("武器");army<< 1 << 2 << 3 << 4 << 3 << 2 << 1;weaponType << 1 << 3 << 5 << 7 << 8 << 6 << 4 << 2;weapon << QString("大黄蜂") << QString("华尔兹")<< QString("扒鸡跑") << QString("尼泊尔")<< QString("M90") << QString("华飞思")<< QString("朴良国") << QString("双飞片");
}
#include
#include
#include"weaponmoedl.h"int main(int argc, char *argv[])
{QApplication app(argc,argv);WeaponMoedl model;QTableView view;view.setModel(&model);view.setWindowTitle(QString("表格/视图"));view.resize(600,400);view.show();return app.exec();
}
#ifndef STRINGLISTMODEL_H
#define STRINGLISTMODEL_H
#includeclass stringListModel : public QAbstractListModel
{Q_OBJECT
public:stringListModel(const QStringList &strList,QObject *parent = 0):QAbstractListModel(parent),m_stringList(strList){}//模型行数int rowCount(const QModelIndex &parent = QModelIndex()) const;//指定模型索引的数据项QVariant data(const QModelIndex &index, int role) const;//表头内容(数或者表格)QVariant headerData(int section, Qt::Orientation orientation,int role = Qt::DisplayRole) const;//项目属性Qt::ItemFlags flags(const QModelIndex &index) const;//编辑数据bool setData(const QModelIndex &index, const QVariant &value,int role = Qt::EditRole);//插入行 参数(插入位置,插入的行数,父项模型索引)bool insertRows(int row, int count,const QModelIndex &parent = QModelIndex());//删除行bool removeRows(int row, int count,const QModelIndex &parent = QModelIndex());private:QStringList m_stringList;
};#endif // STRINGLISTMODEL_H
#include "stringlistmodel.h"int stringListModel::rowCount(const QModelIndex &parent) const
{return m_stringList.count();
}QVariant stringListModel::data(const QModelIndex &index, int role) const
{if(!index.isValid())return QVariant();if(index.row() == m_stringList.size())return QVariant();if(role == Qt::DisplayRole || role == Qt::EditRole)return m_stringList.at(index.row());elsereturn QVariant();
}QVariant stringListModel::headerData(int section, Qt::Orientation orientation, int role) const
{if(role != Qt::DisplayRole)return QVariant();//水平表头if(orientation == Qt::Horizontal)return QString("名字%1").arg(section);elsereturn QString("%1").arg(section);
}Qt::ItemFlags stringListModel::flags(const QModelIndex &index) const
{if(!index.isValid())return Qt::ItemIsEnabled;return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}bool stringListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{//检验索引有效且可编辑if(index.isValid() && role == Qt::EditRole){m_stringList.replace(index.row(),value.toString());emit dataChanged(index,index);return true;}return false;
}bool stringListModel::insertRows(int row, int count, const QModelIndex &parent)
{//告知其他组件指定的行开始插入操作beginInsertRows(parent,row,count + row - 1);for(int i=0; i//告知其他组件指定的行进行删除操作beginRemoveRows(parent,row,count + row - 1);for(int i=0; i
#include
#include
#include
#include"stringlistmodel.h"int main(int argc, char *argv[])
{QApplication app(argc,argv);QStringList list;list << QString("太阳") << QString("月亮")<< QString("火星") << QString("木星");stringListModel model(list);//创建模型model.insertRows(0,2);model.removeRows(2,1);QListView listView;//创建列表视图listView.setModel(&model);//视图设置模型listView.show();//视图显示QTableView tableView;//创建表格视图tableView.setModel(&model);//视图设置模型tableView.show();//视图显示return app.exec();
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#includenamespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();public slots:void getCurrentItemData();//当前选择void toggleSection();//切换选择//更新选择,selected表新的选择,deselected表以前的选择void updateSelection(const QItemSelection &selected,const QItemSelection &deselected);//改变当前模型索引void changeCurrent(const QModelIndex ¤t,const QModelIndex &previous);
private:Ui::MainWindow *ui;QTableView *m_tableView;QTableView *m_tableView2;
};#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
#include
#include MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);//创建标准模型项,7行4列QStandardItemModel *model = new QStandardItemModel(7,4,this);for(int row = 0; row < 7; ++row )for(int column = 0; column < 4; ++column ){QStandardItem *item = new QStandardItem(QString("%1").arg(row * 4 + column));//标准模型项设置数据项model->setItem(row,column,item);}m_tableView = new QTableView;m_tableView->setModel(model);setCentralWidget(m_tableView);//设置主窗口中心部件为表格视图//获取视图的选择模式QItemSelectionModel *selectionModel = m_tableView->selectionModel();QModelIndex topLeft;//左上角模型索引QModelIndex bottomRight;//右下角模型索引topLeft = model->index(1,1);bottomRight = model->index(5,2);//创建模型选择QItemSelection selection(topLeft,bottomRight);//以选择的方式来选择项目selectionModel->select(selection,QItemSelectionModel::Select);//添加动作Action(动作文本,响应者,槽方法)
// ui->menuBar->addAction(QString("当前项目"),this,&MainWindow::getCurrentItemData);
// ui->menuBar->addAction(QString("切换选择"),this,&MainWindow::toggleSection);//6.9.6版本,QAction *action1 = ui->menuBar->addAction(QString("当前项目"));QAction *action2 = ui->menuBar->addAction(QString("切换选择"));connect(action1,&QAction::triggered,this,&MainWindow::getCurrentItemData);connect(action2,&QAction::triggered,this,&MainWindow::toggleSection);//关联选择模型的选择改变,当前项改变信号connect(selectionModel,&QItemSelectionModel::selectionChanged,this,&MainWindow::updateSelection);connect(selectionModel,&QItemSelectionModel::currentChanged,this,&MainWindow::changeCurrent);m_tableView2 = new QTableView;m_tableView2->setWindowTitle("tableView2");m_tableView2->resize(600,400);m_tableView2->setModel(model);m_tableView2->setSelectionModel(selectionModel);m_tableView2->show();//自定义委托spinboxDelegate *dategate = new spinboxDelegate(this);m_tableView->setItemDelegate(dategate );
}MainWindow::~MainWindow()
{delete ui;delete m_tableView2;
}void MainWindow::getCurrentItemData()
{qDebug() << QString("当前数据: ")<selectionModel()->currentIndex().data().toString();
}void MainWindow::toggleSection()
{//左上角模型索引QModelIndex topLeft = m_tableView->model()->index(0,0,QModelIndex());//右下角模型索引QModelIndex bottomRight = m_tableView->model()->index(m_tableView->model()->rowCount(QModelIndex())-1,m_tableView->model()->columnCount(QModelIndex())-1,QModelIndex());//项选择QItemSelection curSelection(topLeft,bottomRight);m_tableView->selectionModel()->select(curSelection,QItemSelectionModel::Toggle);
}void MainWindow::updateSelection(const QItemSelection &selected, const QItemSelection &deselected)
{QModelIndex index;//indexes()返回所有选择项的模型QModelIndexList list = selected.indexes();//给选择项填充数据foreach (index, list) {QString text = QString("%1,%2").arg(index.row()).arg(index.column());m_tableView->model()->setData(index,text);}//清空上一次的内容list = deselected.indexes();foreach (index, list) {m_tableView->model()->setData(index,"");}
}void MainWindow::changeCurrent(const QModelIndex ¤t, const QModelIndex &previous)
{qDebug() << QString("从(%1,%2)到(%3,%4)").arg(previous.row()).arg(previous.column()).arg(current.row()).arg(current.column());
}
#ifndef SPINBOXDELEGATE_H
#define SPINBOXDELEGATE_H
#includeclass spinboxDelegate : public QItemDelegate
{Q_OBJECT
public:spinboxDelegate(QObject *parent = 0);//创建编辑器QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const override;//设置编辑器数据void setEditorData(QWidget *editor,const QModelIndex &index) const override;//更新编辑器几何属性void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,const QModelIndex &index) const override;
};#endif // SPINBOXDELEGATE_H
#include "spinboxdelegate.h"
#includespinboxDelegate::spinboxDelegate(QObject *parent):QItemDelegate(parent)
{}QWidget *spinboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{QSpinBox *editor = new QSpinBox(parent);editor->setMinimum(0);editor->setMaximum(100);return editor;
}void spinboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{int value = index.model()->data(index,Qt::EditRole).toInt();//类型转化QWidget转QSpinBoxQSpinBox *spinBox = static_cast(editor);//编辑器设置数据spinBox->setValue(value);
}void spinboxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{editor->setGeometry(option.rect);
}