表格控件QTableWidget及其项QTableWidgetItem
QTableWidget是一个用来显示表格的类,表格中的每个单元格(item)由QTableWidgetItem提供。
QListWidget 用来描述列表,它和QTableWidget 有很多相似之处:前者继承自QListView,后者继承自QTableView;两者都有自己的内部模型,如果想要使用自定义模型,则应该使用 QListView 或 QTableView;
前者的每个项目由 QListWidgetItem 提供,后者的每个项目由 QTableWidgetItem 提供。
表格控件QTableWidget 是从QTableView 类继承而来的,由多行多列构成,并且含有行表头和列表头
表格控件的每个单元格称为一个项(item),每个项是一个QTableWidgetItem 对象,可以设置每个项的文本图标颜色前景色和背景色等属性用QTableWidget
类创建表格控件的方法如下所示,其中parent 是表格控件QTableWidget所在的父窗口或控件,rows 和 columns 分别指定表格对象的行和列的数量。
from PySide6.QtWidgets import QTableWidget
QTableWidget(parent: Union[PySide6.QtWidgets.QWidget,NoneType]= None)-> None # 先实例化类,后通过setRowCount、setColumnCount调整表格的大小
QTableWidget(rows: int,columns: int,parent: Union[PySide6.QtWidgets.QWidget,NoneType]= None)-> None # 在实例化类时传递行列参数
用QIableWidgetItem 创建表格项的方法如下所示其中type可取QTableWidgetItemType值为0)或QTableWidgetItem,UserType(值为1000),前者是默认值,后者是用户自定义
from PySide6.QtWidgets import QTableWidgetItem
QTableWidgetItem(icon: Union[PySide6.QtGui.QIcon,PySide6.QtGui.QPixmap],text: str,type: int = < ItemType.Type: 0 >)-> None
QTableWidgetItem(Other: PySide6.QtWidgets.QTableWidgetItem)-> None
QTableWidgetItem(text: str,type: int = < ItemType.Type: 0 >)-> None
QTableWidgetItem(type: int = < ItemType.Type: 0 >)-> None
表格控件QTableWidget 的常用方法
表格控件 QTableWidget 的常用方法如表所示,主要方法介绍如下
- 用setRowCount(rows;int)方法和setColumnCount(columns;int)方法分别设置表格控件的行数和列数,行数和列数不含表头;
- 用rowCount()方法和columnCount()方法可以获取表格控件的行数和列数。
- 用insertRow(row:int)方法和insertColumn(column;int)方法可以插入行和插人列;
- 用removeRow(row;int)方法和 removeColumn(column:int)方法可以分别删除指定的行和列;
- 用clear()方法可以清空包含表头在内的所有内容;
- 用clearContents()方法可以清空不含表头的内容。
- 用setItem(row:int;column:int,QTableWidgetItem)方法可以在指定的行和列处设置表格项;
- 用takeItem(row;int,column:int)方法可以从表格控件中移除表格项,并返回此表格项。
- 用setCurrentCell(row:int,column:int)方法可以将指定的行列单元设为当前单元格,
- 用setCurrentItem(QTableWidgetItem)方法将指定的表格项设置成当前项,
- 项信息获取
- 用currentItem()方法获取当前的表格项。
- 用item(row:int,column;int)方法获取指定行和列处的表格项;
- 用itemAt(QPoint)或itemAt(x:int,y;int)方法获取指定位置处的表格项,如果没有,则返回 None。
- 用row(QTableWidgetItem)方法和 column(QTableWidgetItem)方法获取表格项所在的行号和列号。
- 用setSortingEnabled(bool)方法设置表格控件是否可排序,
- 用sortItems(column:int,order= Qt.AscendingOrder)方法对指定的列进行升序或降序排列。
- 用setHorizontalHeaderItem(column; int,QTableWidgetItem)和 setVerticalHeaderItem(row;int,QTableWidgetItem)方法设置水平和竖直表头;
- 用setHorizontalHeaderLabels(labels; Sequence[str])和 setVerticalHeaderLabels(labels;Sequence[str])方法用宇符串序列定义水平和竖直表头;
- 用horizontalHeaderItem(column:int)和verticalHeaderItem(row:int)方法取水平和垂直表头的表格项;
- 用takeHorizontalHeaderItern(column; int)和 takeVerticalHeaderItem(row; int)方法可以移除表头,并返回被移除的表格项
QTableWidget 的方法及参数类型 | 说 明 |
---|---|
setRowCount(rows; int) | 设置行数 |
setColumnCount(columns:int) | 设置列数 |
[slot]insertRow(row:int) | 在指定位置插入行 |
[slot]insertColumn(column:int) | 在指定位置插人列 |
rowCount() | 获取行数 |
columnCount() | 获取列数 |
[slot]removeRow(row:int) | 移除指定的行 |
[slot]removeColumn(column:int) | 移除指定的列 |
setItem(row:int,column:int,QTableWidgetItem) | 在指定行和列处设置表格项 |
takeItem(row:int,column: int) | 移除并返回表格项 |
setCurrentCell(row:int,column:int) | 设置当前的单元格 |
setCurrentItem(QTableWidgetltem) | 设置当前的表格项 |
currentItem() | 获取当前的表格项 |
row(QTableWidgetItem) | 获取表格项所在的行 |
column(QTableWidgetItem) | 获取表格项所在的列 |
currentRow() | 获取当前行 |
currentColumn() | 获取当前列 |
setHorizontalHeaderItem(column:int,QTableWidgetItem) | 设置水平表头 |
setHorizontalHeaderLabels(labels:Sequence[str]). | 用字符串序列设置水平表头 |
horizontalHeaderItem(column:int) | 获取水平表头的表格项 |
takeHorizontalHeaderItem(column: int) | 移除水平表头的表格项,并返回表格项 |
setVerticalHeaderItem(row:int,QTableWidgetItem) | 设置竖直表头 |
setVerticalHeaderLabels(labels:Sequence[str]) | 用字符串序列设置竖直表头 |
verticalHeaderItem(row:int) | 获取竖直表头的表格项 |
takeVerticalHeaderItem(row: int) | 移除竖直表头的表格项,并返回表格项 |
[slot]clear() | 清空表格项和表头的内容 |
[slot]clearContents() | 清空表格项的内容 |
editItem(QTableWidgetItem) | 开始编辑表格项 |
findItems(text: str,flags: Qt.MatchFlags) | 获取满足条件的表格项列表 |
item(row:int,column:int) | 获取指定行和列处的表格项 |
itemAt(QPoint) | 获取指定位置的表格项 |
itemAt(x:int,y:int) | 获取指定位置的表格项 |
openPersistentEditor(QTableWidgetItem) | 打开编辑框 |
isPersistentEditorOpen(QTableWidgetItem) | 获取编辑框是否已经打开 |
closePersistentEditor(QTableWidgetItem) | 关闭编辑框 |
[slot]scrollToItem(QTableWidgetItem) | 滚动表格使表格项可见 |
selectedItems() | 获取选中的表格项列表 |
setCellWidget(row:int,column:int,QWidget) | 设置单元格的控件 |
cellWidget(row:int,column; int) | 获取单元格的控件QWidget |
removeCellWidget(row:int,column:int) | 移除单元格上的控件 |
setSortingEnabled(bool) | 设置是否可以排序 |
isSortingEnabled() | 获取是否可以排序 |
sortItems(column: int,order=Qt.AscendingOrder) | 按列排序 |
supportedDropActions() | 获取支持的拖放动作 Qt.DropAction |
表格控件QTableWidget的信号
表格控件QTableWidget的信号如表所示
QTableWidget的信号及参数类型 | 说明 |
---|---|
cellActivated(row:int,column:int) | 单元格活跃时发送信号 |
cellChanged(row:int,column:int) | 单元格的数据变化时发送信号 |
cellClicked(row:int,column:int) | 单击单元格时发送信号 |
cellDoubleClicked(row: int,column:int) | 双击单元格时发送信号 |
cellEntered(row:int,column: int) | 光标进人单元格时发送信号 |
cellPressed(row: int,column: int) | 光标在单元格上按下按键时发送信号 |
currentCellChanged(currentRow: int,currentColumn:int,previousRow: int,previousColumn: int) | 当前单元格发生改变时发送信号 |
currentItemChanged(currentItem,previousltem) | 当前表格项发生改变时发送信号 |
itemActivated(QTableWidgetltem) | 表格项活跃时发送信号 |
itemChanged(QTableWidgetltem) | 表格项的数据发生改变时发送信号 |
itemClicked(QTableWidgetItem) | 单击表格项时发送信号 |
itemDoubleCIicked(QTableWidgetItem) | 双击表格项时发送信号 |
itemEntered(QTableWidgetltem) | 光标进入表格项时发送信号 |
itemPressed(QTableWidgetItem) | 光标在表格项上按下按键时发送信号 |
itemSelectionChanged() | 选择的表格项发生改变时发送信号 |
表格项QTableWidgetItem
QTableWidgetItem类提供了一个与QTableWidget类一起使用的项。
from PySide6.QtWidgets import QTableWidgetItem
QTableWidgetItem(icon: Union[PySide6.QtGui.QIcon, PySide6.QtGui.QPixmap], text: str, type: int = < ItemType.Type: 0 >)-> None
QTableWidgetItem(other: PySide6.QtWidgets.QTableWidgetItem)-> None
QTableWidgetItem(text: str, type: int = < ItemType.Type: 0 >)-> None
QTableWidgetItem(type: int = < ItemType.Type: 0 >)-> None
other – PySide6.QtWidgets.QTableWidgetItem
type – int
icon – PySide6.QtGui.QIcon
text – str
用给定的图标和文本构造一个表项。
用给定的文本构造一个表项。
构造其他的副本。请注意,type()和tableWidget()不会被复制。
当重新实现clone()时,此函数非常有用。
构造不属于任何表的指定类型的表项。
表格项QTableWidgetItem官方描述
表项用于保存表小部件的信息。项目通常包含文本、图标或复选框
QTableWidgetItem类是一个方便类,它取代了Qt 3中的QTableItem类。它提供了一个与QTableWidget类一起使用的项。
顶层项目是在没有父项的情况下构建的,然后插入到由一对行号和列号指定的位置:
newItem = QTableWidgetItem(tr("%1").arg(()
pow(row, column+1)))
tableWidget.setItem(row, column, newItem)
每个项目都可以有自己的背景笔刷,这是通过setBackground()函数设置的。当前的背景笔刷可以通过background()找到。每个项目的文本标签都可以用自己的字体和画笔呈现。这些是用setFont()和setForeground()函数指定的,并用font()和foreground(。
默认情况下,项目是启用的、可编辑的、可选择的、可检查的,并且既可以用作拖放操作的源,也可以用作拖放目标。可以通过使用适当的值调用setFlags()来更改每个项目的标志(请参见ItemFlags)。可检查项可以使用setCheckState()函数进行检查和取消检查。相应的checkState()函数指示该项当前是否已被检查。
子类别化
当对QTableWidgetItem进行子类化以提供自定义项时,可以为它们定义新的类型,以便将它们与标准项区分开来。需要此功能的子类的构造函数需要调用具有等于或大于UserType的新类型值的基类构造函数。
表格项QTableWidgetItem方法总览
-
PySide6.QtWidgets.QTableWidgetItem.ItemType
您可以在QTableWidgetItem子类中定义新的用户类型,以确保对自定义项进行特殊处理。
(继承enum.IntEnum)此枚举描述用于描述表小部件项的类型。
Constant | Description |
---|---|
QTableWidgetItem.Type | 表小部件项的默认类型。 |
QTableWidgetItem.UserType | 自定义类型的最小值。UserType以下的值由Qt保留。 |
表格项QTableWidgetItem 的常用方法如表所示,其方法与列表项的方法基本
QTableWidgetItem 的方法及参数类型 | 说明 |
---|---|
background()-> PySide6.QtGui.QBrush | 返回用于渲染项目背景的笔刷。 |
checkState()-> PySide6.QtCore.Qt.CheckState | 返回表项的选中状态。 |
clone()-> PySide6.QtWidgets.QTableWidgetItem | 创建项目的副本。 |
column()-> int | 返回表中项目的列。如果该项不在表中,则此函数将返回-1。 |
data(role: int)-> Any | 返回给定角色的项的数据。 |
flags()-> PySide6.QtCore.Qt.ItemFlag | 返回用于描述项目的标志。这些决定了是否可以检查、编辑和选择项目。 |
font()-> PySide6.QtGui.QFont | 返回用于呈现项目文本的字体。 |
foreground()-> PySide6.QtGui.QBrush | 返回用于渲染项目前景(例如文本)的画笔。 |
icon()-> PySide6.QtGui.QIcon | 返回项目的图标。 |
isSelected()-> bool | 如果选择了项,则返回true,否则返回false。 |
__lt__(other) | 如果该项小于另一项,则返回true;否则返回false。 |
read(in_: PySide6.QtCore.QDataStream)-> None | 从中的流中读取项目。 |
row()-> int | 返回表中项目的行。如果该项不在表中,则此函数将返回-1。 |
setBackground(brush: Union[PySide6.QtGui.QBrush, PySide6.QtCore.Qt.BrushStyle, PySide6.QtCore.Qt.GlobalColor, PySide6.QtGui.QColor, PySide6.QtGui.QGradient, PySide6.QtGui.QImage, PySide6.QtGui.QPixmap])-> None | 将项目的背景笔刷设置为指定的笔刷。设置默认构造的笔刷将使视图使用样式中的默认颜色。 |
setCheckState(state: PySide6.QtCore.Qt.CheckState)-> None | 将表项的检查状态设置为state。 |
setData(role: int, value: Any)-> None | 将给定角色的项数据设置为指定值。 |
setFlags(flags: PySide6.QtCore.Qt.ItemFlag)-> None | 将项目的标志设置为给定的标志。这些决定了项目是否可以被选择或修改。 |
setFont(font: Union[PySide6.QtGui.QFont, str, Sequence[str]])-> None | 将用于显示项目文本的字体设置为给定的字体。 |
setForeground(brush: Union[PySide6.QtGui.QBrush, PySide6.QtCore.Qt.BrushStyle, PySide6.QtCore.Qt.GlobalColor, PySide6.QtGui.QColor, PySide6.QtGui.QGradient, PySide6.QtGui.QImage, PySide6.QtGui.QPixmap])-> None | 将项目的前景笔刷设置为指定的笔刷。设置默认构造的笔刷将使视图使用样式中的默认颜色。 |
setIcon(icon: Union[PySide6.QtGui.QIcon, PySide6.QtGui.QPixmap])-> None | 将项目的图标设置为指定的图标。 |
setSelected(select: bool)-> None | 设置要选择的项目的选定状态。 |
setSizeHint(size: PySide6.QtCore.QSize)-> None | 将表项的大小提示设置为大小。如果未设置大小提示或大小无效,则项目委托将根据项目数据计算大小提示。 |
setStatusTip(statusTip: str)-> None | 将表项的状态提示设置为statusTip指定的文本。需要启用QTableWidget鼠标跟踪才能使此功能正常工作。 |
setText(text: str)-> None | 将项目的文本设置为指定的文本。 |
setTextAlignment(alignment: PySide6.QtCore.Qt.AlignmentFlag)-> None setTextAlignment(alignment: int)-> None | 将项目文本的文本对齐方式设置为指定的对齐方式。 使用采用Alignment参数的重载。将项目文本的文本对齐方式设置为指定的对齐方式。 |
setToolTip(toolTip: str)-> None | 将项目的工具提示设置为工具提示指定的字符串。 |
setWhatsThis(whatsThis: str)-> None | 将项目的"What’s This?"帮助设置为whatsThis指定的字符串。 |
sizeHint()-> PySide6.QtCore.QSize | 返回为表项设置的大小提示。 |
statusTip()-> str | 返回项目的状态提示。 |
tableWidget()-> PySide6.QtWidgets.QTableWidget | 返回包含该项的表小部件。 |
textAlignment()-> int | 返回项目文本的文本对齐方式。 |
toolTip()-> str | 返回项目的工具提示。 |
type()-> int | 返回传递给QTableWidgetItem构造函数的类型。 |
whatsThis()-> str | 返回项目的"这是什么?"帮助。 |
write(out: PySide6.QtCore.QDataStream)-> None | 写入要流式输出的项。 |
基于item的操作
对item 的常规操作可以使用函数 setText()、setIcon()、setFont()、setForeground()和setBackground()等,可以参考 QListWidget 的相关内容。
想要增/删item,可以使用 setItem(row,column,item)将 item 插入表中,使用 takeitemrowcolumn)删除item。
增加item 的代码如下:
item = QTableWidgetItem('testItem')
self.tableWidget.setItem(row,column,item)
有时需要合并单元格,这就需要使用 setSpan()函数,其用法和举例如下
# 合并单元格
self.tablewidget.setSpan(1,0,1,2)
item = QTableWidgetItem('合并单元格')
item.setTextAlignment(Qt.AlignCenter)
self.tablewidget.setItem(1,0,item)
基于行列的操作
使用rowHeight(row:int)可以获取行的高度,使用 columnWidth(col:int)可以获取列的宽度。
使用 hideRow(row:int)、hideColumn(col:int)、showRow(row:int)和 showColumn(col: int)可以隐藏和显示行/列。
使用selectRow(row:int)和 selectColumn(col:int)可以选择行/列。
使用showGrid0函数的结果可以判断是否显示网格。
使用函数insertRow()、insertColumn()、removeRow()、removeColumn()可以增加和删除行/列。
使用 rowCount()函数可以获取表格的行数,
使用columnCount()函数可以获取表格的列数,
使用clear()函数可以清空表格
如果要对单元格进行排序,则使用 sortItems()函数,作用是基于column和order对所有行进行排序,默认是升序排列,也可以传递Qt.DescendingOrder,实现降序排列
导航
导航功能继承自QAbstractItemView,它提供了一个标准接口,用于通过信号/槽机制与模型进行互动,使子类能够随着模型更改而保持最新状态。QAbstractItemView 为键盘和鼠标导航、视口滚动、项目编辑与选择提供标准支持。键盘导航可以实现的功能如表所示。
按健 | 功 能 |
---|---|
Arrow keys | 更改当前项目并选择它 |
Ctrl+Arrow keys | 更改当前项目但不选择它 |
Shil+Arrow keys | 更改当前项目并选择它。先前选择的项目不会取消选择 |
Ctrl+Space | 切换当前项目的选择 |
Tab/Backtab | 将当前项目更改为下一个/上一个项目 |
Home/End | 选择模型中的第一个/最后一个项目 |
PagcUp/PageDown | 按视图中的可见行数向上/向下滚动显示的行 |
CtrI+A | 选择模型中的所有项目 |
表头(标题)
表头(标题)的相关操作继承自QTableView,使用 verticalHeader()函数可以获取表格的垂直表头,使用 horizontalHeader()函数可以获取表格的水平表头。
两者都返回QHeaderView,并且 QHeaderView 是 QAbstractItemView 的子类,为QTableWidget(QTableView)函数提供了表头视图。
如果不想看到行或列,则可以使用 hide()函数进行隐藏创建表头
最简单的方法是使用 setHorizontalHeaderLabels()函数和 setVerticalHeaderLabels()函数,两者都将字符串列表作为参数,为表格的列和行提供简单的文本标题。下面举例说明:
rowCount = self.tableWidget.rowCount()
self.tableWidget.setVerticalHeaderLabels([f'row{i}'for i in range(rowCount)]
当然,也可以基于QTableWidgetItem 创建更复杂的表头标题,如下所示
cusHeaderItem = QTableWidgetItem("cusHeader")
cusHeaderItem.setIcon(QIcon("images/android.png"))
cusHeaderItem.setTextAlignment(Qt.AlignVCenter)
cusHeaderItem.setForeground(QBrush(QColor(255,0,0)))
self.tableWidget.setHorizontalHeaderItem(2,cusHeaderItem)
自定义小部件
列表视图中显示的项目默认使用标准委托进行渲染和编辑。但是有些任务需要在表格中插入自定义小部件,而使用 setIndexWidget0函数可以解决这个问题。
使用indexWidget()函数可以查找自定义小部件,如果不存在则返回空。代码如下:
"""
setIndexWidget(self,index: PySide6.QtCore,QModelIndex,widget:PySide6.QtWidgets.Qwidget)-> None
"""
#自定义控件
model = self.tablewidget .model()
self.tablewidget,setIndexWidget(model.index(4,2),QLineEdit('自定义控件'))
self.tableWidget.setIndexWidget(model.index(4,3),QSpinBox())
# 上面是基于 index 的方法,也可以通过 setCellWidget0函数实现一样的效果,如下所示:
self.tablewidget.setCellwidget(4,1,QPushButton("cellwidget"))
需要注意的是,传递的 index 是一个模型的索引,关于模型的详细信息会后面章节介绍
调整行/列的大小
可以通过 QHeaderView 来调整表格中的行/列,每个 QHeaderView 包含 1个方向orientation()和N个section(通过 count0函数获取N),section 表示行/列的一个基本单元QHeaderView 的很多方法都和 section 相关。
可以使用 moveSection(from: int,to: int)和resizeSection(logicalIndex: int,size: int)移动和调整行/列的大小。
QHeaderView 提供了一些方法来控制标题移动、单击、大小调节行为:
- 使用setSectionsMovable(movable:bool)可以开启移动行为,
- 使用 setSectionsClickable(clickable: bool)使其可以单击,
- 使用setSectionResizeMode()可以设置大小
以上几种行为会触发如下槽函数:
- 移动触发 sectionMoved(),
- 调整大小触发sectionResized(),
- 单击鼠标触发 sectionClicked()及 sectionHandleDoubleClicked()。
- 此外,当添加或删除section 时,触发 sectionCountChanged()
ctionResizeMode0支持两种调节方式,即全局设置和局部设置,参数如下:
setSectionResizeMode(self,logicalIndex: int,mode:QHeaderView.ResizeMode
setSectionResizeMode(self,mode: QHeaderView.ResizeMode)-> None
QHeaderViewResizeMode 支持的参数如表所示
参 数 | 值 | 描述 |
---|---|---|
QHeaderView.Interactive | 0 | 用户可以调整该部分的大小。也可以使用resizeSectionO函数以编程方式调 整节的大小,节的大小默认为defaultSectionSize |
QHeaderView.Fixed | 2 | 用户无法调整该部分的大小。该部分只能使用 resizeSectionO函数以编程方 式调整节的大小,节的大小默认为 defaultSectionSize |
QHeaderView.Stretch | 1 | QHeaderView将自动调整该部分的大小以填充可用空间,用户以编程方式 无法更改节的大小 |
QHeaderView.ResizeToContents | 3 | QHeaderView 将根据整列或整行的内容自动将节调整为最佳大小,用户以 编程方式无法更改节的大小(该参数在Qt4.2中引入) |
可以用 hideSection(logicalIndex: int)和 showSection(logicalIndex:int)隐藏和显示行/列。
上面介绍的是根据QHeaderView 的方法来调整表格中的行/列,也可以通过QTableView的相关方法达到相同的目的。
使用QTableWidget(QTableView)/resizeColumnsToContents()或resizeRowsToContents(),根据每列或每行的空间需求分配可用的空间,同时根据每个项目的委托大小提示调整给定行/列的大小。
上面的方法针对的是所有行/列,也可以通过resizeRowToContents(int)和 resizeColumnToContents(int)指定某个行/列。关于委托会在后面章节详细介绍。
拉伸填充剩余空间
在默认情况下,表格中的单元格不会自动扩展以填充剩余空间,但可以通过拉伸最后一个标题(行/列)填充剩余空间。
使用 horizontalHeader0函数或verticalHeader()函数可以获取QHeaderView 并通过 setStretchLastSection(True)启用拉伸剩余空间的功能
坐标系
有时需要对行/列的索引和小部件坐标进行转换,row 对应的y 坐标。
- rowAt(y:int)函数返回视图中y坐标对应的行,
- rowViewportPosition(row:int)函数返回行数
对于列,可以使用 columnAt()函数和 columnViewportPosition())函数获取x 标与列索引之间的关系示例如下:
row = self.tableWidget .currentRow()
rowPositon = self.tablewidget.rowViewportPosition(row)
rowAt = self.tablewidget.rowAt(rowPositon)
表格控件QTableWidget的应用实例
# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/26 20:10
# File_name: 04-表格控件QTableWidget的应用实例.py
from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *
import sys
import os
os.chdir(os.path.dirname(__file__))
class QTableWidgetDemo(QMainWindow):
addCount = 0
insertCount = 0
def __init__(self,parent=None):
super(QTableWidgetDemo,self).__init__(parent)
self.setWindowTitle("QTableWidget案例")
self.resize(500,600)
self.text = QPlainTextEdit('用来显示QTableWidget相关信息:')
self.tableWidget = QTableWidget(5,4)
# 增删行
self.buttonDeleteRow = QPushButton('删除行')
self.buttonAddRow = QPushButton('增加行')
self.buttonInsertRow = QPushButton('插入行')
layoutH = QHBoxLayout()
layoutH.addWidget(self.buttonAddRow)
layoutH.addWidget(self.buttonInsertRow)
layoutH.addWidget(self.buttonDeleteRow)
self.buttonAddRow.clicked.connect(lambda: self.onAdd('row'))
self.buttonInsertRow.clicked.connect(lambda: self.onInsert('row'))
self.buttonDeleteRow.clicked.connect(lambda: self.onDelete('row'))
# 增删列
self.buttonDeleteColumn = QPushButton('删除列')
self.buttonAddColumn = QPushButton('增加列')
self.buttonInsertColumn = QPushButton('插入列')
layoutH2 = QHBoxLayout()
layoutH2.addWidget(self.buttonAddColumn)
layoutH2.addWidget(self.buttonInsertColumn)
layoutH2.addWidget(self.buttonDeleteColumn)
self.buttonAddColumn.clicked.connect(lambda: self.onAdd('column'))
self.buttonInsertColumn.clicked.connect(lambda: self.onInsert('column'))
self.buttonDeleteColumn.clicked.connect(lambda: self.onDelete('column'))
# 选择
self.buttonSelectAll = QPushButton('全选')
self.buttonSelectRow = QPushButton('选择行')
self.buttonSelectColumn = QPushButton('选择列')
self.buttonSelectOutput = QPushButton('输出选择')
layoutH3 = QHBoxLayout()
layoutH3.addWidget(self.buttonSelectAll)
layoutH3.addWidget(self.buttonSelectRow)
layoutH3.addWidget(self.buttonSelectColumn)
layoutH3.addWidget(self.buttonSelectOutput)
self.buttonSelectAll.clicked.connect(lambda: self.tableWidget.selectAll())
# self.buttonSelectAll.clicked.connect(self.onSelectAll)
self.buttonSelectRow.clicked.connect(lambda: self.tableWidget.selectRow(self.tableWidget.currentRow()))
self.buttonSelectColumn.clicked.connect(lambda: self.tableWidget.selectColumn(self.tableWidget.currentColumn()))
self.buttonSelectOutput.clicked.connect(self.onButtonSelectOutput)
# self.buttonSelectColumn.clicked.connect(self.onCheckNone)
layout = QVBoxLayout(self)
layout.addWidget(self.tableWidget)
# layout.addWidget(self.tableWidget2)
layout.addLayout(layoutH)
layout.addLayout(layoutH2)
layout.addLayout(layoutH3)
layout.addWidget(self.text)
widget = QWidget()
self.setCentralWidget(widget)
widget.setLayout(layout)
self.initItem()
# selection
# self.listWidget.setSelectionMode(QAbstractItemView.SingleSelection)
self.tableWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
# self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows)
self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectItems)
# 行列标题
rowCount = self.tableWidget.rowCount()
columnCount = self.tableWidget.columnCount()
self.tableWidget.setHorizontalHeaderLabels([f'col{i}'for i in range(columnCount)])
self.tableWidget.setVerticalHeaderLabels([f'row{i}'for i in range(rowCount)])
cusHeaderItem = QTableWidgetItem("cusHeader")
cusHeaderItem.setIcon(QIcon("images/android.png"))
cusHeaderItem.setTextAlignment(Qt.AlignVCenter)
cusHeaderItem.setForeground(QBrush(QColor(255,0,0)))
self.tableWidget.setHorizontalHeaderItem(2,cusHeaderItem)
# 自定义控件
model = self.tableWidget.model()
self.tableWidget.setIndexWidget(model.index(4,3),QLineEdit('自定义控件-'* 3))
self.tableWidget.setIndexWidget(model.index(4,2),QSpinBox())
self.tableWidget.setCellWidget(4,1,QPushButton("cellWidget"))
# 调整行列宽高
header = self.tableWidget.horizontalHeader()
# # header.setStretchLastSection(True)
# # header.setSectionResizeMode(QHeaderView.Stretch)
# header.setSectionResizeMode(QHeaderView.Interactive)
# header.resizeSection(3,120)
# header.moveSection(0,2)
# # self.tableView.resizeColumnsToContents()
# header.setStretchLastSection(True)
self.tableWidget.resizeColumnsToContents()
self.tableWidget.resizeRowsToContents()
# headerH = self.tableWidget.horizontalHeader()
# 排序单元格
# self.tableWidget.sortItems(1,order=Qt.DescendingOrder)
# 合并单元格
self.tableWidget.setSpan(1,0,1,2)
item = QTableWidgetItem('合并单元格')
item.setTextAlignment(Qt.AlignCenter)
self.tableWidget.setItem(1,0,item)
# 显示坐标
buttonShowPosition = QToolButton(self)
buttonShowPosition.setText('显示当前位置')
self.toolbar.addWidget(buttonShowPosition)
buttonShowPosition.clicked.connect(self.onButtonShowPosition)
# 上下文菜单
self.menu = self.generateMenu()
self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)######允许右键产生子菜单
self.tableWidget.customContextMenuRequested.connect(self.showMenu)####右键菜单
# 信号与槽
self.tableWidget.currentItemChanged[QTableWidgetItem,QTableWidgetItem].connect(self.onCurrentItemChanged)
self.tableWidget.itemActivated[QTableWidgetItem].connect(self.onItemActivated)
self.tableWidget.itemClicked[QTableWidgetItem].connect(self.onItemClicked)
self.tableWidget.itemDoubleClicked[QTableWidgetItem].connect(
lambda item: self.text.appendPlainText(f'"{item.text()}"触发itemDoubleClicked信号:'))
self.tableWidget.itemChanged[QTableWidgetItem].connect(
lambda item: self.text.appendPlainText(f'"{item.text()}"触发itemChanged信号:'))
self.tableWidget.itemEntered[QTableWidgetItem].connect(
lambda item: self.text.appendPlainText(f'"{item.text()}"触发itemEntered信号:'))
self.tableWidget.itemPressed[QTableWidgetItem].connect(
lambda item: self.text.appendPlainText(f'"{item.text()}"触发itemPressed信号:'))
self.tableWidget.itemSelectionChanged.connect(lambda: self.text.appendPlainText(f'触发itemSelectionChanged信号:'))
self.tableWidget.cellActivated[int,int].connect(lambda row,column: self.onCellSignal(row,column,'cellActivated'))
self.tableWidget.cellChanged[int,int].connect(lambda row,column: self.onCellSignal(row,column,'cellChanged'))
self.tableWidget.cellClicked[int,int].connect(lambda row,column: self.onCellSignal(row,column,'cellClicked'))
self.tableWidget.cellDoubleClicked[int,int].connect(lambda row,column: self.onCellSignal(row,column,'cellDoubleClicked'))
self.tableWidget.cellEntered[int,int].connect(lambda row,column: self.onCellSignal(row,column,'cellEntered'))
self.tableWidget.cellPressed[int,int].connect(lambda row,column: self.onCellSignal(row,column,'cellPressed'))
self.tableWidget.currentCellChanged[int,int,int,int].connect(lambda currentRow,currentColumn,previousRow,previousColumn: self.text.appendPlainText(f'row:{currentRow},column:{currentColumn},触发信号:currentCellChanged,preRow:{previousRow},preColumn:{columnCount}'))
def initItem(self):
# flag+check
item = QTableWidgetItem('flag+check1')
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Unchecked)
self.tableWidget.setItem(2,0,item)
item = QTableWidgetItem('flag+check2')
item.setFlags(Qt.NoItemFlags)
item.setCheckState(Qt.Unchecked)
self.tableWidget.setItem(2,1,item)
# setText
item = QTableWidgetItem()
item.setText('右对齐+check')
item.setTextAlignment(Qt.AlignRight)
item.setCheckState(Qt.Checked)
self.tableWidget.setItem(3,0,item)
# setIcon
item = QTableWidgetItem(f'setIcon')
item.setIcon(QIcon('images/music.png'))
item.setWhatsThis('whatsThis提示1')
self.tableWidget.setItem(3,1,item)
# setFont、setFore(Back)ground
item = QTableWidgetItem(f'setFont、setFore(Back)ground')
item.setFont(QFont('宋体'))
item.setForeground(QBrush(QColor(255,0,0)))
item.setBackground(QBrush(QColor(0,255,0)))
self.tableWidget.setItem(3,2,item)
# setToolTip,StatusTip,WhatsThis
item = QTableWidgetItem(f'提示帮助')
item.setToolTip('toolTip提示')
item.setStatusTip('statusTip提示')
item.setWhatsThis('whatsThis提示2')
self.tableWidget.setMouseTracking(True)
self.tableWidget.setItem(3,3,item)
# 开启statusbar
statusBar = self.statusBar()
statusBar.show()
# 开启whatsThis功能
whatsThis = QWhatsThis(self)
self.toolbar = self.addToolBar('help')
# 方式1:QAction
self.actionHelp = whatsThis.createAction(self)
self.actionHelp.setText('显示whatsThis-help')
# self.actionHelp.setShortcuts(QKeySequence(Qt.CTRL | Qt.Key_H))
# self.actionHelp.setShortcuts(QKeySequence(Qt.CTRL | Qt.Key_H))
self.toolbar.addAction(self.actionHelp)
# 方式2:工具按钮
tool_button = QToolButton(self)
tool_button.setToolTip("显示whatsThis2-help")
tool_button.setIcon(QIcon("images/help.jpg"))
self.toolbar.addWidget(tool_button)
tool_button.clicked.connect(lambda: whatsThis.enterWhatsThisMode())
# 初始化表格
for row in range(self.tableWidget.rowCount()):
for col in range(self.tableWidget.columnCount()):
item = self.tableWidget.item(row,col)
if item is None:
_item = QTableWidgetItem(f'row:{row},col:{col}')
self.tableWidget.setItem(row,col,_item)
def generateMenu(self):
menu = QMenu(self)
menu.addAction('增加行',lambda: self.onAdd('row'),QKeySequence(Qt.CTRL | Qt.Key_N))
menu.addAction('插入行',lambda: self.onInsert('row'),QKeySequence(Qt.CTRL | Qt.Key_I))
menu.addAction(QIcon("images/close.png"),'删除行',lambda: self.onDelete('row'),QKeySequence(Qt.CTRL | Qt.Key_D))
menu.addSeparator()
menu.addAction('增加列',lambda: self.onAdd('column'),QKeySequence(Qt.CTRL | Qt.SHIFT | Qt.Key_N))
menu.addAction('插入列',lambda: self.onInsert('column'),QKeySequence(Qt.CTRL | Qt.SHIFT | Qt.Key_I))
menu.addAction(QIcon("images/close.png"),'删除列',lambda: self.onDelete('column'),QKeySequence(Qt.CTRL | Qt.SHIFT | Qt.Key_D))
menu.addSeparator()
menu.addAction('全选',lambda: self.tableWidget.selectAll(),QKeySequence(Qt.CTRL | Qt.Key_A))
menu.addAction('选择行',lambda: self.tableWidget.selectRow(self.tableWidget.currentRow()),QKeySequence(Qt.CTRL | Qt.Key_R))
menu.addAction('选择列',lambda: self.tableWidget.selectColumn(self.tableWidget.currentColumn()),QKeySequence(Qt.CTRL | Qt.SHIFT | Qt.Key_R))
menu.addAction('输出选择',self.onButtonSelectOutput)
menu.addSeparator()
menu.addAction(self.actionHelp)
menu.addAction('显示当前位置',lambda: self.onButtonShowPosition())
return menu
def showMenu(self,pos):
self.menu.exec(QCursor.pos())# 显示菜单
def contextMenuEvent(self,event):
menu = QMenu(self)
menu.addAction('选项1')
menu.addAction('选项2')
menu.addAction('选项3')
menu.exec(event.globalPos())
def onButtonSelectOutput(self):
indexList = self.tableWidget.selectedIndexes()
itemList = self.tableWidget.selectedItems()
_row = indexList[0].row()
text = ''
for index,item in zip(indexList,itemList):
row = index.row()
if _row == row:
text = text + item.text()+ ''
else:
text = text + '\n'+ item.text()+ ''
_row = row
self.text.appendPlainText(text)
def onCurrentItemChanged(self,current: QTableWidgetItem,previous: QTableWidgetItem):
if previous == None:
_str = f'触发currentItemChanged信号,当前项:"{current.text()}",之前项:None'
elif current == None:
_str = f'触发currentItemChanged信号,当前项:None,之前项:"{previous.text()}"'
else:
_str = f'触发currentItemChanged信号,当前项:"{current.text()}",之前项:"{previous.text()}"'
self.text.appendPlainText(_str)
def onItemClicked(self,item: QTableWidgetItem):
self.tableWidget.currentRow()
_str1 = f'当前点击:"{item.text()}"'
if item.checkState()== Qt.Unchecked:
item.setCheckState(Qt.Checked)
_str2 = f'"{item.text()}"被选中'
else:
item.setCheckState(Qt.Unchecked)
_str2 = f'"{item.text()}"被取消选中'
self.text.appendPlainText(f'"{item.text()}"触发itemClicked信号:')
self.text.appendPlainText(_str1)
self.text.appendPlainText(_str2)
return
def onItemActivated(self,item: QTableWidgetItem):
self.text.appendPlainText(f'"{item.text()}"触发itemActivated信号:')
return
def onCellSignal(self,row,column,type):
_str = f'row:{row},column:{column},触发信号:{type}'
self.text.appendPlainText(_str)
def onAdd(self,type='row'):
if type == 'row':
rowCount = self.tableWidget.rowCount()
self.tableWidget.insertRow(rowCount)
self.text.appendPlainText(f'row:{rowCount},新增一行')
elif type == 'column':
columnCount = self.tableWidget.columnCount()
self.tableWidget.insertColumn(columnCount)
self.text.appendPlainText(f'column:{columnCount},新增一列')
def onInsert(self,type='row'):
if type == 'row':
row = self.tableWidget.currentRow()
self.tableWidget.insertRow(row)
self.text.appendPlainText(f'row:{row},插入一行')
elif type == 'column':
column = self.tableWidget.currentColumn()
self.tableWidget.insertColumn(column)
self.text.appendPlainText(f'column:{column},新增一列')
def onDelete(self,type='row'):
if type == 'row':
row = self.tableWidget.currentRow()
self.tableWidget.removeRow(row)
self.text.appendPlainText(f'row:{row},被删除')
elif type == 'column':
column = self.tableWidget.currentColumn()
self.tableWidget.removeColumn(column)
self.text.appendPlainText(f'column:{column},被删除')
def onCheckAll(self):
self.text.appendPlainText('点击了"全选"')
count = self.tableWidget.count()
for i in range(count):
item = self.tableWidget.item(i)
item.setCheckState(Qt.Checked)
def onCheckInverse(self):
self.text.appendPlainText('点击了"反选"')
count = self.tableWidget.count()
for i in range(count):
item = self.tableWidget.item(i)
if item.checkState()== Qt.Unchecked:
item.setCheckState(Qt.Checked)
else:
item.setCheckState(Qt.Unchecked)
def onCheckNone(self):
self.text.appendPlainText('点击了"全不选"')
count = self.tableWidget.count()
for i in range(count):
item = self.tableWidget.item(i)
item.setCheckState(Qt.Unchecked)
def onButtonShowPosition(self):
row = self.tableWidget.currentRow()
rowPositon = self.tableWidget.rowViewportPosition(row)
rowAt = self.tableWidget.rowAt(rowPositon)
column = self.tableWidget.currentColumn()
columnPositon = self.tableWidget.columnViewportPosition(column)
columnAt = self.tableWidget.columnAt(columnPositon)
_str = f'当前row:{row},rowPosition:{rowPositon},rowAt:{rowAt}\n当前column:{column},columnPosition:{columnPositon},columnAt:{columnAt}'
self.text.appendPlainText(_str)
if __name__ =="__main__":
app = QApplication(sys.argv)
demo = QTableWidgetDemo()
demo.show()
sys.exit(app.exec())
下面的程序从Excel文档studentxlsx中读取数据用表格控件显示读取的数据可以统计总成绩和平均成绩,并可以把数据保存到新的 Excel 文档中。
# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/4 17:42
# File_name: 02-表格控件QTableWidget的应用实例.py
import sys,os
from PySide6.QtWidgets import(QApplication,QWidget,QMenuBar,QVBoxLayout,QFileDialog,QTableWidget,QTableWidgetItem)
from openpyxl import load_workbook,Workbook
class MyWindow(QWidget):
def __init__(self,parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
menuBar = QMenuBar(self)
fileMenu = menuBar.addMenu("文件")
self.action_open = fileMenu.addAction("打开")
self.action_saveAs = fileMenu.addAction("另存")
fileMenu.addSeparator()
self.action_exit = fileMenu.addAction("退出")
statisticMenu = menuBar.addMenu("统计")
self.action_total = statisticMenu.addAction("插人总成绩")
self.action_average = statisticMenu.addAction("插人平均分")
self.action_saveAs.setEnabled(False)
self.action_total.setEnabled(False)
self.action_average.setEnabled(False)
self.tableWidget = QTableWidget(self)
v = QVBoxLayout(self)
v.addWidget(menuBar)
v.addWidget(self.tableWidget)
self.action_open.triggered.connect(self.action_open_triggered)# 信号与槽连接
self.action_saveAs.triggered.connect(self.action_saveAs_triggered)# 信号与槽连接
self.action_exit.triggered.connect(self.close)# 信号与槽连接
self.action_total.triggered.connect(self.action_total_triggered)# 信号与槽连接
self.action_average.triggered.connect(self.action_average_triggered)# 信号与槽连接
def action_open_triggered(self): # 打开Excel文件#读取数据后,保存数据的列表
fileName,file = QFileDialog.getOpenFileName(self,"打开文件",".","Excel文件(*.xlsx *.xls)")
score = list()# 读取数据后,保存数据的列表
if os.path.exists(fileName):
wbook = load_workbook(fileName)
wsheet = wbook.active
cell_range = wsheet[wsheet.dimensions] # 按行排列的单元格对象元组
for i in cell_range: # i是Excel行单元格元组
temp = list()# 临时列表
for j in i: # j是单元格对象
temp.append(str(j.value))
score.append(temp)
row_count = len(score)- 1 # 行数,不包含表头
column_count = len(score[0])# 列数
self.tableWidget.setRowCount(row_count)
self.tableWidget.setColumnCount(column_count)
self.tableWidget.setHorizontalHeaderLabels(score[0])
for i in range(row_count):
for j in range(column_count):
cell = QTableWidgetItem()
cell.setText(score[i + 1][j])
self.tableWidget.setItem(i,j,cell)
self.action_saveAs.setEnabled(True)
self.action_total.setEnabled(True)
self.action_average.setEnabled(True)
def action_saveAs_triggered(self): # 另存
score = list()
filename,fil = QFileDialog.getOpenFileName(self,"打开文件",".","excel文件(*.xlsx)")
if filename !="":
temp = list()
for j in range(self.tableWidget.columnCount()):
temp.append(self.tableWidget.horizontalHeaderItem(j).text())
score.append(temp)
for i in range(self.tableWidget.rowCount()):
temp = list()
for j in range(self.tableWidget.columnCount()):
temp.append(self.tableWidget.item(i,j).text())
score.append(temp)
wbook = Workbook()
wsheet = wbook.create_sheet("学生成绩",0)
for i in score:
wsheet.append(i)
wbook.save(filename)
def action_total_triggered(self):
column = self.tableWidget.columnCount()
self.tableWidget.insertColumn(column)
item = QTableWidgetItem("总成绩")
self.tableWidget.setHorizontalHeaderItem(column,item)
for i in range(self.tableWidget.rowCount()): # 计算总成绩
total = 0
for j in range(2,6):
total = total + int(self.tableWidget.item(i,j).text())
item = QTableWidgetItem(str(total))
self.tableWidget.setItem(i,column,item)
def action_average_triggered(self): # 计算平均成绩
column = self.tableWidget.columnCount()
self.tableWidget.insertColumn(column)
item = QTableWidgetItem("平均成绩")
self.tableWidget.setHorizontalHeaderItem(column,item)
for i in range(self.tableWidget.rowCount()):
total = 0
for j in range(2,6):
total = total + int(self.tableWidget.item(i,j).text())
item = QTableWidgetItem(str(total / 4))
self.tableWidget.setItem(i,column,item)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec())