Qt ModelView模式

Qt Model/View模式

Model/View  结构

Qt 4推出了一组新的item view类,它们使用model/view结构来管理数据与表示层的关系。

Model-View-Controller(MVC), 是从Smalltalk发展而来的一种设计模式,常被用于构建用户界面。经典设计模式的著作中有这样的描述:

MVC 由三种对象组成。Model是应用程序对象,View是它的屏幕表示,Controller定义了用户界面如何对用户输入进行响应。在MVC之前,用户界面设计倾向于三者揉合在一起,MVC对它们进行了解耦,提高了灵活性与重用性。

model与数据源通讯,并提供接口给结构中的别的组件使用。通讯的性质依赖于数据源的种类与model实现的方式。

view从model获取model indexes,后者是数据项的引用。

delegate会对数据项进行渲染,当某个数据项被选中时,delegate通过model indexes与model直接进行交流。

models,views,delegates之间通过信号,槽机制来进行通讯:

从model发出的信号通知view数据源中的数据发生了改变。

从view发出的信号提供了有关被显示的数据项与用户交互的信息。

从delegate发生的信号被用于在编辑时通知model和view关于当前编辑器的状态信息。

Models

model为view和delegates使用数据提供了标准接口。

所有的item models都基于QAbstractItemModel类,这个类定义了用于views和delegates访问数据的接口。

数据本身不必存储在model,数据可被置于一个数据结构或另外的类,文件,数据库,或别的程序组件中。

不管数据在底层以何种数据结构存储,QAabstractItemModel的子类会以层次结构的形式来表示数据,结构中包含了数据项表。我们按这种约定来访问model中的数据项,但这个约定不会对如何显示这些数据有任何限制。数据发生改变时,model通过信号槽机制来通知关联的views。

Qt提供的一些现成的Models用于处理数据项的:

QStringListModel 用于存储简单的QString列表。

QStandardItemModel 管理复杂的树型结构数据项,每项都可以包含任意数据。

QDirModel  提供本地文件系统中的文件与目录信息。

QSqlQueryModel, QSqlTableModel,QSqlRelationTableModel用来访问数据库。

假如这些标准Model不满足你的需要,你应该子类化QAbstractItemModel,QAbstractListModel或是QAbstractTableModel来定制。

Model Indexes

为了使数据存储与数据访问分开,引入了model index的概念。通过model index,可以引用model中的数据项,Views和delegates都使用indexes来访问数据项,然后再显示出来。

model indexes提供了对一项数据信息的临时引用,通过它可以访问或是修改model中的数据。model有时会重新组织内部的数据结构,这时model indexes便会失效,因此不应该保存临时的model indexes

数据信息的长期引用,可使用persistent model index,这个会自动保持更新。

行与列

通过指定model中的行列数来获取任一项数据,可以得到与数据项一一对应的那个index。

QModelIndex index = model->index(row, column, ...);

通过Index.data即可获取对应的数据。也可通过model->data(index, Qt::DisplayRole).toString();获取。

一个model的顶级项(根项),由QModelIndex()取得。

父项

在model外部,引用一个数据项的唯一方法就是通过model index,因此需要在求取model index时指定父项的信息。

QModelIndex index = model->index(row, column, parent);

上图中,A项和C项作为model中顶层的兄弟项:

QModelIndex indexA = model->index(0, 0, QModelIndex());

QModelIndex indexC = model->index(2, 1, QModelIndex());

A有许多孩子,它的一个孩子B用以下代码获取:

QModelIndex indexB = model->index(1, 0, indexA);

项角色

model中的项可以作为各种角色来使用,这允许为不同的环境提供不同的数据。

每个数据项都可以为许多不同的角色提供数据,标准的角色在Qt::ItemDataRole中定义。我们可以通过指定model index与角色来获取我们需要的数据:

QVariant value = model->data(index, role);

概念总结:

1、Model indexes为views与delegages提供model中数据项定位的信息,它与底层的数据结构无关。

