为了实现数据的存储和表现分离,Qt提供了Model/View架构,包括三个部分,分别是模型(Model),视图(View),委托(delegate)。
Model用于访问底层数据,也就是说为其他组件访问存储的数据提供了接口。
View通过Model Index来获取底层的数据,将数据绘制到窗体上。
Delegate是在View需要编辑的时候提供一种自定义编辑方式。比如我们可能只允许输入数字,那么就需要委托来做这件事情。
三个部分只有Model知道怎么从内存中读取数据,而View和Delegate则是借助Model Index通过Model来访问。
对于View来说,它不需要知道底层数据是如何存储的(这是Model的任务),它只需要从Model那里要来数据,然后把数据放到屏幕上就可以了。
对于Model来说,它也不需要知道数据是怎么被表现在屏幕上的(那是View的任务),它只需要响应View的请求,从内存中找到数据然后告诉View就可以了。
对于Delegate来说,它什么都不需要知道,它只需要在某个组件需要编辑的时候,提供一个自定义的编译器(如果有某种需求的话,比如只允许输入数字),然后将编辑结果反馈给Model让它来更改底层数据,反馈给View让它更新屏幕显示即可。
Models, Views, Delegates三者之间的相互作用是通过信号槽来实现的:
1.由Model发出的信号告诉View底层数据发生了改变。
2.由View发出的信号提供用户与窗体中元素之间交互的信息,比如说鼠标点击某个位置,需要编辑某个元素。
3.由Delegate发出的信号是在编辑某个元素时发出的,告诉Model和View编辑状态,比如编辑完成。
Model Classes:
Model提供一个标准的接口供View和Delegate访问数据,在Qt中,这个接口定义在QAbstractItemModel类中。
MV架构中使用模型索引(Model Index)来确保数据的表现和它被存储的方式之间相互分离。View和Delegate就是通过模型索引来请求Model获取数据。因此,只有Model知道如何通过Model Index获取数据。
一个完整的model index包括行索引,列索引和它父元素的model index。
View Classes:
在MV架构中,View负责从Model那里获取数据然后展现给用户,数据被展现的方式是自定的,不需要考虑它在底层内存是如何存储的。况且View也根本不清楚这件事情。
View主要用于对需要展现给用户的数据进行布局,Qt也提供了一个标准视图接口QAbstractItemView。
除了呈现给用户数据,View也承担着数据选择相关的责任,以及菜单和拖放的任务。View也具有编辑功能,如果不需要对用户的输入进行控制,那么View自带的编辑器就已经足够了。否则就需要只用Delegate委托生成一个编辑器来对用户输入进行控制。
View使用setModel()来设置它的模型
Delegate Classes:
Delegate主要用于当View中的元素需要编辑时,提供自定义的编辑方式,实现对用户输入进行控制。比如说只允许输入数字等。委托的接口定义在QAbstractItemDelegate中。
当View需要一个Delegate时,那么用户就需要自定义一个子类继承委托接口。但是需要提供一些函数:
createEditor()创建一个编辑器,可以是任何可编辑的组件。
setEditorData()提供编辑器的初始值,即双击编辑时初始显示的内容,通常都是编辑前该位置的内容。
setModelData()当输入完毕后更改底层数据,通过Model Index。将创建的组件中存储的内容取出,再利用model->setData()函数修改数据。
updateEditorGeometry()用于改变编辑器的位置和大小,将它放在需要编辑的位置。
委托对于输入的控制主要是利用createEditor中使用的组件,比如如果在该函数中使用QSpinBox,那这个编辑器可能就只允许输入整数。如果使用QLineEdit,那么可以输入任何内容。输入完成之后,Delegate会发出信号告诉Model和View编辑完成,然后在setModelData函数中我们需要将创建的组件中存储的信息取出,然后利用model->setData()函数来更新数据。当数据发生改变后,Model又发出信号告诉View,随后View会自动更新显示的内容(这是Qt自动完成的)。