作者:奇先生

8.2.3 表头设置

表格控件在指定了行号和列号之后,就会自动生成水平表头和垂直表头,默认的表头都是从数字 1 开始递增编号,如下图所示:

python QTableWidget读取表头 qtablewidget获取表头字符串_单选

虽然默认的表头文本看起来从 1 开始编号,但是内部的表头分段(section)都是从 0 开始编号的,代码里设置表头分段时,需要从 0 编号。
表格控件的既可以简单地设置表头分段文本,也可以利用 QTableWidgetItem 为表头分段设置功能更丰富的表头条目,设置表头文本的函数如下:

void QTableWidget::setHorizontalHeaderLabels(const QStringList & labels) //设置水平表头文本

void QTableWidget::setVerticalHeaderLabels(const QStringList & labels) //设置垂直表头文本

QStringList 内字符串列表,第 0 个字符串就是第 0 号表头分段的文本,一般要求 QStringList 字符串个数与表头分段数目一样,这样不会有漏的,也不会有多的。通常比较多的是设置水平表头(就是每列的列首),垂直表头一般可以不设置,用自动编号。

setHorizontalHeaderLabels() 和 setVerticalHeaderLabels()  其实是两个快捷函数,本质是循环构建表头分段条目并设置给表头分段:

void QTableWidget::setHorizontalHeaderItem(int column, QTableWidgetItem * item) //设置指定列的水平表头分段条目

QTableWidgetItem * QTableWidget::horizontalHeaderItem(int column) const //获取指定列的水平表头分段条目

void QTableWidget::setVerticalHeaderItem(int row, QTableWidgetItem * item) //设置指定行的垂直表头分段条目

QTableWidgetItem * QTableWidget::verticalHeaderItem(int row) const //获取指定行的垂直表头分段条目

这里可以发现 QTableWidgetItem 既可以作为表格控件普通单元格的条目,也可以用来设置表头分段。
QTableWidgetItem 作为表头分段条目使用时,肯定是只读的,程序运行时不能双击编辑表头,而且也不能有复选框。表头分段条目支持显示文本、图标、工具提示信息,并能设置文本对齐、字体、前景 画刷和背景画刷等等,这样表头显示的功能就比较丰富,而不是单纯的文本。
使用 setHorizontalHeaderItem() 和 setVerticalHeaderItem() 函数时,一个 QTableWidgetItem 只能设置给唯一的一个表头分段,并且设置给表头分段之后,也不要把表头分段条目设置给表格控件普通的单元格。
表头分段的条目也可以从表格控件卸载卸载下来:

QTableWidgetItem * QTableWidget::takeHorizontalHeaderItem(int column) //卸下指定列的水平表头条目

QTableWidgetItem * QTableWidget::takeVerticalHeaderItem(int row) //卸下指定行的垂直表头条目

卸载下来的条目并不会自动删除,还存在内存中,如果希望彻底删除需要手动 delete,或者不删除的话,卸载下来的条目可以重新设置给另一个表头分段。
表头分段的条目与普通的单元格条目有重要的区别,普通单元格条目可编辑、可以有复选状态,但是表头分段的条目只有显示功能,不能双击编辑或复选。把 QTableWidgetItem 设置给表头分段后,QTableWidgetItem 条目会有额外的内部特性标志:

if (item) {

        item->view = view;
        item->itemFlags = Qt::ItemFlags(int(item->itemFlags)|ItemIsHeaderItem);
    }

上面小段代码是从 QTableWidget 源代码里截取的,设置表头分段条目时,item 的标志位会额外设置 ItemIsHeaderItem 标志位ItemIsHeaderItem 是内部专用标志位,并不公开),因此设置给表头的条目是不能再设置给普通单元格的。条目 item 设置给一个表头分段后,也不能设置给其他的表头分段,因为表格控件销毁时会 delete 每个表头条目,一个条目指针如果设置给多个表头分段,那么同一个条目指针最后会被 delete 多次,删除野指针会导致程序崩溃。

表格控件并不要求必须设置表头文本或表头条目,在不设置表头文本和表头条目的情况下,默认是所有的表头分段条目是 NULL ,所以要注意表头条目获取和卸载函数返回的指针可能是 NULL。不设置任何表头文本和表头条目时,表格控件依然是有表头的,默认的表头是按照前面截图的从 1 开始自动编号,自动编号表头是没有实体表头条目的,这些自动编号表头是从基类 QTableView 视图类继承而来的。

QTableWidget 本层类带有前面介绍 8 个表头分段操作的函数,各个表头分段设置从微观角度展示表头的功能,而基类 QTableView 有从宏观描述的整体表头,即:

