单击此处编辑母版标题样式,,单击此处编辑母版文本样式,,第二级,,第三级,,第四级,,第五级,,*,,*,第,8,章,Qt 5,模型,/,视图结构,概述,,8.1,,8.2,模型(,Model,),视图(,View,),,8.3,,8.4,代理(,Delegate,),,Qt,的模型,/,视图结构分为三部分:模型(,Model,)、视图(,View,)和代理(,Delegate,)其中,模型与数据源通信,并为其他部件提供接口;而视图从模型中获得用来引用数据条目的模型索引(,Model Index,)在视图中,代理负责绘制数据条目,当编辑条目时,代理和模型直接进行通信模型,/,视图,/,代理之间通过信号和槽进行通信,如图,8.1,所示8.1,概述,8.1.1,基本概念,,1,.模型(,Model,),,InterView,框架中的所有模型都基于抽象基类,QAbstractItemModel,类,此类由,QProxyModel,、,QAbstractListModel,、,QAbstractTableModel,、,QAbstractProxyModel,、,QDirModel,、,QFileSystemModel,、,QHelpContentModel,和,QStandardItemModel,类继承。
其中,,QAbstractListModel,类和,QAbstractTableModel,类是列表和表格模型的抽象基类,如果需要实现列表或表格模型,则应从这两个类继承8.1.1,基本概念,2,.视图(,View,),,InterView,框架中的所有视图都基于抽象基类,QAbstractItemView,类,此类由,QColumnView,、,QHeaderView,、,QListView,、,QTableView,和,QTreeView,类继承其中,,QListView,类由,QUndoView,类和,QListWidget,类继承;,QTableView,类由,QTableWidget,类继承;,QTreeView,类由,QTreeWidget,类继承而,QListWidget,类、,QTableWidget,类和,QTreeWidget,类实际上已经包含了数据,是模型,/,视图集成在一起的类3,.代理(,Delegate,),,InterView,框架中的所有代理都基于抽象基类,QAbstractItemDelegate,类,此类由,QItemDelegate,和,QStyledItemDelegate,类继承。
其中,,QItemDelegate,类由表示数据库中关系代理的,QSqlRelationalDelegate,类继承8.1.2,【实例】:模型,/,视图类使用,【例】(简单),实现一个简单的文件目录浏览器,完成效果如图,8.2,所示实例文件见光盘,CH801,创建工程“,DirModeEx.pro,”,,其源文件“,main.cpp,”中的具体,代码,最后运行结果如图,8.2,所示8.2,模型(,Model,),【例】(难度一般),通过实现将数值代码转换为文字的模型来介绍如何使用自定义模型此模型中保存了不同军种的各种武器,实现效果如图,8.3,所示实例文件见光盘,CH802,8.2,模型(,Model,),具体操作步骤如下1,),ModelEx,类继承自,QAbstractTableModel,类,,头文件“,modelex.h,”中的具体,代码,2,),源文件“,modelex.cpp,”中的具体,代码,populateModel(),函数的具体实现代码如下:,,void ModelEx::populateModel(),,{,,header<
int ModelEx::columnCount(const QModelIndex &parent) const,,{ return 3; },,rowCount(),函数返回模型的行数int ModelEx::rowCount(const QModelIndex &parent) const,,{,,return army.size();,,},,data(),函数返回指定索引的数据,,即将数值映射为文字8.2,模型(,Model,),表,8.1,列出了,Item,主要的角色及其描述常,,量,描,,述,Qt::DisplayRole,显示文字,Qt::DecorationRole,绘制装饰数据(通常是图标),Qt::EditRole,在编辑器中编辑的数据,Qt::ToolTipRole,工具提示,Qt::StatusTipRole,状态栏提示,Qt::WhatsThisRole,What’s This,文字,Qt::SizeHintRole,尺寸提示,Qt::FontRole,默认代理的绘制使用的字体,Qt::TextAlignmentRole,默认代理的对齐方式,Qt::BackgroundRole,默认代理的背景画刷,Qt::ForegroundRole,默认代理的前景画刷,Qt::CheckStateRole,默认代理的检查框状态,Qt::UserRole,用户自定义的数据的起始位置,,8.2,模型(,Model,),headerData(),函数返回固定的表头数据,设置水平表头的标题,具体代码如下:,,QVariant ModelEx::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);,,},,,,8.2,模型(,Model,),(,3,)在源文件“,main.cpp,”中,将模型和视图关联,具体代码如下:,,#include ,,#include "modelex.h",,#include ,,int main(int argc,char *argv[]),,{,,QApplication a(argc,argv);,,ModelEx modelEx;,,QTableView view;,,view.setModel(,,view.setWindowTitle(QObject::tr("modelEx"));,,view.resize(400,400);,,view.show();,,return a.exec();,,},,(,4,)运行结果如图,8.3,所示。
8.3,视图(,View,),【例】(难度中等),通过利用自定义的,View,,实现一个对,TableModel,的表格数据进行显示的柱状统计图例子,以此介绍如何应用自定义的,View,实现效果如图,8.4,所示实例文件见光盘,CH803,8.3,视图(,View,),具体实现步骤如下1,)完成主窗体,以便显示,View,的内容MainWindow,类继承自,QMainWindow,类,作为主窗体以下是,头文件“,mainwindow.h,”的具体,代码,2,)下面是,源文件“,mainwindow.cpp,”中的具体,代码,setupModel(),函数新建一个,Model,,并设置表头数据,其具体实现,代码如下:,,void MainWindow::setupModel(),,{,,model = new QStandardItemModel(4,4,this);,,model->setHeaderData(0,Qt::Horizontal,tr(",部门,"));,,model->setHeaderData(1,Qt::Horizontal,tr(",男,"));,,model->setHeaderData(2,Qt::Horizontal,tr(",女,"));,,model->setHeaderData(3,Qt::Horizontal,tr(",退休,"));,,},,,8.3,视图(,View,),setupView(),函数的具体实现代码如下:,,void MainWindow::setupView(),,{,,table = new QTableView; //,新建一个,QTableView,对象,,table->setModel(model); //,为,QTableView,对象设置相同的,Model,,QItemSelectionModel *selectionModel=new QItemSelectionModel(model,); //(,a),,table->setSelectionModel(selectionModel);,,connect(selectionModel,SIGNAL(selectionChanged(QItemSelection, ItemSelection)),table,SLOT(selectionChanged(QItemSelection,QItemSelec-tion)));,//(,b),,splitter = new QSplitter;,,splitter->setOrientation(Qt::Vertical);,,splitter->addWidget(table);,,setCentralWidget(splitter);,,},,8.3,视图(,View,),(,3,)此时,运行效果如图,8.5,所示。
8.3,视图(,View,),(,1,)在头文件“,mainwindow.h,”中添加代码如下:,,public:,,void openFile(QString);,,public slots:,,void slotOpen();,,(,2,)在源文件,mainwindow.cpp,中添加代码如下:,,#include ,,#include ,,#include ,,#include ,,其中,,在,createAction(),函数中添加代码如下:,,connect(openAct,SIGNAL(triggered()),this,SLOT(slotOpen()));,,,8.3,视图(,View,),槽函数,slotOpen(),完成打开标准文件对话框,具体代码如下:,,void MainWindow::slotOpen(),,{,,QString name;,,name = QFileDialog::getOpenFileName(this,",打开,",".","histogram files (*.txt)");,,if (!name.isEmpty()),,openFile(name);,,},,openFile(),函数完成,打开所选的文件内容,其具体实现代码,。
8.3,视图(,View,),新建一个文本文件,命名为“,histogram.txt,”,保存在项目,D:\Qt\CH8\CH803\ build-ViewEx-Desktop_Qt_5_4_0_MinGW_32bit-Debug,目录下,文件内容及打开后的效果如图,8.6,所示8.3,视图(,View,),以上完成了表格数据的加载,下面介绍柱状统计图的绘制具体实现步骤如下1,)自定义,HistogramView,类继承自,QAbstractItemView,类,用于对表格数据进行柱状图显示下面是,头文件“,histogramview.h,”的具体代码,2,)源文件“,histogramview.cpp,”的具体代码,dataChanged(),函数实现当,Model,中的数据更改时,调用绘图设备的,update(),函数进行,更新,反映数据的变化具体实现代码,void HistogramView::dataChanged(const QModelIndex &topLeft,,,const QModelIndex &bottomRight),,{,,QAbstractItemView::dataChanged(topLeft,bottomRight);,,viewport()->update();,,},,setSelectionModel(),函数为,selections,赋初值,具体代码如下:,,void HistogramView::setSelectionModel(QItemSelectionModel *selectionModel),,{,,selections=selectionModel;,,},,,,,,8.3,视图(,View,),(,3,)下面的工作就是完成对选择项的更新。
selectionChanged(),函数中完成当数据项发生变化时调用,update(),函数,重绘绘图设备即可工作此函数是将其他,View,中的操作引起的数据项选择变化反映到自身,View,的显示中具体代码如下:,,void HistogramView::selectionChanged(const QItemSelection &selected,,,const QItemSelection &deselected),,{,,viewport()->update();,,},,鼠标按下事件函数,mousePressEvent(),,在调用,setSelection(),函数时确定鼠标单击点是否在某个数据项的区域内,并设置选择项具体代码如下:,,void HistogramView::mousePressEvent(QMouseEvent *event),,{,,QAbstractItemView::mousePressEvent(event);,,setSelection(QRect(event->pos().x(),event->pos().y(),1,1),QItem,,Selec tionModel::SelectCurrent);,,},,,8.3,视图(,View,),setSelection(),函数的具体代码如下:,,void HistogramView::setSelection(const QRect &rect,QItemSelectionModel,,::SelectionFlags flags),,{,,int rows = model()->rowCount(rootIndex()); //,获取总行数,,int columns = model()->columnCount(rootIndex()); //,获取总列数,,QModelIndex selectedIndex; //(a),,for (int row=0; rowindex(row,column,rootIndex());,,QRegion region=itemRegion(index); //(c),,if (!region.intersected(rect).isEmpty()),,selectedIndex = index;,,},,},,if(selectedIndex.isValid()) //(d),,selections->select(selectedIndex,flags);,,else,,{,,QModelIndex noIndex;,,selections->select(noIndex,flags);,,},,},,,8.3,视图(,View,),indexAt(),函数的具体内容,。
由于本例未用到以下函数的功能,所以没有实现具体内容,但仍然要写出函数体的框架,代码如下:,,QRect HistogramView::visualRect(const QModelIndex &index)const{},,void HistogramView::scrollTo(const QModelIndex &index,ScrollHint){},,QModelIndex HistogramView::moveCursor(QAbstractItemView::CursorAction cursor Action, Qt::KeyboardModifiers modifiers){},,int HistogramView::horizontalOffset()const{},,int HistogramView::verticalOffset()const{},,bool HistogramView::isIndexHidden(const QModelIndex &index)const{},,QRegion HistogramView::visualRegionForSelection(const QItemSelection & selection)const{},,,8.3,视图(,View,),itemRegion(),函数的具体代码如下:,,QRegion HistogramView::itemRegion(QModelIndex index),,{,,QRegion region;,,if (index.column() == 1) //,男,,region = MRegionList[index.row()];,,if (index.column() == 2) //,女,,region = FRegionList[index.row()];,,if (index.column() == 3) //,退休,,region = SRegionList[index.row()];,,return region;,,},,,8.3,视图(,View,),(,4,)在头文件“,mainwindow.h,”中添加代码如下:,,#include "histogramview.h",,private:,,HistogramView *histogram;,,(,5,)在源文件“,mainwindow.cpp,”中添加代码,其中,,setupView(),函数的代码修改,。
6,)运行结果如图,8.4,所示8.4,代理(,Delegate,),【例】(难度中等),利用,Delegate,设计表格中控件如图,8.7,所示实例文件见光盘,CH804,8.4,代理(,Delegate,),具体实现步骤如下1,)首先,加载表格数据,以便后面的操作源文件“,main.cpp,”中的具体代码如下:,,(,2,)选择“构建”→“构建项目,"DateDelegate",”菜单项,首先按照如图,8.8,所示的格式编辑本例所用的数据文件“,test.txt,”,保存在项目,D:\Qt\CH8\CH804\ build- DateDelegate-Desktop_Qt_5_4_0_MinGW_32bit- Debug,目录下,然后运行程序,结果如图,8.7,所示8.4,代理(,Delegate,),(,3,)在图,8.7,中,使用手动的方式实现对生日的录入编辑下面使用日历编辑框,QDateTimeEdit,控件实现对生日的编辑,用自定义的,Delegate,来实现4,),DateDelegate,继承自,QItemDelegate,类头文件“,datedelegate.h,”中的具体代码如下:,,#include ,,class DateDelegate : public QItemDelegate,,{,,Q_OBJECT,,public:,,DateDelegate(QObject *parent = 0);,,QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem //(a),,void setEditorData(QWidget *editor, const QModelIndex //(b),,void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex //,将,Delegate,中对数据的改变更新至,Model,中,,void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem //,更新控件区的显示,,};,,,,8.4,代理(,Delegate,),(,5,)源文件“,datedelegate.cpp,”中的具体代码如下:,,#include "datedelegate.h",,#include ,,DateDelegate::DateDelegate(QObject *parent) :,,QItemDelegate(parent),,{,,},,createEditor(),函数的具体实现代码如下:,,QWidget *DateDelegate::createEditor(QWidget *parent,const QStyleOption ViewItem &/*option*/,const QModelIndex &/*index*/) const,,{,,QDateTimeEdit *editor = new QDateTimeEdit(parent); //(a),,editor->setDisplayFormat("yyyy-MM-dd"); //(b),,editor->setCalendarPopup(true); //(c),,editor->installEventFilter(const_cast(this)); //(d),,return editor;,,},,,8.4,代理(,Delegate,),setEditorData(),函数的具体代码如下:,,void DateDelegate::setEditorData(QWidget *editor,,,const QModelIndex &index) const,,{,,QString dateStr= index.model()->data(index).toString(); //(a),,QDate date = QDate::fromString(dateStr,Qt::ISODate); //(b),,QDateTimeEdit *edit=static_cast(editor); //(c),,edit->setDate(date); //,设置控件的显示数据,,},,,,8.4,代理(,Delegate,),setModelData(),函数的具体代码如下:,,void DateDelegate::setModelData(QWidget *editor,QAbstractItemModel *model, const QModelIndex &index) const,,{,,QDateTimeEdit *edit=static_cast(editor);//(a),,QDate date = edit->date(); //(b),,model->setData(index,QVariant(date.toString(Qt::ISODate)));//(c),,},,updateEditorGeometry(),函数的具体代码如下:,,void DateDelegate::updateEditorGeometry(QWidget *editor,const QStyle OptionViewItem &option,const QModelIndex &index) const,,{,,editor->setGeometry(option.rect);,,},,,,8.4,代理(,Delegate,),(,6,)在“,main.cpp,”文件中添加如下代码:,,#include "datedelegate.h",,在语句,tableView.setModel(,后面添加如下代码:,,DateDelegate dateDelegate;,,tableView.setItemDelegateForColumn(1,,,(,7,)此时运行程序,双击第,1,行第,2,列,将显示如图,8.9,所示的日历编辑框控件。
8.4,代理(,Delegate,),下面使用下拉列表框,QComboBox,控件实现对职业类型的输入编辑,使用自定义的,Delegate,实现1,),ComboDelegate,继承自,QItemDelegate,类头文件“,combodelegate.h,”中的具体代码如下:,,#include ,,class ComboDelegate : public QItemDelegate,,{,,Q_OBJECT,,public:,,ComboDelegate(QObject *parent = 0);,,QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem ,,void setEditorData(QWidget *editor, const QModelIndex ,,void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex ,,void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem ,,};,,,8.4,代理(,Delegate,),(,2,)源文件“,combodelegate.cpp,”中的具体代码如下:,,#include "combodelegate.h",,#include ,,ComboDelegate::ComboDelegate(QObject *parent) :,,QItemDelegate(parent),,{,,},,,8.4,代理(,Delegate,),createEditor(),函数中创建了一个,QComboBox,控件,并插入可显示的条目,安装事件过滤器。
具体代码如下:,,QWidget *ComboDelegate::createEditor(QWidget *parent,const QStyleOption ViewItem &/*option*/,const QModelIndex &/*index*/) const,,{,,QComboBox *editor = new QComboBox(parent);,,editor->addItem(",工人,");,,editor->addItem(",农民,");,,editor->addItem(",医生,");,,editor->addItem(",律师,");,,editor->addItem(",军人,");,,editor->installEventFilter(const_cast(this));,,return editor;,,},,,,8.4,代理(,Delegate,),setEditorData(),函数中更新了,Delegate,控件中的数据显示,具体代码如下:,,void ComboDelegate::setEditorData(QWidget *editor,const QModelIndex &index) const,,{,,QString str =index.model()->data(index).toString();,,QComboBox *box = static_cast(editor);,,int i=box->findText(str);,,box->setCurrentIndex(i);,,},,setModelData(),函数中更新了,Model,中的数据,具体代码如下:,,void ComboDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const,,{,,QComboBox *box = static_cast(editor);,,QString str = box->currentText();,,model->setData(index,str);,,},,,,,8.4,代理(,Delegate,),updateEditorGeometry(),函数的具体代码如下:,,void ComboDelegate::updateEditorGeometry(QWidget *editor,,,const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const,,{,,editor->setGeometry(option.rect);,,},,在“,main.cpp,”文件中添加以下内容:,,#include "combodelegate.h",,在语句,tableView.setModel(&model),的后面添加以下代码:,,ComboDelegate comboDelegate;,,tableView.setItemDelegateForColumn(2,,,8.4,代理(,Delegate,),此时运行程序,双击第,1,行第,3,列,显示如图,8.10,所示的下拉列表。
8.4,代理(,Delegate,),下面使用,QSpinBox,控件实现对收入的输入编辑,调用自定义的,Delegate,来实现SpinDelegate,类的实现与,ComboDelegate,类的实现类似,此处不再详细讲解1,)头文件“,spindelegate.h,”中的具体代码如下:,,#include ,,class SpinDelegate : public QItemDelegate,,{,,Q_OBJECT,,public:,,SpinDelegate(QObject *parent = 0);,,QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem ,,void setEditorData(QWidget *editor, const QModelIndex ,,void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex ,,void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem ,,};,,,,8.4,代理(,Delegate,),(,2,)源文件“,spindelegate.cpp,”中的具体代码如下:,,#include "spindelegate.h",,#include ,,SpinDelegate::SpinDelegate(QObject *parent): QItemDelegate(parent),,{,,},,createEditor(),函数的具体实现代码如下:,,QWidget *SpinDelegate::createEditor(QWidget *parent,const QStyleOption ViewItem &/*option*/,const QModelIndex &/*index*/) const,,{,,QSpinBox *editor = new QSpinBox(parent);,,editor->setRange(0,10000);,,editor->installEventFilter(const_cast(this));,,return editor;,,},,,,8.4,代理(,Delegate,),setEditorData(),函数的具体实现代码如下:,,void SpinDelegate::setEditorData(QWidget *editor,const QModelIndex &index) const,,{,,int value =index.model()->data(index).toInt();,,QSpinBox *box = static_cast(editor);,,box->setValue(value);,,},,setModelData(),函数的具体实现代码如下:,,void SpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const,,{,,QSpinBox *box = static_cast(editor);,,int value = box->value();,,model->setData(index,value);,,},,,8.4,代理(,Delegate,),updateEditorGeometry(),函数的具体实现代码如下:,,void SpinDelegate::updateEditorGeometry(QWidget *editor,,,const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const,,{,,editor->setGeometry(option.rect);,,},,,,8.4,代理(,Delegate,),(,3,)在“,main.cpp,”文件中添加代码如下:,,#include "spindelegate.h",,在语句,tableView.setModel(&model),的后面添加内容如下:,,SpinDelegate spinDelegate;,,tableView.setItemDelegateForColumn(3,,,,(,4,)此时运行程序,双击第,1,行第,4,列后的效果如图,8.11,所示。
下载提示