2、通过指定行,列数,父项的model index来引用数据项。

3、依照别的组件的要求,model indexes被model构建。

4、使用index()时,如果指定了有效的父项的model index,那么返回得到的model index对应于父项的某个孩子。

5、使用index()时,如果指定了无效的父项的model index,那么返回得到的model index对应于顶层项的某个孩子。

6、角色对一个数据项包含的不同类型的数据给出了区分。

使用Model Indexes

QDirModel *model = new QDirModel;

QModelIndex parentIndex = model->index(QDir::currentPath());

int numRows = model->rowCount(parentIndex);

for (int row = 0; row < numRows; ++row)

{

QModelIndex index = model->index(row, 0, parentIndex);

tring text = model->data(index, Qt::DisplayRole).toString();

// Display the text in a widget.

}

以上的例子说明了从model中获取数据的基本原则:

1、model的行列数可以从rowCount()与columnCount()中得出。这些函数通常都需要一个表示父项的model index。

2、model indexes用来从model中访问数据项,数据项用行,列,父项model index定位。

3、 为了访问model顶层项,可以使用QModelIndex()指定。

4、数据项为不同的角色提供不同的数据。为了获取数据,除了model index之外,还要指定角色。

Views

view从model中获得数据项然后显示给用户。view负责管理从model中读取的数据的外观布局。

不同的view都完整实现了各自的功能:QListView把数据显示为一个列表,QTableView把Model 中的数据以table的形式表现,QTreeView 用具有层次结构的列表来显示model中的数据。这些类都基于QAbstractItemView抽象基类,尽管这些类都是现成的,完整的进行了实现,但它们都可以用于子类化以便满足定制需求。

它们自己可以去渲染每个数据项,也可以利用delegate来既处理渲染又进行编辑。

一个view创建时必不需要model,但在它能显示一些真正有用的信息之前,必须提供一个model。

选择项

view通过使用selections来跟踪用户选择的数据项。每个view可以维护单独使用的selections,也可以在多个views之间共享。

标题

从 model中获取数据的函数是QabstractItemModel::headerDate()。可派生QHeaderView来定制自己得标题。

实操

void MainWindow::stepUI()

{

QSplitter *oSplitter = new QSplitter(this);

QDirModel *oModel = new QDirModel();

QTreeView *oTreeView = new QTreeView(oSplitter);

//配置一个view去显示model中的数据,只需要简单地调用setModel(),并把目录model作为参数传递

oTreeView->setModel(oModel);

//setRootIndex()告诉views显示哪个目录的信息,这需要提供一个model index,然后用这个

//model index去model中去获取数据

//index()这个函数是QDirModel特有的,通过把一个目录做为参数,得到了需要的model index

//其他的代码只是窗口show出来,进入程序的事件循环就好了

oTreeView->setRootIndex(oModel->index(QDir::currentPath()));

oSplitter->setWindowTitle("show DIr");

QTableView *oTableView = new QTableView(oSplitter);

oTableView->setModel(oModel);

oTableView->setRootIndex(oModel->index(QDir::currentPath()));

this->setCentralWidget(oSplitter);

}

几个要点:

1、         同一个model可以绑定多个view

2、         多个view之间共享选择:oTableView ->setSelectionModel(oTreeView ->selectionModel());

Delegates

既提供输入功能又负责渲染view中的每个数据项。

QAbstractItemDelegate 是model/view架构中的用于delegate的抽象基类。缺省的delegate实现在QItemDelegate类中提供。它可以用于Qt标准views的缺省 delegate.

控制delegates的标准接口在QAbstractItemDelegate类中定义。Delegates通过实现paint()和sizeHint()以达到渲染内容的目的。

QT提供的delegate

简单的基于widget的delegates,可以从QItemDelegate子类化,而不是QAbstractItemDelegate,这样可以使用它提供的上述函数的缺省实现。delegate可以使用widget来处理编辑过程,也可以直接对事件进行处理。