QHeaderView * QTableView::horizontalHeader() const //获取整个的水平表头

void QTableView::setHorizontalHeader(QHeaderView * header) //设置整个的水平表头

QHeaderView * QTableView::verticalHeader() const //获取整个的垂直表头

void QTableView::setVerticalHeader(QHeaderView * header) //设置整个的垂直表头

因为表格控件自己就会带有水平表头和垂直表头,所以一般不需要调用 setHorizontalHeader() 和setVerticalHeader() 函数,直接获取原本的表头对象指针,然后调用表头类 QHeaderView 的函数修改特性即可。
水平表头和垂直表头都是同一个类 QHeaderView ,水平表头和垂直表头可以通过如下函数判断:

Qt::Orientation QHeaderView::orientation() const

返回值如果是 Qt::Horizontal 表示水平表头,Qt::Vertical 是垂直表头,这个方向特性是在构造函数时指定的,之后不能进行修改。
QHeaderView 功能是非常丰富的,这里介绍一些比较实用的,以后到模型视图章节还会介绍更多的。
获取当前表头的分段数目,使用函数:

int QHeaderView::count() const

显示整个表头显示的像素点长度(水平表头是表头整体宽度,垂直表头是表头整体高度):

int QHeaderView::length() const

在 Qt 设计师界面,选中表格控件,可以看到水平表头和垂直表头的属性,这里以水平表头的属性举例:

python QTableWidget读取表头 qtablewidget获取表头字符串_单选_02

勾选状态,这样程序运行时根据一个列自动排序时,该列水平表头分段显示三角形排序指示符。如果勾选水平表头的 horizontalHeaderStretchLastSection 属性,那么表格最后一列会自动拉伸占据剩下的空间。水平表头和垂直表头这些属性都有对应的 QHeaderView 类的函数,罗列描述如下:

属性

QHeaderView类设置 函数

QHeaderView类获取 函数

描述

**visible

setVisible(bool v)

isVisible()

是否显示表头。

**cascadingSectionResizes

setCascadingSectionResizes(bool enable)

cascadingSectionResizes()

调整分段尺寸时是否仅相邻分段调整。

**defaultSectionSize

setDefaultSectionSize(int size)

defaultSectionSize()

分段默认尺寸,水平表头是指分段宽度(默认100),垂直表头指分段高度(默认30)。

**highlightSections

setHighlightSections(bool highlight)

highlightSections()

条目高亮选中时,其行首、列首是否也有高亮效果。一般不需要手动设置。

**minimumSectionSize

setMinimumSectionSize(int size)

minimumSectionSize()

分段尺寸下限。水平表头分段默认最小宽度 21,垂直表头分段默认最小高度 21。

**showSortIndicator

setSortIndicatorShown(bool show)

isSortIndicatorShown()

表头是否显示排序指示符。开启自动排序后通常自动显示排序指示符,一般不需要手动设置。

**stretchLastSection

setStretchLastSection(bool stretch)

stretchLastSection()

是否自动拉伸末尾分段。

按照上表描述,如果希望隐藏垂直表头,那么就可以执行如下代码:

ui->tableWidget->verticalHeader()->setVisible(false);

如果修改默认的列宽,让每列显示出更长的文本,可以把默认值 100 修改为 160 像素:

ui->tableWidget->horizontalHeader()->setDefaultSectionSize(160);

其他属性的意义比较明确,关于 cascadingSectionResizes 属性是指用户拖动表头分段的分隔线调整列宽或行高时,是否仅相邻分段调整:
如果 cascadingSectionResizes 为 true,那么调整尺寸操作仅涉及分隔线相邻的两个分段,比如左边列变宽,那么右边一列就会变窄。如果 cascadingSectionResizes 为 false,就是默认的递推调整右侧所有列,左边一列变宽时,右边所有列列宽固定,并且右边所有列整体右移,表格整体拉大。
除了 Qt 设计师里能看到的属性,QHeaderView 还有 defaultAlignment 属性决定表头文本的对齐方式,maximumSectionSize 属性决定分段的尺寸上限。

表头的特性设置通常会影响表格控件整体的外观,之前介绍过表格控件基类 QTableView 调整列宽和隐藏/显示整列的函数,其实 QTableView 很多关于整行或整列调整的函数都是在内部调用表头 QHeaderView 的函数来实现,比如设置列宽的:

void QTableView::setColumnWidth(int column, int width)

{
    Q_D(const QTableView);
    d->horizontalHeader->resizeSection(column, width);
}

