刚完成了一个基于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代码如下:
mport 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(QTableWidget, 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,linewidth=1):
self.setFrameShape(shape) # 设置表格框架样式
self.setFrameShadow(shadow) # 设置边框的样式
self.setLineWidth(linewidth) # 设置边框的线条宽度
#‘NONE’:默认数据,采用表格控件默认的,不在表格位置创建任何依附控件()
#'INT': 原计划用整型数据,采用单行QlineEdit控件,现改为同默认
#'FLOAT':浮点数据,原计划采用单行QlineEdit控件,现改为同默认
#'DATA':日期类型数据,原计划采用单行QlineEdit控件,现改为同默认
#'STR':字符串类型,原计划采用单行QlineEdit控件,现改为同默认
#'MUSTR':多行字符串类型,采用多行QTextEdit控件
#'PASSWORD':遮蔽类型字符串,用单行QlineEditEx控件
#'OWNQLINEEDIT':自定义字符串,用单行QlineEdit控件
#'QComboBox':组合框, 参数3表示此组合框在字典dic_TableItemData KEY对应的列表(选择项)
#'QCheckBox':复选框, 参数3无意义
#'QLabel':标签, 参数3表示标签可选用的图象列表,在字典dic_TableItemData KEY对应的二进制数据(已将文件加载到内存后)
#'QPushButtom':按纽, 参数3表示按纽选用图标在字典dic_TableItemData KEY对应的二进制数据(已将文件加载到内存后)
# 设置第0列为复选框列
def setTableHeader(self,shape=QFrame.Box,shadow=QFrame.Sunken,textCol='black',bkCol='White'):
self.curRowCount = self.rowCount()
headlen=len(self.header_field)
header = QHeaderViewEx(self,0) # 实例化自定义表头,传入表格对象本身
self.setHorizontalHeader(header) # 设置表头
self.setHorizontalHeaderLabels(self.header_field) # 设置行表头字段
self.setColumnWidth(0,80) # 设置第0列宽度
header.setHeaderType(shape,shadow,1,textCol,bkCol) # 设置表头显示类型
#g.gf_setObjCol(header,'QHeaderViewEx','blue','yellow') # 用g.py模块中的全局函数来改表头颜色也可成功
header.select_all_clicked.connect(header.change_state) # 行表头复选框单击信号与槽
#设置标签中的文字/图片/GIF动画对齐方式Qt.AlignLeft:左对齐Qt.AlignRight:右对齐 Qt.AlignTop:顶部对齐Qt.AlignBottom:底部对齐Qt.AlignHCenter:水平居中Qt.AlignVCenter:垂直居中Qt.AlignCenter:同时水平和垂直居中
def SetAlign(self,at='TL'): #
at=at.upper()
self.alignFlags=Qt.AlignTop | Qt.AlignLeft
if(at=='TL'): self.alignFlags=Qt.AlignTop | Qt.AlignLeft
elif(at=='TC'): self.alignFlags=Qt.AlignTop | Qt.AlignHCenter
elif(at=='TR'): self.alignFlags=Qt.AlignTop | Qt.AlignRight
elif(at=='CL'): self.alignFlags=Qt.AlignVCenter | Qt.AlignLeft
elif(at=='CC'): self.alignFlags=Qt.AlignVCenter | Qt.AlignHCenter
elif(at=='CR'): self.alignFlags=Qt.AlignVCenter | Qt.AlignRight
elif(at=='DL'): self.alignFlags=Qt.AlignBottom | Qt.AlignLeft
elif(at=='DC'): self.alignFlags=Qt.AlignBottom | Qt.AlignHCenter
elif(at=='DR'): self.alignFlags=Qt.AlignBottom | Qt.AlignRight
else:self.alignFlags=Qt.AlignVCenter | Qt.AlignLeft
self.setAlignment(self.alignFlags)
self.setText(self.text) #有时并没有出现对齐效果,只能采用先清除再重加载的方式
#得到类字典指定key的值
def getDicKeyValue(self,keyID,defvalue=None):
lst_item = QTableWidgetEx.dic_TableItemData.get(keyID,defvalue) #用get函数来得到对应字典key的值,如果没有,得到默认值,防报错
return lst_item
#清除表格单元格的内容:如是控件,则清控件,如不是控件是默认的表格单元格,只清内容为空
def clearCell(self, row, col):
if(row>0 and row< (self.curRowCount-1) and col>0 and col< (self.curColCount-1)):
item = self.item(row, col) # 清除指定行列的单元格内容
if(item is not None):
item.setText('') #非控件单元格不作移除,但要删除内容
self.itemData[row][col][1]='' #同时清除绑定的数据列表数据
self.itemData[row][col][0]='STR'
self.removeCellWidget(row,col) #移除原位置上的单元格控件
#self.takeItem(row,col) #移除原位置上的单元格控件
#设置指定单元格的数据:控件类时,单元格原内容为None,非控件类,原单元格要初始化。
#lstItemData=[单元格类型,'单元格文本内容',单元格选用字典KEY,扩展1,扩展2]
def setItemData(self,row,col,lstItemData,bNew=False):
if(row>self.curRowCount-1 or row<0 and col>self.curColCount and col<0) :
print('行或列号有误')
return
lstOrdData=self.itemData[row][col] #原始表格数据,设置后,后替换原单元格数据
self.itemData[row][col]=copy.deepcopy(lstItemData)
typestr=lstItemData[0].upper()
itemtext=lstItemData[1]
dicID=lstItemData[2]
self.removeCellWidget(row,col) #移除原位置上的单元格控件
self.clearCell(row,col) #此函数会清空绑定数据列表中的内容,
self.itemData[row][col][0]=typestr #需对绑定的数据重新赋值
self.itemData[row][col][1]=itemtext
self.itemData[row][col][2]=dicID
if(typestr==None or typestr=='None' or typestr=="INT" or typestr=="FLOAT" or typestr=="STR" or typestr=="DATE"):
if bNew: itemtext=''
item = QTableWidgetItem(itemtext)
self.setItem(row,col,item) #!!!!重要:必须用此行代码才真正初始化了非控件类的单元格,否则单元格可以录入数据,但实际值为None
else: #是控件类的单元格时
self.setItem(row,col,None) #对控件类单元格,因控件覆盖在单元格上,应将原单元格设置为None,防止单元格有内容时同控件显示重叠
if typestr=="OWNQLINEEDIT" or typestr=="QLINEEDIT": #自定义编辑框控件,用掩码显示内容
lineedit=OwnQLineEdit(self,'') #当前定义的是默认字符串类型:
if(dicID==4 or dicID==5): #只有指定的值才表示是有掩码的编辑框
mskstr = self.getDicKeyValue(dicID) #从字典中得到掩码字串
lineedit.setEditMask(mskstr)
self.setCellWidget(row,col,lineedit)
if not bNew: lineedit.setText(itemtext)
elif typestr=="MUSTR" or typestr=="QTEXTEDIT": #对多行文本,要支持多行文本
if bNew: itemtext=''
textedit=OwnQTextEdit(itemtext)
self.setCellWidget(row,col,textedit)
if not bNew:textedit.setText(itemtext)
elif typestr=="QCHECKBOX" : #复选框控件
if(bNew):
itemtext='复选框'+str(row)
self.itemData[row][col][1]=itemtext
checkbox = QCheckBox(itemtext) #创建按纽并设置文本
self.all_header0_chkbox.append(checkbox) #以支持在列表头点击复选框时可全选行头的复选框
checkbox.clicked.connect(lambda state, checkbox=checkbox: self.checkbox_clicked(checkbox)) # 连接信号槽
self.setCellWidget(row,col,checkbox) # 将按纽添加到表格的单元格中
elif typestr=="QCOMBOBOX" : #组合框控件
comboBox = QComboBox()
lst = self.getDicKeyValue(dicID)
self.setComboBoxItem(comboBox,lst)
self.setCellWidget(row,col,comboBox)
comboBox.setCurrentIndex(-1)
elif typestr=="QLABEL" : #自定义标签控件
if(bNew):
itemtext='标签'+str(row)
self.itemData[row][col][0]=itemtext
label = QLabelEx(self,itemtext) #定义标签控件
self.setCellWidget(row,col,label)
label.signal_Leftclicked.connect(lambda state, label=label: self.label_clicked(label)) # 连接信号槽:因原生标签没有单位信号,用继承标签类
elif typestr=="QPUSHBUTTON" : #按纽控件
if(bNew):
itemtext='控纽'+str(row)
self.itemData[row][col][1]=itemtext
button = QPushButton(itemtext) # 创建按纽并设置文本
button.clicked.connect(lambda state, button=button: self.button_clicked(button)) # 连接信号槽
item = QTableWidgetItem()
self.setCellWidget(row,col, button) # 将按纽添加到表格的单元格中
item.setToolTip(itemtext)
else: #如控件有错,还是建立标准默认的单元格
if bNew:itemtext='默认'
item = QTableWidgetItem(itemtext) #默认简化单行文本
self.setItem(row,col,item)
#设置单元格的文本(如单元格中原存在文本,会出现重影:原因是控件显示的文本和表格ITEM也显示文本相重叠了,只显示一个就行)
def setItemTxt(self,row,col,objtxt,tooltiptxt=''):
item = QTableWidgetItem(objtxt)
if(rowself.curRowCount-1 or row<0):
print('行号错误')
return
lstRow=self.itemData[row]
col=0
for lstOneItem in lstRow: ##self.lst_one=['INT等数据类型','内容',0,None,None]
self.setItemData(row,col,lstOneItem,bNew)
col+=1
#用当前self.itemData数据重新刷新设置表格中的数据
def updateAllDataToTable(self):
for row in range(self.curRowCount):
self.setOneRowTableData(row)
#将当前表格中的数据回传到对应的self.itemData数据列表中存储
def saveAllTableToItemData(self):
if(self.curRowCount>0 and self.curColCount>0):
for row in range(self.curRowCount):
for col in range(self.curColCount):
txt=self.getItemText(row,col)
self.itemData[row][col][1]=txt
#保存指定行列中的文本到self.itemData数据列表中
def saveTableToItemData(self,row,col):
if(self.curRowCount>0 and self.curColCount>0):
if(rowself.curRowCount-1):return None
lstoneRow=[]
item_one=[]
for col in range(self.curColCount):
item_one=copy.deepcopy(self.itemData[row][col])
s=item_one[0]
#if(s=='NONE' or s=="INT" or s=="FLOAT" or s=="STR" or s=="DATE"):
if(not bCopy):
item_one[1]='加' #非COPY模式下,内容为空
lstoneRow.append(copy.deepcopy(item_one))
return lstoneRow
#得到指定列的全部数据列表
def getColItems(self,col,bCopy=False):
if(col>self.curColCount-1):return None
lstoneCol=[]
item_one=[]
for row in range(self.curRowCount):
item_one=copy.deepcopy(self.itemData[row][col])
s=item_one[0]
#if(s=='NONE' or s=="INT" or s=="FLOAT" or s=="STR" or s=="DATE"):
if(not bCopy):
item_one[1]='' #非COPY模式下,内容为空
lstoneCol.append(item_one)
return lstoneCol
#在当前行前插入一行数据
def InsertItemRow(self,row,bCopy=False):
if(row>self.curRowCount-1 or row<0):
return None
lstItems=self.getRowItems(row-1,bCopy) #得到当行指定行的上一行的数据类型(内容为空)
self.insertRow(row) #在指定行位置上插入一空行
self.curRowCount+=1
self.itemData.insert(row,lstItems) #设置绑定的数据列表也插入一行
self.setOneRowTableData(row) #刷新新插入的一行数据内容
#在表格末尾增加一行数据
def AppendItemRow(self):
lstItems=self.getRowItems(self.curRowCount-1) #得到最后一行的数据
self.insertRow(self.curRowCount)
self.curRowCount+=1
self.itemData.append(copy.deepcopy(lstItems))
self.setOneRowTableData(self.curRowCount-1) #刷新最后一行数据显示
#在当前列前插入一列数据
def InsertItemCol(self,col,bCopy=False):
if(col>self.curColCount-1 or col<0):
return None
lstItems=self.getColItems(col-1) #得到当前列前一列的数据类型(内容为空或复制了前一列)
self.insertColumn(col) #在指定列位置上插入一空列
for ci in range(self.curRowCount):
self.itemData[ci].insert(col,lstItems[ci]) #设置绑定的数据列表也插入一列
self.setItemData(ci,col,lstItems[ci])
self.curColCount+=1
#删除指定行的数据
def DelOneRowItem(self,row):
if(row>self.curRowCount-1 or row<0):
return None
self.removeRow(row)
self.itemData[row].pop(row)
self.curRowCount-=1
#删除指定列数据
def DelOneColItem(self,col):
if(col>self.curColCount-1 or col<0):
return None
self.removeColumn(col)
for ci in range(self.curRowCount):
self.itemData[ci].pop(col)
self.curColCount-=1
#允许、禁止表格可以被编辑
def setTableEdited(self,bEdit):
if(bEdit):
self.setEditTriggers(QTableWidget.NoEditTriggers) # 禁止表格内编辑
else:
pass
#按纽控件槽函数
def button_clicked(self, button):
print(f'单击的按纽控件: {button.text()}')
#复选按纽控件槽函数
def checkbox_clicked(self, button):
print(f'单击的复合框控件: {button.text()}')
#标签控件槽函数
def label_clicked(self, label):
print(f'单击的标签控件: {label.text}')
#设置单元格左端显示为图标
def setItemIco(self,row,col,icofile):
icon = QIcon(icofile)
item = QTableWidgetItem()
item.setIcon(icon)
self.setItem(row, col, item)
#得到当前选择的表格单元格:
def getSelectedeItems(self):
selectedItems = self.selectedItems()
if not selectedItems: return None
rowMin,colMin,rowMax,colMax=self.getSelectedRanges()
selLstItems=[]
selLstItemData=[]
#selItem=['STR','',0,None,None]
for row in range(rowMin,rowMax+1):
selRowItems=[] #一行单元格
selRowItemData=[] #一行数据所对应的数据
for col in range(colMin,colMax+1):
self.saveTableToItemData(row,col) #先将表格中显示的文本更新回绑定的数据中(因有此单元格是用了控件,并不是用的默认的,如在控件扣相关事件中处理了更新数据,此行就没必要了)
item=self.item(row,col) #表格对象
selRowItems.append(item)
itemdata=self.itemData[row][col] #表格对象绑定的数据
selRowItemData.append(itemdata)
selLstItems.append(selRowItems)
selLstItemData.append(selRowItemData)
print(f'当前得到的表格数据共{rowMax-rowMin+1}行,{colMax-colMin+1}列')
return selLstItems,selLstItemData
#得到当前选择的表格区域
def getSelectedRanges(self):
# 获取所有选中的项目
selected_items = self.selectedItems()
# 如果没有选中的项目,返回空区域
if not selected_items:
return -1,-1,-1,-1
# 获取选中项目的最小和最大行和列索引
top_left_cell = selected_items[0]
bottom_right_cell = selected_items[-1]
top_row = top_left_cell.row()
left_column = top_left_cell.column()
bottom_row = bottom_right_cell.row()
right_column = bottom_right_cell.column()
print(f'当前选择表格左上行号列号={top_row}, {left_column}, 右下行列号={bottom_row}, {right_column}')
# 返回选中区域
return (top_row, left_column, bottom_row, right_column)
#为表格控件增加右键菜单
def contextMenuEvent(self, event):
menu = QMenu(self) # 创建一个菜单
if(self.menuType==0) : #0类型的菜单
# 添加动作到菜单
act_copyItem = menu.addAction("复制单元格") #支持单元格为一区域
act_pasteItem = menu.addAction("粘贴单元格")
act_cutItem = menu.addAction("剪切单元格")
act_delItem = menu.addAction("清除单元格")
act_initItem = menu.addAction("初始化单元格")
menu.addSeparator()
act_insertRow = menu.addAction("插入一行数据")
act_insertCol = menu.addAction("插入一列数据")
act_appendRow = menu.addAction("表格未尾增加一行数据")
act_delOneRow = menu.addAction("删除当前行数据")
act_delOneCol = menu.addAction("删除当前列数据")
menu.addSeparator()
act_defItem = menu.addAction("默认单元格类型")
act_chkBoxItem = menu.addAction("复选框单元格类型")
act_btnItem = menu.addAction("按纽单元格类型")
act_labelItem = menu.addAction("标签单元格类型")
act_lineEditItem = menu.addAction("编辑框单元格类型")
act_textEditItem = menu.addAction("多行编辑框单元格类型")
act_comboBoxEditItem = menu.addAction("组合框单元格类型")
elif(self.menuType==1):
act_point2Num = menu.addAction("设置小数点位数为2位")
act_point3Num = menu.addAction("设置小数点位数为3位")
# 将菜单显示在鼠标点击的位置
action = menu.exec(event.globalPos())
# 处理动作:需要用异常处理代码
try:
top_row, left_column, bottom_row, right_column = self.getSelectedRanges() #得到当前选择的行列号
if action == act_copyItem:
print("复制单元格")
selLstItems,selLstItemData = self.getSelectedeItems()
if selLstItems==None: return
QTableWidgetEx.copyItems=selLstItems
QTableWidgetEx.copyItemData=copy.deepcopy(selLstItemData)
QTableWidgetEx.bItemCopy=True
elif action == act_pasteItem:
print("粘贴单元格")
if(QTableWidgetEx.bItemCopy):
copyRow=0
for row in range(top_row,bottom_row+1):
copyCol=0
for col in range(left_column,right_column+1):
if copyCol>(len(QTableWidgetEx.copyItems[0])-1):
break #复制的对象列数据不足时,退出列不再复制
print(f'copyrow={copyRow},copycol={copyCol}')
item=QTableWidgetEx.copyItems[copyRow][copyCol]
itemData=QTableWidgetEx.copyItemData[copyRow][copyCol]
self.clearCell(row,col)
#self.setItem(row,col,item)
self.setItemData(row,col,itemData)
copyCol+=1
copyRow+=1
if copyRow>(len(QTableWidgetEx.copyItems)-1):
copyRow=0 #复制的对象行数不足时,循环再从0开始叠加
continue
elif action == act_cutItem:
print("剪切单元格")
selLstItems,selLstItemData = self.getSelectedeItems()
if selLstItems==None: return
QTableWidgetEx.copyItems=selLstItems
QTableWidgetEx.copyItemData=copy.deepcopy(selLstItemData) #剪切前将数据先COPY备用
QTableWidgetEx.bItemCopy=True
top_row, left_column, bottom_row, right_column=self.getSelectedRanges() #得到当前选择的行列号
#清除选定的表格内容
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
elif action == act_delItem:
print("清除单元格")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
elif action == act_initItem:
print("初始化单元格")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
item=self.item(row,col)
obj=self.cellWidget(row, col)
if(item is None and obj is not None): #单元格没用被初始化且单元格位置上没有覆盖控件时才进行初始化
self.setItem(row,col,None)
elif action == act_insertRow:
print("插入一行数据")
currow=self.currentRow()
if(currow>0):
self.InsertItemRow(currow)
elif action == act_insertCol:
print("插入一列数据")
curcol=self.currentColumn()
if(curcol>0):
self.InsertItemCol(curcol,True)
elif action == act_appendRow:
print("表格未尾增加一行数据")
self.AppendItemRow()
elif action == act_delOneRow:
print("删除当前行数据")
currow=self.currentRow()
if(currow>0):
self.DelOneRowItem(currow)
elif action == act_delOneCol:
print("删除当前列数据")
curcol=self.currentColumn()
if(curcol>0):
self.DelOneColItem(curcol)
elif action == act_defItem:
print("默认单元格类型")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
item = QTableWidgetItem(QTableWidgetEx.DEF_STR)
self.setItem(row,col,item)
lstdata=['STR',QTableWidgetEx.DEF_STR,0,None,None] #将默认值赋值组列表数据
#item=self.item(row,col)
self.itemData[row][col]=copy.deepcopy(lstdata)
elif action == act_chkBoxItem:
print("复选框单元格类型")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
lstdata=['QCheckBox','复选框',0,None,None] #创建单元格的复选框
self.setItemData(row,col,lstdata)
elif action == act_btnItem:
print("按纽单元格类型")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
lstdata=['QPushButton','按纽',0,None,None] #创建单元格为按纽控件
self.setItemData(row,col,lstdata)
elif action == act_labelItem:
print("标签单元格类型")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
lstdata=['QLabel','标签',0,None,None] #创建单元格为标签控件
self.setItemData(row,col,lstdata)
elif action == act_lineEditItem:
print("编辑框单元格类型")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
lstdata=['OWNQLINEEDIT','编辑框',5,None,None] #创建单元格为单行编辑框控件,掩码为字典的key=5(仅示例,根据实际修改)
self.setItemData(row,col,lstdata)
elif action == act_textEditItem:
print("多行编辑框单元格类型")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
lstdata=['MUSTR','多行编辑框',0,None,None] #创建单元格为多行编辑框控件
self.setItemData(row,col,lstdata)
elif action == act_comboBoxEditItem:
print("组合框单元格类型")
for row in range(top_row,bottom_row+1):
for col in range(left_column,right_column+1):
self.clearCell(row,col)
lstdata=['QComboBox','',2,None,None] #创建单元格的组合框,组合框默认选项为字典中的KEY=2的列表
self.setItemData(row,col,lstdata)
else:
print("其他类型。。。。。")
except Exception as e: # 如果发生异常,执行这里的代码
reply = QMessageBox.question(self, '信息',
"表格还没有被初始化,不能执行下面的代码,请在主窗口中调用相关初始化代码后再执行相关操作?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
finally: #无论是否发生异常,都会执行finally中的代码
pass
###############################################################################
#定自义扩展表格头类:带复选框等功能的表头 ,当前类只是初始化了在第0列位置增加了一复选框
class QHeaderViewEx(QHeaderView):
# 自定义 复选框全选信号
select_all_clicked = pyqtSignal(bool,int)
# 以下这4个变量控制列头复选框的样式,位置以及大小(下面的位置刚好画在第一列上,如其他列上也要使用,需深化代码)
_x_offset = 0
_y_offset = 0
_width = 20
_height = 20
def __init__(self,parent,headType=0,orientation=Qt.Horizontal):
super(QHeaderViewEx, self).__init__(orientation, parent)
self.isOn = False
self.table=parent #表格本身对象为开表头的父类
self.checkBoxInCol=0 #当前要创建的复选框所位于的列号
def paintSection(self, painter, rect, logicalIndex):
painter.save()
super(QHeaderViewEx, self).paintSection(painter, rect, logicalIndex)
painter.restore()
self._y_offset = int((rect.height() - self._width) / 2.)
if logicalIndex == 0:
option = QStyleOptionButton()
option.rect = QRect(rect.x() + self._x_offset, rect.y() + self._y_offset, self._width, self._height)
option.state = QStyle.State_Enabled | QStyle.State_Active
if self.isOn:
option.state |= QStyle.State_On
else:
option.state |= QStyle.State_Off
self.style().drawControl(QStyle.CE_CheckBox, option, painter)
def mousePressEvent(self, event):
clickColNum=-1 #当前鼠标单击的列上的控件位置
index = self.logicalIndexAt(event.pos())
if 0 == index:
x = self.sectionPosition(index)
if x + self._x_offset < event.pos().x() < x + self._x_offset + self._width and self._y_offset < event.pos().y() < self._y_offset + self._height:
clickColNum=0
if self.isOn:
self.isOn = False
else:
self.isOn = True
# 当用户点击了行表头复选框,发射 自定义信号 select_all_clicked()
self.select_all_clicked.emit(self.isOn,clickColNum)
self.updateSection(0)
super(QHeaderViewEx, self).mousePressEvent(event)
# 自定义信号当单击表头复选框选择全部行时 select_all_clicked 的槽方法
def change_state(self, isOn,clickColID):
# 如果行表头复选框为勾选状态
if(clickColID==0): #单击的是第0列的复选框
if isOn:
# 将所有的复选框都设为勾选状态
for i in self.table.all_header0_chkbox:
i.setCheckState(Qt.Checked)
else:
for i in self.table.all_header0_chkbox:
i.setCheckState(Qt.Unchecked)
# 设置表头为彩色:仅示例,不太好看
def setHeaderType(self,shape=QFrame.Box,shadow=QFrame.Sunken,linewidth=1,txtCol='black',bkCol='white'):
self.setFrameShape(shape) # 设置表格框架样式
self.setFrameShadow(shadow) # 设置边框的样式
self.setLineWidth(linewidth) # 设置边框的线条宽度
stylestr="QHeaderView::section {background-color: "+bkCol+";color:"+txtCol+"};} "
self.setStyleSheet(stylestr)
#设置一代理类,用于表格控件中显示指定格式的字符串(-----------暂未用此类-------)
class IntegerDelegate(QAbstractItemDelegate):
def createEditor(self, parent, option, index):
editor = QTableWidget(parent)
validator = QIntValidator()
editor.setItemDelegate(self)
editor.setValidator(validator)
return editor
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.EditRole)
editor.setItem(value)
def setModelData(self, editor, model, index):
value = editor.item(editor.currentItem())
model.setData(index, value, Qt.EditRole)
#自定义格式的编辑框控件,可用为自定义的表格单元格
class OwnQTextEdit(QTextEdit):
def __init__(self,parent,txtType='',font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super(QTextEdit, self).__init__(parent)
self.txtType=txtType.upper() #编辑框的文本类型:'INT'=整型 'FLOAT'=浮点数,'DATE'=日期,'TIME'=时间,'PASSWORD'=密码
self.setFrameShape(QFrame.NoFrame)
#自定义格式的编辑框控件,可用为自定义的表格单元格
class OwnQLineEdit(QLineEdit):
def __init__(self,parent,txtType='',font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super(QLineEdit, self).__init__(parent)
self.txtType=txtType.upper() #编辑框的文本类型:'INT'=整型 'FLOAT'=浮点数,'DATE'=日期,'TIME'=时间,'PASSWORD'=密码
self.setFrame(QFrame.NoFrame)
self.setEditMask(txtType)
#设置文本框的掩码类型
def setEditMask(self,txtType=''):
if(len(txtType)>0):
self.styleTxt=txtType
if(self.txtType=='INT'):
styleTxt='999999999999;#'
elif(self.txtType=='FLOAT'):
styleTxt='99999999.999'
elif(self.txtType=='DATE'):
styleTxt='9999-99-99'
elif(self.txtType=='TIME'):
styleTxt='99:99:99'
elif(self.txtType=='PASSWORD'):
self.setEchoMode(QLineEdit.Password)
return
else:
self.setInputMask(txtType)
#########################################################################################################################
#重载标签类,标签可透明显示图像,用于在窗体上加载小分部图像
lst_ImgExName=['BMP','JPG','JPEG','PNG','TIF','TIFF','TGA','WMF','SVG','HEIF','RAW','WEBP']
lst_MovExName=['GIF','AVI','MPEG','MP4','MOV','MKV','WMV','FLV','RMVB','RM','RAM']
lst_AlignType=['TL','TC','TR','CL','CC','CR','DL','DC','DR']
class QLabelEx(QLabel):
objcount=0 #
signal_Leftclicked = QtCore.pyqtSignal(object) #自定信号,标签被左键单击,传回参数:控件对象本身
signal_Rightclicked = QtCore.pyqtSignal(object) #自定信号,标签被右键单击,传回参数:控件对象本身
signal_Midclicked = QtCore.pyqtSignal(object) #自定信号,标签被中键单击,传回参数:控件对象本身
signal_LeftDropRelease = QtCore.pyqtSignal(object) #自定信号,标签被左键拖动后释放,传回参数:控件对象本身
#初始化对角需传递的参数为 父类,创建矩形,内容, 控件透明度 字体 字体颜色 背景颜色
def __init__(self,parent,text='',transt=1.0,font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super(QLabel, self).__init__(parent)
self.type='TXT' #标签控件的类型,'TXT'=纯文本标签,‘IMG'=可显示图片标签 'MOV':可播放动画标签
#self.setGeometry(x,y,w,h)
#self.ctlRect=QRect(x,y,w,h) #控件的矩形区域
self.imgRect=QRect() #如果控件加载了图象或视频自身尺寸的矩形区域
self.bDrawRect = False #是否在标签控件外边画出矩形框
self.rectCol=QColor(255,0,0) #画矩形边框的颜色
self.rectPenWidth=2 #画矩形边框的线宽度
self.bChgCtlRect=False #如果self.ctlRect不能满足文字、图象的矩形区域时,是否允许控件变化其矩形来适应文字或图象要求的矩形区域
self.move_Flag = False #标签控件是否可以主窗体上拖动:对窗体元素,应设置为False
self.bZoomImgSize=True #控件的矩形区同图象的矩形区不相符时,是否允许图象或视频自动缩放以适应控件矩形区
self.setScaledContents(self.bZoomImgSize) # 设置标签的图片,设置True时图片自适应控件,为False时,只显示控件范围图片
self.setAutoFillBackground(False) #不允许自动填充背景底色
self.text=text #标签是文本类型时显示的内容
self.drawText=text #标签是图片或视频类型时显示的内容
self.alignFlags=Qt.AlignTop | Qt.AlignLeft #对齐方式
self.bDrawTxt = False #显示图片的同时,是否将self.drawText画到图象上
self.fontCol=fcolor #字体颜色
self.bkCol=QColor(255,255,255) #如设置不透明时的标签背景颜色
self.setFont(font)
palette = QPalette()
palette.setColor(QPalette.WindowText, self.fontCol) #设置字体颜色
self.setPalette(palette)
self.SetTransparent(transt) #设置控件的透明度,1=不透明,0=完全透明
self.setText(text)
self.global_X=self.gobal_Y=0 #标签相对屏幕左上点(0,0)的坐标
self.startPoint=QPoint() #鼠标在标签控件上压下开始的坐标点
self.endPoint=QPoint() #鼠标在标签控件上压下结束时的坐标点
self.mouse_X=self.mouse_Y=0 #鼠标在标签控件上相对标签控件范围的坐标
self.origin_x=self.origin_y=0
self.globalmouse_X=self.globalmouse_Y=0 #鼠标在标签控件上相对屏幕左上点(0,0)的坐标
self.oldPos=QPoint() #移动前标签控件的位置
self.curImgfilename=''
self.curMovFileName=''
self.curData=None #当标签是加载的图片或动画时,将文件同容加载到内存中再显示,避免频繁读写文件
self.image=QImage()
self.curRotAngle=0.0 #图片当前旋转角度(角度,非弧度,顺时针为正)
self.gifSpeed=200 #当前要播放的GIF动画的速度
self.drawtxtX=self.drawtxtY=0
#如要要不透明的标签,设置标签背景色
def setBkCol(self,bkcol=QColor(255,255,255)):
self.bkCol=bkcol
palette = QPalette()
self.setAutoFillBackground(True)
palette.setColor(QPalette.Background, self.bkCol)
self.setPalette(palette)
#设置标签中的文字/图片/GIF动画对齐方式Qt.AlignLeft:左对齐Qt.AlignRight:右对齐 Qt.AlignTop:顶部对齐Qt.AlignBottom:底部对齐Qt.AlignHCenter:水平居中Qt.AlignVCenter:垂直居中Qt.AlignCenter:同时水平和垂直居中
def SetAlign(self,at='TL'): #
at=at.upper()
self.alignFlags=Qt.AlignTop | Qt.AlignLeft
if(at=='TL'): self.alignFlags=Qt.AlignTop | Qt.AlignLeft
elif(at=='TC'): self.alignFlags=Qt.AlignTop | Qt.AlignHCenter
elif(at=='TR'): self.alignFlags=Qt.AlignTop | Qt.AlignRight
elif(at=='CL'): self.alignFlags=Qt.AlignVCenter | Qt.AlignLeft
elif(at=='CC'): self.alignFlags=Qt.AlignVCenter | Qt.AlignHCenter
elif(at=='CR'): self.alignFlags=Qt.AlignVCenter | Qt.AlignRight
elif(at=='DL'): self.alignFlags=Qt.AlignBottom | Qt.AlignLeft
elif(at=='DC'): self.alignFlags=Qt.AlignBottom | Qt.AlignHCenter
elif(at=='DR'): self.alignFlags=Qt.AlignBottom | Qt.AlignRight
else:self.alignFlags=Qt.AlignVCenter | Qt.AlignLeft
self.setAlignment(self.alignFlags)
self.setText(self.text) #有时并没有出现对齐效果,只能采用先清除再重加载的方式
#旋转控件中的图片一指定的角度:角度为正东向,向顺时针旋转的角度为正,反之为负(非弧度)
def RotateImg(self,angle):
if(self.type=='IMG' and self.curData!=None):
transform = QTransform()
transform.rotate(angle)
self.image=self.image.transformed(transform);
self.setPixmap(QPixmap.fromImage(self.image)) # 显示图片到Qlabel控件
if(self.bChgCtlRect): #为真时,旋转后同时调整控件大小
self.resize(self.image.width(),self.image.height())
#设置标签控件在加载图片时,控件尺寸同图片尺寸不符时,是否允许控件调整自身的矩形区域,以适应1:1的图象显示
def ObjToImgSize(self):
self.setScaledContents(self.bZoomImgSize) #不允许自适应控件,只1:1显示到控件中,同时调整控件大小
if(self.bChgCtlRect): #只有先设置此属性为真时,才允许变化控件尺寸
if(self.curData!=None):
image= QImage.fromData(self.curData)
self.resize(image.width(),image.height()) #用下行后用设置参数中的矩形,用本行就是图片本身的尺寸
self.ctlRect=QRect(self.x(),self.y(),self.width(),self.height())
#设置标签加载的文件名称,可以是图片也可以是动画GIF或视频
def LoadFile(self,filename=''):
if(os.path.exists(filename)):
file_extension = str(filename.split(".")[-1]).upper()
bOK=False
for exname in lst_ImgExName:
if file_extension == exname:
self.type='IMG'
bOK=True
break
for exname in lst_MovExName:
if file_extension == exname:
self.type='MOV'
bOK=True
break
if (bOK):
with open(filename, 'rb') as f:
self.curData = f.read()
self.image= QImage.fromData(self.curData)
self.curMovFileName=filename
self.RefreshLable()
else:
print(f'没有找到对应扩展名: {file_extension} 的分类')
self.type='TXT'
self.ReshowText(self.text)
self.ObjToImgSize()
else:
self.type='TXT'
self.ReshowText(self.text)
#清除图象,重新显示标签的文本
def ReshowText(self,txt):
self.text=txt
self.clear()
self.type='TXT'
self.setText(txt)
#设置显示图片的同时,画到标签控件上的文本,传入文本为空时,同标签控件初始化时的字符串一致,图形模式下,不调用此函数,默认不会绘出文本
def setDrawText(self,txt,x=0,y=0):
self.bDrawTxt=True
if(len(txt)==0):
self.drawText=self.text
else:
self.drawText=txt
self.drawtxtX=x
self.drawtxtY=y
#重新显示标签(在用了LoadFile后)
def RefreshLable(self): #如果图片被调整乱了,且不想要控件尺寸同图片尺寸
self.setScaledContents(self.bZoomImgSize) #不允许自适应控件,只1:1显示到控件中,同时调整控件大小
if(self.type=='IMG'):
self.image= QImage.fromData(self.curData)
self.setPixmap(QPixmap.fromImage(self.image)) # 显示图片到Qlabel控件
self.imgRect=QRect(self.x(),self.y(),self.image.width(),self.image.height())
if(self.bChgCtlRect):
self.resize(self.image.width(),self.image.height()) #用下行后用设置参数中的矩形,用本行就是图片本身的尺寸
elif(self.type=='MOV'):
self.movie = QMovie(self.curMovFileName)
# 将movie应用到label上
self.setMovie(self.movie)
self.total_frame = self.movie.frameCount()
self.gifSpeed=self.movie.speed()
#print(f'当前GIF文件=’{self.curMovFileName}‘,总帧数={self.total_frame},默认正常播放速度={self.gifSpeed}')
self.set_GifSpeed(self.gifSpeed) #设置播放动画GIF的整速度:方法接受的是每1000毫秒播放的帧数比例,如是1:表示,一秒显示全部帧数,0.5表示一秒显示半数的帧数。
self.movie.start()
#"""
else: #self.type=='TXT'
pass
#设置播放GIF动画的速度: interval值哦本准备播放GIF的默认播放速度的倍数,如当前GIF默认播放速度为100
def set_GifSpeed(self,interval=100.0):
self.gifSpeed=interval
if(self.type=='MOV' and self.movie!=None):
self.movie.setSpeed(interval) # 设置播放速度
#设置标签控件的透明程度:对文字及图片均有效
def SetTransparent(self,trans):
if trans>1:trans=1
elif trans<0:trans=0
self.Transparent=trans
opacity_effect = QGraphicsOpacityEffect(parent=self)
opacity_effect.setOpacity(trans) # 设置透明度
self.setGraphicsEffect(opacity_effect) # 将透明度效果应用到标签上
#self.setWindowOpacity(self.Transparent)
#设置本标签对象是在最上方还是在最下方
def setLabelLayer(self,bTop=True):
if(bTop):self.raise_()
else:self.lower()
#设置标签显示的文本的字体
def setTextFont(self,fontname='宋体',fontsize=11,bBold=False,bItalic=False,bUnderline=False):
font = QFont()
font.setFamily(fontname) # 设置字体名称
font.setPointSize(fontsize) # 设置字体大小
font.setBold(bBold) # 设置字体加粗
font.setItalic(False)
font.setUnderline(False)
self.setFont(font)
#设置标签显示的文本的字体
def setTextCol(self,fcol=QColor(0,0,0)):
self.setStyleSheet(f"QLabel {{ color: {fcol.name()}; }}")
#设置标签显示的文本的字体
def setTextBkCol(self,bkcol=QColor(255,255,255)):
if( not self.bTransparent): #对非透明模式才支持设置标签背景颜色
self.setAutoFillBackground(True) # 确保背景自动填充
palette = self.palette()
palette.setColor(QPalette.Window, bkcol)
self.setPalette(palette)
#得到标签矩形中心位置
def getObjRect(self):
size = self.geometry()
self.centerX=size.x()+size.width()/2
self.centerY=size.y()+size.height()/2
return size.x(),size.y(),size.width(),size.height()
#鼠标按下事件重载
def mousePressEvent(self, event):
self.startPoint=event.pos()
self.oldPos=QPoint(event.globalX(),event.globalY())
# 核心部分: 当鼠标点击是左键 并且 在top控件内点击时候触发
if (event.button() == Qt.LeftButton and self.move_Flag): #and self.top.underMouse():
self.setCursor(Qt.OpenHandCursor) #移动时设置成手型光标
# 但判断条件满足时候, 把拖动标识位设定为真
#self.move_Flag = True
self.globalmouse_X = event.globalX()
self.globalmouse_Y = event.globalY()
# 获取窗体当前坐标
self.origin_x = self.x()
self.origin_y = self.y()
#鼠标移动事件重载
def mouseMoveEvent(self, event):
# 拖动标识位设定为真时, 进入移动事件
if self.move_Flag:
# 计算鼠标移动的x,y位移
move_x = event.globalX() - self.globalmouse_X
move_y = event.globalY() - self.globalmouse_Y
# 计算窗体更新后的坐标:更新后的坐标 = 原本的坐标 + 鼠标的位移
dest_x = self.origin_x + move_x
dest_y = self.origin_y + move_y
# 移动本标签控件
size = self.geometry()
self.move(dest_x, dest_y)
self.ctlRect=QRect(self.x(),self.y(),self.width(),self.height())
self.setLabelLayer(True) #拖动的标签控件角色在最顶端显示
# 鼠标左键释放
def mouseReleaseEvent(self, event):
self.endPoint=event.pos()
newPos=QPoint(event.globalX(),event.globalY())
if (event.button() == Qt.LeftButton and self.move_Flag):
self.setCursor(Qt.ArrowCursor) # 设定鼠标为普通状态: 箭头
if(self.move_Flag==False): #非对象拖动状态,鼠标在控件区域移动一位置
if(abs(self.endPoint.x()-self.startPoint.x())<2 and abs(self.endPoint.y()-self.startPoint.y())<2 ): #判断是否单击
if(event.button() == Qt.LeftButton):
print('非拖动状态下:是左键单击不是拖动')
self.signal_Leftclicked.emit(self)
elif(event.button() == Qt.RightButton):
print('非拖动状态下:是右键单击不是拖动')
self.signal_Rightclicked.emit(self)
elif(event.button() == Qt.MidButton):
print('非拖动状态下:是中键单击不是拖动')
self.signal_Midclicked.emit(self)
else:
print('非拖动状态下,鼠标在控件上移动了一位置')
else: #拖动对象状态,除非一点也没拖动,否则不对单位处理
if(abs(self.oldPos.x()-newPos.x())<2 and abs(self.oldPos.y()-newPos.y())<2 ):
print('虽是拖动状态但是并没拖动对象')
if(event.button() == Qt.LeftButton):
print('拖动状态下:是左键单击不是拖动')
self.signal_Leftclicked.emit(self)
elif(event.button() == Qt.RightButton):
print('拖动状态下:是右键单击不是拖动')
self.signal_Rightclicked.emit(self)
elif(event.button() == Qt.MidButton):
print('拖动状态下:是中键单击不是拖动')
self.signal_Midclicked.emit(self)
else: #拖动对象移动了位置
print('拖动状态下:左键拖动控件移动了位置')
self.signal_LeftDropRelease.emit(self)
#重载绘图函数:
def paintEvent(self, event):
if (self.bDrawRect): #标签控件是否绘制边框架
self.DrawObjRect(self.rectCol,self.rectPenWidth) #为控件画出外边框
if(self.bDrawTxt): #是否在标签控件上画出文本
pen = QPen() # 创建画笔对象
painter = QPainter(self) # 此QPainter只能在paintEvent中定义,不能定义成类的self成员对象,也不能在其地方(如其他窗口,线程中)定义,否则没有绘画功能显示
#绘制
pen.setColor(self.fontCol)
painter.drawText(self.drawtxtX,self.drawtxtY,self.width(),self.height(),self.alignFlags,self.drawText)
return super().paintEvent(event) #调用主窗口的重绘事件,不用不会加载动画只显示第一帖,用了动画加载正常,但又多了一静态图第一帖
#画出当前控件的矩形框(用于对象被选择时)
def DrawObjRect(self,pencol,penwidth):
self.rectCol=pencol
self.rectPenWidth=penwidth
if(self.bDrawRect):
pen = QPen() # 创建画笔对象
brush = QBrush() # 创建画刷对象
painter = QPainter(self) # 此QPainter只能在paintEvent中定义,不能定义成类的self成员对象,也不能在其地方(如其他窗口,线程中)定义,否则没有绘画功能显示
#绘制
pen.setColor(pencol)
pen.setStyle(Qt.SolidLine)
pen.setWidth(penwidth) # 设置画笔宽度
painter.setPen(pen) # 设置画笔
self.pixmap = QPixmap.fromImage(self.image)
#painter.drawPixmap(0, 0, self.pixmap)
painter.drawRect(0,0,self.width(),self.height())
全局变量及全局函数模块g.py中的代码如下,好象类模块没有用到,也一并粘贴如下
#g.py:全局变量及全局函数模块,将常用的一个通用变量,全局变量,通用函数在此模块中定义,可直接使用,如报错可使用g.变量或g.模块名也行。
#定义一全局模块,定整个应用程序用的全局变量和全局函数
import os,sys #导入系统模块
import time
import math
import copy
import random
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
##########################################################################
#定义全局变更
g_appPath="应用程序运行目录" #应用程序主程序目录,要在所有模块使用
g_MAX_ID=10000 #定义最大ID号的合局变量,此变更在外部引用此变量并设置别名时,初使值会同它一样,但实际各是各的值了,互不关联,除非直接引用
#在其他模块要使用此全局变量,用 模块名.变量名 #在本模块中的函数及类中要使用此变量,需加上语句:global 变量名,否则函数体内的同名变量只是函数中的一私有变量
g_PI=3.14159265798932
#PYTHONT或PYQT5/6支持的可用文字表示的颜色列表
dic_Col={"lightpink":[QColor(255,192,193),QColor(0xFFB6C1),"浅粉红"],
"pink":[QColor(255,192,203),QColor(0xFFC0CB),"粉红"],
"crimson":[QColor(220,20,60),QColor(0xDC143C),"猩红"],
"lavenderblush":[QColor(255,240,245),QColor(0xFFF0F5),"脸红的淡紫色"],
"palevioletred":[QColor(219,112,147),QColor(0xDB7093),"苍白的紫罗兰红色"],
"hotpink":[QColor(255,105,180),QColor(0xFF69B4),"热情的粉红"],
"deeppink":[QColor(255,20,147),QColor(0xFF1493),"深粉色"],
"mediumvioletred":[QColor(199,21,133),QColor(0xC71585),"适中的紫罗兰红色"],
"orchid":[QColor(218,112,214),QColor(0xDA70D6),"兰花的紫色"],
"thistle":[QColor(216,191,216),QColor(0xD8BFD8),"蓟"],
"plum":[QColor(221,160,221),QColor(0xDDA0DD),"李子"],
"violet":[QColor(238,130,238),QColor(0xEE82EE),"紫罗兰"],
"magenta":[QColor(255,0,255),QColor(0xFF00FF),"洋红"],
"fuchsia":[QColor(255,0,255),QColor(0xFF00FF),"灯笼海棠(紫红色)"],
"darkmagenta":[QColor(139,0,139),QColor(0x8B008B),"深洋红色"],
"purple":[QColor(128,0,128),QColor(0x800080),"紫色"],
"mediumorchid":[QColor(186,85,211),QColor(0xBA55D3),"适中的兰花紫"],
"darkvoilet":[QColor(148,0,211),QColor(0x9400D3),"深紫罗兰色"],
"darkorchid":[QColor(153,50,204),QColor(0x9932CC),"深兰花紫"],
"indigo":[QColor(75,0,130),QColor(0x4B0082),"靛青"],
"blueviolet":[QColor(138,43,226),QColor(0x8A2BE2),"深紫罗兰的蓝色"],
"mediumpurple":[QColor(147,112,219),QColor(0x9370DB),"适中的紫色"],
"mediumslateblue":[QColor(123,104,238),QColor(0x7B68EE),"适中的板岩暗蓝灰色"],
"slateblue":[QColor(106,90,205),QColor(0x6A5ACD),"板岩暗蓝灰色"],
"darkslateblue":[QColor(72,61,139),QColor(0x483D8B),"深岩暗蓝灰色"],
"lavender":[QColor(230,230,250),QColor(0xE6E6FA),"熏衣草花的淡紫色"],
"ghostwhite":[QColor(248,248,255),QColor(0xF8F8FF),"幽灵的白色"],
"blue":[QColor(0,0,255),QColor(0x0000FF),"纯蓝"],
"mediumblue":[QColor(0,0,205),QColor(0x0000CD),"适中的蓝色"],
"midnightblue":[QColor(25,25,112),QColor(0x191970),"午夜的蓝色"],
"darkblue":[QColor(0,0,139),QColor(0x00008B),"深蓝色"],
"navy":[QColor(0,0,128),QColor(0x000080),"海军蓝"],
"royalblue":[QColor(65,105,225),QColor(0x4169E1),"皇军蓝"],
"cornflowerblue":[QColor(100,149,237),QColor(0x6495ED),"矢车菊的蓝色"],
"lightsteelblue":[QColor(176,196,222),QColor(0xB0C4DE),"淡钢蓝"],
"lightslategray":[QColor(119,136,153),QColor(0x778899),"浅石板灰"],
"slategray":[QColor(112,128,144),QColor(0x708090),"石板灰"],
"doderblue":[QColor(30,144,255),QColor(0x1E90FF),"道奇蓝"],
"aliceblue":[QColor(240,248,255),QColor(0xF0F8FF),"爱丽丝蓝"],
"steelblue":[QColor(70,130,180),QColor(0x4682B4),"钢蓝"],
"lightskyblue":[QColor(135,206,250),QColor(0x87CEFA),"淡蓝色"],
"skyblue":[QColor(135,206,235),QColor(0x87CEEB),"天蓝色"],
"deepskyblue":[QColor(0,191,255),QColor(0x00BFFF),"深天蓝"],
"lightblue":[QColor(173,216,230),QColor(0xADD8E6),"淡蓝"],
"powderblue":[QColor(176,224,230),QColor(0xB0E0E6),"火药蓝"],
"cadetblue":[QColor(95,158,160),QColor(0x5F9EA0),"军校蓝"],
"azure":[QColor(240,255,255),QColor(0xF0FFFF),"蔚蓝色"],
"lightcyan":[QColor(225,255,255),QColor(0xE1FFFF),"淡青色"],
"paleturquoise":[QColor(175,238,238),QColor(0xAFEEEE),"苍白的绿宝石"],
"cyan":[QColor(0,255,255),QColor(0x00FFFF),"青色"],
"aqua":[QColor(0,255,255),QColor(0x00FFFF),"水绿色"],
"darkturquoise":[QColor(0,206,209),QColor(0x00CED1),"深绿宝石"],
"darkslategray":[QColor(47,79,79),QColor(0x2F4F4F),"深石板灰"],
"darkcyan":[QColor(0,139,139),QColor(0x008B8B),"深青色"],
"teal":[QColor(0,128,128),QColor(0x008080),"水鸭色"],
"mediumturquoise":[QColor(72,209,204),QColor(0x48D1CC),"适中的绿宝石"],
"lightseagreen":[QColor(32,178,170),QColor(0x20B2AA),"浅海洋绿"],
"turquoise":[QColor(64,224,208),QColor(0x40E0D0),"绿宝石"],
"auqamarin":[QColor(127,255,170),QColor(0x7FFFAA),"绿玉\碧绿色"],
"mediumaquamarine":[QColor(0,250,154),QColor(0x00FA9A),"适中的碧绿色"],
"mediumspringgreen":[QColor(245,255,250),QColor(0xF5FFFA),"适中的春天的绿色"],
"mintcream":[QColor(0,255,127),QColor(0x00FF7F),"薄荷奶油"],
"springgreen":[QColor(60,179,113),QColor(0x3CB371),"春天的绿色"],
"seagreen":[QColor(46,139,87),QColor(0x2E8B57),"海洋绿"],
"honeydew":[QColor(240,255,240),QColor(0xF0FFF0),"蜂蜜"],
"lightgreen":[QColor(144,238,144),QColor(0x90EE90),"淡绿色"],
"palegreen":[QColor(152,251,152),QColor(0x98FB98),"苍白的绿色"],
"darkseagreen":[QColor(143,188,143),QColor(0x8FBC8F),"深海洋绿"],
"limegreen":[QColor(50,205,50),QColor(0x32CD32),"酸橙绿"],
"lime":[QColor(0,255,0),QColor(0x00FF00),"酸橙色"],
"forestgreen":[QColor(34,139,34),QColor(0x228B22),"森林绿"],
"green":[QColor(0,128,0),QColor(0x008000),"纯绿"],
"darkgreen":[QColor(0,100,0),QColor(0x006400),"深绿色"],
"chartreuse":[QColor(127,255,0),QColor(0x7FFF00),"查特酒绿"],
"lawngreen":[QColor(124,252,0),QColor(0x7CFC00),"草坪绿"],
"greenyellow":[QColor(173,255,47),QColor(0xADFF2F),"绿黄色"],
"olivedrab":[QColor(85,107,47),QColor(0x556B2F),"橄榄土褐色"],
"beige":[QColor(107,142,35),QColor(0x6B8E23),"米色(浅褐色)"],
"lightgoldenrodyellow":[QColor(250,250,210),QColor(0xFAFAD2),"浅秋麒麟黄"],
"ivory":[QColor(255,255,240),QColor(0xFFFFF0),"象牙"],
"lightyellow":[QColor(255,255,224),QColor(0xFFFFE0),"浅黄色"],
"yellow":[QColor(255,255,0),QColor(0xFFFF00),"纯黄"],
"olive":[QColor(128,128,0),QColor(0x808000),"橄榄"],
"darkkhaki":[QColor(189,183,107),QColor(0xBDB76B),"深卡其布"],
"lemonchiffon":[QColor(255,250,205),QColor(0xFFFACD),"柠檬薄纱"],
"palegodenrod":[QColor(238,232,170),QColor(0xEEE8AA),"灰秋麒麟"],
"khaki":[QColor(240,230,140),QColor(0xF0E68C),"卡其布"],
"gold":[QColor(255,215,0),QColor(0xFFD700),"金"],
"cornislk":[QColor(255,248,220),QColor(0xFFF8DC),"玉米色"],
"goldenrod":[QColor(218,165,32),QColor(0xDAA520),"秋麒麟"],
"floralwhite":[QColor(255,250,240),QColor(0xFFFAF0),"花的白色"],
"oldlace":[QColor(253,245,230),QColor(0xFDF5E6),"老饰带"],
"wheat":[QColor(245,222,179),QColor(0xF5DEB3),"小麦色"],
"moccasin":[QColor(255,228,181),QColor(0xFFE4B5),"鹿皮鞋"],
"orange":[QColor(255,165,0),QColor(0xFFA500),"橙色"],
"papayawhip":[QColor(255,239,213),QColor(0xFFEFD5),"番木瓜"],
"blanchedalmond":[QColor(255,235,205),QColor(0xFFEBCD),"漂白的杏仁"],
"navajowhite":[QColor(255,222,173),QColor(0xFFDEAD),"Navajo白"],
"antiquewhite":[QColor(250,235,215),QColor(0xFAEBD7),"古代的白色"],
"tan":[QColor(210,180,140),QColor(0xD2B48C),"晒黑"],
"brulywood":[QColor(222,184,135),QColor(0xDEB887),"结实的树"],
"bisque":[QColor(255,228,196),QColor(0xFFE4C4),"(浓汤)乳脂,番茄等"],
"darkorange":[QColor(255,140,0),QColor(0xFF8C00),"深橙色"],
"linen":[QColor(250,240,230),QColor(0xFAF0E6),"亚麻布"],
"peru":[QColor(205,133,63),QColor(0xCD853F),"秘鲁"],
"peachpuff":[QColor(255,218,185),QColor(0xFFDAB9),"桃色"],
"sandybrown":[QColor(244,164,96),QColor(0xF4A460),"沙棕色"],
"chocolate":[QColor(210,105,30),QColor(0xD2691E),"巧克力"],
"saddlebrown":[QColor(139,69,19),QColor(0x8B4513),"马鞍棕色"],
"seashell":[QColor(255,245,238),QColor(0xFFF5EE),"海贝壳"],
"sienna":[QColor(160,82,45),QColor(0xA0522D),"黄土赭色"],
"lightsalmon":[QColor(255,160,122),QColor(0xFFA07A),"浅鲜肉(鲑鱼)色"],
"coral":[QColor(255,127,80),QColor(0xFF7F50),"珊瑚"],
"orangered":[QColor(255,69,0),QColor(0xFF4500),"橙红色"],
"darksalmon":[QColor(233,150,122),QColor(0xE9967A),"深鲜肉(鲑鱼)色"],
"tomato":[QColor(255,99,71),QColor(0xFF6347),"番茄"],
"mistyrose":[QColor(255,228,225),QColor(0xFFE4E1),"薄雾玫瑰"],
"salmon":[QColor(250,128,114),QColor(0xFA8072),"鲜肉(鲑鱼)色"],
"snow":[QColor(255,250,250),QColor(0xFFFAFA),"雪"],
"lightcoral":[QColor(240,128,128),QColor(0xF08080),"淡珊瑚色"],
"rosybrown":[QColor(188,143,143),QColor(0xBC8F8F),"玫瑰棕色"],
"indianred":[QColor(205,92,92),QColor(0xCD5C5C),"印度红"],
"red":[QColor(255,0,0),QColor(0xFF0000),"纯红"],
"brown":[QColor(165,42,42),QColor(0xA52A2A),"棕色"],
"firebrick":[QColor(178,34,34),QColor(0xB22222),"耐火砖"],
"darkred":[QColor(139,0,0),QColor(0x8B0000),"深红色"],
"maroon":[QColor(128,0,0),QColor(0x800000),"栗色"],
"white":[QColor(255,255,255),QColor(0xFFFFFF),"纯白"],
"whitesmoke":[QColor(245,245,245),QColor(0xF5F5F5),"白烟"],
"gainsboro":[QColor(220,220,220),QColor(0xDCDCDC),"Gainsboro"],
"lightgrey":[QColor(211,211,211),QColor(0xD3D3D3),"浅灰色"],
"silver":[QColor(192,192,192),QColor(0xC0C0C0),"银白色"],
"darkgray":[QColor(169,169,169),QColor(0xA9A9A9),"深灰色"],
"gray":[QColor(128,128,128),QColor(0x808080),"灰色"],
"dimgray":[QColor(105,105,105),QColor(0x696969),"暗淡的灰色"],
"black":[QColor(0,0,0),QColor(0x000000),"纯黑"]
}
# 设置对象文本及背景颜色,调用此函数时要确传入的控件对象支持.setStyleSheet方法
def gf_setObjCol(obj,objClassName,txtCol='black',bkCol='white'):
stylestr=objClassName+"::section {background-color: "+bkCol+";color:"+txtCol+"};} "
try:
obj.setStyleSheet(stylestr)
except Exception as e: # 如果发生异常,执行这里的代码
print('你所调用的控件对象不支持setStyleSheet方法')
##########################################################################
#示例定义应用程序的一些基本设置参数字典,因是定义的字典,如果采用函数传递字典参数有调用函数的模块公用此字典中的数据(列表、元组类似,其他变量不公用,函数传递参数量会建副本)
g_dic_set={'AppName':'我的PYTHON程序',
'AppVer':'0.0.1.0',
'frmWidth':800,
'frmHeight':600,
'saveTime':1800,
'defTempFile':'tmp.bak'
}
#初始化应用程序:将应用程序的所有子目录全部加入到工作路径中,程序运时时,就不会出现找不到模块的问题了
#例lstPath=['\\MyModules','\\Data','\\Res','\\Frm'],或lstPath=['D:\\MyPrg\\MyModules','D:\\MyPrg\\Data','D:\\MyPrg\\Res','D:\\MyPrg\\Frm']
def gf_InitPrgPath(lstPath,bAbsPath=True): #bAbsPath=True:表示传递的lstPath为相对路径,否则表示传递的是绝对路径
global g_apppath
lst_modlist=sys.path
print(f"当前模块可查找的目录列表是:\t{lst_modlist}\n")
if(bAbsPath):
for path in lstPath:
sys.path.append(f"{os.path.dirname(os.path.abspath(__file__))}{path}")
else: #传入的是绝对路径时
for path in lstPath:
sys.path.append(f"{path}")
print(f"增加路径后,当前模块可查找的目录列表是:\t{lst_modlist}\n")
g_appPath=sys.path[0] #主运行目录位于数组列表的0位,不可更改
print(f"当前程序所在的目录是:\t{g_appPath}\n")
#得到模块中的全局变量值
def gf_getAppPath():
return g_appPath
#得到PYQT5/6颜色字符串转换成QColor颜色对象
def gf_getColRgb(colstr,defcol=QColor(0,0,0)):
lst_col = dic_Col.get(colstr,defcol) #用get函数来得到对应字典key的值,如果没有,得到默认值,防报错
return lst_col[1] #返回lst_col[2]结果一样
#设置表格显示类型:在主窗口处调用此函数
#框架形状:self.setFrameShape(..)
# QFrame.NoFrame=什么都没画, QFrame.Box=围绕其内容绘制一个框(需要设置外线和中线的宽度),QFrame.Panel=绘制一个面板,使内容显得凸起或凹陷(设置中线宽度没用)
# QFrame.HLine=绘制一条没有框架的水平线(用作分隔符)QFrame.VLine=绘制一条无框架的垂直线(用作分隔符)
# QFrame.StyledPanel=样式化的平面框架,
# QFrame.WinPanel=绘制一个可以像Windows中那样凸起或凹陷的矩形面板
#框架阴影:self.setFrameShadow(..)
# QFrame.Plain=框架和内容与周围环境呈现水平;(没有任何3D效果)
# QFrame.Raised=框架和内容出现; 使用当前颜色组的浅色和深色绘制3D凸起线
# QFrame.Sunken=框架和内容出现凹陷; 使用当前颜色组的浅色和深色绘制3D凹陷线
#框架线宽:setLineWidth(int width)=设置外线宽度 lineWidth() =获取外线宽度
# setMidLineWidth(int width) =设置中线宽度(部分框架形状,设置框架中线宽度是没用的)midLineWidth()=获取中线宽度
# frameWidth() =获取总宽度(2*外线宽度 + 中线宽度).个人理解:总线宽 = 外线宽度 + 中线宽度 + 内线宽度(内线宽度 = 外线宽度,类似于内部阴影)
def gf_setFrmaeype(ctlobj,shape=QFrame.Box,shadow=QFrame.Sunken,linewidth=1):
ctlobj.setFrameShape(shape) # 设置表格框架样式
ctlobj.setFrameShadow(shadow) # 设置边框的样式
ctlobj.setLineWidth(linewidth) # 设置边框的线条宽度
#得到文件的路径,文件名,扩展名三个返回参数,bUpper=返回值时是否全转为大写,pathEndBackslash=返回前是否为路径值尾部加一反斜杠
def gf_getFilePathName(filePathName,bUpper=False,pathEndBackslash=True):
bExist=os.path.exists(filePathName)
file_path, file_name = os.path.split(filePathName) #file_path的值没有最后一个反斜杠,使用时要单独在其末尾加上一个反斜杠再加文件名
file_ext = os.path.splitext(filePathName)[1]
#或file_ext = str(filePathName.split(".")[-1]).upper() #得到扩展名(同时转大写)
if(bUpper): #是否转大写
file_path=file_path.upper()
file_name=file_name.upper()
file_ext=file_ext.upper()
if(pathEndBackslash): #为防止目录再组合文件形成新的绝对路径时,忘记对路径尾部加上反斜杠,通过此参数来加上
file_path=file_path+"\\"
return file_path,file_name,file_ext
###################################################################################
#定义应用程序公用的通用内存二进制文件类
class gc_MemFiles():
#构造初始化函数:指定的文件生成内存文件,参数1:初始化的内存文件要传入的文件或文件列表(一个或多个文件)
def __init__(self,fileNames,defFile):
self.dicMemFiles={} #将指定列表中的全部文件读入内存,并保存在此字典中,通过KEY来访问,默认为KEY=0时,留用给没有从字典中得到文件数据时的默认值
self.lstMemFileName=[] #对应字典读入的内存文件(文件应为绝对路径或模块可识别的路径不含路径的文件名),第0索引文件为默认文件留用,本变量仅用于调试用
self.fileCount=0 #当前字典中文件的数量(含KEY=0的默认文件数量)
self.MakeMemFile(fileNames,defFile,True) #初始化时,第三个参数应为True
#根据传入的文件名初始化内存文件(bNew=True时表示先清空原有的再重新创建,bNew=False时表是增加):本函数不允许传入的文件不能正确导入到内存文件中,因每个KEY对应其他代码的调用,如果KEY不一一对应,程序运行结果将不可预见
#不论传入的文件不论是否创建成功,均会为内存字典分配一个KEY,只是对应的KEY的文件读入内存的二进制数据为None
def MakeMemFile(self,fileNames,defFile='',bNew=False):
if bNew: #如果新建内存文件,会用defFile文件作为KEY=0的内存文件数据
self.fileCount=0
self.dicMemFiles.clear()
self.lstMemFileName.clear()
self.lstMemFileName.append(defFile)
if(defFile==''):
print('新建内存文件时,必须要指定一个默认文件为作字典的默认内存数据,现将字典KEY=0的默认值为None')
self.dicMemFiles[0]=None
self.fileCount=1
#处理参数表传入的默认文件名
if(defFile==''): #表示无需对默认文件进行任何操作
pass
else: #如有定义默认文件名,则更新字典中的KEY=0的默认文件数据
self.lstMemFileName[0]=defFile #默认文件无论是否正确加载到内存文件中,列表中的值均为传入的内存文件名称
if(os.path.exists(defFile)):
with open(defFile, 'rb') as f:
binData = f.read()
self.dicMemFiles[0]=binData
print(f'当前默认文件数据对应的默认文件名="{defFile}"')
else:
print(f'指定默认文件"{defFile}"不存在,字典KEY=0的默认内存文件暂为None,请检查文件')
self.dicMemFiles[0]=None
#判断传入的fileNames是一列表文件组,还是单一的一个字符串文件
vartype=type(fileNames)
if(isinstance(fileNames,str)): #是单一字符串,即传入了一个单文件
print(f'参数为字符串,即传入了一个单一文件"{fileNames}"')
if(os.path.exists(fileNames)):
with open(fileNames, 'rb') as f:
binData = f.read()
self.dicMemFiles[self.fileCount]=binData
#f.close()
else:
print(f'指定文件"{fileNames}"不存在,向字典中增加一个内存文件出现错误!!,暂将此序号key={self.fileCount}文件同key=0的默认文件内容相同')
self.dicMemFiles[self.fileCount]=self.dicMemFiles[0]
self.lstMemFileName.append(fileNames)
self.fileCount+=1
elif(isinstance(fileNames,list)): #是一列表变量,即传入了多个文件
print(f'参数为列表,即传入了多个文件{fileNames}')
for filename in fileNames:
if(os.path.exists(filename)):
with open(filename, 'rb') as f:
rData = f.read()
self.dicMemFiles[self.fileCount]=rData #内存文件增加一个
else:
print(f'指定文件"{filename}"不存在,向字典中增加一个内存文件出现错误!!,暂将此序号key={self.fileCount}文件同key=0的默认文件内容相同')
self.dicMemFiles[self.fileCount]=self.dicMemFiles[0]
self.lstMemFileName.append(filename)
self.fileCount+=1
#得到内存文件中的二进制数据:参数1=要得到字典内存文件的KEY值,
# bDeepCopy=True时,返回值时会用深度COPY一副本来使用,这样避免调用端对字典内存文件改动后影响其他同样在使用这个内存文件的
# defData不为空时:如果没有从字典中得到相对应的,用defData作为返回值,为None时,以字典的key=0 的内容作为出错返回值
def getMemFileData(self,dicKeyID,bDeepCopy=False,defData=None,bUseDicDefValue=True):
data=None
if(len(self.dicMemFiles)>0):
if(defData==None): #参数未再指定从字典中得到内存文件数据出错时的默认数据时,且bUseDicDefValue==Ture时得到字典中的KEY=0的默认值
if(bUseDicDefValue):
data= self.dicMemFiles.get(dicKeyID, self.dicMemFiles[0]) #用get函数来得到对应字典key的值,如果调用函数defData值不为空,则用defData值,否则用g_lstMemFileName[0]作为默认值
else:
data= self.dicMemFiles.get(dicKeyID, None) #如没有正确得到字典中的值,返回None,而不是key=0的默认值
else:
data= self.dicMemFiles.get(dicKeyID, defData) #用get函数来得到对应字典key的值,如果调用函数defData值不为空,则用defData值,否则用g_lstMemFileName[0]作为默认值
#如指定了defData的数据,但还是得到失败,且又指定了bUseDicDefValue=True,失败时采用KEY=0的默认数据时
if(data==None and bUseDicDefValue):
data= self.dicMemFiles.get(0,None) #再次失败还是会得到一个None
if(bDeepCopy):
return copy.deepcopy(data)
return data
代码中的有关1.png,1.gif等图片或动画文件,可以任意改名COPY到模块运行目录即可