提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、MVD的理解
- 二、代码
- 1.实现的功能
- 2.效果
- 3.代码示例
- 总结
前言
模型和视图的主要作用就是实现数据显示和存储分离;Qt通过MVD(模型、视图、代理)实现了这种功能。
一、MVD的理解
- 模型 M:模型类定义了标准的接口,供视图(View)和委托(Delegate)来访问数据。数据本身不需要存储在模型中;它可以保存在由单独的类、文件、数据库或其它应用程序组件提供的数据结构或存储库中。
模型对数据进行封装例如封装成List、Tree、Table等,并提供了数据的访问接口,例如data()、setData()等。
有的简单的数据可以保存在Model中,复杂的可以保存在类、文件、数据库等组件中。 例如:
QStringListModel类可以保存QString数据;
QFileSystemModel类可以组织、封装文件系统数据,并提供访问接口;
QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel组织、封装数据库数据,并提供数据的访问接口。
- 视图 V:视图提供了一个标准接口,通过信号和槽机制与模型进交互操作,使子类能够随时更新其模型的更改。
- 视图类负责人眼能看到的一些属性,包括数据的绘制,管理表头、数据项的选择,网格的显示隐藏以及行高列宽的设置等。另外,提供信号,当交互操作时,通知模型更新数据等。
- 代理D:代理为模型/视图架构中的代理提供了接口和通用函数。代理在视图中显示单独的数据项,并且处理模型数据的编辑。
- 代理提供了访问模型和视图的接口,当要实现复杂功能的显示时(例如表格中显示下拉框、按钮、进度条等),只靠模型和视图,不能实现;需要借助代理来实现。代理可以通过创建编辑器(编辑器可以是下拉框、按钮或者进度条等),并实现代理提供的虚函数,来实现这些功能。
二、代码
1.实现的功能
该demo使用QTableView、QStandardItemModel、QStyledItemDelegate类创建了表格,并表格中自定义了QComboBox、QSpinBox、QPushButton、QProgressBar等,其中按钮关联槽函数,获取表格item的值并打印;并且创建了定时器,在超时槽函数中更新进度条的值。实现时,代理我选择了QStyledItemDelegate类,因为更新进度条时,需要重绘进度条样式。若只有交互组件,不用重绘组件样式,也可选用QItemDelegate。
2.效果
3.代码示例
3.1 Mainwindow.h头文件代码示例
#ifndefMAINWINDOW_H#defineMAINWINDOW_H#include<QMainWindow>#include<QTableView>#include<QStandardItemModel>#include"Myitemydelegate.h"#include<QVBoxLayout>#include<QTextBrowser>#include<QTimer>namespaceUi{classMainWindow;}classMainWindow:publicQMainWindow{Q_OBJECTpublic:explicitMainWindow(QWidget*parent=0);~MainWindow();private:Ui::MainWindow*ui;QString tableInitData[4][5]={{"张一","男","21","10","获取"},{"张二","男","22","20","获取"},{"张三","女","23","30","获取"},{"张四","女","24","40","获取"}};QTimer*updateBarTimer;QWidget*centralWidget;QTableView*tableView;QStandardItemModel*itemModel;MyItemyDelegate*delegate;QVBoxLayout*widgetVLayout;QTextBrowser*displayBrowser;voidinitTable();publicslots:voidtableBtnClickedSlot(introw);voidupdateProgressBarData();};#endif// MAINWINDOW_H3.2 Mainwindow.cpp文件代码示例
#include"mainwindow.h"#include"ui_mainwindow.h"#include<QDebug>MainWindow::MainWindow(QWidget*parent):QMainWindow(parent),ui(newUi::MainWindow){ui->setupUi(this);initTable();}MainWindow::~MainWindow(){deleteui;}voidMainWindow::initTable(){/*新建对象*/updateBarTimer=newQTimer;centralWidget=newQWidget(this);tableView=newQTableView;itemModel=newQStandardItemModel;delegate=newMyItemyDelegate;widgetVLayout=newQVBoxLayout;displayBrowser=newQTextBrowser;/*设置表格格式*/itemModel->setColumnCount(5);itemModel->setRowCount(4);itemModel->setHeaderData(0,Qt::Horizontal,"姓名");itemModel->setHeaderData(1,Qt::Horizontal,"性别");itemModel->setHeaderData(2,Qt::Horizontal,"年龄");itemModel->setHeaderData(3,Qt::Horizontal,"学习进度");itemModel->setHeaderData(4,Qt::Horizontal,"获取进度");/*表格设置模型和代理*/tableView->setModel(itemModel);tableView->setItemDelegate(delegate);/*布局*/widgetVLayout->addWidget(tableView);widgetVLayout->addWidget(displayBrowser);centralWidget->setLayout(widgetVLayout);this->setCentralWidget(centralWidget);/*表格item初始化*/for(inti=0;i<4;i++){for(intj=0;j<5;j++){itemModel->setItem(i,j,newQStandardItem(tableInitData[i][j]));itemModel->item(i,j)->setTextAlignment(Qt::AlignCenter);}}connect(delegate,SIGNAL(clicked(int)),this,SLOT(tableBtnClickedSlot(int)));connect(updateBarTimer,SIGNAL(timeout()),this,SLOT(updateProgressBarData()));updateBarTimer->start(1000);}/*表格中按钮的响应槽函数*/voidMainWindow::tableBtnClickedSlot(introw){displayBrowser->clear();QString itemName[4]={"姓名:","性别:","年龄:","学习进度:"};for(inti=0;i<4;i++){displayBrowser->append(itemName[i]+itemModel->item(row,i)->text());}}/*定时器超时函数:更新进度条*/voidMainWindow::updateProgressBarData(){for(inti=0;i<4;i++){intsetData=(itemModel->item(i,3)->text().toInt()+5)%100;itemModel->setData(itemModel->index(i,3),setData);}}3.3 MyItemyDelegate.h文件代码示例
在使用代理时,有几个虚函数需要在子类中重新实现,具体可根据需要去实现,例如当需要绘制样式时,需要实现paint()函数,否则不用实现。
#ifndefMYITEMYDELEGATE_H#defineMYITEMYDELEGATE_H#include<QObject>#include<QPushButton>#include<QComboBox>#include<QSpinBox>#include<QProgressBar>#include<QStyledItemDelegate>classMyItemyDelegate:publicQStyledItemDelegate{Q_OBJECTpublic:explicitMyItemyDelegate(QObject*parent=nullptr);~MyItemyDelegate();/*当需要创建自定义交互编辑器时,需要实现下面这几个函数*/QWidget*createEditor(QWidget*parent,constQStyleOptionViewItem&option,constQModelIndex&index)constoverride;voidsetEditorData(QWidget*editor,constQModelIndex&index)constoverride;voidsetModelData(QWidget*editor,QAbstractItemModel*model,constQModelIndex&index)constoverride;voidupdateEditorGeometry(QWidget*editor,constQStyleOptionViewItem&option,constQModelIndex&index)constoverride;/*当需要绘制样式时,需要重新实现paint()函数*/voidpaint(QPainter*painter,constQStyleOptionViewItem&option,constQModelIndex&index)const;/*当需要过滤操作事件(例如按钮点击事件)时,需要重新实现editorEvent()函数*/booleditorEvent(QEvent*event,QAbstractItemModel*model,constQStyleOptionViewItem&option,constQModelIndex&index);signals:voidclicked(introw);};#endif// MYITEMYDELEGATE_H3.4MyItemyDelegate.cpp文件代码示例
#include"Myitemydelegate.h"#include<QApplication>#include<QMouseEvent>#include<QDebug>MyItemyDelegate::MyItemyDelegate(QObject*parent):QStyledItemDelegate(parent){}MyItemyDelegate::~MyItemyDelegate(){}QWidget*MyItemyDelegate::createEditor(QWidget*parent,constQStyleOptionViewItem&option,constQModelIndex&index)const{if(index.column()==1)/*性别*/{QComboBox*tableComBox=newQComboBox(parent);tableComBox->addItem("男");tableComBox->addItem("女");returntableComBox;}elseif(index.column()==2)/*年龄*/{QSpinBox*tableSpinBox=newQSpinBox(parent);tableSpinBox->setFrame(false);tableSpinBox->setMinimum(0);tableSpinBox->setMaximum(100);returntableSpinBox;}elseif(index.column()==3)/*进度*/{returnnullptr;}elseif(index.column()==4)/*控制按钮*/{returnnullptr;}returnQStyledItemDelegate::createEditor(parent,option,index);}voidMyItemyDelegate::setEditorData(QWidget*editor,constQModelIndex&index)const{if(index.column()==1)/*性别*/{QString value=index.model()->data(index,Qt::EditRole).toString();QComboBox*comboBox=static_cast<QComboBox*>(editor);inttindex=comboBox->findText(value);comboBox->setCurrentIndex(tindex);}elseif(index.column()==2)/*年龄*/{intvalue=index.model()->data(index,Qt::EditRole).toInt();QSpinBox*spinBox=static_cast<QSpinBox*>(editor);spinBox->setValue(value);}else{QStyledItemDelegate::setEditorData(editor,index);}}voidMyItemyDelegate::setModelData(QWidget*editor,QAbstractItemModel*model,constQModelIndex&index)const{if(index.column()==1)/*性别*/{QComboBox*comboBox=static_cast<QComboBox*>(editor);QString text=comboBox->currentText();model->setData(index,text,Qt::EditRole);}elseif(index.column()==2)/*年龄*/{QSpinBox*spinBox=static_cast<QSpinBox*>(editor);spinBox->interpretText();intvalue=spinBox->value();model->setData(index,value,Qt::EditRole);}else{QStyledItemDelegate::setModelData(editor,model,index);}}voidMyItemyDelegate::updateEditorGeometry(QWidget*editor,constQStyleOptionViewItem&option,constQModelIndex&index)const{Q_UNUSED(index)editor->setGeometry(option.rect);}voidMyItemyDelegate::paint(QPainter*painter,constQStyleOptionViewItem&option,constQModelIndex&index)const{if(index.isValid()&&(index.column()==3)){QStyleOptionProgressBar*progressBar=newQStyleOptionProgressBar;progressBar->rect=option.rect;progressBar->progress=index.data().toInt();progressBar->maximum=100;progressBar->minimum=0;progressBar->text=QString::number(progressBar->progress)+"%";progressBar->textVisible=true;progressBar->textAlignment=Qt::AlignCenter;QApplication::style()->drawControl(QStyle::CE_ProgressBar,progressBar,painter);}elseif(index.isValid()&&(index.column()==4)){QStyleOptionButton*btnStyle=newQStyleOptionButton;btnStyle->text="获取";btnStyle->rect=option.rect;btnStyle->state=QStyle::State_Enabled;QPushButton btn;btn.style()->drawControl(QStyle::CE_PushButton,btnStyle,painter,&btn);}else{QStyledItemDelegate::paint(painter,option,index);}}boolMyItemyDelegate::editorEvent(QEvent*event,QAbstractItemModel*model,constQStyleOptionViewItem&option,constQModelIndex&index){Q_UNUSED(model);QMouseEvent*mouseEvent=static_cast<QMouseEvent*>(event);if(option.rect.contains(mouseEvent->pos())){if((event->type()==QEvent::MouseButtonPress)&&(index.column()==4)){emitclicked(index.row());}}returnQStyledItemDelegate::editorEvent(event,model,option,index);}总结
以上就是今天要讲的内容,本文以QTableView为例,仅仅简单介绍模型、视图、代理的简单使用方法;Qt还提供了其它丰富的类,感兴趣可继续深入学习。