模型/视图编程
Qt中的模型/视图架构用来实现大量的数据存储、处理及显示。
MVC(Model-View-Controller)包括了3个组件:模型(model)是应用对象,用来表示数据;视图(View)是模型的用户界面,用来显示数据;控制(Controller)定义了用户界面对用户输入的反应方式。
委托(Delegate)用于定制数据的渲染和编辑方式。
![image-20220203134951360](https://gitee.com/Do2eM0N/blogimg/raw/master/202202061756409.png)
模型
所有的模型都基于QAbstractItemModel类,该类提供了十分灵活的接口来处理各种视图,这些视图的数据表现形式为表格(table)、列表(list)、树(tree)。
Qt提供了一些现成的模型来处理数据项:
QStringListModel存储简单的QString项目列表;
QStandardItemModel管理复杂的属性结构数据项,每一个数据项可以包含任意的数据;
QFileSystemModel、QSqlTableModel和QSqlRelationTableModel用来访问数据库。
当标准模型还无法满足需要时,可子类化QAbstractItemModel、QAbstractListModel或QAbstractTableModel来创建自定义的模型。
常见的3种模型为列表模型、表格模型、树模型,如下图所示:
![image-20220203161907307](https://gitee.com/Do2eM0N/blogimg/raw/master/202202031619351.png)
为确保数据的表示与数据获取相分离,Qt引入了模型索引的概念,输入和委托均可通过模型索引来请求数据并显示。只有模型需要知道怎样获取数据,被模型管理的数据类型可以被广泛的定义。模型索引包含一个指针,指向创建他们的模型,使用多个模型时可避免混淆。模型索引QModeIIndex类提供对一块数据的临时引用, 用来修改或检索模型中的数据,获取一个数据项的模型索引必须指定模型的3个属性:行号、列号和父项的模型索引。如:
QModelIndex index = model->index(row,column,parent);
也可以通过模型指定的相关数据项对应的模型索引以及特定的角色来获取需要的类型数据,如:
QVariant value = model->data(index,role);
常用的角色类型:
![image-20220203174738323](https://gitee.com/Do2eM0N/blogimg/raw/master/202202061756428.png)
示例:
![image-20220203174829807](https://gitee.com/Do2eM0N/blogimg/raw/master/202202031748877.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<QApplication> #include<QDebug> #include<QTreeView> #include<QStandardItemModel> int main(int argc,char* argv[]) { QApplication a(argc,argv); QStandardItemModel model; QStandardItem* parentItem = model.invisibleRootItem(); QStandardItem* item0 = new QStandardItem; item0->setText(QString("A")); QPixmap pixmap0(50,50); pixmap0.fill(Qt::red); item0->setIcon(QIcon(pixmap0)); item0->setToolTip(QString("A项")); parentItem->appendRow(item0); parentItem = item0; QStandardItem* item1 = new QStandardItem; item1->setText(QString("B")); QPixmap pixmap1(50,50); pixmap1.fill(Qt::blue); item1->setIcon(pixmap1); item1->setToolTip(QString("B项"));
QStandardItem* item2 = new QStandardItem; QPixmap pixmap2(50,50); pixmap2.fill(Qt::green); item2->setData("C",Qt::EditRole); item2->setData("indexC",Qt::ToolTipRole); item2->setData(QIcon(pixmap2),Qt::DecorationRole);
parentItem->appendRow(item1); parentItem->appendRow(item2); QTreeView view; view.setModel(&model); view.show(); QModelIndex indexA = model.index(0,0,QModelIndex()); qDebug()<<"indexA row count:"<<model.rowCount(indexA); QModelIndex indexB = model.index(0,0,indexA); qDebug()<<"indexB text:"<<model.data(indexB,Qt::EditRole).toString(); qDebug()<<"indexB toolTip:"<<model.data(indexB,Qt::ToolTipRole).toString(); return a.exec(); }
|
视图
Qt提供了QListView、QTabelView视图、QTreeView视图分别实现列表、表格与树视图效果。QListView将数据项显示为一个列表;QTableView将模型中的数据显示在一个表格中;QTreeView将模型中的数据项显示在具有层次的列表中。QTableView和QTreeView在显示项目的时候同时还可以显示标头,通过QHeaderView类实现。自定义视图类是基于QAbstractItemView抽象基类,如实现条形图,饼状图等特殊显示方式。
![image-20220204121337670](https://gitee.com/Do2eM0N/blogimg/raw/master/202202041213743.png)
示例1:
![image-20220204121752455](https://gitee.com/Do2eM0N/blogimg/raw/master/202202061756252.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include<QApplication> #include<QAbstractItemModel> #include<QAbstractItemView> #include<QDirModel> #include<QTreeView> #include<QListView> #include<QTableView> #include<QSplitter> int 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(); }
|
示例2:
![image-20220204115328476](https://gitee.com/Do2eM0N/blogimg/raw/master/202202041153536.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| #include "weaponmodel.h"
Weaponmodel::Weaponmodel(QObject* parent):QAbstractTableModel(parent) { armyMap[1] = QString("空军"); armyMap[2] = QString("海军"); armyMap[3] = QString("陆军"); armyMap[4] = QString("海军陆战队"); weaponMap[1] = QString("战斗机1"); weaponMap[2] = QString("战斗机2"); weaponMap[3] = QString("战斗机3"); weaponMap[4] = QString("战斗机4"); weaponMap[5] = QString("战斗机5"); weaponMap[6] = QString("战斗机6"); weaponMap[7] = QString("战斗机7"); weaponMap[8] = QString("战斗机8"); populateModel();
}
int Weaponmodel::rowCount(const QModelIndex &parent)const { return army.size(); }
int Weaponmodel::columnCount(const QModelIndex &parent)const { return 3; }
QVariant Weaponmodel::data(const QModelIndex &index, int role) const { if(!index.isValid()) { return QVariant(); } if(role == Qt::DisplayRole) { switch (index.column()) { case 0: return army[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 Weaponmodel::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 Weaponmodel::populateModel() { header<<QString("军种")<<QString("种类")<<QString("武器"); army<<1<<2<<3<<4<<2<<4<<3<<1; weaponType<<1<<3<<5<<7<<4<<8<<6<<2; weapon<<QString("B-2")<<QString("尼尔兹级")<<QString("阿帕奇")<<QString("黄蜂级")<<QString("比例伯克级")<<QString("AAAV")<<QString("M1A1")<<QString("F-22"); }
|
示例3
![image-20220205110959913](https://gitee.com/Do2eM0N/blogimg/raw/master/202202061756028.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| #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()); } else { return QVariant(); }
}
QVariant StringListModel::headerData(int section, Qt::Orientation orientation, int role) const { if(role != Qt::DisplayRole) { return QVariant(); } if(orientation == Qt::Horizontal) { return QString("column %1").arg(section); } else { return QString("Row %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::inseratRows(int position, int rows, const QModelIndex &index) { beginInsertRows(QModelIndex(),position,position + rows -1 ); for(int row = 0 ; row < rows; row++) { m_stringList.insert(position,QString("你好")); } endInsertRows(); return true; }
bool StringListModel::removeRows(int position, int rows, const QModelIndex &index) { beginRemoveRows(QModelIndex(),position,position + rows - 1); for(int row = 0 ; row < rows;row++) { m_stringList.removeAt(position); } endRemoveRows(); return true; }
#include<QApplication> #include"stringlistmodel.h" #include<QListView> #include<QTableView> int main(int argc,char* argv[]) { QApplication app(argc,argv); QStringList list; list<<QString("太阳")<<QString("地球")<<QString("月亮")<<QString("木星"); StringListModel model(list);//创建模型 model.insertRows(3,2); model.removeRows(0,1);
QListView listView;//创建列表视图 listView.setModel(&model);//视图设置模型 listView.show();//视图显示
QTableView tableView;//创建表格视图 tableView.setModel(&model);//视图设置模型 tableView.show();//视图显示 return app.exec(); }
|
示例4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| #include "mainwindow.h" #include "ui_mainwindow.h" #include<QStandardItem> #include<QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this);
QStandardItemModel* model = new QStandardItemModel(7,4,this); for(int row = 0 ; row <7 ;row++) { for(int colum= 0;colum < 4;colum++) { QStandardItem* item = new QStandardItem(QString("%1").arg(row+4 +colum)); model->setItem(row,colum,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); ui->menuBar->addAction(QString("当前项目"),this,&MainWindow::getCurrenItemData); ui->menuBar->addAction(QString("切换选择"),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(400,300); m_tableView2->setModel(model); m_tableView2->setSelectionModel(selectionModel); m_tableView2->show();
}
MainWindow::~MainWindow() { delete ui; }
void MainWindow::getCurrenItemData() { qDebug()<<QString("当前项数据:")<<m_tableView->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; 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()); }
|
委托
在模型/视图框架中,QAbstractItemDelegate是委托类的抽象基类,Qt默认的委托实现由QStyledItemDelegate类提供,这也被用作Qt标准视图的默认委托,选择 QStyledItemDelegate或QItemDelegate中其一来为视图中的项目绘制和提供编辑器。不同的是QStyledItemDelegate使用当前的样式来绘制项目,实现自定义委托建议使用QStyledItemDelegate作为基类。
Qt提供了项目试图的便捷类,这些类底层通过模型/视图框架实现。这些部件分别是QListWidget提供一个项目列表,QTreeWidget显示一个多层次的树结构,QTableWidget提供了一个以项目作为单元的表格。它们每一个类都继承了QAbstractItemView类的行为。之所以成为便捷因其用起来比较简单,使用于少量的数据的存储和显示。因没有将视图与模型分离,所以没有视图类灵活,不能和任意的模型一起使用。
通过自定义委托来实现更高级的渲染。
示例:QSpinbox
![image-20220206170615666](https://gitee.com/Do2eM0N/blogimg/raw/master/202202061706744.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include "spinboxdelexgate.h" #include<QSpinBox>
SpinBoxDelexgate::SpinBoxDelexgate(QObject* parent):QItemDelegate(parent) {
}
QWidget *SpinBoxDelexgate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QSpinBox *editor = new QSpinBox(parent); editor->setMinimum(0); editor->setMaximum(100); return editor; }
void SpinBoxDelexgate::setEditorData(QWidget *editor, const QModelIndex &index) const { int value = index.model()->data(index,Qt::EditRole).toInt(); QSpinBox* spinBox = static_cast<QSpinBox*>(editor); spinBox->setValue(value); }
void SpinBoxDelexgate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { editor->setGeometry(option.rect); }
|
便捷部件类
示例:
![image-20220206174702701](https://gitee.com/Do2eM0N/blogimg/raw/master/202202061756349.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include<QApplication> #include<QDebug> #include<QListWidget> #include<QTreeWidget> #include<QTableWidget>
int main(int argc,char* argv[]) { QApplication app(argc,argv); QListWidget listWidget; new QListWidgetItem("天涯",&listWidget);
QListWidgetItem* listWidgetItem = new QListWidgetItem; listWidgetItem->setText("海角");
QPixmap pixmap(50,50); pixmap.fill(Qt::blue); listWidgetItem->setIcon(QIcon(pixmap)); listWidgetItem->setToolTip("大海的角落");
listWidget.insertItem(1,listWidgetItem); listWidget.sortItems(Qt::DescendingOrder);
listWidget.show();
QTreeWidget treeWidget; treeWidget.setColumnCount(2); QStringList headers; headers<<"名字"<<"数量"; treeWidget.setHeaderLabels(headers); QTreeWidgetItem *item1 = new QTreeWidgetItem(&treeWidget); item1->setText(0,"开心超人"); QTreeWidgetItem *item11 = new QTreeWidgetItem(item1); item11->setText(0,"甜心超人"); item11->setText(1,"小心超人"); QTreeWidgetItem* item2 = new QTreeWidgetItem(&treeWidget,item1); item2->setText(0,"小小怪"); treeWidget.show();
QTableWidget tableWidget(3,2); QTableWidgetItem *tableWidgetem = new QTableWidgetItem("Tom"); tableWidget.setItem(1,1,tableWidgetem); QTableWidgetItem* headerV = new QTableWidgetItem("Cat"); tableWidget.setVerticalHeaderItem(0,headerV); QTableWidgetItem* headerH = new QTableWidgetItem("What"); tableWidget.setHorizontalHeaderItem(0,headerH); tableWidget.show(); return app.exec(); }
|