13.5 委托(Delegates)(1)
Qt的Model/View框架中,委托类负责显示数据项,创建与管理编辑器对象,以对数据项进行编辑。具体地说,在显示一个模型对象中的数据项时,虽然视图对象负责绘制总体结构(比如树状层次结构、标头等),但是它不会负责绘制每个数据项。每个视图对象总会指向一个默认的委托对象,视图对象把绘制数据项的任务"委托"给这个默认的委托对象。
除此之外,当用户双击某个数据项,希望对其进行编辑时,视图对象并不会亲自处理这个任务,它会请求委托对象创建一个编辑器。委托对象依据被编辑数据项的数据类型,创建并返回一个编辑器。这个编辑器将负责目标数据项的编辑任务。编辑完成后,委托对象负责将编辑器中的数据写回模型对象的目标数据项之中。
由于一个视图对象中所显示的数据项具有多种类型,视图对象可能需要为每一个数据项创建一个独立的编辑器。视图对象在其内部定义了一个容器保存这些编辑器,以便用户第二次编辑某个数据项时,能在这个容器中找到并直接使用对应的编辑器,不用为其再创建一个。
对于大多数应用程序,默认的委托对象能够胜任数据项的绘制任务,它所创建的编辑器也能胜任数据项的编辑任务,这些应用程序无需创建新类型的委托对象。而且,这个默认的委托对象会调用模型类的data()函数,读取一个数据项中角色FontRole、TextAlignmentRole、TextColorRole以及BackgroundColorRole对应的数据子项,以控制数据项的外观,因而提供了一定程度的灵活性。
然而,当我们需要彻底地控制数据项的绘制,或者需要使用新的编辑器来编辑某些数据项时,就需要创建新类型的委托对象。Model/View框架提供了三个委托类:抽象基类QAbstractItemDelegate定义了所有委托类的接口,子类QStyledItemDelegate使用应用程序当前的界面风格(参见15.3节)绘制数据项的外观,另外一个子类QItemDelegate仅适用固定的风格绘制数据项外观。由于后者显示的风格很可能和应用程序中其他控件的风格不一致,本书采用的Qt版本不推荐使用它,我们也应该从QStyledItemDelegate派生新的委托类。
通过调用一个视图对象的setItemDelegate()函数,我们可以令这个视图对象指向一个新委托类的对象。新委托类可以重载成员函数paint()以及sizeHint(),以新的方式绘制数据项。它也可以重载成员函数createEditor(),创建新类型的编辑器对数据项进行编辑。下面,我们举一个具体例子来说明如何定义新的委托类。
这是Qt软件包中的一个例子,我们对其做了细微的改动,存放在目录z:\examples\mvc\ spinboxdelegate下。该例子的主界面如图13 19所示,它以表格方式显示一些数字。用户双击其中某个数据项,可以对该数据项进行编辑。默认情况下,Model/View框架使用一个简单的编辑框对其中的数据进行编辑。本例采用一个SpinBox控件对数据进行编辑,该控件包含一个编辑框以及两个小箭头。用户单击上(下)箭头时,编辑框中的数字将被增加(减少)。
|
| 图13 19 例子SpinBox的主界面 |
该例子中各个类之间的协作关系如图13 20所示。程序使用QStandardItemModel存放表格中的数据项,用QTableView显示这些数据项。程序派生了QStyltedItemDelegate的子类SpinBoxDelegate,该类被设置为QTableView的委托,负责每个数据项的显示。当用户双击某个数据项时,QTableView调用SpinBoxDelegate的成员函数createEditor(),请求创建一个能够编辑这个数据项的编辑器。本例中,SpinBoxDelegate创建一个QSpinBox对象作为目标数据项的编辑器,返回给QTableView。QTableView将返回的编辑器登记到内部定义的一个容器中,以便用户第二次编辑同一个目标数据项时,直接使用这个已经登记的编辑器,不用再创建一个新的编辑器。
|
| (点击查看大图)图13 20 例子SpinBox中各个类之间的协作关系 |
编辑器被创建之后,委托类的成员函数setEditorData()会被调用,对编辑器所要编辑的数据设置一个初始值。具体地说,委托类依据目标数据项的索引,调用模型类的成员函数data(),读取目标数据项的值,再调用编辑器(本例中的QSpinBox)的成员函数setValue(),设置编辑器的数据。用户完成编辑后,委托类调用QSpinBox的成员函数value(),读取编辑器的数据,再调用模型类的成员函数setData(),将数据写回模型类。
模型对象、视图对象、委托对象是在主函数中被创建的,如代码段13 19所示。行①调用视图对象的成员函数setItemDelegate(),令该视图对象不再使用默认的委托对象,而是使用类SpinBoxDelegate的对象。
代码段13 19,例子SpinBox的主函数,取自z:\examples\mvc\spinboxdelegate\main.cpp
- int main(int argc, char *argv[])
- {
- ……
- QStandardItemModel model(4, 2);
- QTableView tableView;
- tableView.setModel(&model);
- SpinBoxDelegate spinbox_dg;
- tableView.setItemDelegate(&spinbox_dg); ①
- for (int row = 0; row < 4; ++row) {
- for (int column = 0; column < 2; ++column) {
- QModelIndex index = model.index(row, column, QModelIndex());
- model.setData(index, QVariant((row+1) * (column+1)));
- }
- }
- ……
- }