75-输入和显示-列表、表格、树-表格控件QTableWidget及其项QTableWidgetItem

表格控件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

typeint

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)此枚举描述用于描述表小部件项的类型。

ConstantDescription
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.Interactive0用户可以调整该部分的大小。也可以使用resizeSectionO函数以编程方式调 整节的大小,节的大小默认为defaultSectionSize
QHeaderView.Fixed2用户无法调整该部分的大小。该部分只能使用 resizeSectionO函数以编程方 式调整节的大小,节的大小默认为 defaultSectionSize
QHeaderView.Stretch1QHeaderView将自动调整该部分的大小以填充可用空间,用户以编程方式 无法更改节的大小
QHeaderView.ResizeToContents3QHeaderView 将根据整列或整行的内容自动将节调整为最佳大小,用户以 编程方式无法更改节的大小(该参数在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的应用实例

image-20230326202808835

# -*- 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 文档中。

image-20230304212312654

# -*- 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())

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Delphi是一个非常流行的编程语言,广泛用于Windows平台下的软件开发。在Delphi中,表格控件是一种常见的界面元素,用于显示和编辑数据的表格结构。 Delphi提供了多种表格控件,最常见的是TDBGrid控件和TStringGrid控件。TDBGrid控件是基于数据集的,可以直接与数据库进行交互,显示数据库中的数据,并提供排序、过滤、编辑等功能。TStringGrid控件则是一个自定义的表格,可以手动添加和编辑数据,适用于不需要与数据库交互的场景。 表格控件可以根据需要自定义列数和行数,并可以设置每列的标题、数据对齐方式、列宽等属性。通过编程,我们可以动态地向表格控件中添加数据,并对数据进行操作,如插入、修改、删除等。此外,表格控件还提供了选择单元格、行或列的功能,以便进行批量操作。 通过事件处理机制,我们可以对表格控件的各种操作进行响应,如单击某个单元格、选择某一行等。我们可以根据用户的操作,进行相应的逻辑处理,如更新数据、保存修改等。 除了基本的显示和编辑功能,Delphi的表格控件还提供了丰富的样式和外观设置,可以进行行、列、单元格的颜色、字体、背景图等的自定义。这样可以使得界面更加美观,符合实际需求。 总之,Delphi的表格控件开发人员提供了方便快捷的数据显示和编辑功能,可以极大地简化开发过程,提高开发效率。无论是对于需要与数据库交互的应用程序,还是简单的数据展示,表格控件都是不可或缺的重要组件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

士别三日,当挖目相待

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值