QHeaderView::resizeSection(int logicalIndex, int size),就是调整分段尺寸,对于水平表头是调整列宽, 对于垂直表头是调整行高。
QTableView 设置某列隐藏或显示的函数,内部代码为:

void QTableView::setColumnHidden(int column, bool hide)

{
    Q_D(QTableView);
    if (column < 0 || column >= d->horizontalHeader->count())
        return;
    d->horizontalHeader->setSectionHidden(column, hide);
}

QTableView 很多功能都是通过表头 QHeaderView 的函数实现的,如果表格控件需要设置特定的功能找不到直接的设置或获取函数,那么在 QHeaderView 类里面找函数是比较有效的方法。比 如 QHeaderView 类可以查询隐藏的分段计数:

int QHeaderView::hiddenSectionCount() const

如果表格控件隐藏了一些列,水平表头的 hiddenSectionCount() 就是隐藏的列数目。
默认情况下,表格控件所有的列显示顺序是固定的,如果用户希望可以自己调整列的顺序,那么可以设置表头的分段是可移动的:

void QHeaderView::setSectionsMovable(bool movable)

利用表头类的函数,我们可以通过调整水平表头和垂直表头的默认分段尺寸设置单元格默认尺寸:   

ui->tableWidget->horizontalHeader()->setDefaultSectionSize(150);

 ui->tableWidget->verticalHeader()->setDefaultSectionSize(50);

这样表格的单元格尺寸就会变大很多:

python QTableWidget读取表头 qtablewidget获取表头字符串_表格控件_03

现在单元格的尺寸是可以按自己意愿调整的,但是如果希望调整上面水平表头的高度或调整左侧垂直表头的宽度,那怎么办呢?
SectionSize 只能指定表头分段延伸的一维尺寸,不会影响水平表头高度,也不影响垂直表头的宽度。 QHeaderView 表头类其实也是通用控件和窗口类 QWidget 的派生类,QWidget 设置控件或窗口最小尺寸的函数对 QHeaderView 也适用,表头 QHeaderView 相当于是表格控件内嵌的子控件,表头 QHeaderView 还可以有自己独立的 QSS 样式表。我们使用表头类的基类 QWidget 的函数可以设置水平表头的最小高度和垂直表头的最小宽度,这样可以调整表头自身尺寸:  

//水平表头高度设置

    ui->tableWidget->horizontalHeader()->setMinimumHeight(50);
    //垂直表头宽度设置
    ui->tableWidget->verticalHeader()->setMinimumWidth(100);

设置效果如下:

python QTableWidget读取表头 qtablewidget获取表头字符串_表格控件_04

关于表头的内容大致介绍到这,QtCreator 设计模式和 Qt 设计师可以编辑表格控件的表头和单元格,后面再介绍。提前说一下,Qt 5.4.* 开发环境里的 QtCreator 设计模式和 Qt 设计师在添加表格控件表头时有 bug,需要第二次编辑并调整顺序,表头才能正常显示,因此更推荐用代码来设置水平表头和垂直表头。
 

8.2.4 选中区域和选中行为

(1)选中区域

之前介绍过,获取选中的条目可以如下函数:

QList<QTableWidgetItem *> QTableWidget::selectedItems() const

选中的单元格如果有实体条目,会记录在 selectedItems() 返回的条目列表里,但是如果选中了内部为 NULL 的单元格,那是无法存到选中条目列表的。为了能更完整地描述选中的单元格区域, Qt 专门提供了一个 QTableWidgetSelectionRange 类,记录表格控件的选中区域,不管单元格有没有条目,都可以描述选中单元格范围。
表格控件可以同时有多个选中区域,获取表格当前选中区域列表的函数如下:

QList<QTableWidgetSelectionRange> QTableWidget::selectedRanges() const

一般用 Shift 键选中的连续矩形区域对应一个选中区域,而使用 Ctrl 键多次点击选中的多个离散单元格对应多个选中区域
QTableWidget 也能通过代码设置某个矩形区域是否为高亮选中:

void QTableWidget::setRangeSelected(const QTableWidgetSelectionRange & range, bool select)

QTableWidgetSelectionRange 类的构造函数可以指定矩形区域,是以单元格的行列号标出矩形区域:

QTableWidgetSelectionRange(int top, int left, int bottom, int right)

QTableWidgetSelectionRange(const QTableWidgetSelectionRange & other) //复制构造函数

