本文转自:《Qt编程指南》 作者:奇先生
Qt编程指南,Qt新手教程,Qt Programming Guide
8.2.1 QTableWidget
在 Qt 设计师可以拖动表格控件到界面窗口,下图可以直观看到表格控件的大致组成:
表格控件最上面一排是只读的水平表头,最左边一列是只读的垂直表头。表头又可以细分为多个分段(section),水平表头的分段就是表格各个列的列首,垂直表头 分段就是表格各个行的行首。表格控件的实体区域是按行、列排布的单元格,单元格内容一般用 QTableWidgetItem 条目填充,单元格如果不填充任何东西,那么默认是 NULL。本小节先介绍表格控件基本的内容,并且讲解基类中比较实用的内容;后面 8.2.2 小节介绍单元格条目 QTableWidgetItem;控件表头的内容也比较丰富,单独放在 8.2.3 小节介绍;然后详细介绍关于表格控件的选中区域、选中行为、选中模式、单次选中命令等内容,选中区域是表格控件独有的特性,而选中行为、选中模式、单次选中命令是本章所有 基于条目的控件都具有的特性,放在 8.2.4 节一块讲解了。
先来看看表格控件的构造函数:
QTableWidget(QWidget * parent = 0)
QTableWidget(int rows, int columns, QWidget * parent = 0)
构造函数里最重要的参数是行数 rows 和列数 columns,表格控件必须指定行数、列数,才能进行后续操作。第一个构造函数没指定行数、列数,必须用下面 两个函数设置行数和列数:
void setRowCount(int rows) //设置行数
void setColumnCount(int columns) //设置列数
行数、列数对应的获取函数为:
int rowCount() const //获取行数
int columnCount() const //获取列数
设置好行数、列数之后就可以进行后续的操作。表格控件在设置行数、列数之后,就有排布好的单元格,默认这些单元格是空的,即 NULL。程序员可以通过 setItem() 函数可以设置各个单元格条目;程序运行时,表格控件默认所有单元格都可以双击编辑,用户的编辑操作会使表格控件自动生成条目存储用户输入的内容。setItem() 函数和用户编辑都可以填充单元格。
这里提前说明一下,表格控件的有很多名字相似的两套函数或信号,比如设置当前高亮单元格函数 setCurrentCell(int row, int column) 和设置当前高亮条目 setCurrentItem(QTableWidgetItem * item),这两个函数名字很类似,二者最主要的区别就是:名字带 Cell 的函数是基于单元格的,对 NULL 空单元格也可以使用;而名字带 Item 的函数,必须是填充了实体条目的单元格,针对实际存在的单元格条目操作。下面大致按函数功能介绍表格控件的相关内容。
(1)设置条目
之前 8.1 节 QListWidget 有很多添加条目或设置条目的函数,但是本节表格控件因为设置单元格必须同时知道行号、列号,所以没有 Add** 和 Insert** 之类的添加函数,表格只有如下设置条目函数:
void QTableWidget::setItem(int row, int column, QTableWidgetItem * item)
一般用 new 新建条目,然后设置到指定行号、列号的单元格里面,比如下面一段示范代码:
QTableWidgetItem *newItem = new QTableWidgetItem(tr("newitem");
tableWidget->setItem(row, column, newItem);
从代码而言,就只有 setItem() 函数设置单元格条目;当然,程序运行时用户可以双击编辑单元格,表格控件可以自动新建条目保存用户编辑的内容存到单元格里面。单元格条目不仅支持文本,还能设置图标、丰富 字体、字体颜色、工具提示等信息,后面还会介绍 QTableWidgetItem 类。
关于 setItem() 函数需要注意的问题是:一个条目只能设置给唯一的一个单元格,不能重复设置给多个单元格!如果希望把条目换一个单元格放置,必须用下面移除条目 函数把条目从表格控件卸载下来,再重新设置给新的单元格。
(2)移除条目
QTableWidgetItem * QTableWidget::takeItem(int row, int column)
takeItem() 函数会把指定行号、列号的条目从表格控件卸载下来,但不会完全删除。返回的指针如果非空,那么该指针会指向内存中的条目,如果希望把条目 彻底删除,还需要自己手动 delete 删掉内存空间。如果不彻底删除,那么还可以把非空指针重新设置给表格控件。
表格控件还有关于整行删除、整列删除、清空表格的几个槽函数,等会再介绍。
(3)条目访问函数
表格控件可以根据行号、列号获取条目对象指针:
QTableWidgetItem * QTableWidget::item(int row, int column) const
注意判断返回的指针是否为空,非空指针才能进行后续操作。
另外还能根据表格控件内部的坐标点位来获取位于该坐标位置的条目:
QTableWidgetItem * QTableWidget::itemAt(const QPoint & point) const
QTableWidgetItem * QTableWidget::itemAt(int ax, int ay) const
这两个 itemAt() 函数等价的,坐标是指表格控件内部的相对坐标,表格控件内部左上角是 (0,0) 原点。itemAt() 函数也是可能返回空指针的,一定要判断返回值是否为 NULL。
如果知道了条目 item 非空指针,可以用条目的函数获取行号、列号,如 item->row() 是行号,item->column() 是列号。根据条目的非空指针,也可以获取条目的可视矩形(条目在表格可见范围内的显示矩形):
QRect QTableWidget::visualItemRect(const QTableWidgetItem * item) const
(4)当前选中条目的操作
表格控件一般是多行多列的,实际的选中操作是比较复杂的,我们在这里先介绍最简单的内容,就是不按键盘,只用鼠标左边点击选中当前条目的情况,就是简单的单选操 作。
获取当前选中条目的函数为:
QTableWidgetItem * QTableWidget::currentItem() const
返回的值如果非空那就是实际存在的条目,如果返回了 NULL,说明单元格是空的。
不管选中的单元格内部是否为空,都可以获取当前选中单元格的行号、列号:
int QTableWidget::currentRow() const //当前选中行号
int QTableWidget::currentColumn() const //当前选中列号
除了鼠标点击选中当前高亮的单元格,也可以用函数设置当前单元格:
void QTableWidget::setCurrentCell(int row, int column)
void QTableWidget::setCurrentCell(int row, int column, QItemSelectionModel::SelectionFlags command)
单元格不管是不是空的,上面两个函数都可以设置当前高亮选中状态,第二个 setCurrentCell() 多了个单次选中命令的参数,等到 8.4.4 节再 详细说。
如果知道了非空条目指针,可以设置该条目为当前选中条目:
void QTableWidget::setCurrentItem(QTableWidgetItem * item)
void QTableWidget::setCurrentItem(QTableWidgetItem * item, QItemSelectionModel::SelectionFlags command)
第二个函数的 command 参数等到 8.4.4 节再 详细说。
无论是通过代码改变当前选中条目还是用户点击操作改变当前选中条目,都会触发当前单元格变化的信号:
void currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
//单元格无论是否为空都起作用,当前选中单元格的变化信号,参数指出了新当前单元格行列号和旧单元格的行列号
void currentItemChanged(QTableWidgetItem * current, QTableWidgetItem * previous)
//当前选中条目变化信号,新、旧当前选中单元格至少有一个非空才会触发这个信号
(5)条目查找和排序
表格控件也可以根据模版字符串查找匹配的条目:
QList<QTableWidgetItem *> QTableWidget::findItems(const QString & text, Qt::MatchFlags flags) const
参数里的 text 是模板字符串,第二个 flags 是匹配标志,匹配标志 Qt::MatchFlags 在 8.1.1 节列举过了,一模一样,比如 Qt::MatchContains 是包含子串,Qt::MatchWildcard 是通配符(*、?)匹配,Qt::MatchRegExp 是正则表达式匹配,等等。
表格控件的排序操作复杂一些,因为有很多列,开启自动排序后需要指定按照哪一列来排序。开启自动排序函数为:
void setSortingEnabled(bool enable) //设置是否自动排序
bool isSortingEnabled() const //判断是否开启了自动排序
开启自动排序之后,需要指定按照哪一列来排序:
void QTableView::sortByColumn(int column, Qt::SortOrder order)
排序的函数是从基类 QTableView 继承的,指定自动排序的列 column 和 order (升序 Qt::AscendingOrder 或降序 Qt::DescendingOrder)。
除了上面自动排序的函数,还有一个手动排序函数,可以在不开启自动排序时手动调整排序:
void QTableWidget::sortItems(int column, Qt::SortOrder order = Qt::AscendingOrder)
开启自动排序之后,会对 setItem(int row, int column, QTableWidgetItem * item) 函数产生重要影响:新设置的条目如果正好在指定的自动排序列,那么新条目会自动排序,被挪到按序排列好的行号,setItem() 函数指定的行号不一定有效。
如果用代码设置某一行的条目,该行的条目是联系在一起的,比如是某人的个人信息,那么就需要特别注意自动排序的问题:比如预期一行条目的行号是 therow,自动排序列为 sortcol,对于前几个列的条目,使用 therow 设置条目给表格,该行位置不变;当设置到 therow 行、sortcol 列的条目时,问题来了,自动排序列会根据新条目内容调整该行序号,很有可能把该行移动到了新的行号 newrow,那么如果后面代码继续用 旧的 therow 行号设置后面几列的条目,后面几列条目就会错位。这是很严重的问题。
因此在调用 setItem() 函数设置整行的多列条目时,一定要提前关闭自动排序,把新条目都设置完毕后再考虑开启自动排序!
(6)单元格控件和运行时条目编辑
单元格除了设置条目,还可以设置独立的单元格控件:
void QTableWidget::setCellWidget(int row, int column, QWidget * widget)
需要注意的问题是单元格控件 widget 会完全覆盖住单元格条目 item,默认情况下单元格条目 item 与单元格控件 widget 是完全没关联的,比如设置给单元格一个组合框,组合框显示的东西与单元格原本条目没关系,修改了组合框内容不会影响单元格条目内容。如果希望单元格控件与单元格条 目有关,需要自己编代码。
获取单元格控件使用如下函数:
QWidget * QTableWidget::cellWidget(int row, int column) const
注意返回的指针可能为空。
单元格控件也可以被删除掉:
void QTableWidget::removeCellWidget(int row, int column)
删除单元格控件后,单元格就会显示原来的条目内容(如果条目原本是 NULL ,就剩下空单元格)。
表格控件的单元格也可以开启持续编辑器和关闭持续编辑器(必须配对使用):
void QTableWidget::openPersistentEditor(QTableWidgetItem * item) //开启持续编辑器
void QTableWidget::closePersistentEditor(QTableWidgetItem * item) //关闭持续编辑器
因为表格控件默认的条目全都带了可以编辑标志,所以一般用不到持续编辑器,如果想用代码让单元格进行编辑状态,可以用更简单的函数:
void QTableWidget::editItem(QTableWidgetItem * item)
对于可编辑的条目,都可以用 editItem() 开启编辑器,这个临时编辑器可以自动关闭的,不需要调用其他函数。
程序运行时,表格控件默认所有单元格都可以双击编辑,但如果希望表格全部是只读的,那么可以用下面函数关闭表格控件的整体编辑触发器:
void setEditTriggers(EditTriggers triggers) //设置编辑触发器
EditTriggers editTriggers() const //获取编辑触发器
默认的编辑器触发器是双击、回车键等可以编辑单元格,修改为 QAbstractItemView::NoEditTriggers 就不会开启任何编辑器了,相当于整个表格只读了。如果不希望整个表格只读,而只是希望某部分的单元格只读,那么只有设置单元格条目自身的标志 位,item->setFlags(Qt::ItemFlags flags),把 flags 设置为不带可编辑标志的:
(item->flags()) & (~Qt::ItemIsEditable) 。
(7)信号
除了之前介绍的 currentCellChanged() 和 currentItemChanged() 信号,还有多个关于单元格和条目操作的信号,按照基于单元格操作触发还是实体条目操作触发。
大致分为两类:
第一类是基于实体单元格触发的:
void cellActivated(int row, int column) //单元格被激活
void cellChanged(int row, int column) //单元格内部发生变化,如从NULL变成有条目,或者条目内部数据发生变化
void cellClicked(int row, int column) //单元格被点击
void cellDoubleClicked(int row, int column) //单元格被双击
void cellEntered(int row, int column)//鼠标进入单元格,只有在 mouseTracking 开启时或鼠标移动时点击了单元格,才触发该信号
void cellPressed(int row, int column) //单元格被鼠标点击按下
第二类是基于实体条目触发的:
void itemActivated(QTableWidgetItem * item) //条目被激活
void itemChanged(QTableWidgetItem * item) //条目内部数据发生变化,如文本修改、图标变化、复选状态变化等
void itemClicked(QTableWidgetItem * item) //条目被点击
void itemDoubleClicked(QTableWidgetItem * item) //条目被双击
void itemEntered(QTableWidgetItem * item)
//鼠标进入条目,只有在 mouseTracking 开启时或鼠标移动时点击了条目,才触发该信号
void itemPressed(QTableWidgetItem * item) //条目被鼠标点击按下
因为表格条目是多选的,可以有很多高亮选中条目,高亮选中的状态发生变化时,会触发如下信号:
void itemSelectionChanged()
上面信号参数没有指明哪些条目被选中,要获取所有选中的条目,可以用如下函数:
QList<QTableWidgetItem *> QTableWidget::selectedItems() const
更多关于表格选中操作的内容后面再介绍。
(8)槽函数
表格有两个清空内容的槽函数,首先是 clear() 槽函数:
void QTableWidget::clear()
clear() 函数删除表格内所有条目内容,单元格全为 NULL,清除选中状态,水平表头和垂直表头的设置内容清空,但是会保留表格的行数、列数,表头清掉 之后,会使用默认的数字行号、列号。
第二个清除内容的槽函数:
void QTableWidget::clearContents()
这个函数删除所有条目内容,单元格全为 NULL,清除选中状态,但是表头的设置内容都保留,表头文本等照旧显示,表格行数、列数也不变。
还有关于整行、整列插入删除操作的槽函数:
void QTableWidget::insertColumn(int column) //插入新的一列,新列序号 column,新列的单元格默认 NULL
void QTableWidget::insertRow(int row) //插入新的一行,新行序号 row,新行的单元格默认 NULL
void QTableWidget::removeColumn(int column) //删除第 column 整列条目
void QTableWidget::removeRow(int row) //删除第 row 整行条目
最后是关于条目滚动显示的函数,表格的行、列很多时,只有一部分的单元格显示在表格控件可视矩形里,如果希望滚动表格,让指定的条目显示出来,使用如下函数:
void QTableWidget::scrollToItem(const QTableWidgetItem * item, QAbstractItemView::ScrollHint hint = EnsureVisible)
QAbstractItemView::ScrollHint 枚举常量在 8.1.1 节列举过,这里不重复介绍了。
(9)基类 QTableView 的函数
表格控件还从基类 QTableView 继承了很多函数,这里介绍一些比较实用的,更多关于 QTableView 视图的内容会等到模型/视图章节再详解。
表头控件的表头分段有分隔线,拖动分隔线可以调整各列的宽度,各行高度也可以拖动分隔线调整。如果通过函数代码调整列宽、行高,可以用下面的函数:
void QTableView::setColumnWidth(int column, int width) //设置列宽
int QTableView::columnWidth(int column) const //获取列宽
void QTableView::setRowHeight(int row, int height) //设置行高
int QTableView::rowHeight(int row) const //获取行高
用户在程序运行时可以双击表头分段的分隔线,那样表格控件会自动根据该列单元格内容最宽的条目,调整列宽,让该列条目内容都显示出来,这个操作也有对应的函数: (下面四个都是槽函数)
void resizeColumnToContents(int column) //自动调整第 column 列列宽,将该列条目显示完整
void resizeColumnsToContents() //自动调整所有列宽
void resizeRowToContents(int row) //自动调整第 row 行行高,将该行条目显示完整
void resizeRowsToContents() //自动调整所有行高
如果希望隐藏或显示指定列的所有条目或指定行的条目,可以用如下函数:
void QTableView::setColumnHidden(int column, bool hide) //设置指定列隐藏或显示
bool QTableView::isColumnHidden(int column) const //判断指定列是否被隐藏
void QTableView::setRowHidden(int row, bool hide) //设置指定行隐 藏或显示
bool QTableView::isRowHidden(int row) const //判断指定行是否被隐藏
关于显示或隐藏行列,还有四个快捷槽函数:hideColumn(int column),hideRow(int row),showColumn(int column),showRow(int row),这些函数都可以灵活使用,效果与 setColumnHidden() 和setRowHidden() 函数是一样的。
QTableView 也有更抽象的基类 QAbstractItemView ,QAbstractItemView 里面也有一些实用函数,这里先介绍一个设置条目显示图标大小的函数:
void QAbstractItemView::setIconSize(const QSize & size)
单元格里图标默认显示为 16*16 的,可以用该函数设置图标显示尺寸为 24*24 或 32*32 。 QAbstractItemView 类还有关于条目选中模式、选中行为等重要函数,后面再介绍。基类的内容暂时介绍的到这,下面来学学单元格条目的知识。