刚完成了一个基于PYQT5的表格控件类QTableWidget的扩展子类QTableWidgetEx,扩展类可以实现自定义表格头类型,表格中的单元格可以是标签、复选框、按纽、组合框、有掩码的编辑框、多行文本编辑等任意窗体类控件(读者可继续扩展)
在编写此扩展子类的过程中的相关总结如下:
1、QTableWidget控件刚被初始化后(虽然界面上有表格,也可以在表格中录入数据),这时每个单元格不能直接被使用,需要对每个单元格进行如下初始化后,才可以对单元格进行赋值得到值等数据操作
item = QTableWidgetItem('单元格内容')
self.setItem(row,col,item)
2、每个PYQT5Q单元格默认对象TableWidgetItem猜想应是由两部份构成,单元格前小段可以显示一图标,后大段为显示表格数据内容,详测试代码。
3、可以在单元格上创建QLabel等控件,此时控件同单元格的尺寸绑定,并覆盖在单元格上面显示,如果此单元格有初始化,也有文本内容,默认的单元格文本和控件文本都会显示出来,
建议,有控件的单元格,就无需对此单元格进行初始化了,单元格本身仍为None,如对此单元格进行文本操作,实际是操作的控件文本,而不是单元格默认QTableWidgetItem对象中的文本
如单元格上没有控件绑定,则必须要对单元格按第1项进行初始化成QTableWidgetItem对象,以正确操作单元格中的数据。
我的此练习编写的QTableWidgetEx继承类可以实现对表格的各种复制(支持多行多列)、粘贴、剪切,增加行,增加列等操作,复制时,会将选定的区域的不论是默认单元格类型还是控件类型一并进行复制粘贴,
此扩展类为实现对单元格(类型多)正确操作,引入了一数据列表self.itemData=[[行][列]],同表格的行列相对应,同时对每个单元格的数据也是个列表对象['单元格对象类型','单元格的文本',字典引用数据的KEY值,扩展1,扩展2],其中单元格对象类型可以是默认的整数,字符串等类型,也可以是标签控件等各种控件类型,单元格文本当不是控件时就是默认表格单元格对象QTableWidgetItem的内容,如是控件时,就是控件的相关内容,第3个参数为扩展类如是组合框等需要需初始化的控件时,组合框选项内容的数据来源于第三个参数指定的字典的KEY值,第4第5个参数未用,留作扩展功能用。
本扩展类没有经过专业的详细的评测,可能存在一些BUG,读者发现后可自行修复和扩展其他功能
本表格扩展类在主窗体中测试的界面如:
测试扩展类的主模块QTalbeWidgeEx_Test.py代码如下:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QByteArray
from g import * #导入自定义的全局变量模块
from QTableWidgetEx import * #导入自定义的表格控件扩展类
#测试表格控件扩展类主窗体
class MyWidget(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('测试QTableWidget的继承类QTableWidgetEx')
self.setGeometry(0, 0, 1920, 900)
self.statusbar = self.statusBar()
self.statusbar.showMessage('准备')
self.setStatusTip('准备...')
self.qimage = QImage('1.jpg') #底图
self.initUI()
def initUI(self):
self.vlayout = QVBoxLayout(self)
self.vlayout.setAlignment(Qt.AlignTop) # 设置 垂直布局 的对齐方式
self.btn_Test1=QPushButton('1初始化表格控件',self)
self.btn_Test1.setGeometry(1600,50,150,50)
self.btn_Test1.clicked.connect(self.test1)
self.btn_Test2=QPushButton('2测试代码',self)
self.btn_Test2.setGeometry(1600,150,150,50)
self.btn_Test2.clicked.connect(self.test2)
self.btn_Test3=QPushButton('3测试代码',self)
self.btn_Test3.setGeometry(1600,250,150,50)
self.btn_Test3.clicked.connect(self.test3)
font=QFont('宋体',9)
font.setBold(True)
font.setItalic(True)
font.setUnderline(True)
head=['全选', '默认','整数', '浮点数', '字符串', '组合框', '按纽', '标签','多行文本','自定义编辑框'] #定义表格列头
#定义一个30行10列的表格控件
#注意:下面一行代码会在主窗口创建一30行10列的表格控件,但此时控件全部单元格对象为None,无法进行任何操作,需要对每个单元格进行初始化
#初始化时,可以将单元格初始化为QT5默认的 QTableWidgetItem类型,也可以初始单元格为按纽,标签,组合框等控件。
#如果表格中的控件类型过多,会卡死,应在2000行以下为好,不用控件类型时不会限制
#为方便测试,初始化表格的代码放在了点击按纽1的方法中:def test1(self):
self.table01 = QTableWidgetEx(self,30,len(head),head,50,50,1400,800)
#self.vlayout.addWidget(self.table01)
#标签被单击时的信号槽函数
def lab_LeftClick(self,obj):
print(f'主窗体接收到了标签类发送来的信号signal_Leftclicked,鼠标点击相对控件内的坐标x={obj.startPoint.x()},y={obj.startPoint.y()},相对主窗体位置x={obj.startPoint.x()+obj.x()},y={obj.startPoint.y()+obj.y()}')
def lab_RightClick(self,obj):
print(f'主窗体接收到了标签类发送来的信号signal_Rightclicked,鼠标点击相对控件内的坐标x={obj.startPoint.x()},y={obj.startPoint.y()},相对主窗体位置x={obj.startPoint.x()+obj.x()},y={obj.startPoint.y()+obj.y()}')
def lab_MidClick(self,obj):
print(f'主窗体接收到了标签类发送来的信号signal_Midclicked,鼠标点击相对控件内的坐标x={obj.startPoint.x()},y={obj.startPoint.y()},相对主窗体位置x={obj.startPoint.x()+obj.x()},y={obj.startPoint.y()+obj.y()}')
def lab_LeftDropClick(self,obj):
print(f'主窗体接收到了标签类发送来的信号signal_LeftDropRelease,鼠标点击相对控件内的坐标x={obj.startPoint.x()},y={obj.startPoint.y()},相对主窗体位置x={obj.startPoint.x()+obj.x()},y={obj.startPoint.y()+obj.y()}')
def mouseMoveEvent(self, event):
#如果没有鼠标双击,执行
globalPos = self.mapToGlobal(event.pos())
x = globalPos.x()
y = globalPos.y()
self.text = '鼠标位置 {0:4d},{1:4d} '.format(x, y)
self.update()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
#painter.drawImage(QRect(10, 50, self.qimage.width(),self.qimage.height()), self.qimage) #会拉伸的在指定矩形中绘出: 本行为1:1
painter.drawImage(QRect(0,0, self.width(),self.height()-32), self.qimage)
painter.end()
####################################################################################################################################
def test1(self):
self.table01.setTableFrmType(QFrame.Panel,QFrame.Raised,1) #设置表格的界面类型
lst0=self.table01.itemData
self.table01.setTableColDataType(0,0,"QCheckBox") #第0列全为复选框
self.table01.setTableColDataType(1,1,"None") #第1列全为默认,单元格不创建任何控件
self.table01.setTableColDataType(2,1,"INT") #第2列全为整数
self.table01.setTableColDataType(3,1,"FLOAT") #第3列全为浮点数
self.table01.setTableColDataType(4,1,"STR") #第4列全为字符串或日期
self.table01.setTableColDataType(5,1,"QComboBox") #第5列全为组合框,内容选用字典ID=1
self.table01.setTableColDataType(6,0,"QPushButton") #第6列全为按纽
self.table01.setTableColDataType(7,0,"QLabel") #第7列全为标签:可显示图象
self.table01.setTableColDataType(8,0,"MUSTR") #第8列全为多行文本
self.table01.setTableColDataType(9,0,"OWNQLINEEDIT") #第9列全为自定义编辑框控件,
self.table01.updateAllDataToTable() #将数据刷新到表格控件中显示:未调用此函数前单元格对象中的值为None,即未初始化
self.table01.setItemBkCol(3,3,QColor(255,0,0)) #设置第3行第3列背景色为红色(表格的行列均从0起排号,实际是第4行第4列,余同)
self.table01.setTableRowBkCol(5,QColor(255,0,0)) #设置第4行背景色为红色
self.table01.setTableColBkCol(2,QColor(255,0,0)) #设置第1列背景色为红色
lab1 = self.table01.cellWidget(1, 7) #1行7列位置的标签控件类单元格导入一图象
lab1.bZoomImgSize=True #此单元格的标签加载的图象支持缩放到表格单元格大小
lab1.LoadFile('1.png')
lab2 = self.table01.cellWidget(2, 7) #2行7列位置的标签控件类单元格导入一图象
lab2.bZoomImgSize=False #此单元格的标签加载的图象按原大小在单元格是显示
lab2.LoadFile('1.png')
lab3 = self.table01.cellWidget(3, 7) #3行7列位置的标签控件类单元格导入GIF动画
lab3.LoadFile('1.gif')
self.table01.setRowHeight(3,100) #第3行要用标签控件放GIF动画,将行间距放大
lineedt=self.table01.cellWidget(1, 9) #1行9列位置的编辑框改为日期类型
mskstr = self.table01.getDicKeyValue(5) #从字典中得到掩码字串key=5
lineedt.setEditMask(mskstr) #设置此单元格的掩码
lstitem=['QCOMBOBOX','组合框控件',2,None,None] #参数2表示组合框选用类中字典KEY=2的列表项
self.table01.setItemData(2,2,lstitem) #只将第2行第2列采用组合框控件:组合框采用字典中的2序号中的列表值
lstitem=['QLabel','标签控件',0,None,None]
self.table01.setItemData(2,3,lstitem) #只将第2行第3列采用标签控件
self.table01.setItemTxt(9,1,'变化91') #对第9行1例的表格内容进行变化(此处为表格默认单元格,未在其上再创建有控件)
self.table01.setItemTxt(9,6,'变化96') #对第9行6例的表格内容进行变化(此处为一按纽控件)
self.table01.setItemTxt(9,7,'变化97') #对第9行7例的表格内容进行变化(此处为一标签控件)
self.table01.setItemTxt(9,8,'变化98') #对第9行8例的表格内容进行变化(此处为编辑框控件)
for i in range(10):
self.table01.setItemIco(10,i,'1.png') #第10行每列都设置一个图标头(图标只占单元格的左端部)
for n in range(9):
self.table01.setItemTxt(11,n,f'11行{n+1}列',f'11行{n+1}列')
self.table01.setItemTxt(11,9,'2024-08-18') #因第11行9列为掩码编辑框,需要按掩码格式赋值才行
for col in range(10):
txt=self.table01.getItemText(1,col)
print(f'行2列{col}值={txt}') #测试各单元格中的值的情况(单元格或控件)
def test2(self):
top_row, left_column, bottom_row, right_column = self.table01.getSelectedRanges() #得到当前选择的行列号
print(f'当前选择的矩形区域为:左上行列索引={top_row},{left_column},右下行列索引号={bottom_row},{right_column}')
def test3(self):
#self.table01.saveAllTableToItemData() #表格中的数据全部更新到列表数据中
self.table01.updateAllDataToTable() #用列表数据再一次重新刷新表格内容,检查,对表格进行了很多操作后,表格上的内容是否同表格绑定的self.itemdata是同步的
#########################################################################################
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec())
继承QTableWidget类的扩展类QTableWidgetEx的模块文件 QTableWidgetEx.py的代码如下:
#模块名:QTableWidgetEx.py:将QT5/6的表格控件窗体扩展子类
#包含类名: QTableWidgetEx: 继承QTableWidget类的扩展类:支持表格中的单元格可加载多种控件等功能
# QLabelEx:继承QLabel类的扩展类:可作为表格单元格的一个控件,自动加载图片,动画等功能
# QHeaderViewEx:可在表格列头显示一个复选框控件,用于全选一项组合框的功能
# OwnQTextEdit:继承QTextEdit类的扩展类:可作为表格单元格的一个控件,主要是去除了默认的粗边框,其他功能自行扩充
# OwnQLineEdit:继承QLineEdit类的扩展类:可作为表格单元格的一个控件,可自定义掩码,如密码等格式化输入
import os,sys
import PyQt5
from PyQt5 import *
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QByteArray
from PyQt5.QtWidgets import QApplication, QTableWidget, QHeaderView, QApplication, QTableWidgetItem
from PyQt5.QtGui import QBrush, QColor
from PyQt5.QtCore import Qt
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
import time
import math
import copy
import random
import g
lst_AlignType=['TL','TC','TR','CL','CC','CR','DL','DC','DR']
#将颜色字符串转为RGB
def color_string_to_rgb(color_string):
color = QColor(color_string)
return color.getRgb()
#########################################################################################################################
#重载表格类
class QTableWidgetEx(QTableWidget):
#__init__构造函数之前定义的变量为本类的公用全局变量,所有该类的实例化对象都可操作这些变量,并公和。用法:QTableWidgetEx.变量名,
DEF_STR='默认'
DEF_ITEMDATA=['STR','',0,None,None] #默认单元格绑定的数据格式
objcount=0 #
bItemCopy=False #单元格(可是一个区域的被选中的单元格)是否已被执行COPY
copyItems=[]
copyItemData=[]
signal_Leftclicked = QtCore.pyqtSignal(object) #自定信号,标签被左键单击,传回参数:控件对象本身
signal_Rightclicked = QtCore.pyqtSignal(object) #自定信号,标签被右键单击,传回参数:控件对象本身
signal_Midclicked = QtCore.pyqtSignal(object) #自定信号,标签被中键单击,传回参数:控件对象本身
signal_LeftDropRelease = QtCore.pyqtSignal(object) #自定信号,标签被左键拖动后释放,传回参数:控件对象本身
MAX_ROWCOUNT=100000 #最大支持行数:注意,如果表格中设置单元格为控件时过多,会造成运行卡顿,
MAX_COLCOUNT=100 #最大支持列数
#定义表格控件要用的的组合框等下拉列表要用到的数据,为单元格列表对象的第2个元素值
dic_TableItemData={0:[None], #0: KEY容错处理用
1:['选项01','选项02','选项03'], #1:示例-供表格中的下拉组合框用,选择项列表1
2:['选项A','选项B','选项C','选项D'], #2:示例-供表格中的下拉组合框用,选择项列表2
3:['1.png','2.png','3.png','4.png'], #3:示例-供表格中的标签、按纽等使用,显示图象
4:'999999999999;#', #4:示例-供表格中的自定义编辑框使用的掩码
5:'0000-00-00', #5:示例-供表格中的自定义编辑框使用的掩码
6:''
}
#初始化对角需传递的参数为父类, 行数,列数, 表头 创建矩形, 行宽 列宽 字体 字体颜色
def __init__(self, parent,rowNum,colNum, lstHead,x,y,w,h,rowWidth=20,colWidth=50,font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super(QTableWidgetEx, self).__init__(parent)
self.menuType=0 #当前右键菜单的弹出类型
self.curRowCount=rowNum #表格当前的总行数
self.curColCount=colNum #表格当前的总列数
self.header_field = lstHead #表头字段:支持动态增加
self.all_header0_chkbox = [] # 用来装行表头所有复选框 (第0列)
self.itemData=[[[],[]],[[],[]]] #定义同表格绑定的列表
self.itemData.clear()
#表格的单元格类型为:QTableWidgetItem
#self.lst_one=['QComboBox','内容',0]
#参数1,用于对应表格位采用的对象类型: 实际就是单行QlineEdit控件
#‘NONE’:默认数据,采用默认表格单元格,不在表格位置创建任何依附控件()
#'INT': 整型数据,采用默认表格单元格
#'FLOAT':浮点数据,采用默认表格单元格
#'STR':字符串类型,采用默认表格单元格
#'DATA':日期类型数据,采用单行OwnQLineEdit控件(设置掩码)
#'OWNQLINEEDIT':自定义字符串,采用单行OwnQLineEdit控件
#'QComboBox':组合框控件, 参数3表示此组合框在字典dic_TableItemData KEY对应的列表(选择项)
#'QCheckBox':复选框控件, 参数3无意义
#'QLabel':采用扩展标签控件QLabelEx, 参数3表示标签可选用的图象列表,在字典dic_TableItemData KEY对应的二进制数据(已将文件加载到内存后)
#'QPushButtom':采用按纽, 参数3表示按纽选用图标在字典dic_TableItemData KEY对应的二进制数据(已将文件加载到内存后)
#'
#
# 参数3为dic_TableItemData中的KEY值
self.item_one=copy.deepcopy(QTableWidgetEx.DEF_ITEMDATA) #对列表要用deepcopy来赋值,防止列表变量默认是同内存
item = QTableWidgetItem("") #空表格单元
for row in range(rowNum):
item_onerow=[['STR','',0,None,None]]
item_onerow.clear()
for col in range(colNum):
self.item_one[0]=str('STR')
self.item_one[1]=str(row) #示例测试用,应初始化为''
item_onerow.append(copy.deepcopy(self.item_one))
self.itemData.append(copy.deepcopy(item_onerow))
self.type=0 #表格类型:0=普通表格 1=第一列为全复选框表格,2=全自定义表格
self.setGeometry(x,y,w,h)
self.ctlRect=QRect(x,y,w,h) #控件的矩形区域
self.setFixedWidth(w) # 表格宽度
self.setFixedHeight(h) # 表格高度
self.setRowCount(rowNum) # 表格行数
self.setColumnCount(colNum) #表格列数
self.alignFlags=Qt.AlignVCenter | Qt.AlignLeft #对齐方式:竖向居中,前后居左
self.fontCol=fcolor #字体颜色
self.bkCol=QColor(255,255,255) #如设置不透明时的标签背景颜色
self.setFont(font)
palette = QPalette()
palette.setColor(QPalette.WindowText, self.fontCol) #设置字体颜色
self.setPalette(palette)
self.initTable()
#对表格进一步初始化
def initTable(self,bAlRow=True):
self.setTableHeader(QFrame.Box,QFrame.Sunken,'red','greenyellow') #设置表格头内容和样式
self.setAlternatingRowColors(bAlRow) # 交替行颜色
#self.setTableData() #设置表格初始化数据:在主窗体中调用
#设置表格某个单元格的数据类型:注本函数应在数据初始化前调用,后期要更改单元格控件类型时用setItemData方法
def setTableDataType(self,row,col,dicID,typestr=None):
if(col>(self.curColCount-1)):return
if(col>(self.curRowCount-1)):return
if typestr!=None:
typestr=typestr.upper()
self.itemData[row][col][0]=typestr.upper()
self.itemData[row][col][2]=dicID
if typestr=="OWNQLINEEDIT" or typestr=='QLINEEDIT': #自定义编辑框控件,用掩码显示内容
pass
elif typestr=="MUSTR" or typestr=='QTEXTEDIT': #对多行文本,要支持多行文本
pass
elif typestr=="QCHECKBOX" :
self.itemData[row][col][1]='复选框'+str(row)
elif typestr=="QCOMBOBOX" :
pass
elif typestr=="QLABEL" :
self.itemData[row][col][1]='标签'+str(row)
elif typestr=="QPUSHBUTTON" :
self.itemData[row][col][1]='按纽'+str(row)
#设置表格某列为控件类型
def setTableColDataType(self,col,dicID,objType=None):
if(col>(self.curColCount-1)):return
for rowID in range(self.curRowCount):
self.setTableDataType(rowID,col,dicID,objType)
#以列表参数的形式设置表格各列的数据类型:lst_type=[['QCheckBox',0],['QComboBox',1],['QLabel',0],.........]
#传入列表每个列表无素的第0索引支持以下字符串:'QCheckBox'=复选框控件,'QComboBox'=组合框控件,'QLabel'=标签控件,'QPushButton'=按纽控件,,‘INT’=整数,'FLOAT'=浮点数,'STR'=字符串,'DATE'=日期
def setTableAllDataType(self,lst_types):
col=0
for lst in lst_types:
self.setTableDataType(col,lst[0],lst[1])
#设置表格控件外观类型
def setTableFrmType(self,shape=QFrame.Box,shadow=QFrame.Sunken,linew