-
Notifications
You must be signed in to change notification settings - Fork 7
/
ch08-03.htm
5914 lines (3860 loc) · 377 KB
/
ch08-03.htm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<title>ch08-03</title>
<link href="css/style.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="thumbnailviewer.css" type="text/css">
<script src="thumbnailviewer.js" type="text/javascript">
/***********************************************
* Image Thumbnail Viewer Script- © Dynamic Drive (www.dynamicdrive.com)
* This notice must stay intact for legal use.
* Visit http://www.dynamicdrive.com/ for full source code
***********************************************/
</script> </head>
<body>
<div class="os1">8.3 树形控件</div>
<br>
本节首先介绍树形控件 QTreeWidget 的内容,然后介绍它的树形节点条目 QTreeWidgetItem。 树形控件的节点可以有多层、多个子节点,
如果将子节点全部展开,那么每一行都是一个数据条目。QTreeWidgetItem
比较特殊,一个条目内部可以有多列数据信息,相当于表格控件一整行的表格单元集成为一个条目,所以树形条目要比前面两节的列表条目和表格条目都复杂。第三小节介绍迭代器和
递归遍历算法,因为树形控件每行的条目之间可以是兄弟关系或父子关系,含有子节点的条目可以折叠也可以展开,进行遍历时有专门的迭代器
QTreeWidgetItemIterator 实现,也可以自行编写递归算法遍历所有条目。<br>
本节介绍完 QTreeWidget 、QTreeWidgetItem、QTreeWidgetItemIterator
等内容之后,通过两个例子展示树形控件的用法,如果读者提前学习一下树形遍历的递归算法,对本节的内容理解会有帮助。<br>
<br>
<div class="os2">8.3.1 QTreeWidget</div>
<br>
在 Qt 设计师界面可以直接拖动树形控件到窗口里,下图展示树形控件的外观和构成:<br>
<center> <img src="images/ch08/ch08-03-01.png" alt="treewidget" width="800"></center>
默认情况下,树形控件最上面是一个树头条目,树头条目也是 QTreeWidgetItem 对象,可以有多列内容。<br>
树头下面是真正的树形控件所有条目,在折叠的情况下,如上图所示,每行一个顶级条目,顶级条目也是 QTreeWidgetItem 对象,顶级条目的父节点指针
QTreeWidgetItem::parent() 为 NULL。<br>
将所有节点展开之后,可以看到每个节点可以有多个子节点:<br>
<center> <img src="images/ch08/ch08-03-02.png" alt="treewidget2" width="800"></center>
对于包含子节点的父节点,左边会有小的三角形指示器,用于控制折叠或展开父节点。子节点也可以拥有更低级别的子节点(孙节点),以此类推,树形控件没
有限定子节点 的层数。顶级节点和其子孙节点的数据结构一样,都可以有多列数据,只是添加的函数、父节点指针不一样。下面介绍树形控件的函数和功能,在
8.3.2 节介绍树形节点条目 QTreeWidgetItem,8.3.3 节介绍树形条目迭代器 QTreeWidgetItemIterator
和递归遍历算法,最后两个小节是示例程序的实践。<br>
<br>
树形控件的构造函数很简单:<br>
<div class="code"> QTreeWidget(QWidget * parent = 0)</div>
参数里只有指定父窗口或父控件的指针 parent 。树形控件在添加条目之前,必须要先设置列数:<br>
<div class="code">void setColumnCount(int columns) //设置列数</div>
<div class="code">int columnCount() const //获取列数</div>
默认的列数是 1 列,如果涉及到多列数据,比如文件浏览树,有文件名、文件类型、大小、修改时间等等,就需要设置为多列数据的树。<br>
树形控件设置好列数就可以添加相应的顶级条目,添加顶级条目是由树形控件自身的函数实现,而子条目则由 QTreeWidgetItem
的函数实现。本小节主要围绕树形控件和其基类的函数来讲,树形控件也可以设置和表格控件类似的表头,这里称为树头条目,放在本小节末尾再讲。<br>
<br>
(1)添加和访问顶级条目<br>
树形控件顶级条目的操作比较类似 QListWidget 的列表条目操作函数。新建条目之后,可以用如下函数把条目添加到树形控件的顶级条目列表末尾:
<div class="code">void QTreeWidget::addTopLevelItem(QTreeWidgetItem * item)
//添加一个顶级条目到末尾</div>
<div class="code">void QTreeWidget::addTopLevelItems(const
QList<QTreeWidgetItem *> & items) //添加多个顶级条目到末尾</div>
如果希望将条目插入到指定顶级条目列表的 index 序号位置,使用如下函数:<br>
<div class="code">void QTreeWidget::insertTopLevelItem(int index,
QTreeWidgetItem * item)</div>
<div class="code">void QTreeWidget::insertTopLevelItems(int index, const
QList<QTreeWidgetItem *> & items)</div>
树形控件所有的顶级条目父节点指针都为 NULL (父节点是指树形层次中的节点关系,而条目的父控件依然是树形控件本身)。<br>
添加了顶级条目之后,可以对顶级条目进行计数:<br>
<div class="code">int QTreeWidget::topLevelItemCount() const</div>
<br>
(2)移除顶级条目<br>
移除顶级条目的函数也是take*打头:<br>
<div class="code">QTreeWidgetItem * QTreeWidget::takeTopLevelItem(int
index)</div>
index是顶级条目的序号,该函数只是从树形控件卸下顶级条目,但不会删除条目的内存空间,如果希望彻底删除,那么手动 delete 该函数返回的条目。<br>
如果要清空所有的顶级条目和子条目,使用槽函数:<br>
<div class="code">void QTreeWidget::clear()</div>
<br>
(3)条目访问函数<br>
对于顶级条目,如果知道顶级条目的序号获取对应的条目:<br>
<div class="code">QTreeWidgetItem * QTreeWidget::topLevelItem(int index)
const</div>
反过来,对于已知顶级条目对象,查看其顶级序号:<br>
<div class="code">int QTreeWidget::indexOfTopLevelItem(QTreeWidgetItem *
item) const</div>
如果条目不是顶级条目或者条目不属于该控件,那么会返回 -1。<br>
树形控件实际运行时,可能既有顶级条目,也有展开后的子孙条目同时显示,所以某个条目上面或下面的相邻条目不一定是同级别的兄弟条目,有可能是叔辈祖
辈的条目,也 可能是子辈孙辈条目。获取某个条目的相邻条目函数为:<br>
<div class="code">QTreeWidgetItem * QTreeWidget::itemAbove(const
QTreeWidgetItem * item) const //上面相邻条目</div>
<div class="code">QTreeWidgetItem * QTreeWidget::itemBelow(const
QTreeWidgetItem * item) const //下面相邻条目</div>
<br>
从屏幕控件显示角度,如果根据树形控件内部相对坐标获取条目(树形控件显示区域的左上角为原点),使用下面函数:<br>
<div class="code">QTreeWidgetItem * QTreeWidget::itemAt(const QPoint &
p) const</div>
<div class="code">QTreeWidgetItem * QTreeWidget::itemAt(int x, int y) const</div>
这两个函数是一个意思,一个用 QPoint 对象表示相对坐标,另一个直接用 x 和 y 数值表示坐标,如果对应坐标没有条目,会返回 NULL,注意判断
返回值。<br>
树形控件也是自带滚动条的,如果条目特别多,自动显示滚动条,对于树形控件在屏幕可见的条目,可以根据条目对象获取它的可视矩形(树形控件显示区域的 左上角为原
点):<br>
<div class="code">QRect QTreeWidget::visualItemRect(const QTreeWidgetItem *
item) const</div>
<br>
(4)当前条目的操作<br>
树形控件的选中操作默认比较像 QListWidget,如果不手动设置,只能选中一个高亮条目。<br>
获取当前高亮选中条目的函数为:<br>
<div class="code">QTreeWidgetItem * QTreeWidget::currentItem() const</div>
树形控件可以有多列,当前条目被点击选中的列号为:<br>
<div class="code">int QTreeWidget::currentColumn() const</div>
树形控件内的条目一般都没有固定行号,因为条目可以展开也可以折叠,行号是变化的,所以没有基于行号的操作函数。<br>
<br>
如果要设置某个条目为当前选中的状态:<br>
<div class="code">void QTreeWidget::setCurrentItem(QTreeWidgetItem * item)</div>
<div class="code">void QTreeWidget::setCurrentItem(QTreeWidgetItem * item,
int column)</div>
<div class="code">void QTreeWidget::setCurrentItem(QTreeWidgetItem * item,
int column, QItemSelectionModel::SelectionFlags command)</div>
第一个 setCurrentItem() 函数相当于设置该条目整行高亮选中,第二个是设置该条目行的 column
列高亮选中,第三个函数是单次选中命令,参考“8.2.4 选中区域和选中行为”的单次选中命令内容,只是树形控件是一整行为一个条目,定位到条目的某列数据,就
类似指定表格控件的单元格。<br>
如果当前高亮选中的状态发生变化,会触发如下信号:<br>
<div class="code">void QTreeWidget::currentItemChanged(QTreeWidgetItem *
current, QTreeWidgetItem * previous)</div>
参数里分别是当前高亮选中的条目,和之前高亮选中的条目,注意指针可能是 NULL,使用指针前一定要判断指针非空。<br>
<br>
(5)条目查找和排序<br>
如果要根据模板子串查找某列文本匹配的条目,使用如下函数:<br>
<div class="code">QList<QTreeWidgetItem *>
QTreeWidget::findItems(const QString & text, Qt::MatchFlags flags,
int column = 0) const</div>
参数里text是模板子串,flags是匹配标志(参看“8.1.1
QListWidget”中的字符串匹配标志表格),第三个参数是指定查找的列。该函数只查找一列的文本,其他列的文本是不查找的。如果需要查找所有列数据,那么要根据不
同列号逐列查询。<br>
<br>
类似表格控件,树形控件也可以按照列的文本进行自动排序,自动排序的设置函数为:<br>
<div class="code">bool isSortingEnabled()
const //设置是否自动排序</div>
<div class="code">void setSortingEnabled(bool
enable) //查看是否开启自动排序 </div>
指定排序的列号和升序降序,使用从基类继承的函数:<br>
<div class="code">void QTreeView::sortByColumn(int column, Qt::SortOrder
order)</div>
在没有开启自动排序的情况下,也可以调用该函数进行一次性的条目排序。<br>
<br>
(6)条目显示和运行时条目编辑<br>
可以为条目的某列“单元格”设置单独的控件来静态显示(控件不具有编辑功能):<br>
<div class="code">void QTreeWidget::setItemWidget(QTreeWidgetItem * item,
int column, QWidget * widget) //设置条目列控件</div>
<div class="code">QWidget * QTreeWidget::itemWidget(QTreeWidgetItem * item,
int column) const //获取条目列控件,不设置就是NULL</div>
注意该函数只能在条目添加到树形控件之后才能调用,否则无效,并且条目列控件只能用于显示,无法编辑,如果要定制可编辑的“单元格”控件,必须用基类
QTreeView 并继承 QItemDelegate 做代理,这些内容到后面模型视图章节讲解。<br>
<span style="font-weight: bold;">再次强调:itemWidget
条目控件,在默认情况下是与条目本身数据完全无关的,是条目数据的替换品,而不是协作模式。只有手动设置信号与槽,它们才可能关联上。</span><br>
QListWidget 和 QTreeWidget 的条目控件都是静态显示,不能编辑。<br>
QTreeWidget 控件的条目列控件 widget 还必须把 autoFillBackground 属性设置为
true,如果不是自动填充背景,那么默认是透明背景,这样控件的内容和内部模型数据(就是条目的列数据)同时显示,文本会重影,效果就糟糕了。<br>
删除条目的列控件使用如下函数:<br>
<div class="code">void QTreeWidget::removeItemWidget(QTreeWidgetItem *
item, int column)</div>
这个函数没有返回值,会自动地彻底删除条目列控件。<br>
<br>
在大多数情况下都用不到 itemWidget ,因为能够为条目设置可编辑标志位,然后调用如下函数开启树形控件自带的文本编辑器:<br>
<div class="code">void QTreeWidget::editItem(QTreeWidgetItem * item, int
column = 0)</div>
参数 item 是指定的条目,column
是条目的列(类似“单元格”)。在没有为条目设置可编辑标志位的情况下,可以调用下面一对函数进行持续编辑器的开启和关闭:<br>
<div class="code">void QTreeWidget::openPersistentEditor(QTreeWidgetItem *
item, int column = 0)</div>
<div class="code">void QTreeWidget::closePersistentEditor(QTreeWidgetItem *
item, int column = 0)</div>
注意这对函数一开一关,要成对调用,否则编辑完了不会自动关闭持续编辑器。<br>
<br>
(7)信号<br>
关于当前高亮选中变化的信号 currentItemChanged() 前面讲过了,这里先列几个常规的信号,然后再将树形控件独有的信号。常规信号就是下面这
几个:(条目列就类似表格控件的单元格)<br>
<div class="code">void itemActivated(QTreeWidgetItem * item, int column)
//条目列被激活</div>
<div class="code">void itemChanged(QTreeWidgetItem * item, int
column) //条目列的数据发生变化,比如文本或图标修改了</div>
<div class="code">void itemClicked(QTreeWidgetItem * item, int column)
//条目列被单击</div>
<div class="code">void itemDoubleClicked(QTreeWidgetItem * item, int column)
//条目列被双击</div>
<div class="code">void itemEntered(QTreeWidgetItem * item, int column)
//进入条目列</div>
<div class="code">void itemPressed(QTreeWidgetItem * item, int column)
//条目列被点 击按下</div>
树形控件最独特的就是展开和折叠信号:<br>
<div class="code">void QTreeWidget::itemExpanded(QTreeWidgetItem *
item) //条目展开时发送信号</div>
<div class="code">void QTreeWidget::itemCollapsed(QTreeWidgetItem * item)
//条目折叠时发送信号</div>
如果调用槽函数 expandAll() 展开所有子孙条目,那么不会触发 itemExpanded() 信号,因为触发太多会非常影响性能。<br>
类似地,如果用槽函数 collapseAll() 折叠所有子孙条目,也不会触发 itemCollapsed() 信号,以免影响性能。<br>
举例来说,在文件夹浏览的时候,因为操作系统里的文件太多,没法一次性构建完整的文件树,那么就可以用展开和折叠信号实时枚举某一层次文件夹的内容,
而不是一次性 枚举文件系统所有文件,因为一次性枚举所有文件的性能太糟糕。<br>
树形控件还有一个 itemSelectionChanged() 信号,一般在多选模式才会用到,稍后讲解。<br>
<br>
(8)槽函数<br>
树形控件的槽函数包括四个(基类的另算):<br>
<div class="code">void clear() //清空整个树形控件</div>
<div class="code">void collapseItem(const QTreeWidgetItem * item) //折叠指定的条目</div>
<div class="code">void expandItem(const QTreeWidgetItem * item) //展开指定
条目</div>
<div class="code">void scrollToItem(const QTreeWidgetItem * item,
QAbstractItemView::ScrollHint hint = EnsureVisible) //滚动到指定条目</div>
滚动函数 scrollToItem() 第二个参数是滚到到该条目的显示方式,参考“8.1.1
QListWidget”QAbstractItemView:: ScrollHint 枚举常量的表格。<br>
<br>
(9)基类 QTreeView 的函数<br>
QTreeView 的功能函数也很多,这里列举几个可能常用的,详细的内容等到模型视图章节讲解。关于列隐藏或显示、设置列宽的函数如下:<br>
<div class="code">void QTreeView::setColumnHidden(int column, bool hide)
//设置列隐藏或显示</div>
<div class="code">bool QTreeView::isColumnHidden(int column) const
//判断列是否隐藏 </div>
<div class="code">void QTreeView::hideColumn(int column) //槽函数,隐藏指定列</div>
<div class="code">void QTreeView::showColumn(int column) //槽函数,显示指定列</div>
<div class="code">void QTreeView::setColumnWidth(int column, int width)
//设置列宽</div>
<div class="code">int QTreeView::columnWidth(int column) const //获取指定列的宽度</div>
<div class="code">void QTreeView::resizeColumnToContents(int column)
//槽函数,自动调整 指定列的宽度</div>
属性 indentation 控制显示父子节点的缩进宽度:<br>
<div class="code">int indentation() const //获取父子节点的缩进宽度</div>
<div class="code">void setIndentation(int i) //设置缩进宽度</div>
<div class="code">void resetIndentation() //重置缩进宽度为默认值</div>
基类还有几个常用的折叠和展开槽函数:<br>
<div class="code">void collapseAll() //折叠所有子孙节点,这样只能看到顶级节点</div>
<div class="code">void expandAll() //展开所有子孙节点,完全展开的树</div>
<div class="code">void expandToDepth(int depth) //展开 depth 层级的子节点</div>
expandToDepth() 函数是指一直展开,直到将第 depth 层级的子节点都展开为止。以顶级条目为第 0 层级,顶级条目的直接子节点为第 1
层级,孙子节点为第 2 层级,依次类推。<br>
例如 expandToDepth(0) 的效果如下:<br>
<center> <img src="images/ch08/ch08-03-03.png" alt="expand0"></center>
如果调用 expandToDepth(1) 展开第1级的节点:<br>
<center> <img src="images/ch08/ch08-03-04.png" alt="expand1"></center>
如果把 expandToDepth() 参数设置成负数,那么相当于展开无穷大级别,就是展开所有的子孙节点。<br>
<br>
(10)树头条目<br>
树形控件只有一个表头,就是显示在上面的水平表头,本节也叫树头条目。设置树头条目的函数为:<br>
<div class="code">void QTreeWidget::setHeaderItem(QTreeWidgetItem * item)
//设置树头条目,树头条目可以有多列数据,相当于多列的表头一次性设置了</div>
<div class="code">void QTreeWidget::setHeaderLabel(const QString &
label) //只设置第 0 列的表头</div>
<div class="code">void QTreeWidget::setHeaderLabels(const QStringList &
labels) //设置多列的表头</div>
<div class="code">QTreeWidgetItem * QTreeWidget::headerItem() const
//获取树头条目</div>
树头条目本质其实也是由 QHeaderView 子控件来显示的,可以在基类找到相关函数:<br>
<div class="code">QHeaderView * QTreeView::header() const //获取表头视图控件</div>
<div class="code">void QTreeView::setHeader(QHeaderView * header) //设置表头视图,
一般树形控件不需要用这个函数</div>
<div class="code">void QTreeView::setHeaderHidden(bool hide) //设置表头是否隐藏</div>
<div class="code">bool QTreeView::isHeaderHidden() const //判断是否隐藏了表头</div>
无论是 QTableWidget 还是 QTreeWidget 的表头,都是 QHeaderView 子控件显示,QHeaderView
参考“8.2.3 表头设置”的内容。<br>
<br>
(11)选中行为和选中模式<br>
与 QTableWidget 类似,QTreeWidget也从祖类 QAbstractItemView 继承了选中行为和选中模式的属性:<br>
<div class="code">QAbstractItemView::SelectionBehavior
selectionBehavior() const //获取选中行为,按条目选中、整行或整列选中</div>
<div class="code">void
setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)
//设置选中行为</div>
<div class="code">QAbstractItemView::SelectionMode selectionMode()
const //获取选中模式,比如单选、多选、扩展选择</div>
<div class="code">void setSelectionMode(QAbstractItemView::SelectionMode
mode) //设置选中模式</div>
关于选中模式和选中行为的枚举常量参看“8.2.4 选中区域和选中行为”小节中的枚举常量表格,单次选中命令的函数和枚举常量也参考该小节。<br>
<br>
默认情况下,树形控件是按照整行选中,并且是单选模式,如果把选中模式改成多选的
QAbstractItemView::ExtendedSelection,那么树形控件也可以使多选的,这时候信号
itemSelectionChanged() 就能派上用场:<br>
<div class="code">void QTreeWidget::itemSelectionChanged()</div>
多选状态变化时会触发该信号(单选模式也触发,只是不需要用这个信号),可以关联该信号,监视当前所有选中的条目:<br>
<div class="code">QList<QTreeWidgetItem *>
QTreeWidget::selectedItems() const</div>
注意,这里的选中条目仅仅是指实际显示的直接选中的条目,不包括折叠隐藏的子孙条目计数,因为选中父节点与选中其子孙节点没关系,不会递归选中所有子 孙:<br>
<center> <img src="images/ch08/ch08-03-05.png" alt="select"></center>
树形控件及其基类没有递归选中子条目的属性或函数,如果希望递归选中某个节点的所有子孙节点,那么需要自行编写递归函数。关于树形控件类本身的内容介
绍到这,因为 涉及到父子节点隶属关系、节点展开和折叠,树形控件还有很大一部分功能都是由其条目类 QTreeWidgetItem
的函数实现的,下面来学习这个树形控件条 目类。<br>
<br>
<div class="os2">8.3.2 QTreeWidgetItem</div>
<br>
树形控件条目的内容是最复杂的,因为每一个条目涉及到内部多列数据的操作、父子节点操作,这些都是之前列表控件条目和表格控件条目不具备的特性。我们
首先介绍树形 控件条目的构造函数,然后按父子节点操作、通用数据操作和非通用数据操作三方面介绍树形控件条目。<br>
(1)构造函数和复制函数<br>
QTreeWidgetItem 构造函数较多,首先看不带父对象指针的构造函数:<br>
<div class="code">QTreeWidgetItem(int type = Type)</div>
<div class="code">QTreeWidgetItem(const QStringList & strings, int type
= Type)</div>
<div class="code">QTreeWidgetItem(const QTreeWidgetItem & other)</div>
类型 type 一般用于派生类的自定义条目类型,基本用不到。第二个构造函数字符串列表 strings
就是条目内多列的文本,类似把表格控件一整行的多列文本塞到一个条目内部了。第三个是复制构造函数,复制时除了
type()、treeWidget()、parent(),其他的都复制。<br>
接下来是直接以树形控件为父对象的构造函数:<br>
<div class="code">QTreeWidgetItem(QTreeWidget * parent, int type = Type)</div>
<div class="code">QTreeWidgetItem(QTreeWidget * parent, const QStringList
& strings, int type = Type)</div>
<div class="code">QTreeWidgetItem(QTreeWidget * parent, QTreeWidgetItem *
preceding, int type = Type)</div>
以树形控件为父对象,代表是顶级条目,自动添加到指定的树形控件;type 参数是自定义条目类型;strings 是条目内的多列文本;<br>
preceding 条目是指排序上的前一个顶级条目,紧贴着 preceding 条目之后插入新构造的顶级条目。<br>
构造函数没有指定 preceding 条目时,自动添加到父对象现有子节点的末尾。<br>
最后是以树形条目为父对象的构造函数:<br>
<div class="code">QTreeWidgetItem(QTreeWidgetItem * parent, int type = Type)</div>
<div class="code">QTreeWidgetItem(QTreeWidgetItem * parent, const
QStringList & strings, int type = Type)</div>
<div class="code">QTreeWidgetItem(QTreeWidgetItem * parent, QTreeWidgetItem
* preceding, int type = Type)</div>
以树形条目为父对象,新构造的条目自动添加给 parent 作为子节点;type 参数是自定义条目类型;strings 是条目内的多列文本;<br>
preceding 条目是指排序上的前一个兄弟条目,紧贴着 preceding 条目之后插入新构造的条目。<br>
构造函数没有指定 preceding 条目时,自动添加到父对象现有子节点的末尾。<br>
<br>
树形条目还以使用克隆函数生成:<br>
<div class="code">QTreeWidgetItem * QTreeWidgetItem::clone() const</div>
clone()是按照本条目一模一样造出一个新的条目,是深拷贝,与本条目(包括子孙节点)不共享内存,函数返回的新条目也没有复制
type()、treeWidget()、parent() ,新条目是自由的,没归属。<span style="font-weight: bold;">
clone() 函数会克隆所有的子孙节点,并且新子孙节点之间的关系也一样</span>,QTreeWidgetItem 源码中
使用压栈出栈 方式实现了子孙节点的遍历复制。<br>
赋值=函数:<br>
<div class="code">QTreeWidgetItem & operator=(const QTreeWidgetItem
& other)</div>
等于号函数与克隆函数有本质区别,它只拷贝
other 这一个节点内的数据到本节点里,包括显示的字符串和多列数据、标志位等,type() 和
treeWidget()、parent()的内容不会修改。<span style="font-weight: bold;">等于号函数不涉
及任何子孙节 点,也不改变隶属的父节点。</span><br>
顺便提一下小于号函数:<br>
<div class="code">virtual bool operator<(const QTreeWidgetItem &
other) const</div>
如果为树形控件指定了排序的列号sortColumn(),那么按该列的文本字典序比较大小,否则都按照第0列的文本比较。<br>
<br>
(2)父子节点操作<br>
条目查看父节点的指针使用函数:<br>
<div class="code">QTreeWidgetItem * QTreeWidgetItem::parent() const
//常量,子节点不能改父节点指针</div>
注意子节点是不能修改父节点指针的,只能父节点换子节点,不能子节点换父节点。<br>
<br>
最常用的是节点控制自己的直接子节点,添加子节点使用函数:<br>
<div class="code">void addChild(QTreeWidgetItem * child) //添加一个子节点到末尾</div>
<div class="code">void addChildren(const QList<QTreeWidgetItem *>
& children) //添加多个子节点末尾</div>
<div class="code">void insertChild(int index, QTreeWidgetItem * child)
//插入子节点序号 index 序号位置</div>
<div class="code">void insertChildren(int index, const
QList<QTreeWidgetItem *> & children)//插入多个子节点到 index 位置</div>
直接子节点的计数(与孙辈或更低辈分的节点数目无关)用如下函数:
<div class="code">int childCount() const</div>
<br>
根据序号获取直接子节点的指针使用函数(如果序号超界返回NULL):<br>
<div class="code">QTreeWidgetItem * child(int index) const //序号查子节点指针</div>
反过来,根据子节点指针查序号的函数如下(如果查不到序号返回-1):
<div class="code">int indexOfChild(QTreeWidgetItem * child) const</div>
移除子节点使用如下函数:
<div class="code">void removeChild(QTreeWidgetItem * child)
//根据子节点指针解除父子关系</div>
<div class="code">QTreeWidgetItem * takeChild(int index) //根据子节点序号解除父子
关系,返回卸下后的自由节点指针</div>
<div class="code">QList<QTreeWidgetItem *> takeChildren()
//卸下所有子节点</div>
注意这几个函数只是解除父子关系,卸下的子节点还存在内存中,如果要完全删除需要手动 delete 每个节点。<br>
当节点有隶属的树形控件时,可以使用下面函数对子节点排序:<br>
<div class="code">void sortChildren(int column, Qt::SortOrder order)
// 根据指定列号column排序,升序或降序由order指定</div>
如果条目不属于任何树形控件,那么该排序函数无效。<br>
<br>
条目还可以控制自己的子节点指示器(条目显示时左边的加号 +)如何显示:<br>
<div class="code">QTreeWidgetItem::ChildIndicatorPolicy
childIndicatorPolicy() const //获取子节点指示器显示策略</div>
<div class="code">void
QTreeWidgetItem::setChildIndicatorPolicy(QTreeWidgetItem::ChildIndicatorPolicy
policy) //设置子节点指示器显示策略</div>
子节点指示器显示策略 QTreeWidgetItem::ChildIndicatorPolicy 有三种:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td style="width: 337px;" align="center"><b>ChildIndicatorPolicy 枚举常量</b></td>
<td style="width: 88px;" align="center"><b>数值</b></td>
<td style="width: 397px;" align="center"><b> 描述</b></td>
</tr>
<tr>
<td>QTreeWidgetItem::ShowIndicator</td>
<td> 0 </td>
<td>无论有无子节点都显示指示符。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItem::DontShowIndicator</td>
<td> 1 </td>
<td>始终不显示指示符。 </td>
</tr>
<tr>
<td>QTreeWidgetItem::DontShowIndicatorWhenChildless</td>
<td> 2</td>
<td> 条目有子节点就显示指示符,没子节点就不显示。 </td>
</tr>
</tbody>
</table>
<br>
默认值是最后一个,有子节点就显示指示符,没有子节点就不显示指示符,这种方式也最为科学,一般不需要改指示符显示策略。<br>
<br>
关于父子节点操作函数介绍到这,这些函数的特点就是只处理直接的子节点,与孙子辈、更低辈分节点无关,孙子辈由儿子辈去管理,以此类推,族谱树中各层
节点只管理亲 儿 子,其他辈分都不管。 递归操作 就是这样,只管处理儿子辈代码,孙子辈的由儿子辈去管,层层下推,就是递归的过程。<br>
<br>
(3)通用数据操作<br>
通用数据一般是用于 QDataStream
保存条目的信息到文件中,也可以从文件中加载通用数据生成以前的树。树形控件条目的通用数据函数与前面章节列表条目、表格条目类似,但是多了指定列号的参数,因为每个属
性条目有多列数据,每列数据有分多种角色,因此属性条目使用二维向量存储通用数据:<br>
<div class="code"><span style=" color:#008000;">//</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">One</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">item</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">has</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">a</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">vector</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">of</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">column</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">entries.</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">Each</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">column</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">has</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">a</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">vector</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">of</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">(role,</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">value)</span><span style=" color:#c0c0c0;">
</span><span style=" color:#008000;">pairs.</span>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">QVector</span><span
style=" color:#000000;"><</span><span style=" color:#c0c0c0;"> </span><span style=" color:#800080;">QVector</span><span
style=" color:#000000;"><</span><span style=" color:#800080;">QWidgetItemData</span><span
style=" color:#000000;">></span><span style=" color:#c0c0c0;"> </span><span style=" color:#000000;">></span><span
style=" color:#c0c0c0;"> </span><span style=" color:#800000;">values</span><span
style=" color:#000000;">;</span></pre>
</div>
对于通用数据的设置和读取,也有 data() 和 setData() 函数,只是多了列号:<br>
<div class="code">QVariant QTreeWidgetItem::data(int column, int role)
const</div>
<div class="code">void QTreeWidgetItem::setData(int column, int role, const
QVariant & value)</div>
其他针对各个角色的读写函数如下表所示:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td style="width: 183.867px;" align="center"><b>获取函数</b></td>
<td style="width: 218.667px;" align="center"><b>设置函数</b></td>
<td style="width: 166.733px;" align="center"><b>数据角色</b></td>
<td style="width: 269.5px;" align="center"><b> 描述 </b></td>
</tr>
<tr>
<td>text(int column)</td>
<td> setText(int column, const QString &text)</td>
<td> Qt::DisplayRole </td>
<td> 条目显示的文本。 </td>
</tr>
<tr class="d1">
<td>icon(int column)</td>
<td> setIcon(int column, const QIcon &icon)</td>
<td> Qt::DecorationRole </td>
<td> 条目显示的图标。 </td>
</tr>
<tr>
<td>statusTip(int column)</td>
<td> setStatusTip(int column, const QString &statusTip)</td>
<td> Qt::StatusTipRole </td>
<td> 如果主界面有状态栏,鼠标悬停在该条目上时显示该状态信息到状态栏。 </td>
</tr>
<tr class="d1">
<td>toolTip(int column)</td>
<td> setToolTip(int column, const QString &toolTip) </td>
<td> Qt::ToolTipRole </td>
<td> 鼠标悬停在该条目上时显示的工具提示信息。 </td>
</tr>
<tr>
<td>whatsThis(int column)</td>
<td> setWhatsThis(int column, const QString &whatsThis)</td>
<td> Qt::WhatsThisRole </td>
<td> 如果主界面窗口标题栏有?帮助按钮,点击帮助按钮再点击该条目会显示该帮助信息。 </td>
</tr>
<tr class="d1">
<td>font(int column)</td>
<td> setFont(int column, const QFont &font)</td>
<td> Qt::FontRole </td>
<td> 显示条目文本用的字体。 </td>
</tr>
<tr>
<td>textAlignment(int column</td>
<td> setTextAlignment(int column, int alignment)</td>
<td> Qt::TextAlignmentRole </td>
<td> 文本的对齐方式。 </td>
</tr>
<tr class="d1">
<td>backgroundColor(int column)</td>
<td> setBackgroundColor(int column, const QColor &color)</td>
<td> Qt::BackgroundColorRole </td>
<td> 文本背景色。 </td>
</tr>
<tr>
<td>textColor(int column)</td>
<td> setTextColor(int column, const QColor &color)</td>
<td> Qt::TextColorRole </td>
<td> 文字颜色。 </td>
</tr>
<tr class="d1">
<td>background(int column)</td>
<td> setBackground(int column, const QBrush &brush) </td>
<td> Qt::BackgroundRole </td>
<td> 条目的背景画刷。 </td>
</tr>
<tr>
<td>foreground(int column)</td>
<td> setForeground(int column, const QBrush &brush) </td>
<td> Qt::ForegroundRole </td>
<td> 条目的前景画刷。 </td>
</tr>
<tr class="d1">
<td>checkState(int column)</td>
<td> setCheckState(int column, Qt::CheckState state)</td>
<td> Qt::CheckStateRole </td>
<td> 条目自带的复选框选中状态,可以是三态复选框。 </td>
</tr>
<tr>
<td>sizeHint(int column)</td>
<td> setSizeHint(int column, const QSize &size) </td>
<td> Qt::SizeHintRole </td>
<td> 条目显示的建议尺寸。 </td>
</tr>
</tbody>
</table>
<br>
树形条目也有相应的数据流读写函数,就是用于读取或保存这些通用数据:<br>
<div class="code">QDataStream &
operator<<(QDataStream & out, const QTreeWidgetItem &
item) //外部函数,将条目写入数据流</div>
<div class="code">QDataStream &
operator>>(QDataStream & in, QTreeWidgetItem & item)
//外部函数,读取数据流中的条目数据</div>
<div class="code">void QTreeWidgetItem::write(QDataStream & out) const
//成员函数,将条目数据写入数据流</div>
<div class="code">void QTreeWidgetItem::read(QDataStream & in)
//成员函数,从数据流中读取条目数据</div>
运算符重载函数 operator<<() 和 operator>>() 本质就是调用上面的 write() 和
read() 函数。<br>
<br>
(4)非通用数据操作<br>
条目在构造函数指定的类型可以用如下函数获取,这个类型是只读的:<br>
<div class="code">int QTreeWidgetItem::type() const</div>
在添加到树形控件之后,都可以用如下函数查看条目隶属的树形控件:<br>
<div class="code">QTreeWidget * QTreeWidgetItem::treeWidget() const
//常量,节点不能自行更换隶属,要从树形控件增删节点</div>
<br>
程序运行时除了树形控件本身的
QTreeWidget::selectedItems() 可以判断选中条目,每个条目对象自己也有函数获取高亮选中状态或设置是否高亮选中:(默认情况
下,树形条目自身是否高亮选中与子孙条目的情况无关)<br>
<div class="code">bool QTreeWidgetItem::isSelected() const</div>
<div class="code">void QTreeWidgetItem::setSelected(bool select)</div>
<br>
树形条目初始化时也有默认的标志位,并且运行时可以修改标志位:<br>
<div class="code">Qt::ItemFlags QTreeWidgetItem::flags() const</div>
<div class="code">void QTreeWidgetItem::setFlags(Qt::ItemFlags flags)</div>
树形条目构造时的默认标志位如下: <br>
Qt::ItemIsSelectable<br>
|Qt::ItemIsUserCheckable<br>
|Qt::ItemIsEnabled<br>
|Qt::ItemIsDragEnabled<br>
|Qt::ItemIsDropEnabled<br>
树形条目默认不能编辑,如果希望条目文本可以双击编辑,可以用下面一句代码:<br>
<div class="code"> item->setFlags( (item->flags()) |
Qt::ItemIsEditable ); //双击条目会自动 开启文本编辑器</div>
这个标志会对树形条目所有列的文本编辑都生效,开启后该条目每个列的数据都能双击编辑。对于开启
Qt::ItemIsEditable 标志位的条目,除了用户双击等操作启用编辑器,也可以用函数代码指定开启条目的某列数据编辑器:<br>
<div class="code">void QTreeWidget::editItem(QTreeWidgetItem * item, int
column = 0)</div>
<br>
树形条目默认有 Qt::ItemIsUserCheckable 标志,可以复选,但是复选框默认却看不到,可以用下面代码真正地显示复选框:<br>
<div class="code">item->setCheckState(0, Qt::Unchecked);
//显示第0列的复选框,要指定列号</div>
<br>
树形条目的复选框状态很特殊,如果不使用三态复选(默认情况),那么当前条目的复选状态与子孙条目复选状态无关。<br>
如果开启树形条目的三态复选,那么当前条目的复选状态与子孙有关:<br>
如果所有子孙勾选,那么父节点勾选 Qt::Checked;<br>
如果部分子孙勾选,那么父节点部分勾选 Qt::PartiallyChecked;<br>
如果所有子孙不勾选,那么父节点不勾选 Qt::Unchecked。<br>
三态勾选其实是真正反映父子勾选关系的,如果用复选框,那么有子孙的节点应该用三态的,无子孙的叶子节点用二态的,并且应当将所有树形控件的条目都显 示复选框:<br>
<div class="code"><span style=" color:#c0c0c0;"> </span><span style=" color:#008000;">//
迭代器</span>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">QTreeWidgetItemIterator</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">it</span><span style=" color:#000000;">(</span><span
style=" color:#800000;">ui</span><span style=" color:#000000;">-></span><span
style=" color:#800000;">treeWidget</span><span style=" color:#000000;">);</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">while</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">(*</span><span style=" color:#000000;">it</span><span
style=" color:#000000;">)</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">{</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#008000;">//取出当前条目</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">QTreeWidgetItem</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">*</span><span style=" color:#000000;">item</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">=</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000000;">*</span><span style=" color:#000000;">it</span><span style=" color:#000000;">;</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">if</span><span
style=" color:#000000;">(</span><span style=" color:#000000;">item</span><span style=" color:#000000;">-></span><span
style=" color:#000000;">childCount</span><span style=" color:#000000;">()</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">></span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000080;">0</span><span style=" color:#c0c0c0;"> </span><span style=" color:#000000;">)</span><span
style=" color:#008000;">//有子节点开启三态复选,没子节点是二态复选</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">{</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">item</span><span
style=" color:#000000;">-></span><span style=" color:#000000;">setFlags</span><span
style=" color:#000000;">(</span><span style=" color:#c0c0c0;"> </span><span style=" color:#000000;">item</span><span
style=" color:#000000;">-></span><span style=" color:#000000;">flags</span><span
style=" color:#000000;">()</span><span style=" color:#c0c0c0;"> </span><span style=" color:#000000;">|</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">Qt</span><span style=" color:#000000;">::</span><span
style=" color:#800080;">ItemIsTristate</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000000;">);</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">}</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">item</span><span
style=" color:#000000;">-></span><span style=" color:#000000;">setCheckState</span><span
style=" color:#000000;">(</span><span style=" color:#000080;">0</span><span style=" color:#000000;">,</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">Qt</span><span style=" color:#000000;">::</span><span
style=" color:#800080;">Unchecked</span><span style=" color:#000000;">);</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#008000;">//正常应该只用第0列的复选框,代表一整行条目</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#008000;">//找下一个条目</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">++</span><span
style=" color:#000000;">it</span><span style=" color:#000000;">;</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">}</span></pre>
</div>
迭代器 QTreeWidgetItemIterator
专门用于遍历树形控件或某个父节点的所有子孙条目,因为树形结构是分叉结构,不同于列表控件的一维遍历,也不同于表格控件的二维遍历,因此需要迭代器或递归算法来穷举子孙
条目,下面小节专门介绍这些内容。<br>
<br>
<div class="os2">8.3.3 迭代器和递归遍历</div>
<br>
我们拿一棵经典的二叉树做例子:<br>
<center> <img src="images/ch08/ch08-03-06.png" alt="tree"></center>
在上面树图中,A是总树的根,总树有两棵子树,分别隶属B、C。没有子孙的是叶子节点:D、E、F、G。<br>
对于树的遍历,存在多种方式:<br>
①先序遍历( pre-order traversal ):根节点->左子树->右子树;对于每棵子树内的遍历顺序也一样类推。<br>
以上图为例,先序遍历为:A ->B->D->E -> C->F->G。<br>
对于多叉树,先序遍历规则就是:根节点->第一棵子树->第二棵子树->第三棵子树 等等。<br>
<br>
②后序遍历( post-order traversal
):左子树->右子树->根节点;对于每棵子树内的遍历顺序也一样类推。后序遍历时父节点和根节点一定是在后面出现的,所以遍历时打头的是叶子。<br>
以上图为例,后序遍历为:D->E->B -> F->G->C ->A。<br>
对于多叉树,后序遍历规则就是:第一棵子树->第二棵子树->第三棵子树....->根节点。<br>
<br>
③中序遍历( in-order traversal
):左子树->根节点->右子树;对于每棵子树内的遍历顺序也一样类推。这种遍历仅对二叉树有意义,二叉树的父节点正好在左右子树中间,但多叉树没有中间的概
念。<br>
以上图为例,中序遍历为:D->B->E ->A ->F->C->G。<br>
<br>
④按层遍历( level-order traversal ):第0层->第1层->第2层 ……。这种很直观,比如上面的树就是
A->B->C->D->E->F->G 。<br>
<br>
通常遍历树的节点需要写较为复杂的递归代码或者用压栈出栈代码实现,这些代码都比较麻烦,因此 Qt 专门为树形控件提供了迭代器
QTreeWidgetItemIterator ,这个迭代器以先序遍历方式访问树形控件每个节点,大多数情况下就不需要我们自己去编写遍历算法了。我们可以将
QTreeWidgetItemIterator 想象成为一个保存节点指针的一维链表,链表每个元素指向树形控件一个节点,顺序是按照先序遍历。<br>
QTreeWidgetItemIterator 构造函数如下:<br>
<div class="code"> QTreeWidgetItemIterator(const
QTreeWidgetItemIterator & it) </div>
<div class="code"> QTreeWidgetItemIterator(QTreeWidget *
widget, IteratorFlags flags = All)</div>
<div class="code"> QTreeWidgetItemIterator(QTreeWidgetItem
* item, IteratorFlags flags = All)</div>
第一个是复制构造函数,按照参数 it 指定的迭代器一样构造新的迭代器,构造时新迭代器的当前条目也与 it 的当前条目一样。<br>
第二个构造函数是根据树形控件指针,获取该控件内所有节点,形成遍历的链表,参数
flags 是迭代器标志位,可以筛选节点类型,等会列举迭代器标志位。<br>
第三个构造函数是以某个节点作为根,遍历以这个节点为根的子树。参数 flags 是迭代器标志位,用于筛选节点类型,如下表所示:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td align="center"><b>IteratorFlags 枚举常量</b></td>
<td align="center"><b>数值</b></td>
<td align="center"><b> 描述</b></td>
</tr>
<tr>
<td>QTreeWidgetItemIterator::All</td>
<td> 0x00000000 </td>
<td> 默认值,枚举所有节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::Hidden</td>
<td> 0x00000001 </td>
<td> 枚举隐藏节点。 </td>
</tr>
<tr>
<td>QTreeWidgetItemIterator::NotHidden</td>
<td> 0x00000002 </td>
<td> 枚举非隐藏节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::Selected</td>
<td> 0x00000004</td>
<td> 枚举高亮选中节点。 </td>
</tr>
<tr>
<td>QTreeWidgetItemIterator::Unselected</td>
<td> 0x00000008 </td>
<td> 枚举未选中节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::Selectable</td>
<td> 0x00000010 </td>
<td> 枚举可以选中的节点。 </td>
</tr>
<tr>
<td>QTreeWidgetItemIterator::NotSelectable</td>
<td> 0x00000020 </td>
<td> 枚举不可选中的节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::DragEnabled</td>
<td> 0x00000040 </td>
<td> 枚举能够拽出的节点。 </td>
</tr>
<tr>
<td style="width: 273px;">QTreeWidgetItemIterator::DragDisabled</td>
<td style="width: 193px;"> 0x00000080 </td>
<td> 枚举不能拽出去的节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::DropEnabled</td>
<td> 0x00000100</td>
<td> 枚举可接收拖进来的节点。 </td>
</tr>
<tr>
<td style="width: 273px;">QTreeWidgetItemIterator::DropDisabled</td>
<td style="width: 193px;"> 0x00000200 </td>
<td> 枚举不能接收拖进来的节点。 </td>
</tr>
<tr class="d1">
<td style="height: 16px;">QTreeWidgetItemIterator::HasChildren</td>
<td> 0x00000400</td>
<td> 枚举所有父节点。 </td>
</tr>
<tr>
<td style="width: 273px;">QTreeWidgetItemIterator::NoChildren</td>
<td style="width: 193px;"> 0x00000800 </td>
<td> 枚举所有叶子节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::Checked</td>
<td> 0x00001000</td>
<td> 枚举复选框勾选的节点。 </td>
</tr>
<tr>
<td style="width: 273px;">QTreeWidgetItemIterator::NotChecked</td>
<td style="width: 193px;"> 0x00002000 </td>
<td> 枚举复选框没有勾选的节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::Enabled</td>
<td> 0x00004000</td>
<td> 枚举所有启用的节点。 </td>
</tr>
<tr>
<td style="width: 273px;">QTreeWidgetItemIterator::Disabled</td>
<td style="width: 193px;"> 0x00008000 </td>
<td> 枚举所有禁用的节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::Editable</td>
<td> 0x00010000</td>
<td> 枚举可以编辑的节点。 </td>
</tr>
<tr>
<td style="width: 273px;">QTreeWidgetItemIterator::NotEditable</td>
<td style="width: 193px;"> 0x00020000 </td>
<td> 枚举不能编辑的节点。 </td>
</tr>
<tr class="d1">
<td>QTreeWidgetItemIterator::UserFlag</td>
<td> 0x01000000</td>
<td> 枚举自定义的用户节点。 </td>
</tr>
</tbody>
</table>
<br>
这些枚举标志位大部分与节点条目自身的标志位 Qt::ItemFlags
枚举常量相对应,比如是否可编辑,是否能选中,用于筛选指定标志位的节点;还有一部分标志位,如是否折叠隐藏,是否高亮选中,与用户实时的操作相关。<br>
<br>
QTreeWidgetItemIterator 重载了多个运算符函数,方便程序员使用,比如 Qt 文档中的示例 代码:<br>
<div class="code"> QTreeWidgetItemIterator it(treeWidget);<br>
while (*it) {<br>
if ((*it)->text(0) ==
itemText) //查找匹配文本的条目<br>
(*it)->setSelected(true); //文本找到了就高亮显示<br>
++it;<br>
}</div>
这段代码根据树形控件
treeWidget 构造了迭代器 it,it内部有个游标,实时指向遍历的属性条目,在未遍历的时候,指向先序遍历第一个条目,运算符函数
operator*() 用于取出当前条目的指针,比如 *it 就是遍历过程中的当前条目;<br>
迭代器支持 ++it 和 it++,第一个前加加是在执行语句前移到下一条目,第二个后加加是执行语句之后移到下一个条目;<br>
还有 --it 和 it-- ,是逆向遍历,与加加的遍历顺序是反的;<br>
迭代器也支持跳跃式遍历,如即 it += 5, it-=5 ,这是跳到后面第 5 个或跳到前面第 5 个。<br>
QTreeWidgetItemIterator 类似链表,与数组不同,它没有 []
运算符函数,不能按照序号取节点指针,在穷举完所有节点之前也是不知道总数目多少。遍历过程中,*it
数值为 NULL 时,就是穷举完了,后面已经没有节点了。可以用 while 循环单独对节点数目计数,或者指定
IteratorFlags 去穷举父节点多少个,叶子节点多少个等。关于迭代器的内容介绍到这,我们在下面小节专门用例子来解释递归算 法。<br>
<br>
<div class="os2">8.3.4 省市行政区示例</div>
<br>
本小节示范使用设计师添加编辑树形条目,树形控件设置三列信息,第一列是省市名称,第二列是经度,第三列是纬度。行政区划分正好是树形结构,省级的经
纬度用省会城 市的经纬度代替,城市经纬度用自身的数值。省市经纬度举例如下:<br>
<div class="code"> 安徽省 合肥市 117.250 31.833 <br>
安徽省 安庆市 117.050 30.533 <br>
广东省 广州市 113.267 23.133 <br>
广东省 深圳市 114.050 22.550 <br>
湖南省 长沙市 112.933 28.233 <br>
湖南省 株洲市 113.133 27.833 </div>
我们以省级作为树形控件顶级节点,城市作为子节点。下面开始示例,打开 QtCreator,新建一个 Qt Widgets Application
项目,在新建项目的向导里填写:<br>
①项目名称 citytree,创建路径 D:\QtProjects\ch08,点击下一步;<br>
②套件选择里面选择全部套件,点击下一步;<br>
③基类选择 QWidget,点击下一步;<br>
④项目管理不修改,点击完成。<br>
我们打开 widget.ui 界面文件,按照下图拖入控件:<br>
<center> <img src="images/ch08/ch08-03-07.png" alt="ui" width="800"></center>
界面拖入三行控件,第一行:树形控件,默认对象名 treeWidget。<br>
第二行:三个标签,文本分别为“省市名称”、“经度”、“纬度”,三个单行编辑器,对象名分别为
lineEditName、lineEditLon、lineEditLat,第二行使用水平布局。<br>
第三行:四个按钮,文本、对象名分别为“添加顶级节点”pushButtonAddTop、“添加子节点”pushButtonAddChild、 “删除叶子节
点”pushButtonDelLeaf、“删除节点子树”pushButtonDelSubtree,第三行也是用水平布局。<br>
窗口整体使用垂直布局,尺寸 440*330。<br>
<br>
布局完成后,<b>我们选中树形控件,在右下角设置 columnCount 为 3</b>,然后右击树形控件:<br>
<center> <img src="images/ch08/ch08-03-08.png" alt="ui" width="800"></center>
在右键菜单选择编辑项目。弹出如下编辑对话框:<br>
<center> <img src="images/ch08/ch08-03-09.png" alt="ui"></center>
首先看到的“列”标签页,指的是树头条目,有三列内容,分别是 1、2、3,我们点击“属性”按钮,展开右边的树头属性编辑,把树头的三个列 text 值修改为
“省市名称”、“经度”、“纬度”,修改后如下图所示:<br>
<center> <img src="images/ch08/ch08-03-10.png" alt="ui"></center>
然后我们点击上面的“项目”标签页,即树形节点的编辑界面:<br>
<center> <img src="images/ch08/ch08-03-11.png" alt="ui"></center>
绿色加号按钮就是添加节点的按钮,我们点击该按钮,添加一个新条目:<br>
<center> <img src="images/ch08/ch08-03-12.png" alt="ui"></center>
我们编辑第一列文本为 “安徽省”,第二列经度 117.250,第三列纬度 31.833 ,使用的是省会经纬度。每列信息都可以双击编辑文本。添加后如下图所
示:<br>
<center> <img src="images/ch08/ch08-03-13.png" alt="ui"></center>
然后我们选中“安徽省”条目,点击左下角第二个按钮添加子节点,是转折箭头和加号的图标,添加安徽省的子节点:<br>
<center> <img src="images/ch08/ch08-03-14.png" alt="ui"></center>
我们编辑子节点名字为 “合肥市 ”,第二列 117.250,第三列 31.833 。然后如法炮制,为安徽省添加安庆市节点,添加两个子节点后显
示如下:<br>
<center> <img src="images/ch08/ch08-03-15.png" alt="ui"></center>
<b>注意,左下角第一个按钮(加号图标)意为添加选中节点的同级别节点;<br>
第二个按钮(转折箭头和小加号)意为添加选中节点的子节点;<br>
第三个按钮(减号)是删除选中节点。添加节点功能和添加子节点功能都是相对于选中节点而言的。</b><br>
上面示范的是先建立父节点,后建立子节点。下面示范反过来的情况,先创建子节点,后添加到父节点。<br>
<br>
我们选中“安徽省”节点,点击左下角第一个添加同级别节点按钮,添加一个 “广州市 ” 113.267 23.133 ,<br>
然后接着点击该按钮,再添加一个 “深圳市” 114.050 22.550 ,如下图所示:<br>
<center> <img src="images/ch08/ch08-03-16.png" alt="ui"></center>
然后我们继续点击加号按钮,添加节点“广东省” 113.267 23.133:<br>
<center> <img src="images/ch08/ch08-03-17.png" alt="ui"></center>
广东的省市节点新建后,下一步是建立父子节点关系,我们选中广东省上面一个节点“深圳市”:<br>
<center> <img src="images/ch08/ch08-03-18.png" alt="ui"></center>
我们点击中间第二个按钮,即将选中节点设置为下一个同级节点的首个子节点,点击后的效果如下图所示:<br>
<center> <img src="images/ch08/ch08-03-19.png" alt="ui"></center>
这样深圳市就变成广东省的子节点了。然后我们再选中“广州市”节点,也点击中间第二个按钮,使得广州市也成为广东省的子节点:<br>
<center> <img src="images/ch08/ch08-03-20.png" alt="ui"></center>
这样就完成广东省两个子节点设置了。<b>上面对话框中间四个按钮意义为:<br>
中间第一个按钮(转折箭头向上):将选中节点提高级别,升格为父节点同级别,并移动到父节点前面;<br>
中间第二个按钮(转折箭头向右):将选中节点设置为下一个同级别节点的首个子节点,是降级操作;<br>
中间第三个按钮(向上):将选中节点上移,不改变级别,就是排到上面亲兄弟节点之前;<br>
中间第三个按钮(向下):将选中节点下移,不改变级别,就是排到下面亲兄弟节点之后。<br>
</b><br>
安徽省和广东省的节点设置完后,我们点击“OK”按钮,回到设计师界面。湖南省的节点留着程序运行时用。<br>
我们在设计师界面,右击各个按钮,为每个按钮添加槽函数:<br>
<center> <img src="images/ch08/ch08-03-21.png" alt="ui" width="800"></center>
添加四个按钮的槽函数之后,我们开始代码的编辑。首先是 widget.h 头文件代码:<br>
<div class="code"> <span style=" color:#000080;">#ifndef</span><span style=" color:#c0c0c0;">
</span>WIDGET_H
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#define</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000080;">WIDGET_H</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;"><QWidget></span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;"><QTreeWidgetItem></span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;">//条目类的头文件</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;"><QTreeWidget></span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;">//树形控件头文件</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#808000;">namespace</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#800080;">Ui</span><span style=" color:#c0c0c0;"> </span><span style=" color:#000000;">{</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#808000;">class</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#800080;">Widget</span><span style=" color:#000000;">;</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000000;">}</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#808000;">class</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#800080;">Widget</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000000;">:</span><span style=" color:#c0c0c0;"> </span><span style=" color:#808000;">public</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">QWidget</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000000;">{</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000080;">Q_OBJECT</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#808000;">public</span><span style=" color:#000000;">:</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">explicit</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">Widget</span><span
style=" color:#000000;">(</span><span style=" color:#800080;">QWidget</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">*</span>parent<span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">=</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000080;">0</span><span style=" color:#000000;">);</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">~</span><span style=" font-style:italic; color:#000000;">Widget</span><span
style=" color:#000000;">();</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#808000;">private</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#808000;">slots</span><span style=" color:#000000;">:</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">void</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">on_pushButtonAddTop_clicked</span><span
style=" color:#000000;">();</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">void</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">on_pushButtonAddChild_clicked</span><span
style=" color:#000000;">();</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">void</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">on_pushButtonDelLeaf_clicked</span><span
style=" color:#000000;">();</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">void</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">on_pushButtonDelSubtree_clicked</span><span
style=" color:#000000;">();</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#808000;">private</span><span style=" color:#000000;">:</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">Ui</span><span
style=" color:#000000;">::</span><span style=" color:#800080;">Widget</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">*</span><span style=" color:#800000;">ui</span><span
style=" color:#000000;">;</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#008000;">//递归删除节点子树</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">void</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">RemoveSubtree</span><span
style=" color:#000000;">(</span><span style=" color:#800080;">QTreeWidgetItem</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">*</span>curLevelItem<span
style=" color:#000000;">);</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000000;">};</span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#endif</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;">//</span><span style=" color:#c0c0c0;"> </span><span style=" color:#008000;">WIDGET_H</span></pre>
</div>
头文件新增内容主要是三块: <br>
添加了树形条目和树形控件的头文件引用; <br>
之前添加了四个按钮的槽函数; <br>
最后添加了递归删除节点子树的函数 void RemoveSubtree(QTreeWidgetItem *curLevelItem)
,这个函数后面详细讲解。<br>
然后我们分块来讲解源码文件 widget.cpp 的内容:<br>
<div class="code">
<style type="text/css">
p, li { white-space: pre-wrap; }
</style> <pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;">"widget.h"</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;">"ui_widget.h"</span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;"><QMessageBox></span></pre>
<pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span
style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#008000;"><QDebug></span></pre>
<pre style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br></pre>