Qt提供的标准views都使用QItemDelegate的实例来提供编辑功能。

通过view获取delegate:可以用itemDelegate()函数取得,而setItemDelegate() 函数可以设置一个定制delegate。

实操:使用QSpinBox提供编辑功能

class SpinBoxDelegate : public QItemDelegate

{

Q_OBJECT

public:

SpinBoxDelegate(QObject *parent = 0);

// 获取编辑方式的窗体

QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;

// 向编辑窗体里面写入数据

void setEditorData(QWidget *editor, const QModelIndex &index) const;

// 向模型写入数据

void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE;

// 更新编辑器几何布局

void updateEditorGeometry(QWidget *editor,

const QStyleOptionViewItem &option,

const QModelIndex &index) const;

};

①  delegate创建时不需要创建widget,只需要在真正使用的时候才需要创建,在createEditor中真正的调用。

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;

}

此处我们不需要释放editor,因为view用完以后会释放。

②   将model中的数据同步到新创建的编辑器里面

 void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    int value = index.model()->data(index, Qt::DisplayRole).toInt();
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->setValue(value);

}

③   当编辑完成,向model中提交数据

 void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->interpretText();
    int value = spinBox->value();
    model->setData(index, value);

}

标准的QItemDelegate类当它完成编辑时会发射closeEditor()信号来通知view。view保证编辑器widget关闭与销毁。

④   更新编辑器的位置,在编辑器创建或view尺寸或者位置变化的时候进行调用。

 void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    editor->setGeometry(option.rect);
}
⑤  编辑完成提示
编辑完成以后会发送信号closeEditor来通知其他组件完成编辑和数据提交。我们也可以通过绑定信号closeEditor来进行其他操作。

排序

在model/view架构中,有两种方法进行排序:

1、 对model进行排序

①   编程实现:重新实现QAbstractItemModel::sort()函数

交互式实现:用户点击表头排序,把QHeaderView::sectionClicked()信号与QTableView::sortByColum()槽或QTreeView::sortByColumn()槽进行联结就好了。
QTableView中的sortByColumn方法,他有两个方法可调用,一个是槽函数:
void QTableView::sortByColumn(int column)

{

Q_D(QTableView);

if (column == -1)

return;

d->model->sort(column, d->horizontalHeader->sortIndicatorOrder());

}

另一个是公共函数:最终还是调用的槽函数。
void QTableView::sortByColumn(int column, Qt::SortOrder order)

{

Q_D(QTableView);

d->horizontalHeader->setSortIndicator(column, order);

sortByColumn(column);

}

其实最终还是调用的model中的排序。

2、  通过代理model在view表示数据之前对model数据结构进行转换。

QT提供的常用类

QListWidget,QTreeWidget,QTableWidget这些类qt建议不要去进行派生子类。

时间: 2024-08-24 05:57:34

Qt ModelView模式的相关文章

[Qt] Release模式下产生调试信息

分两步,设置Qt配置文件,设置VS. https://blog.csdn.net/itas109/article/details/83652387 F:\Qt\Qt5.7.1\5.7\msvc2015_64\mkspecs\common\msvc-desktop.conf QMAKE_CFLAGS_RELEASE = -O2 -MD -Zi QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG https://blog.csdn.net/guo5036040

《Linux与Qt程序设计》知识框架

本文主要是通过一本书来大致了解Qt开发的框架,不对具体内容做详细分析. 1.首先弄清楚概念:定义->以自己的话理解是什么-> 实现的是什么功能->用在哪些地方 2.前面认识到的知识点的特点-> 代码实现-> 工程代码分析 第一部分 Linux基础知识第二部分 Qt程序基础第5章 OtCreator下载与安装5.1 QtCreator下载5.1.1 使用软件中心下载QtCreator5.1.2 访问Qt网站下载相关资源5.2 第一个Qt程序5.3 QtCreator介绍5.3.

