-
Notifications
You must be signed in to change notification settings - Fork 7
/
ch08-02.htm
4150 lines (2899 loc) · 264 KB
/
ch08-02.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-02</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.2 表格控件</div>
<br>
本节先介绍表格控件 QTableWidget 的功能特性,表格控件一般有多行多列,它的单元格条目为
QTableWidgetItem,单元格条目可以有丰富的图标、文字、工具提示,支持设置显示的字体、字体颜色、背景色等。另外表格控件拥有水平表头和垂直表头,可以设
置表头的丰富特性,表头设置通常会影响表格控件整体外观。由于表格控件的具有多行多列,它的选择区域和选择行为也变得复杂起来,本节会依次讲述这些内容,最后通过
两个示例展示表格控件相关知识运用。<br>
<br>
<div class="os2">8.2.1 QTableWidget</div>
<br>
在 Qt 设计师可以拖动表格控件到界面窗口,下图可以直观看到表格控件的大致组成:<br>
<center> <img src="images/ch08/ch08-02-01.png" alt="tablewidget" width="800"></center>
表格控件最上面一排是只读的水平表头,最左边一列是只读的垂直表头。表头又可以细分为多个分段(section),水平表头的分段就是表格各个列的列首,垂直表头
分段就是表格各个行的行首。表格控件的实体区域是按行、列排布的单元格,单元格内容一般用 QTableWidgetItem
条目填充,单元格如果不填充任何东西,那么默认是 NULL。本小节先介绍表格控件基本的内容,并且讲解基类中比较实用的内容;后面 8.2.2
小节介绍单元格条目 QTableWidgetItem;控件表头的内容也比较丰富,单独放在 8.2.3
小节介绍;然后详细介绍关于表格控件的选中区域、选中行为、选中模式、单次选中命令等内容,选中区域是表格控件独有的特性,而选中行为、选中模式、单次选中命令是本章所有
基于条目的控件都具有的特性,放在 8.2.4 节一块讲解了。<br>
<br>
先来看看表格控件的构造函数:<br>
<div class="code"> QTableWidget(QWidget * parent = 0)</div>
<div class="code"> QTableWidget(int rows, int columns,
QWidget * parent = 0)</div>
构造函数里最重要的参数是行数 rows 和列数 columns,表格控件必须指定行数、列数,才能进行后续操作。第一个构造函数没指定行数、列数,必须用下面
两个函数设置行数和列数:<br>
<div class="code">void setRowCount(int rows) //设置行数</div>
<div class="code">void setColumnCount(int columns) //设置列数</div>
行数、列数对应的获取函数为:<br>
<div class="code">int rowCount() const //获取行数</div>
<div class="code">int columnCount() const //获取列数</div>
设置好行数、列数之后就可以进行后续的操作。表格控件在设置行数、列数之后,就有排布好的单元格,默认这些单元格是空的,即 NULL。程序员可以通过
setItem()
函数可以设置各个单元格条目;程序运行时,表格控件默认所有单元格都可以双击编辑,用户的编辑操作会使表格控件自动生成条目存储用户输入的内容。setItem()
函数和用户编辑都可以填充单元格。<br>
<br>
这里提前说明一下,表格控件的有很多名字相似的两套函数或信号,比如设置当前高亮单元格函数 setCurrentCell(int row, int
column) 和设置当前高亮条目 setCurrentItem(QTableWidgetItem *
item),这两个函数名字很类似,二者最主要的区别就是:名字带 Cell 的函数是基于单元格的,对 NULL 空单元格也可以使用;而名字带 Item
的函数,必须是填充了实体条目的单元格,针对实际存在的单元格条目操作。下面大致按函数功能介绍表格控件的相关内容。<br>
<br>
(1)设置条目<br>
之前 8.1 节 QListWidget 有很多添加条目或设置条目的函数,但是本节表格控件因为设置单元格必须同时知道行号、列号,所以没有 Add** 和
Insert** 之类的添加函数,表格只有如下设置条目函数:<br>
<div class="code">void QTableWidget::setItem(int row, int column,
QTableWidgetItem * item)</div>
一般用 new 新建条目,然后设置到指定行号、列号的单元格里面,比如下面一段示范代码:<br>
<div class="code"> QTableWidgetItem *newItem = new
QTableWidgetItem(tr("newitem");<br>
tableWidget->setItem(row, column, newItem);</div>
从代码而言,就只有 setItem()
函数设置单元格条目;当然,程序运行时用户可以双击编辑单元格,表格控件可以自动新建条目保存用户编辑的内容存到单元格里面。单元格条目不仅支持文本,还能设置图标、丰富
字体、字体颜色、工具提示等信息,后面还会介绍 QTableWidgetItem 类。<br>
<span style="font-weight: bold; color: red;">关于 setItem()
函数需要注意的问题是:一个条目只能设置给唯一的一个单元格,不能重复设置给多个单元格!</span>如果希望把条目换一个单元格放置,必须用下面移除条目
函数把条目从表格控件卸载下来,再重新设置给新的单元格。<br>
<br>
(2)移除条目<br>
<div class="code">QTableWidgetItem * QTableWidget::takeItem(int row, int
column)</div>
takeItem() 函数会把指定行号、列号的条目从表格控件卸载下来,但不会完全删除。返回的指针如果非空,那么该指针会指向内存中的条目,如果希望把条目
彻底删除,还需要自己手动 delete 删掉内存空间。如果不彻底删除,那么还可以把非空指针重新设置给表格控件。<br>
表格控件还有关于整行删除、整列删除、清空表格的几个槽函数,等会再介绍。<br>
<br>
(3)条目访问函数<br>
表格控件可以根据行号、列号获取条目对象指针:<br>
<div class="code">QTableWidgetItem * QTableWidget::item(int row, int
column) const</div>
注意判断返回的指针是否为空,非空指针才能进行后续操作。<br>
另外还能根据表格控件内部的坐标点位来获取位于该坐标位置的条目:<br>
<div class="code">QTableWidgetItem * QTableWidget::itemAt(const QPoint
& point) const</div>
<div class="code">QTableWidgetItem * QTableWidget::itemAt(int ax, int ay)
const</div>
这两个 itemAt() 函数等价的,坐标是指表格控件内部的相对坐标,表格控件内部左上角是 (0,0) 原点。itemAt()
函数也是可能返回空指针的,一定要判断返回值是否为 NULL。<br>
如果知道了条目 item 非空指针,可以用条目的函数获取行号、列号,如 item->row() 是行号,item->column()
是列号。根据条目的非空指针,也可以获取条目的可视矩形(条目在表格可见范围内的显示矩形):<br>
<div class="code">QRect QTableWidget::visualItemRect(const QTableWidgetItem
* item) const</div>
<br>
(4)当前选中条目的操作<br>
表格控件一般是多行多列的,实际的选中操作是比较复杂的,我们在这里先介绍最简单的内容,就是不按键盘,只用鼠标左边点击选中当前条目的情况,就是简单的单选操
作。<br>
获取当前选中条目的函数为:<br>
<div class="code">QTableWidgetItem * QTableWidget::currentItem() const</div>
返回的值如果非空那就是实际存在的条目,如果返回了 NULL,说明单元格是空的。<br>
不管选中的单元格内部是否为空,都可以获取当前选中单元格的行号、列号:<br>
<div class="code">int QTableWidget::currentRow() const //当前选中行号</div>
<div class="code">int QTableWidget::currentColumn() const //当前选中列号</div>
除了鼠标点击选中当前高亮的单元格,也可以用函数设置当前单元格:<br>
<div class="code">void QTableWidget::setCurrentCell(int row, int column)</div>
<div class="code">void QTableWidget::setCurrentCell(int row, int column,
QItemSelectionModel::SelectionFlags command)</div>
单元格不管是不是空的,上面两个函数都可以设置当前高亮选中状态,第二个 setCurrentCell() 多了个单次选中命令的参数,等到 8.4.4
节再 详细说。<br>
如果知道了非空条目指针,可以设置该条目为当前选中条目:<br>
<div class="code">void QTableWidget::setCurrentItem(QTableWidgetItem *
item)</div>
<div class="code">void QTableWidget::setCurrentItem(QTableWidgetItem *
item, QItemSelectionModel::SelectionFlags command)</div>
第二个函数的 command 参数等到 8.4.4 节再 详细说。<br>
<br>
无论是通过代码改变当前选中条目还是用户点击操作改变当前选中条目,都会触发当前单元格变化的信号:<br>
<div class="code">void currentCellChanged(int currentRow,
int currentColumn, int previousRow, int previousColumn)<br>
//单元格无论是否为空都起作用,当前选中单元格的变化信号,参数指出了新当前单元格行列号和旧单元格的行列号</div>
<div class="code">void currentItemChanged(QTableWidgetItem
* current, QTableWidgetItem * previous)<br>
//当前选中条目变化信号,新、旧当前选中单元格至少有一个非空才会触发这个信号</div>
<br>
(5)条目查找和排序<br>
表格控件也可以根据模版字符串查找匹配的条目:<br>
<div class="code">QList<QTableWidgetItem *>
QTableWidget::findItems(const QString & text, Qt::MatchFlags flags)
const</div>
参数里的 text 是模板字符串,第二个 flags 是匹配标志,匹配标志 Qt::MatchFlags 在 8.1.1 节列举过了,一模一样,比如
Qt::MatchContains 是包含子串,Qt::MatchWildcard 是通配符(*、?)匹配,Qt::MatchRegExp
是正则表达式匹配,等等。<br>
<br>
表格控件的排序操作复杂一些,因为有很多列,开启自动排序后需要指定按照哪一列来排序。开启自动排序函数为:<br>
<div class="code">void setSortingEnabled(bool enable)
//设置是否自动排序</div>
<div class="code">bool isSortingEnabled() const
//判断是否开启了自动排序</div>
开启自动排序之后,需要指定按照哪一列来排序:<br>
<div class="code">void QTableView::sortByColumn(int column, Qt::SortOrder
order)</div>
排序的函数是从基类 QTableView 继承的,指定自动排序的列 column 和 order (升序 Qt::AscendingOrder 或降序
Qt::DescendingOrder)。<br>
除了上面自动排序的函数,还有一个手动排序函数,可以在不开启自动排序时手动调整排序:<br>
<div class="code">void QTableWidget::sortItems(int column, Qt::SortOrder
order = Qt::AscendingOrder)</div>
<br>
开启自动排序之后,会对 setItem(int row, int column, QTableWidgetItem * item)
函数产生重要影响:新设置的条目如果正好在指定的自动排序列,那么新条目会自动排序,被挪到按序排列好的行号,setItem() 函数指定的行号不一定有效。<br>
如果用代码设置某一行的条目,该行的条目是联系在一起的,比如是某人的个人信息,那么就需要特别注意自动排序的问题:比如预期一行条目的行号是
therow,自动排序列为 sortcol,对于前几个列的条目,使用 therow 设置条目给表格,该行位置不变;当设置到 therow
行、sortcol 列的条目时,问题来了,自动排序列会根据新条目内容调整该行序号,很有可能把该行移动到了新的行号 newrow,那么如果后面代码继续用
旧的 therow 行号设置后面几列的条目,后面几列条目就会错位。这是很严重的问题。<br>
<span style="color: red; font-weight: bold;">因此在调用 setItem()
函数设置整行的多列条目时,一定要提前关闭自动排序,把新条目都设置完毕后再考虑开启自动排序!</span><br>
<br>
(6)单元格控件和运行时条目编辑<br>
单元格除了设置条目,还可以设置独立的单元格控件:<br>
<div class="code">void QTableWidget::setCellWidget(int row, int column,
QWidget * widget)</div>
需要注意的问题是单元格控件 widget 会完全覆盖住单元格条目 item,默认情况下单元格条目 item 与单元格控件 widget
是完全没关联的,比如设置给单元格一个组合框,组合框显示的东西与单元格原本条目没关系,修改了组合框内容不会影响单元格条目内容。如果希望单元格控件与单元格条
目有关,需要自己编代码。<br>
获取单元格控件使用如下函数:<br>
<div class="code">QWidget * QTableWidget::cellWidget(int row, int column)
const</div>
注意返回的指针可能为空。<br>
单元格控件也可以被删除掉:<br>
<div class="code">void QTableWidget::removeCellWidget(int row, int column)</div>
删除单元格控件后,单元格就会显示原来的条目内容(如果条目原本是 NULL ,就剩下空单元格)。<br>
<br>
表格控件的单元格也可以开启持续编辑器和关闭持续编辑器(必须配对使用):<br>
<div class="code">void QTableWidget::openPersistentEditor(QTableWidgetItem
* item) //开启持续编辑器</div>
<div class="code">void QTableWidget::closePersistentEditor(QTableWidgetItem
* item) //关闭持续编辑器</div>
因为表格控件默认的条目全都带了可以编辑标志,所以一般用不到持续编辑器,如果想用代码让单元格进行编辑状态,可以用更简单的函数:<br>
<div class="code">void QTableWidget::editItem(QTableWidgetItem * item)</div>
对于可编辑的条目,都可以用 editItem() 开启编辑器,这个临时编辑器可以自动关闭的,不需要调用其他函数。<br>
<br>
程序运行时,表格控件默认所有单元格都可以双击编辑,但如果希望表格全部是只读的,那么可以用下面函数关闭表格控件的整体编辑触发器:<br>
<div class="code">void setEditTriggers(EditTriggers
triggers) //设置编辑触发器</div>
<div class="code">EditTriggers editTriggers() const
//获取编辑触发器</div>
默认的编辑器触发器是双击、回车键等可以编辑单元格,修改为 QAbstractItemView::NoEditTriggers
就不会开启任何编辑器了,相当于整个表格只读了。如果不希望整个表格只读,而只是希望某部分的单元格只读,那么只有设置单元格条目自身的标志
位,item->setFlags(Qt::ItemFlags flags),把 flags 设置为不带可编辑标志的:<br>
(item->flags()) & (~Qt::ItemIsEditable) 。<br>
<br>
(7)信号<br>
除了之前介绍的 currentCellChanged() 和 currentItemChanged()
信号,还有多个关于单元格和条目操作的信号,按照基于单元格操作触发还是实体条目操作触发,大致分为两类:<br>
<div class="code">void cellActivated(int row, int column)
//单元格被激活</div>
<div class="code">void cellChanged(int row, int column)
//单元格内部发生变化,如从NULL变成有条目,或者条目内部数据发生变化</div>
<div class="code">void cellClicked(int row, int column)
//单元格被点击</div>
<div class="code">void cellDoubleClicked(int row, int
column) //单元格被双击</div>
<div class="code">void cellEntered(int row, int column) <br>
//鼠标进入单元格,只有在 mouseTracking 开启时或鼠标移动时点击了单元格,才触发该信号</div>
<div class="code">void cellPressed(int row, int column)
//单元格被鼠标点击按下</div>
第二类是基于实体条目触发的:
<div class="code">void itemActivated(QTableWidgetItem *
item) //条目被激活</div>
<div class="code">void itemChanged(QTableWidgetItem *
item) //条目内部数据发生变化,如文本修改、图标变化、复选状态变化等</div>
<div class="code">void itemClicked(QTableWidgetItem *
item) //条目被点击</div>
<div class="code">void itemDoubleClicked(QTableWidgetItem
* item) //条目被双击</div>
<div class="code">void itemEntered(QTableWidgetItem *
item)<br>
//鼠标进入条目,只有在 mouseTracking 开启时或鼠标移动时点击了条目,才触发该信号</div>
<div class="code">void itemPressed(QTableWidgetItem *
item) //条目被鼠标点击按下</div>
因为表格条目是多选的,可以有很多高亮选中条目,高亮选中的状态发生变化时,会触发如下信号:
<div class="code">void itemSelectionChanged()</div>
上面信号参数没有指明哪些条目被选中,要获取所有选中的条目,可以用如下函数:
<div class="code">QList<QTableWidgetItem *>
QTableWidget::selectedItems() const</div>
更多关于表格选中操作的内容后面再介绍。<br>
<br>
(8)槽函数<br>
表格有两个清空内容的槽函数,首先是 clear() 槽函数:<br>
<div class="code">void QTableWidget::clear()</div>
clear() 函数删除表格内所有条目内容,单元格全为 NULL,清除选中状态,水平表头和垂直表头的设置内容清空,但是会保留表格的行数、列数,表头清掉
之后,会使用默认的数字行号、列号。<br>
第二个清除内容的槽函数:<br>
<div class="code">void QTableWidget::clearContents()</div>
这个函数删除所有条目内容,单元格全为 NULL,清除选中状态,但是表头的设置内容都保留,表头文本等照旧显示,表格行数、列数也不变。<br>
还有关于整行、整列插入删除操作的槽函数:<br>
<div class="code">void QTableWidget::insertColumn(int column) //插入新的一列,新列序号
column,新列的单元格默认 NULL</div>
<div class="code">void QTableWidget::insertRow(int row) //插入新的一行,新行序号
row,新行的单元格默认 NULL</div>
<div class="code">void QTableWidget::removeColumn(int column) //删除第 column
整列条目</div>
<div class="code">void QTableWidget::removeRow(int row) //删除第 row 整行条目</div>
最后是关于条目滚动显示的函数,表格的行、列很多时,只有一部分的单元格显示在表格控件可视矩形里,如果希望滚动表格,让指定的条目显示出来,使用如下函数:<br>
<div class="code">void QTableWidget::scrollToItem(const QTableWidgetItem *
item, QAbstractItemView::ScrollHint hint = EnsureVisible)</div>
QAbstractItemView::ScrollHint 枚举常量在 8.1.1 节列举过,这里不重复介绍了。<br>
<br>
(9)基类 QTableView 的函数<br>
表格控件还从基类 QTableView 继承了很多函数,这里介绍一些比较实用的,更多关于 QTableView 视图的内容会等到模型/视图章节再详解。<br>
表头控件的表头分段有分隔线,拖动分隔线可以调整各列的宽度,各行高度也可以拖动分隔线调整。如果通过函数代码调整列宽、行高,可以用下面的函数:<br>
<div class="code">void QTableView::setColumnWidth(int column, int width)
//设置列宽</div>
<div class="code">int QTableView::columnWidth(int column) const //获取列宽</div>
<div class="code">void QTableView::setRowHeight(int row, int height) //设置行高</div>
<div class="code">int QTableView::rowHeight(int row) const //获取行高</div>
用户在程序运行时可以双击表头分段的分隔线,那样表格控件会自动根据该列单元格内容最宽的条目,调整列宽,让该列条目内容都显示出来,这个操作也有对应的函数:
(下面四个都是槽函数)<br>
<div class="code">void resizeColumnToContents(int column)
//自动调整第 column 列列宽,将该列条目显示完整</div>
<div class="code">void resizeColumnsToContents()
//自动调整所有列宽</div>
<div class="code">void resizeRowToContents(int row)
//自动调整第 row 行行高,将该行条目显示完整</div>
<div class="code">void resizeRowsToContents() //自动调整所有行高</div>
如果希望隐藏或显示指定列的所有条目或指定行的条目,可以用如下函数:<br>
<div class="code">void QTableView::setColumnHidden(int column, bool hide)
//设置指定列隐藏或显示</div>
<div class="code">bool QTableView::isColumnHidden(int column) const
//判断指定列是否被隐藏</div>
<div class="code">void QTableView::setRowHidden(int row, bool hide)
//设置指定行隐 藏或显示</div>
<div class="code">bool QTableView::isRowHidden(int row) const //判断指定行是否被隐藏</div>
关于显示或隐藏行列,还有四个快捷槽函数:hideColumn(int column),hideRow(int row),showColumn(int
column),showRow(int row),这些函数都可以灵活使用,效果与 setColumnHidden() 和
setRowHidden() 函数是一样的。<br>
<br>
QTableView 也有更抽象的基类 QAbstractItemView ,QAbstractItemView
里面也有一些实用函数,这里先介绍一个设置条目显示图标大小的函数:<br>
<div class="code">void QAbstractItemView::setIconSize(const QSize &
size)</div>
单元格里图标默认显示为 16*16 的,可以用该函数设置图标显示尺寸为 24*24 或 32*32 。 QAbstractItemView
类还有关于条目选中模式、选中行为等重要函数,后面再介绍。基类的内容暂时介绍的到这,下面来学学单元格条目的知识。<br>
<br>
<div class="os2">8.2.2 QTableWidgetItem</div>
<br>
表格控件条目 QTableWidgetItem 与 8.1.2 节 QListWidgetItem 有很多相似的地方,<br>
QTableWidgetItem 也是一个纯数据类,不是控件,没有基类,也就没有信号和槽函数。QTableWidgetItem 可以直接用数据流
QDataStream 读写,表格条目不单单有文本,还可以有自己的图标、复选框等特性,表格控件会根据条目对象的丰富特性来呈现数据并进行交互操作。<br>
(1)首先来看看条目的构造函数:<br>
<div class="code"> QTableWidgetItem(int type = Type)</div>
<div class="code"> QTableWidgetItem(const QString &
text, int type = Type)</div>
<div class="code"> QTableWidgetItem(const QIcon &
icon, const QString & text, int type = Type)</div>
<div class="code"> QTableWidgetItem(const QTableWidgetItem
& other) //复制构造函数</div>
参数里的 type 一般用于派生类,指定条目独特的分类,对于普通的表格控件条目,通常用默认的数值即可。第二个构造函数是指定了条目的文本,第三个构造函数同
时指定了条目图标和文本。<br>
在构造函数里不能直接指定所属的表格控件,因为表格控件有行号、列号所以不能简单追加到表格里,而只能用表格控件的
QTableWidget::setItem()
函数把条目设置到指定单元格里。注意一个条目只能设置给表格控件唯一的一个单元格,不能把一个条目设置给多个单元格。如果希望为多个单元格设置相似的条目数据内容,那么可
以用后面介绍的 clone() 函数制造大量新的复制体条目。<br>
复制构造函数不会复制旧条目的 type() 类型值和tableWidget() 所属表格控件指针,条目其他的内部数据和条目标志都会复制。<br>
<br>
(2)复制函数和运算符函数<br>
除了复制构造函数,还有专门的克隆函数:<br>
<div class="code">QTableWidgetItem * QTableWidgetItem::clone() const</div>
克隆函数会新建一个当前条目的复制体,然后返回新复制体条目的指针,如果要构造并添加大量重复内容的条目,这个函数就比较实用。clone()
函数内部会调用复制构造函数创建新条目并返回。<br>
条目复制还可以用等于号函数:<br>
<div class="code">QTableWidgetItem & QTableWidgetItem::operator=(const
QTableWidgetItem & other)</div>
等于号函数复制的内容与复制构造函数差不多,type() 类型值和 tableWidget() 所属表格控件指针也不复制。<br>
还有个小于号函数,可以比较条目文本的字典序:<br>
<div class="code">QTableWidgetItem & QTableWidgetItem::operator=(const
QTableWidgetItem & other)</div>
如果希望使用更多的比较函数,可以直接用 QString 的比较函数。<br>
<br>
(3)QTableWidgetItem 的功能函数与内部数据<br>
QTableWidgetItem 内部的数据也大致分为两类:第一类是以数据角色形式管理的通用数据,这些数据自动参与 QDataStream
数据流的读写;第二类是非通用数据,不参与数据流读写,与 QListWidgetItem 和 QListWidget
自身特性有关。QTableWidgetItem 主要的功能函数也是围绕这两类内部数据展开的。<br>
<br>
● 第一类:通用数据及其处理函数<br>
通用数据是以数据角色与数据变量一一对应的形式存储管理,比如设置文本 setText()、设置图标 setIcon()
等函数,其本质都是根据各自的角色调用通用设置数据的函数:<br>
<div class="code">virtual void setData(int role, const QVariant & value)</div>
也可以根据角色来获取各个数据变量:<br>
<div class="code">virtual QVariant data(int role) const</div>
查看表格控件的源代码文件可以看到关于图标操作函数的源码:<br>
<div class="code">inline QIcon icon() const<br>
{ return
qvariant_cast<QIcon>(data(Qt::DecorationRole)); }</div>
<div class="code">inline void QTableWidgetItem::setIcon(const QIcon
&aicon)<br>
{ setData(Qt::DecorationRole, aicon); }</div>
QTableWidgetItem 的通用数据与 QListWidgetItem 的通用数据一模一样,函数名也一样,列表如下:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td style="width: 110px;" align="center"><b>获取函数</b></td>
<td style="width: 280px;" align="center"><b>设置函数</b></td>
<td style="width: 150px;" align="center"><b>数据角色</b></td>
<td align="center"><b> 描述 </b></td>
</tr>
<tr>
<td>text()</td>
<td> setText(const QString &text) </td>
<td> Qt::DisplayRole </td>
<td> 条目显示的文本。 </td>
</tr>
<tr class="d1">
<td>icon()</td>
<td> setIcon(const QIcon &icon) </td>
<td> Qt::DecorationRole </td>
<td> 条目显示的图标。 </td>
</tr>
<tr>
<td>statusTip()</td>
<td> setStatusTip(const QString &statusTip) </td>
<td> Qt::StatusTipRole </td>
<td> 如果主界面有状态栏,鼠标悬停在该条目上时显示该状态信息到状态栏。 </td>
</tr>
<tr class="d1">
<td>toolTip()</td>
<td> setToolTip(const QString &toolTip) </td>
<td> Qt::ToolTipRole </td>
<td> 鼠标悬停在该条目上时显示的工具提示信息。 </td>
</tr>
<tr>
<td>whatsThis()</td>
<td> setWhatsThis(const QString &whatsThis) </td>
<td> Qt::WhatsThisRole </td>
<td> 如果主界面窗口标题栏有?帮助按钮,点击帮助按钮再点击该条目会显示该帮助信息。 </td>
</tr>
<tr class="d1">
<td>font()</td>
<td> setFont(const QFont &font) </td>
<td> Qt::FontRole </td>
<td> 显示条目文本用的字体。 </td>
</tr>
<tr>
<td>textAlignment()</td>
<td> setTextAlignment(int alignment) </td>
<td> Qt::TextAlignmentRole </td>
<td> 文本的对齐方式。 </td>
</tr>
<tr class="d1">
<td>backgroundColor()</td>
<td> setBackgroundColor(const QColor &color)</td>
<td> Qt::BackgroundColorRole </td>
<td> 文本背景色。 </td>
</tr>
<tr>
<td>textColor()</td>
<td> setTextColor(const QColor &color)</td>
<td> Qt::TextColorRole </td>
<td> 文字颜色。 </td>
</tr>
<tr class="d1">
<td>background()</td>
<td> setBackground(const QBrush &brush) </td>
<td> Qt::BackgroundRole </td>
<td> 条目的背景画刷。 </td>
</tr>
<tr>
<td>foreground()</td>
<td> setForeground(const QBrush &brush) </td>
<td> Qt::ForegroundRole </td>
<td> 条目的前景画刷。 </td>
</tr>
<tr class="d1">
<td>checkState()</td>
<td> setCheckState(Qt::CheckState state)</td>
<td> Qt::CheckStateRole </td>
<td> 条目自带的复选框选中状态,可以是三态复选框。 </td>
</tr>
<tr>
<td>sizeHint()</td>
<td> setSizeHint(const QSize &size) </td>
<td> Qt::SizeHintRole </td>
<td> 条目显示的建议尺寸。 </td>
</tr>
</tbody>
</table>
<br>
表格条目 QTableWidgetItem 可以直接用数据流 QDataStream 读写,是通过下面运算符重载函数实现的:<br>
<div class="code">QDataStream &
operator<<(QDataStream & out, const QTableWidgetItem &
item)</div>
<div class="code">QDataStream &
operator>>(QDataStream & in, QTableWidgetItem & item)</div>
使用 >> 和 << 运算符进行流输入输出比较常见,当然也可以用 QTableWidgetItem
类内部的读写函数进行输入输出:<br>
<div class="code">void QTableWidgetItem::read(QDataStream & in)</div>
<div class="code">void QTableWidgetItem::write(QDataStream & out) const</div>
<br>
● 第二类:非通用数据及其操作函数<br>
表格控件的条目也是在构造函数一次性指定条目类型,然后这个类型是只读的:<br>
<div class="code">int QTableWidgetItem::type() const</div>
类型值一般用于派生类区分不同的条目类型,自定义的条目类型值需要大于等于 QTableWidgetItem::UserType 。<br>
新条目通过表格控件 QTableWidget::setItem() 函数设置给表格控件之后,条目会自动保存所属的表格控件指针,可以用如下函数获取:<br>
<div class="code">QTableWidget * QTableWidgetItem::tableWidget() const</div>
<br>
程序运行时,除了可以通过表格控件的 QTableWidget::selectedItems() 获取用户选中的条目列表,每个单元格条目自身也会记录是否
被用户选中,并且能设置自身是否被选中:<br>
<div class="code">bool QTableWidgetItem::isSelected() const //判断条目自身是否高亮选中</div>
<div class="code">void QTableWidgetItem::setSelected(bool select) //设置条目自身是
否被选中</div>
<br>
表格控件条目初始化时也有默认的标志位,并且运行时可以修改条目的特性标志:<br>
<div class="code">Qt::ItemFlags QTableWidgetItem::flags() const //获取特性标志位</div>
<div class="code">void QTableWidgetItem::setFlags(Qt::ItemFlags flags)
//设置特性标志位</div>
QTableWidgetItem 构造时默认的标志位为:<br>
Qt::ItemIsEditable<br>
|Qt::ItemIsSelectable<br>
|Qt::ItemIsUserCheckable<br>
|Qt::ItemIsEnabled<br>
|Qt::ItemIsDragEnabled<br>
|Qt::ItemIsDropEnabled<br>
因此程序代码生成的所有表格条目默认都是可编辑的,程序运行时用户双击空的单元格时,表格控件会自动创建新条目保存用户编辑内容,这些自动创建的条目也都是可编辑
的。如果希望条目是只读的,那么把标志位设置不带Qt::ItemIsEditable 的新标志位即可。<br>
<br>
Qt::ItemFlags 所有的枚举常量在 8.1.2
节末尾有详细的表格描述,这里不重复贴了。表格条目默认就是支持复选状态的,只是默认没有显示出来,要让表格控件显示条目对应的复选框,可以用下面一句代码实现:<br>
<div class="code"> item->setCheckState( Qt::Unchecked
); //显示复选框</div>
对于表格控件和表格条目,自带单行编辑器和复选框功能,因此不需要为单元格添加自定义的单行编辑器或复选框,如果要用到其他更多的输入控件,那才需要定制单元格。<br>
<br>
表格控件条目与 QListWidgetItem 最大的一个区别就是表格条目既有行号,也有列号:<br>
<div class="code">int QTableWidgetItem::row() const //获取行号</div>
<div class="code">int QTableWidgetItem::column() const //获取列号</div>
行号和列号也是在 QTableWidget::setItem() 函数里指定的,条目自身并不能修改行号或列号。表格控件条目与
QListWidgetItem 另一个区别是,表格控件条目没有隐藏函数,所有表格条目都是显示的(只要不被单元格控件QTableWidget::
cellWidget() 遮挡)。<br>
<br>
在 QtCreator 设计模式和 Qt 设计师界面,表格控件 QTableWidget 和其条目 QTableWidgetItem
都是可以可视化编辑的,上面介绍的通用数据和非通用数据都是可以在属性栏设置的,后面会介绍表格控件及其条目的可视化编辑。<br>
<br>
<div class="os2">8.2.3 表头设置</div>
<br>
表格控件在指定了行号和列号之后,就会自动生成水平表头和垂直表头,默认的表头都是从数字 1 开始递增编号,如下图所示:<br>
<center> <img src="images/ch08/ch08-02-02.png" alt="autoheader"></center>
虽然默认的表头文本看起来从 1 开始编号,但是内部的表头分段(section)都是从 0 开始编号的,代码里设置表头分段时,需要从 0 编号。<br>
表格控件的既可以简单地设置表头分段文本,也可以利用 QTableWidgetItem 为表头分段设置功能更丰富的表头条目,设置表头文本的函数如下:<br>
<div class="code">void QTableWidget::setHorizontalHeaderLabels(const
QStringList & labels) //设置水平表头文本</div>
<div class="code">void QTableWidget::setVerticalHeaderLabels(const
QStringList & labels) //设置垂直表头文本</div>
QStringList 内字符串列表,第 0 个字符串就是第 0 号表头分段的文本,一般要求 QStringList
字符串个数与表头分段数目一样,这样不会有漏的,也不会有多的。通常比较多的是设置水平表头(就是每列的列首),垂直表头一般可以不设置,用自动编号。<br>
<br>
setHorizontalHeaderLabels() 和 setVerticalHeaderLabels()
其实是两个快捷函数,本质是循环构建表头分段条目并设置给表头分段:<br>
<div class="code">void QTableWidget::setHorizontalHeaderItem(int column,
QTableWidgetItem * item) //设置指定列的水平表头分段条目</div>
<div class="code">QTableWidgetItem * QTableWidget::horizontalHeaderItem(int
column) const //获取指定列的水平表头分段条目</div>
<div class="code">void QTableWidget::setVerticalHeaderItem(int row,
QTableWidgetItem * item) //设置指定行的垂直表头分段条目</div>
<div class="code">QTableWidgetItem * QTableWidget::verticalHeaderItem(int
row) const //获取指定行的垂直表头分段条目</div>
这里可以发现 QTableWidgetItem 既可以作为表格控件普通单元格的条目,也可以用来设置表头分段。<br>
QTableWidgetItem
作为表头分段条目使用时,肯定是只读的,程序运行时不能双击编辑表头,而且也不能有复选框。表头分段条目支持显示文本、图标、工具提示信息,并能设置文本对齐、字体、前景
画刷和背景画刷等等,这样表头显示的功能就比较丰富,而不是单纯的文本。<br>
使用 setHorizontalHeaderItem() 和 setVerticalHeaderItem() 函数时,一个
QTableWidgetItem 只能设置给唯一的一个表头分段,并且设置给表头分段之后,也不要把表头分段条目设置给表格控件普通的单元格。<br>
表头分段的条目也可以从表格控件卸载卸载下来:<br>
<div class="code">QTableWidgetItem *
QTableWidget::takeHorizontalHeaderItem(int column) //卸下指定列的水平表头条目</div>
<div class="code">QTableWidgetItem *
QTableWidget::takeVerticalHeaderItem(int row) //卸下指定行的垂直表头条目</div>
卸载下来的条目并不会自动删除,还存在内存中,如果希望彻底删除需要手动 delete,或者不删除的话,卸载下来的条目可以重新设置给另一个表头分段。<br>
<br>
表头分段的条目与普通的单元格条目有重要的区别,普通单元格条目可编辑、可以有复选状态,但是表头分段的条目只有显示功能,不能双击编辑或复选。把
QTableWidgetItem 设置给表头分段后,QTableWidgetItem 条目会有额外的内部特性标志:<br>
<div class="code"><span style=" color:#c0c0c0;"> </span><span style=" color:#808000;">
if</span><span style=" color:#c0c0c0;"> </span><span style=" color:#000000;">(</span><span
style=" color:#000000;">item</span><span style=" color:#000000;">)</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">{</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:#000000;">item</span><span
style=" color:#000000;">-></span><span style=" color:#800000;">view</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">=</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000000;">view</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:#800000;">itemFlags</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;">ItemFlags</span><span
style=" color:#000000;">(</span><span style=" color:#808000;">int</span><span style=" color:#000000;">(</span><span
style=" color:#000000;">item</span><span style=" color:#000000;">-></span><span
style=" color:#800000;">itemFlags</span><span style=" color:#000000;">)|</span><span
style=" color:#800080;">ItemIsHeaderItem</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>
上面小段代码是从 QTableWidget 源代码里截取的,设置表头分段条目时,item 的标志位会额外设置 ItemIsHeaderItem
标志位(ItemIsHeaderItem 是内部专用标志位,并不公开),因此设置给表头的条目是不能再设置给普通单元格的。条目 item
设置给一个表头分段后,也不能设置给其他的表头分段,因为表格控件销毁时会 delete
每个表头条目,一个条目指针如果设置给多个表头分段,那么同一个条目指针最后会被 delete 多次,删除野指针会导致程序崩溃。<br>
<br>
表格控件并不要求必须设置表头文本或表头条目,在不设置表头文本和表头条目的情况下,默认是所有的表头分段条目是 NULL
,所以要注意表头条目获取和卸载函数返回的指针可能是 NULL。不设置任何表头文本和表头条目时,表格控件依然是有表头的,默认的表头是按照前面截图的从 1
开始自动编号,自动编号表头是没有实体表头条目的,这些自动编号表头是从基类 QTableView 视图类继承而来的。<br>
<br>
QTableWidget 本层类带有前面介绍 8 个表头分段操作的函数,各个表头分段设置从微观角度展示表头的功能,而基类 QTableView
有从宏观描述的整体表头,即:<br>
<div class="code">QHeaderView * QTableView::horizontalHeader() const
//获取整个的水平表头</div>
<div class="code">void QTableView::setHorizontalHeader(QHeaderView *
header) //设置整个的水平表头</div>
<div class="code">QHeaderView * QTableView::verticalHeader() const
//获取整个的垂直表头</div>
<div class="code">void QTableView::setVerticalHeader(QHeaderView * header)
//设置整个的垂直表头</div>
因为表格控件自己就会带有水平表头和垂直表头,所以一般不需要调用 setHorizontalHeader() 和
setVerticalHeader() 函数,直接获取原本的表头对象指针,然后调用表头类 QHeaderView 的函数修改特性即可。<br>
水平表头和垂直表头都是同一个类 QHeaderView ,水平表头和垂直表头可以通过如下函数判断: <br>
<div class="code">Qt::Orientation QHeaderView::orientation() const</div>
返回值如果是 Qt::Horizontal 表示水平表头,Qt::Vertical 是垂直表头,这个方向特性是在构造函数时指定的,之后不能进行修改。<br>
QHeaderView 功能是非常丰富的,这里介绍一些比较实用的,以后到模型视图章节还会介绍更多的。<br>
获取当前表头的分段数目,使用函数:<br>
<div class="code">int QHeaderView::count() const</div>
显示整个表头显示的像素点长度(水平表头是表头整体宽度,垂直表头是表头整体高度):<br>
<div class="code">int QHeaderView::length() const</div>
<br>
在 Qt 设计师界面,选中表格控件,可以看到水平表头和垂直表头的属性,这里以水平表头的属性举例:<br>
<center> <img src="images/ch08/ch08-02-03.png" alt="header2" width="800"></center>
当表格控件开启自动排序属性 sortingEnabled 之后,水平表头就会自动设置 horizontalHeaderShowSortIndicator
为勾选状态,这样程序运行时根据一个列自动排序时,该列水平表头分段显示三角形排序指示符。如果勾选水平表头的
horizontalHeaderStretchLastSection
属性,那么表格最后一列会自动拉伸占据剩下的空间。水平表头和垂直表头这些属性都有对应的 QHeaderView 类的函数,罗列描述如下:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td style="width: 180px;" align="center"><b>属性</b></td>
<td style="width: 280px;" align="center"><b>QHeaderView类设置 函数</b></td>
<td style="width: 180px;" align="center"><b>QHeaderView类获取 函数</b></td>
<td align="center"><b> 描述 </b></td>
</tr>
<tr>
<td>**visible</td>
<td> setVisible(bool v) </td>
<td>isVisible()</td>
<td> 是否显示表头。</td>
</tr>
<tr class="d1">
<td>**cascadingSectionResizes</td>
<td>setCascadingSectionResizes(bool enable) </td>
<td> cascadingSectionResizes()</td>
<td> 调整分段尺寸时是否仅相邻分段调整。 </td>
</tr>
<tr>
<td>**defaultSectionSize</td>
<td>setDefaultSectionSize(int size) </td>
<td> defaultSectionSize()</td>
<td> 分段默认尺寸,水平表头是指分段宽度(默认100),垂直表头指分段高度(默认30)。 </td>
</tr>
<tr class="d1">
<td>**highlightSections</td>
<td> setHighlightSections(bool highlight)</td>
<td> highlightSections()</td>
<td> 条目高亮选中时,其行首、列首是否也有高亮效果。一般不需要手动设置。 </td>
</tr>
<tr>
<td>**minimumSectionSize</td>
<td> setMinimumSectionSize(int size) </td>
<td> minimumSectionSize() </td>
<td> 分段尺寸下限。水平表头分段默认最小宽度 21,垂直表头分段默认最小高度 21。 </td>
</tr>
<tr class="d1">
<td>**showSortIndicator</td>
<td> setSortIndicatorShown(bool show) </td>
<td> isSortIndicatorShown() </td>
<td> 表头是否显示排序指示符。开启自动排序后通常自动显示排序指示符,一般不需要手动设置。 </td>
</tr>
<tr>
<td>**stretchLastSection</td>
<td> setStretchLastSection(bool stretch) </td>
<td> stretchLastSection() </td>
<td> 是否自动拉伸末尾分段。 </td>
</tr>
</tbody>
</table>
<br>
按照上表描述,如果希望隐藏垂直表头,那么就可以执行如下代码:<br>
<div class="code"><span style=" color:#c0c0c0;"> </span><span style=" color:#800000;">ui</span><span
style=" color:#000000;">-></span><span style=" color:#800000;">tableWidget</span><span
style=" color:#000000;">-></span><span style=" color:#000000;">verticalHeader</span><span
style=" color:#000000;">()-></span><span style=" font-style:italic; color:#000000;">setVisible</span><span
style=" color:#000000;">(</span><span style=" color:#808000;">false</span><span
style=" color:#000000;">);</span></div>
如果修改默认的列宽,让每列显示出更长的文本,可以把默认值 100 修改为 160 像素:<br>
<div class="code"><span style=" color:#800000;"> ui</span><span style=" color:#000000;">-></span><span
style=" color:#800000;">tableWidget</span><span style=" color:#000000;">-></span><span
style=" color:#000000;">horizontalHeader</span><span style=" color:#000000;">()-></span><span
style=" color:#000000;">setDefaultSectionSize</span><span style=" color:#000000;">(</span><span
style=" color:#000080;">160</span><span style=" color:#000000;">);</span></div>
其他属性的意义比较明确,关于 cascadingSectionResizes 属性是指用户拖动表头分段的分隔线调整列宽或行高时,是否仅相邻分段调整:<br>
如果 cascadingSectionResizes 为
true,那么调整尺寸操作仅涉及分隔线相邻的两个分段,比如左边列变宽,那么右边一列就会变窄。如果 cascadingSectionResizes 为
false,就是默认的递推调整右侧所有列,左边一列变宽时,右边所有列列宽固定,并且右边所有列整体右移,表格整体拉大。<br>
<br>
除了 Qt 设计师里能看到的属性,QHeaderView 还有 defaultAlignment
属性决定表头文本的对齐方式,maximumSectionSize 属性决定分段的尺寸上限。<br>
<br>
表头的特性设置通常会影响表格控件整体的外观,之前介绍过表格控件基类 QTableView 调整列宽和隐藏/显示整列的函数,其实 QTableView
很多关于整行或整列调整的函数都是在内部调用表头 QHeaderView 的函数来实现,比如设置列宽的:<br>
<div class="code"><span style=" color:#808000;">void</span><span style=" color:#c0c0c0;">
</span><span style=" color:#800080;">QTableView</span><span style=" color:#000000;">::</span><span
style=" color:#000000;">setColumnWidth</span><span style=" color:#000000;">(</span><span
style=" color:#808000;">int</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000000;">column</span><span style=" color:#000000;">,</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">int</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">width</span><span
style=" color:#000000;">)</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:#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_D</span><span
style=" color:#000000;">(</span><span style=" color:#808000;">const</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#800080;">QTableView</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;">d</span><span style=" color:#000000;">-></span><span
style=" color:#800000;">horizontalHeader</span><span style=" color:#000000;">-></span><span
style=" color:#000000;">resizeSection</span><span style=" color:#000000;">(</span><span
style=" color:#000000;">column</span><span style=" color:#000000;">,</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">width</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>
</div>
QHeaderView::resizeSection(int logicalIndex, int size),就是调整分段尺寸,对于水平表头是调整列宽,
对于垂直表头是调整行高。<br>
QTableView 设置某列隐藏或显示的函数,内部代码为:<br>
<div class="code"><span style=" color:#808000;">void</span><span style=" color:#c0c0c0;">
</span><span style=" color:#800080;">QTableView</span><span style=" color:#000000;">::</span><span
style=" color:#000000;">setColumnHidden</span><span style=" color:#000000;">(</span><span
style=" color:#808000;">int</span><span style=" color:#c0c0c0;"> </span><span
style=" color:#000000;">column</span><span style=" color:#000000;">,</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#808000;">bool</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">hide</span><span
style=" color:#000000;">)</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:#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_D</span><span
style=" color:#000000;">(</span><span style=" color:#800080;">QTableView</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:#c0c0c0;"> </span><span style=" color:#000000;">(</span><span style=" color:#000000;">column</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:#c0c0c0;"> </span><span style=" color:#000000;">column</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">>=</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">d</span><span style=" color:#000000;">-></span><span
style=" color:#800000;">horizontalHeader</span><span style=" color:#000000;">-></span><span
style=" color:#000000;">count</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;">return</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;">d</span><span style=" color:#000000;">-></span><span
style=" color:#800000;">horizontalHeader</span><span style=" color:#000000;">-></span><span
style=" color:#000000;">setSectionHidden</span><span style=" color:#000000;">(</span><span
style=" color:#000000;">column</span><span style=" color:#000000;">,</span><span
style=" color:#c0c0c0;"> </span><span style=" color:#000000;">hide</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>
</div>
<span style="font-weight: bold;">QTableView 很多功能都是通过表头 QHeaderView
的函数实现的,如果表格控件需要设置特定的功能找不到直接的设置或获取函数,那么在 QHeaderView 类里面找函数是比较有效的方法。</span>比
如 QHeaderView 类可以查询隐藏的分段计数:<br>
<div class="code">int QHeaderView::hiddenSectionCount() const</div>
如果表格控件隐藏了一些列,水平表头的 hiddenSectionCount() 就是隐藏的列数目。<br>
默认情况下,表格控件所有的列显示顺序是固定的,如果用户希望可以自己调整列的顺序,那么可以设置表头的分段是可移动的:<br>
<div class="code">void QHeaderView::setSectionsMovable(bool movable)</div>
<br>
利用表头类的函数,我们可以通过调整水平表头和垂直表头的默认分段尺寸设置单元格默认尺寸:<br>
<div class="code"><span style=" color:#c0c0c0;"> </span><span
style=" color:#800000;">ui</span><span style=" color:#000000;">-></span><span
style=" color:#800000;">tableWidget</span><span style=" color:#000000;">-></span><span
style=" color:#000000;">horizontalHeader</span><span style=" color:#000000;">()-></span><span
style=" color:#000000;">setDefaultSectionSize</span><span style=" color:#000000;">(</span><span
style=" color:#000080;">150</span><span style=" color:#000000;">);</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:#800000;">ui</span><span
style=" color:#000000;">-></span><span style=" color:#800000;">tableWidget</span><span
style=" color:#000000;">-></span><span style=" color:#000000;">verticalHeader</span><span
style=" color:#000000;">()-></span><span style=" color:#000000;">setDefaultSectionSize</span><span
style=" color:#000000;">(</span><span style=" color:#000080;">50</span><span style=" color:#000000;">);</span></pre>
</div>
这样表格的单元格尺寸就会变大很多:<br>
<center> <img src="images/ch08/ch08-02-04.png" alt="cellsize"></center>
现在单元格的尺寸是可以按自己意愿调整的,但是如果希望调整上面水平表头的高度或调整左侧垂直表头的宽度,那怎么办呢?<br>
**SectionSize 只能指定表头分段延伸的一维尺寸,不会影响水平表头高度,也不影响垂直表头的宽度。 QHeaderView
表头类其实也是通用控件和窗口类 QWidget 的派生类,QWidget 设置控件或窗口最小尺寸的函数对 QHeaderView 也适用,表头
QHeaderView 相当于是表格控件内嵌的子控件,表头 QHeaderView 还可以有自己独立的 QSS 样式表。我们使用表头类的基类
QWidget 的函数可以设置水平表头的最小高度和垂直表头的最小宽度,这样可以调整表头自身尺寸:<br>
<div class="code"><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:#800000;">ui</span><span
style=" color:#000000;">-></span><span style=" color:#800000;">tableWidget</span><span
style=" color:#000000;">-></span><span style=" color:#000000;">horizontalHeader</span><span
style=" color:#000000;">()-></span><span style=" color:#000000;">setMinimumHeight</span><span
style=" color:#000000;">(</span><span style=" color:#000080;">50</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:#800000;">ui</span><span
style=" color:#000000;">-></span><span style=" color:#800000;">tableWidget</span><span
style=" color:#000000;">-></span><span style=" color:#000000;">verticalHeader</span><span
style=" color:#000000;">()-></span><span style=" color:#000000;">setMinimumWidth</span><span
style=" color:#000000;">(</span><span style=" color:#000080;">100</span><span style=" color:#000000;">);</span></pre>
</div>
设置效果如下:<br>
<center> <img src="images/ch08/ch08-02-05.png" alt="headersize"></center>
<br>
关于表头的内容大致介绍到这,QtCreator 设计模式和 Qt 设计师可以编辑表格控件的表头和单元格,后面再介绍。提前说一下,Qt 5.4.*
开发环境里的 QtCreator 设计模式和 Qt 设计师在添加表格控件表头时有 bug,需要第二次编辑并调整顺序,表头才能正常显示,因此更推荐用代码来
设置水平表头和垂直表头。<br>
<br>
<div class="os2">8.2.4 选中区域和选中行为</div>
<br>
(1)选中区域<br>
之前介绍过,获取选中的条目可以如下函数:<br>
<div class="code">QList<QTableWidgetItem *>
QTableWidget::selectedItems() const</div>
选中的单元格如果有实体条目,会记录在 selectedItems() 返回的条目列表里,但是如果选中了内部为 NULL
的单元格,那是无法存到选中条目列表的。为了能更完整地描述选中的单元格区域, Qt 专门提供了一个 QTableWidgetSelectionRange
类,记录表格控件的选中区 域,不管单元格有没有条目,都可以描述选中单元格范围。<br>
表格控件可以同时有多个选中区域,获取表格当前选中区域列表的函数如下:<br>
<div class="code">QList<QTableWidgetSelectionRange>
QTableWidget::selectedRanges() const</div>
一般用 Shift 键选中的连续矩形区域对应一个选中区域,而使用 Ctrl 键多次点击选中的多个离散单元格对应多个选中区域。<br>
QTableWidget 也能通过代码设置某个矩形区域是否为高亮选中:<br>
<div class="code">void QTableWidget::setRangeSelected(const
QTableWidgetSelectionRange & range, bool select)</div>
<br>
QTableWidgetSelectionRange 类的构造函数可以指定矩形区域,是以单元格的行列号标出矩形区域:<br>
<div class="code">QTableWidgetSelectionRange(int top, int left, int bottom,
int right)</div>
<div class="code">QTableWidgetSelectionRange(const
QTableWidgetSelectionRange & other) //复制构造函数</div>
参数里的 top 是矩形区域最上面的行号,bottom 是最下面的行号;left 是矩形选择区域最左边的列号,right 是矩形最右边的列号。如果
top 和 bottom 数值一样,同时 left 与 right 数值一样,那么矩形区域就退化为一个单元格了。<br>
QTableWidgetSelectionRange 的功能函数也比较简单,就是获取上下行号、左右列号、行列计数:<br>
<div class="code">int topRow() const //最上面的行号</div>
<div class="code">int bottomRow() const //最下面的行号</div>
<div class="code">int leftColumn() const //最左边的列号</div>
<div class="code">int rightColumn() const //最右边的列号</div>
<div class="code">int rowCount() const //区域内行计数</div>
<div class="code">int columnCount() const //区域内列计数</div>
QTableWidgetSelectionRange 比较简单,只能在构造函数设置矩形范围,没有其他修改矩形范围的函数。如果要修改矩形选中区域,可以直接
新创建一个矩形选中区域。<br>
<br>
QTableWidgetSelectionRange 是表格控件 QTableWidget
独有的东西,接下来介绍选中行为、选中模式和单次选中命令,后面三项内容对本章所有基于条目控件都适用的,在这里统一介绍。<br>
<br>
(2)选中行为<br>
表格控件默认情况下,点击一个单元格只会选中该单元格本身,如果表格的行内部数据关联性很紧密,比如是同一个人的各种身份信息(姓名、性别、住址等),这时候用户
可能希望点击一次选中整行的单元格,这就是选中行为决定的特性。表格控件从祖辈基类 QAbstractItemView 继承了
selectionBehavior 属性,其获取和设置函数如下:<br>
<div class="code">QAbstractItemView::SelectionBehavior
selectionBehavior() const</div>
<div class="code">void
setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)</div>
调用设置函数就可以改变选中行为,具体的选中行为枚举常量如下表所示:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td style="width: 360px;" align="center"><b>QAbstractItemView::SelectionBehavior
枚举常量</b></td>
<td style="width: 140px;" align="center"><b>数值</b></td>
<td align="center"><b> 描述</b></td>
</tr>
<tr>
<td>QAbstractItemView::SelectItems</td>
<td> 0 </td>
<td> 鼠标点击时只选中该条目,这个是默认值。 </td>
</tr>
<tr class="d1">
<td>QAbstractItemView::SelectRows</td>
<td> 1 </td>
<td> 鼠标点击时选中整行的条目。 </td>
</tr>
<tr>
<td>QAbstractItemView::SelectColumns </td>
<td> 2 </td>
<td> 鼠标点击时选中整列的条目。 </td>
</tr>
</tbody>
</table>
<br>
如果我们希望用户点击时自动选中整行,那么把选中行为设置为 QAbstractItemView::SelectRows 即可。<br>
对于 8.1 节列表控件和 8.3 节树形控件,它们也有选中行为属性,但是它们每行只有一个条目,所以一般用不到选中行为属性。<br>
<br>
(3)选中模式<br>
对于表格控件,默认就支持使用 Ctrl 和 Shift 键进行多选操作,而 8.1
节的列表控件默认是单选模式,决定到底是单选还是多选,就是选中模式决定的特性。表格控件、列表控件以及后文的属性控件都从祖辈基类
QAbstractItemView 继承了 selectionMode 属性,其获取和设置函数如下:<br>
<div class="code">QAbstractItemView::SelectionMode
selectionMode() const</div>
<div class="code">void
setSelectionMode(QAbstractItemView::SelectionMode mode)</div>
选中模式的枚举常量比较多,列举描述如下:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td style="width: 360px;" align="center"><b>QAbstractItemView::SelectionMode
枚举常量</b></td>
<td style="width: 140px;" align="center"><b>数值</b></td>
<td align="center"><b> 描述</b></td>
</tr>
<tr>
<td>QAbstractItemView::SingleSelection</td>
<td> 1 </td>
<td> 单选模式。用户点击选中新条目时,旧的选中条目取消选中状态。只用鼠标点击不能取消选中当前条目,鼠标点击总有一个条目是高亮选中的。</td>
</tr>
<tr class="d1">
<td>QAbstractItemView::ContiguousSelection</td>
<td> 4 </td>
<td> 连续多选模式。平常点击与单选模式特性一样;但如果用户按下 Shift 键同时点击条目,可以进行连续区域的选中或取消选中。以按下
Shift键之前的当前条目为基准条目,基准条目与Shift键按下后最后一次点击的条目之间的连续区域会被选中,其他条目取消选中。 </td>
</tr>
<tr>
<td>QAbstractItemView::ExtendedSelection </td>
<td> 3 </td>
<td> 扩展多选模式。平常点击与单选模式特性一样;但如果用户按下Shift键同时点击条目可以选中连续区域;如果用户按下Ctrl键可以选中或
取消选中离散条目的高亮选中状态。另外,鼠标左键按下后连续拖动也可以选中连续的区域。 </td>
</tr>
<tr class="d1">
<td>QAbstractItemView::MultiSelection</td>
<td> 2 </td>
<td> 普通多选模式。用户点击某个条目时,该条目的选中状态取反:如果之前是选中就变为非选中,如果之前是非选中就变为选中。鼠标左键按下后连续
拖动可以选中或取消选中连续区域。</td>
</tr>
<tr>
<td>QAbstractItemView::NoSelection </td>
<td> 0 </td>
<td> 无选模式。不能选中条目,这个很少用到。 </td>
</tr>
</tbody>
</table>
<br>
对于 8.1 列表控件和 8.3 树形控件,默认选中模式为单选模式 SingleSelection ,本节的表格控件默认是扩展多选模式
ExtendedSelection ,操作系统的文件资源管理器一般都是扩展多选模式。程序中常用的就是单选模式、扩展多选模式。<br>
<br>
(4)单次选中命令<br>
通常使用上面介绍的选中行为和选中模式就能满足绝大多数情况下的选中特性需求,但也有少数例外的情况,比如我们不希望修改控件默认的选中行为和选中模式,但又想使
用默认选中行为和选中模式规定之外的选中操作,那就涉及到单次选中命令这一特性了。对于列表控件、表格控件以及后面的树形控件,都有类似下面的函数:<br>
<div class="code">void setCurrentItem(QTableWidgetItem * item,
QItemSelectionModel::SelectionFlags command)</div>
setCurrentItem() 函数第一个参数是条目指针,第二个参数就是单次选中命令(可以是一个标志位或者多个选中标志位的或值),通过
setCurrentItem() 函数可以在控件默认的选中行为和选中模式之外进行一些特定的选中或取消选中的操作。<br>
<br>
在解释单次选中命令之前,我们先明确一下“当前条目”和“选中条目”的区别:<br>
①
当前条目通常是指鼠标最后一次点击的条目,这个条目是唯一的,在图形上用虚线框标记,当前条目可以处于非选中状态,也可以处于选中状态,如下面两个图所示(假定下面两个图
的所有单元格都有条目,非 NULL):<br>
<center> <img src="images/ch08/ch08-02-06.png" alt="curItem1"> </center>
<center> <img src="images/ch08/ch08-02-07.png" alt="curItem2"> </center>
当前条目通常情况下都是用户鼠标最后一次点击的条目,但 setCurrentItem()
函数也可以把当前条目设置为参数里指定的非空条目。获取当前条目的函数是 currentItem() 。<br>
<br>
②选中条目就是从图形界面上看到的有高亮背景的条目,选中条目可以没有,可以有一个,也可以有多个。对于没有选中、选中一个的情况,看上面两个图即可。<br>
高亮选中的多个条目可以与当前条目有重叠,也可以不重叠,如下面两个图示范(假定下面两个图的所有单元格都有条目,非 NULL):<br>
<center> <img src="images/ch08/ch08-02-08.png" alt="selected1"> </center>
<center> <img src="images/ch08/ch08-02-09.png" alt="selected2"> </center>
获取高亮选中的条目函数为 selectedItems(),设置某个条目选中或非选中,可以直接用条目自身的函数 item->setSelected(
bool ) 。<br>
<br>
归纳一下就是:<span style="font-weight: bold;">图形界面上虚线框包裹的唯一条目,是当前条目;拥有高亮选中背景的是选中条
目,选中条目可以没有,也可以有一个或多个。当前条目与选中条目可以有重叠,也可以无重叠。</span><br>
<br>
解释完当前条目和选中条目,下面介绍具体的选中标志位。一个选中标志位或者多个选中标志位的或值 构成单次选中命令,选中标志位的枚举常量如下表描述:<br>
<br>
<table class="tabel">
<tbody>
<tr class="d1">
<td style="width: 360px;" align="center"><b>QItemSelectionModel::
SelectionFlag 枚举常量</b></td>
<td style="width: 140px;" align="center"><b>数值</b></td>
<td align="center"><b> 描述</b></td>
</tr>
<tr>
<td>QItemSelectionModel::NoUpdate</td>
<td> 0x0000</td>
<td> 保持旧的选中状态,不更新选中条目。 </td>
</tr>
<tr class="d1">
<td>QItemSelectionModel::Clear</td>
<td> 0x0001 </td>
<td> 清除所有选中状态,没有选中条目了。 </td>
</tr>
<tr>
<td>QItemSelectionModel::Select </td>
<td> 0x0002 </td>
<td style="height: 16px;"> 选中指定的条目。 </td>
</tr>
<tr class="d1">
<td>QItemSelectionModel::Deselect</td>
<td> 0x0004 </td>
<td style="height: 16px;"> 指定的条目取消选中状态。 </td>
</tr>
<tr>
<td>QItemSelectionModel::Toggle </td>
<td> 0x0008 </td>
<td> 指定条目的选中状态取反,原先选中就不选中,原先不选中就选中。 </td>
</tr>
<tr class="d1">
<td>QItemSelectionModel::Current</td>
<td> 0x0010 </td>
<td> 将指定的条目设为当前条目。 </td>
</tr>
<tr>
<td>QItemSelectionModel::Rows </td>
<td> 0x0020 </td>
<td> 对指定条目所在的整行进行选中或非选中。 </td>
</tr>
<tr class="d1">
<td>QItemSelectionModel::Columns</td>
<td> 0x0040 </td>
<td> 对指定条目所在的整列进行选中或非选中。 </td>
</tr>
<tr>
<td>QItemSelectionModel::SelectCurrent </td>
<td> Select | Current </td>
<td> 选中指定条目,并且将其设置为当前条目。 </td>
</tr>
<tr class="d1">
<td>QItemSelectionModel::ToggleCurrent</td>
<td> Toggle | Current </td>
<td> 指定条目的选中状态取反,并将其设置为当前条目。 </td>
</tr>
<tr>
<td>QItemSelectionModel::ClearAndSelect </td>
<td> Clear | Select </td>
<td> 先清除旧的选中状态,然后设置指定条目为选中状态。 </td>
</tr>
</tbody>
</table>
<br>
选中标志位的按位或值,一般要考虑实际意义会不会冲突,比如 Select、Deselect、Toggle
三者是冲突的,不应该同时用。意义不冲突的标志位就可以进行位或,比如 Select | Rows 就是整行选中。<br>
在不修改选中行为和选中模式的时候,下面一句代码就会选中指定条目所在的整行:<br>
<div class="code">ui->tableWidget->setCurrentItem(pItem ,
QItemSelectionModel::Select | QItemSelectionModel::Rows);</div>