13.7.2 QTreeWidget
类QTreeWidget在其内部定义了一个具有树状层次结构的模型,并以树状外观显示这个模型。虽然显示出来的外观和QTreeView的类似,但是程序员并不需要自己创建模型对象,因而使用起来更加方便。使用QTreeWidget时,模型中的数据项被表示为QTreeWidgetItem。向模型添加一个数据项时,程序员只须创建一个QTreeWidgetItem对象并指定其父节点,QTreeWidget即可记录并维护所有节点的父子关系,形成一个完整的树状模型。
为了使得该类更容易被使用,Qt甚至定义了一个类QTreeWidgetItemIterator,以对该类中的树进行前序遍历。所谓前序遍历,是指先访问某个父节点本身,然而再依次对其所含的子树进行前序遍历。关于树以及树的遍历,请参考文献[27]。
下面我们举一例子来说明QTreeWidget的用法。该例子曾在13.2节提及,其输出如图13 26所示,显示的是C++(www.cppentry.com)创始人Bjarne Stroustrup所著书籍The Design and Evolution of C++(www.cppentry.com)的部分目录。
|
| 图13 26 使用QTreeWidget显示具有树状结构的数据集 |
我们使用代码段13 34中的结构体SectionInfo表示目录中每个章节的信息。该结构体含有四个类型为字符指针的成员。这些指针所指的字符串依次表示:章节号(比如"Section 2.4.1"),章节题目(比如"Inlining"),在书中的页码(比如"33"),其父节点的章节号(比如"Section 2.4")。所有章节的信息存放在数组directory中。
代码段13 34,书籍目录的表示,取自z:\examples\mvc\QTreeWidget_demo\main.cpp
- typedef struct {
- char * sect_id, * title, * page_num;
- char * parent_id;
- } SectionInfo;
- SectionInfo directory[] = {
- {"Chapter 1", "The Prehistory of C++(www.cppentry.com)", "19", ""},
- {"Chapter 2", "C with Classes", "27", ""},
- ……
- {"Section 1.1", "Simula and Distributed Systems", "19", "Chapter 1"},
- {"Section 1.2", "C and Systems Programming", "22", "Chapter 1"},
- ……
- {"Section 2.4", "Run-Time Efficiency", "32", "Chapter 2"},
- {"Section 2.4.1", "Inlining", "33", "Section 2.4"},
- };
分析这些章节信息并构造一个树状模形的过程如代码段13 35所示。行①、②设置一个QTreeWidget视图对象的列数以及每列的标头。对于存放在结构体SectionInfo中每条章节信息,如果父节点的章节号为空字符串,这条信息表示的是书中某"章"的信息,应该作为模型的最顶层节点存放。行③创建一个QTreeWidgetItem对象时,令其父节点为QTreeWidget视图对象,就表达了这样的意图。
对于一个章所含的小节,由于在创建其对应的QTreeWidgetItem对象时需要指定其父节点,我们首先要在树状模形中寻找其父节点。一个小节的父节点可能表示一个"章"(比如"Section 1.1"的),也可能表示另外一个更高层的小节(比如"Section 2.4.1"的)。表示该小节的结构体SectionInfo只保存了其父节点的章节号信息,因此我们需要遍历整棵树,依据其父节点的章节号信息确定其父节点的位置。行④定义了一个类型为QTreeWidgetItemIterator的迭代器,它能够对树状模型进行前序遍历。在遍历过程中,我们判断迭代器所指当前节点的章节号是否是我们正在寻找的。找到后,我们创建一个QTreeWidgetItem对象,令其父节点为刚刚找到的那个节点。
代码段13 35,构建QTreeWidget中的树状模型,取自z:\examples\mvc\QTreeWidget_demo\main.cpp
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- QTreeWidget * treeWidget = new QTreeWidget();
- treeWidget->setColumnCount(3); ①
- QStringList headers;
- headers << "Section Number" << "Title" << "Page Number";
- treeWidget->setHeaderLabels(headers); ②
-
- for (int i=0; i<sizeof(directory)/sizeof(directory[0]); i++) {
- SectionInfo info = directory[i];
- QTreeWidgetItem * item=NULL;
- if (strcmp(info.parent_id, "")==0 ){
- item = new QTreeWidgetItem(treeWidget); ③
- }else{
- QString parent_id(info.parent_id);
- QTreeWidgetItemIterator it (treeWidget); ④
- while ( (*it)->text(0) != parent_id)
- ++it;
- item = new QTreeWidgetItem( *it ); ⑤
- }
- if ( item) {
- item->setText(0, info.sect_id);
- item->setText(1, info.title );
- item->setText(2, info.page_num);
- }
- }
- treeWidget->resize(400,200);
- treeWidget->show();
- return app.exec();
- }
从这个例子可以看出,和QTreeView相比,QTreeWidget的用法更加简单。我们用QTreeWidgetItem存放数据项的信息。创建该类的每个对象时,恰当地指定其父节点,然而将数据项的信息写入该对象即可。