qt 多线程之间通讯

问题描述:界面线程MainApp为主线程,工作线程MyThread为一子线程,从工作线程向主线程传递字符串用于在主线程中显示. Qt的信号与槽机制可以将任何继承自QObject类的对象捆绑在一起,使不同对象之间能够相互通信. 成功的实现工作线程:mythread.h C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #ifndef MYTHREAD_H #define MYTHREAD_H #include <QThread> cla

在非UI线程中更改UI(Delphi使用隐藏窗口来处理,QT使用信号槽)

在Delphi里我记得是使用TThread.Synchronize(TThreadMethod),原理是利用了一个隐藏窗口来处理. 在QT Debug模式一下,碰到了同样的问题,显示错误: cannot send events to objects owned by a different thread 解决方案是使用信号槽,就是在线程里不断的发信号,UI线程的槽函数不断的接受信号并做处理: So as a solution I would propose the following: Defi

Qt Quick编程(1)——QML的核心部分ECMAScript

说道QML,不得不先说一下ECMAScript: ECMAScript语言的标准是由Netscape.Sun.微软.Borland等公司基于JavaScript和JScript锤炼.定义出来的. ECMAScript可以为不同种类的宿主环境提供核心的脚本编程能力.ECMAScript仅仅是一个描述,定义了脚本语言的所有属性.方法和对象.它描述了一下内容: 语法 类型 语句 关键字 保留字 运算符 对象 其他语言可以以它为基础拓展出新特性,比如QML引入了Qt对象系统中的信号与槽等特色功能. QM

Qt绘图之QGraphicsScene QGraphicsView QGraphicsItem详解(转)

Graphics View提供了一个界面,它既可以管理大数量的定制2D graphical items,又可与它们交互,有一个view widget可以把这些项绘制出来,并支持旋转与缩放.这个柜架也包含一个事件传播结构,对于在scene中的这些items,它具有双精度的交互能力. Items能处理键盘事件,鼠标的按,移动.释放.双击事件,也可以跟踪鼠标移动.Graphics View使用BSP树来提供对item的快速查找,使用这种技术,它可以实时地绘制大规模场景,甚至以百万items计.Grap

Qt Quick编程(1)

说道QML,不得不先说一下ECMAScript: ECMAScript语言的标准是由Netscape.Sun.微软.Borland等公司基于JavaScript和JScript锤炼.定义出来的. ECMAScript可以为不同种类的宿主环境提供核心的脚本编程能力.ECMAScript仅仅是一个描述,定义了脚本语言的所有属性.方法和对象.它描述了一下内容: 语法 类型 语句 关键字 保留字 运算符 对象 其他语言可以以它为基础拓展出新特性,比如QML引入了Qt对象系统中的信号与槽等特色功能. QM

Qt绘图之QGraphicsScene QGraphicsView QGraphicsItem详解

Graphics View提供了一个界面,它既可以管理大数量的定制2D graphical items,又可与它们交互,有一个view widget可以把这些项绘制出来,并支持旋转与缩放.这个柜架也包含一个事件传播结构,对于在scene中的这些items,它具有双精度的交互能力.Items能处理键盘事件,鼠标的按,移动.释放.双击事件,也可以跟踪鼠标移动.Graphics View使用BSP树来提供对item的快速查找,使用这种技术,它可以实时地绘制大规模场景,甚至以百万items计.Graph

Qt Widgets——动作类与小部件菜单项

本文主要涉及以下三个类: QAction ——QWidgetAction QActionGroup QAction可称为动作类,它一般可当作菜单中的项组成菜单,也可作为工具栏上的按钮,它主要由图标.文本及快捷键三部分组成.QActionGroup用于将QAction分组,设置组内各QAction的互斥性质(exclusive ),设置后,组内的动作,在外观上形成多选框(不互斥)或单选框(互斥).QWidgetAction继承自QAction,它可将自定义的小部件插入到菜单项中,用于QSystem