参数里的 top 是矩形区域最上面的行号,bottom 是最下面的行号;left 是矩形选择区域最左边的列号,right 是矩形最右边的列号。如果 top 和 bottom 数值一样,同时 left 与 right 数值一样,那么矩形区域就退化为一个单元格了。
QTableWidgetSelectionRange 的功能函数也比较简单,就是获取上下行号、左右列号、行列计数:

int    topRow() const //最上面的行号

int    bottomRow() const //最下面的行号

int    leftColumn() const //最左边的列号

int    rightColumn() const //最右边的列号

int    rowCount() const //区域内行计数

int    columnCount() const //区域内列计数

QTableWidgetSelectionRange 比较简单,只能在构造函数设置矩形范围,没有其他修改矩形范围的函数。如果要修改矩形选中区域,可以直接 新创建一个矩形选中区域

QTableWidgetSelectionRange 是表格控件 QTableWidget 独有的东西,接下来介绍选中行为、选中模式和单次选中命令,后面三项内容对本章所有基于条目控件都适用的,在这里统一介绍。

(2)选中行为

表格控件默认情况下,点击一个单元格只会选中该单元格本身,如果表格的行内部数据关联性很紧密,比如是同一个人的各种身份信息(姓名、性别、住址等),这时候用户可能希望点击一次选中整行的单元格,这就是选中行为决定的特性。表格控件从祖辈基类 QAbstractItemView 继承了 selectionBehavior 属性,其获取和设置函数如下:

QAbstractItemView::SelectionBehavior    selectionBehavior() const

void    setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)

调用设置函数就可以改变选中行为,具体的选中行为枚举常量如下表所示:

QAbstractItemView::SelectionBehavior 枚举常量

数值

描述

QAbstractItemView::SelectItems

0

鼠标点击时只选中该条目,这个是默认值。

QAbstractItemView::SelectRows

1

鼠标点击时选中整行的条目。

QAbstractItemView::SelectColumns

2

鼠标点击时选中整列的条

如果我们希望用户点击时自动选中整行,那么把选中行为设置为 QAbstractItemView::SelectRows 即可。
对于 8.1 节列表控件和 8.3 节树形控件,它们也有选中行为属性,但是它们每行只有一个条目,所以一般用不到选中行为属性。

(3)选中模式

对于表格控件,默认就支持使用 Ctrl 和 Shift 键进行多选操作,而 8.1 节的列表控件默认是单选模式,决定到底是单选还是多选,就是选中模式决定的特性。表格控件、列表控件以及后文的属性控件都从祖辈基类 QAbstractItemView 继承了 selectionMode 属性,其获取和设置函数如下:

QAbstractItemView::SelectionMode    selectionMode() const

void    setSelectionMode(QAbstractItemView::SelectionMode mode)

选中模式的枚举常量比较多,列举描述如下:

QAbstractItemView::SelectionMode 枚举常量

数值

描述

QAbstractItemView::SingleSelection

1

单选模式。用户点击选中新条目时,旧的选中条目取消选中状态。只用鼠标点击不能取消选中当前条目,鼠标点击总有一个条目是高亮选中的。

QAbstractItemView::ContiguousSelection

4

连续多选模式。平常点击与单选模式特性一样;但如果用户按下 Shift 键同时点击条目,可以进行连续区域的选中或取消选中。以按下 Shift键之前的当前条目为基准条目,基准条目与Shift键按下后最后一次点击的条目之间的连续区域会被选中,其他条目取消选中。

QAbstractItemView::ExtendedSelection

3

扩展多选模式。平常点击与单选模式特性一样;但如果用户按下Shift键同时点击条目可以选中连续区域;如果用户按下Ctrl键可以选中或 取消选中离散条目的高亮选中状态。另外,鼠标左键按下后连续拖动也可以选中连续的区域。

QAbstractItemView::MultiSelection

2

普通多选模式。用户点击某个条目时,该条目的选中状态取反:如果之前是选中就变为非选中,如果之前是非选中就变为选中。鼠标左键按下后连续 拖动可以选中或取消选中连续区域。

QAbstractItemView::NoSelection

0

无选模式。不能选中条目,这个很少用到。

对于 8.1 列表控件和 8.3 树形控件,默认选中模式为单选模式 SingleSelection ,本节的表格控件默认是扩展多选模式 ExtendedSelection ,操作系统的文件资源管理器一般都是扩展多选模式。程序中常用的就是单选模式、扩展多选模式。

(4)单次选中命令

通常使用上面介绍的选中行为和选中模式就能满足绝大多数情况下的选中特性需求,但也有少数例外的情况,比如我们不希望修改控件默认的选中行为和选中模式,但又想使用默认选中行为和选中模式规定之外的选中操作,那就涉及到单次选中命令这一特性了。对于列表控件、表格控件以及后面的树形控件,都有类似下面的函数:

void setCurrentItem(QTableWidgetItem * item, QItemSelectionModel::SelectionFlags command)

setCurrentItem() 函数第一个参数是条目指针,第二个参数就是单次选中命令(可以是一个标志位或者多个选中标志位的或值),通过 setCurrentItem() 函数可以在控件默认的选中行为和选中模式之外进行一些特定的选中或取消选中的操作。

在解释单次选中命令之前,我们先明确一下“当前条目”和“选中条目”的区别:
① 当前条目通常是指鼠标最后一次点击的条目,这个条目是唯一的,在图形上用虚线框标记,当前条目可以处于非选中状态,也可以处于选中状态,如下面两个图所示(假定下面两个图的所有单元格都有条目,非 NULL):

python QTableWidget读取表头 qtablewidget获取表头字符串_Qt_05

           

python QTableWidget读取表头 qtablewidget获取表头字符串_Qt_06

当前条目通常情况下都是用户鼠标最后一次点击的条目,但 setCurrentItem() 函数也可以把当前条目设置为参数里指定的非空条目。获取当前条目的函数是 currentItem() 。

②选中条目就是从图形界面上看到的有高亮背景的条目,选中条目可以没有,可以有一个,也可以有多个。对于没有选中、选中一个的情况,看上面两个图即可。
高亮选中的多个条目可以与当前条目有重叠,也可以不重叠,如下面两个图示范(假定下面两个图的所有单元格都有条目,非 NULL):

python QTableWidget读取表头 qtablewidget获取表头字符串_单选_07

             

python QTableWidget读取表头 qtablewidget获取表头字符串_Qt_08

获取高亮选中的条目函数为 selectedItems(),设置某个条目选中或非选中,可以直接用条目自身的函数 item->setSelected( bool ) 。
归纳一下就是:图形界面上虚线框包裹的唯一条目,是当前条目;拥有高亮选中背景的是选中条 目,选中条目可以没有,也可以有一个或多个。当前条目与选中条目可以有重叠,也可以无重叠。

解释完当前条目和选中条目,下面介绍具体的选中标志位。一个选中标志位或者多个选中标志位的或值 构成单次选中命令,选中标志位的枚举常量如下表描述:

QItemSelectionModel::SelectionFlag 枚举常量

数值

描述

QItemSelectionModel::NoUpdate

0x0000

保持旧的选中状态,不更新选中条目。

QItemSelectionModel::Clear

0x0001

清除所有选中状态,没有选中条目了。

QItemSelectionModel::Select

0x0002

选中指定的条目。

QItemSelectionModel::Deselect

0x0004

指定的条目取消选中状态。

QItemSelectionModel::Toggle

0x0008

指定条目的选中状态取反,原先选中就不选中,原先不选中就选中。

QItemSelectionModel::Current

0x0010

将指定的条目设为当前条目。

QItemSelectionModel::Rows

0x0020

对指定条目所在的整行进行选中或非选中。

QItemSelectionModel::Columns

0x0040

对指定条目所在的整列进行选中或非选中。

QItemSelectionModel::SelectCurrent

Select | Current

选中指定条目,并且将其设置为当前条目。

QItemSelectionModel::ToggleCurrent

Toggle | Current

指定条目的选中状态取反,并将其设置为当前条目。

QItemSelectionModel::ClearAndSelect

Clear | Select

先清除旧的选中状态,然后设置指定条目为选中状态。

选中标志位的按位或值,一般要考虑实际意义会不会冲突,比如 Select、Deselect、Toggle 三者是冲突的,不应该同时用。意义不冲突的标志位就可以进行位或,比如 Select | Rows 就是整行选中。
在不修改选中行为和选中模式的时候,下面一句代码就会选中指定条目所在的整行:

ui->tableWidget->setCurrentItem(pItem , QItemSelectionModel::Select | QItemSelectionModel::Rows);

setCurrentItem() 会自动把当前条目设置为参数里指定的非空条目。
如果需要清空所有旧的选中状态,可以用下面这句:

ui->tableWidget->setCurrentItem(NULL, QItemSelectionModel::Clear);

setCurrentItem() 参数里指定的条目是 NULL 时,控件自动把当前条目设置为打头的第 0 个条目,对应到表格就是第 0 行 0 列的单元格。
本小节的选中行为、选中模式、单次选中命令对列表控件、表格控件和树形控件都是通用的,而且以后讲到模型和视图章节时,这三小块内容对列表视图、表格视图、树形视 图也是通用的。