PySide6界面编程中较常用的控件还有QTableWidget表格控件,用来将加载的数据在表格中显示出来,下面继承QTableWidget编写其扩展子类QTableWidgetEx,来实现用单元格来显示除数据文字外,还可以对表格的单元格的文字颜色、背景底色进行设置,还可以对任一单元格用来加载显示图形、动画、组合框、复选框、标签等
本示例为我原学习QT5控件时练习编写的表格类的扩展代码,现转为PySide6格式,发布到此专栏供学习使用PySide6的参考学习。代码是学习期间写的,质量不高,仅供参考
本示例共三个模块文件
1、主窗口模块文件MyWidget.py
import os,sys,time,math,copy,random
from PySide6 import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
#from PySide6.QtGui import * #如果运行时没有任何界面调出,也不报错,请屏蔽此行,原因不详from PySide6.QtCore import Signal, QEvent,Property, QSize
from PySide6.QtGui import (QCursor,QIcon,QImage,QPicture,QDesktopServices, QGuiApplication,QKeySequence, QShortcut, QStandardItem,QStandardItemModel)
from PySide6.QtGui import (QPen,QBrush,QColor,QFont,QPalette,QPainter,QGradient,QMatrix4x4,QPlatformSurfaceEvent, QSurface, QWindow,QSurfaceFormat)
from PySide6.QtWidgets import (QApplication, QDialog,QWidget, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtWidgets import *from g import * #导入自定义的全局变量模块
from QTableWidgetEx import * #导入自定义的表格控件扩展类#测试表格控件扩展类主窗体
class MyWidget(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle('测试QTableWidget的继承类QTableWidgetEx')self.setGeometry(0, 0,1180, 800)self.statusbar = self.statusBar()self.statusbar.showMessage('准备')self.qimage = QImage('1.jpg') #主窗体的底图self.initUI()def initUI(self):self.btn_Test1=QPushButton('1初始化表格控件',self)self.btn_Test1.setGeometry(1020,50,150,50)self.btn_Test1.clicked.connect(self.test1)self.btn_Test2=QPushButton('2测试代码',self)self.btn_Test2.setGeometry(1020,150,150,50)self.btn_Test2.clicked.connect(self.test2)self.btn_Test3=QPushButton('3测试代码',self)self.btn_Test3.setGeometry(1020,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,无法进行任何操作,需要对每个单元格进行初始化#初始化时,可以将单元格初始化为PySide6默认的 QTableWidgetItem类型,也可以初始单元格为按纽,标签,组合框等控件。#如果表格中的控件类型过多,会卡死,应在2000行以下为好,不用控件类型时不会限制#为方便测试,初始化表格的代码放在了点击按纽1的方法中:def test1(self): self.table01 = QTableWidgetEx(self,30,len(head),head,3,3,1000,760) #标签被单击时的信号槽函数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:1painter.drawImage(QRect(0,0, self.width(),self.height()-32), self.qimage)painter.end() #####################################################################################################################################单击按纽1的信号槽函数def test1(self): self.table01.setTableFrmType(QFrame.Panel,QFrame.Raised,1) #设置表格的界面类型lst0=self.table01.itemDataself.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=1self.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=5lineedt.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}') #测试各单元格中的值的情况(单元格或控件)#单击按纽2的信号槽函数def test2(self): top_row, left_column, bottom_row, right_column = self.table01.getSelectedRanges() #得到当前选择的行列号QMessageBox.information(self,'提示信息',f'当前选择的矩形区域为:左上行列索引={top_row},{left_column},右下行列索引号={bottom_row},{right_column}',QMessageBox.Yes)#单击按纽3的信号槽函数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())
2、自定义表格扩展类 模块QTableWidgetEx.py
#模块名:QTableWidgetEx.py:PySide6的表格控件QTableWidget的扩展子类
#包含类名: QTableWidgetEx: 继承QTableWidget类的扩展类:支持表格中的单元格可加载多种控件等功能
# QLabelEx:继承QLabel类的扩展类:可作为表格单元格的一个控件,自动加载图片,动画等功能
# QHeaderViewEx:可在表格列头显示一个复选框控件,用于全选一项组合框的功能
# OwnQTextEdit:继承QTextEdit类的扩展类:可作为表格单元格的一个控件,主要是去除了默认的粗边框,其他功能自行扩充
# OwnQLineEdit:继承QLineEdit类的扩展类:可作为表格单元格的一个控件,可自定义掩码,如密码等格式化输入
import os,sys,time,math,copy,random
from PySide6 import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
#from PySide6.QtGui import * #如果运行时没有任何界面调出,也不报错,请屏蔽此行,原因不详
import PySide6.QtChartsfrom PySide6.QtCore import Signal, QEvent,Property, QSize
from PySide6.QtCore import (QDateTime, QFile,QDir, QLibraryInfo, QSysInfo, Qt,QTimer,Slot, QAbstractTableModel, QModelIndex,QPoint,QPointF,QStandardPaths, QUrl, QIODevice, QRectF,qFatal,qWarning,qVersion)
from PySide6.QtGui import (QCursor,QIcon,QImage,QPicture,QDesktopServices, QGuiApplication,QKeySequence, QShortcut, QStandardItem,QStandardItemModel)
from PySide6.QtGui import (QPen,QBrush,QColor,QFont,QPalette,QPainter,QGradient,QMatrix4x4,QPlatformSurfaceEvent, QSurface, QWindow,QSurfaceFormat)
from PySide6.QtGui import (QRhi, QRhiBuffer,QPixmap,QAction,QWheelEvent,QRhiDepthStencilClearValue,QRhiGraphicsPipeline, QRhiNullInitParams,QRhiGles2InitParams, QRhiRenderBuffer,QRhiSampler, QRhiShaderResourceBinding,QRhiShaderStage, QRhiTexture,QMovie,QRhiVertexInputAttribute, QRhiVertexInputBinding,QRhiVertexInputLayout, QRhiViewport, QShader)
from PySide6.QtWidgets import (QApplication, QDialog,QWidget, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtWidgets import (QCheckBox, QComboBox,QCommandLinkButton, QDateTimeEdit, QDial,QDialog, QDialogButtonBox, QFileSystemModel,QGridLayout, QGroupBox, QHBoxLayout, QLabel,QLineEdit, QListView, QMenu, QPlainTextEdit,QProgressBar, QPushButton, QRadioButton,QScrollBar, QSizePolicy, QSlider, QSpinBox,QStyleFactory, QTableWidget, QTabWidget,QTextBrowser, QTextEdit, QToolBox, QToolButton,QTreeView, QVBoxLayout)from g import * #导入自定义的全局变量模块
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 #单元格(可是一个区域的被选中的单元格)是否已被执行COPYcopyItems=[]copyItemData=[]signal_Leftclicked = Signal(object) #自定信号,标签被左键单击,传回参数:控件对象本身signal_Rightclicked = Signal(object) #自定信号,标签被右键单击,传回参数:控件对象本身signal_Midclicked = Signal(object) #自定信号,标签被中键单击,传回参数:控件对象本身signal_LeftDropRelease = Signal(object) #自定信号,标签被左键拖动后释放,传回参数:控件对象本身MAX_ROWCOUNT=100000 #最大支持行数:注意,如果表格中设置单元格为控件时过多,会造成运行卡顿,MAX_COLCOUNT=100 #最大支持列数#定义表格控件要用的的组合框等下拉列表要用到的数据,为单元格列表对象的第2个元素值dic_TableItemData={0:[None], #0: KEY容错处理用1:['选项01','选项02','选项03'], #1:示例-供表格中的下拉组合框用,选择项列表12:['选项A','选项B','选项C','选项D'], #2:示例-供表格中的下拉组合框用,选择项列表23:['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)):returnif(col>(self.curRowCount-1)):returnif typestr!=None:typestr=typestr.upper()self.itemData[row][col][0]=typestr.upper()self.itemData[row][col][2]=dicIDif typestr=="OWNQLINEEDIT" or typestr=='QLINEEDIT': #自定义编辑框控件,用掩码显示内容pass elif typestr=="MUSTR" or typestr=='QTEXTEDIT': #对多行文本,要支持多行文本passelif typestr=="QCHECKBOX" :self.itemData[row][col][1]='复选框'+str(row)elif typestr=="QCOMBOBOX" :passelif 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)):returnfor 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=0for 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.AlignLeftif(at=='TL'): self.alignFlags=Qt.AlignTop | Qt.AlignLeftelif(at=='TC'): self.alignFlags=Qt.AlignTop | Qt.AlignHCenterelif(at=='TR'): self.alignFlags=Qt.AlignTop | Qt.AlignRightelif(at=='CL'): self.alignFlags=Qt.AlignVCenter | Qt.AlignLeftelif(at=='CC'): self.alignFlags=Qt.AlignVCenter | Qt.AlignHCenterelif(at=='CR'): self.alignFlags=Qt.AlignVCenter | Qt.AlignRightelif(at=='DL'): self.alignFlags=Qt.AlignBottom | Qt.AlignLeftelif(at=='DC'): self.alignFlags=Qt.AlignBottom | Qt.AlignHCenterelif(at=='DR'): self.alignFlags=Qt.AlignBottom | Qt.AlignRightelse:self.alignFlags=Qt.AlignVCenter | Qt.AlignLeftself.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('行或列号有误')returnlstOrdData=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]=dicIDif(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) #!!!!重要:必须用此行代码才真正初始化了非控件类的单元格,否则单元格可以录入数据,但实际值为Noneelse: #是控件类的单元格时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]=itemtextcheckbox = 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]=itemtextlabel = 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]=itemtextbutton = 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(row<self.curRowCount or col<self.curColCount): objtype= self.itemData[row][col][0].upper()obj=self.cellWidget(row, col)item=self.item(row, col)if(obj is not None): #当单元格表面是一控件时self.itemData[row][col][1]=objtxt #更新文本到绑定的数据列表中去if(objtype=='QLABEL'):obj.setText(objtxt)obj.text=objtxtself.setItem(row, col, None)obj.toolTip=tooltiptxtelif(objtype=='QPUSHBUTTON'):obj.setText(objtxt)self.setItem(row, col, None)obj.toolTip=tooltiptxtelif(objtype=='QCHECKBOX'):obj.setText(objtxt)obj.toolTip=tooltiptxtelif(objtype=='OWNQTEXTEDIT' or objtype=='MUSTR'):obj.setText(objtxt)self.setItem(row, col, None)obj.toolTip=tooltiptxtelif(objtype=='OWNQLINEEDIT' or objtype=='QLINEEDIT') : obj.setText(objtxt) self.setItem(row, col, None)obj.toolTip=tooltiptxt elif(objtype=='QCOMBOBOX'):obj.toolTip=tooltiptxt self.setItem(row, col, None)elif(item is not None): #当前单元格上没有控件,是默认的单元格控件self.itemData[row][col][1]=objtxt #更新文本到绑定的数据列表中去item.setText(objtxt)#self.setItem(row, col, item)else:print('类型有错误') #设置一行表格数据(从self.itemData中的数据反映到表格中),如果bNew=True,表示为新建,数据采用默认,反之采用当前self.itemData的数据来写入表格单元格def setOneRowTableData(self,row,bNew=False):if(row>self.curRowCount-1 or row<0):print('行号错误') returnlstRow=self.itemData[row]col=0for 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(row<self.curRowCount-1 and col<self.curColCount-1):txt=self.getItemText(row,col)self.itemData[row][col][1]=txt#设置组合框控件的选项def setComboBoxItem(self,comboObj,lstItem):comboObj.clear()for item in lstItem:comboObj.addItem(item)#设置表格整行背景色(对组合框等控件无效,如要效果,需对组合框控件重载继承类)def setTableRowBkCol(self,row,bkcol=QColor(255,255,255)):for colIndex in range(self.curColCount):self.setItemBkCol(row,colIndex,bkcol)#设置表格整列背景色(对组合框等控件无效,如要效果,需对组合框控件重载继承类)def setTableColBkCol(self,column,bkcol=QColor(255,255,255)):for rowIndex in range(self.curRowCount):self.setItemBkCol(rowIndex,column,bkcol)#设置指定单元格的背景色(对组合框等控件无效,如要效果,需对组合框控件重载继承类)def setItemBkCol(self,row,column, bkcol=QColor(255,255,255)):item = self.item(row,column)if(item is not None):item.setBackground(QColor(bkcol)) #得到指定单元格中的文本(是控件的要用其他方法得到)def getItemText(self, row, col):txt=''if(row<self.curRowCount or col<self.curColCount): item = self.item(row, col)obj=self.cellWidget(row, col)if(obj is not None): #优先得到控件中的文本值objtype= self.itemData[row][col][0]if(obj==None):return Noneelif(objtype=='QLABEL'):txt=obj.textelif(objtype=='QPUSHBUTTON'):txt=obj.text()elif(objtype=='QCHECKBOX'):txt=obj.text()elif(objtype=='QCOMBOBOX'):txt=obj.currentText()elif(objtype=='OWNQTEXTEDIT' or objtype=='MUSTR'):txt=obj.toPlainText() elif(objtype=='OWNQLINEEDIT' or objtype=='QLINEEDIT'):txt= obj.text() elif(item is not None): #typestr=='NONE' or typestr=="INT" or typestr=="FLOAT" or typestr=="STR" or typestr=="DATE" txt=item.text()else: print('有错误,对象类型未能识别,或指定单元格没有被初始化,当前单元格还是None对象') #两者都为空时,return txt#得到指定行全部的数据def getRowItems(self,row,bCopy=False):if(row>self.curRowCount-1):return NonelstoneRow=[]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 NonelstoneCol=[]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 NonelstItems=self.getRowItems(row-1,bCopy) #得到当行指定行的上一行的数据类型(内容为空)self.insertRow(row) #在指定行位置上插入一空行self.curRowCount+=1self.itemData.insert(row,lstItems) #设置绑定的数据列表也插入一行self.setOneRowTableData(row) #刷新新插入的一行数据内容#在表格末尾增加一行数据def AppendItemRow(self):lstItems=self.getRowItems(self.curRowCount-1) #得到最后一行的数据self.insertRow(self.curRowCount)self.curRowCount+=1self.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 NonelstItems=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 Noneself.removeRow(row)self.itemData[row].pop(row)self.curRowCount-=1#删除指定列数据def DelOneColItem(self,col):if(col>self.curColCount-1 or col<0):return Noneself.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: returnQTableWidgetEx.copyItems=selLstItemsQTableWidgetEx.copyItemData=copy.deepcopy(selLstItemData)QTableWidgetEx.bItemCopy=Trueelif action == act_pasteItem:print("粘贴单元格")if(QTableWidgetEx.bItemCopy):copyRow=0for row in range(top_row,bottom_row+1):copyCol=0for 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+=1copyRow+=1if copyRow>(len(QTableWidgetEx.copyItems)-1): copyRow=0 #复制的对象行数不足时,循环再从0开始叠加 continueelif action == act_cutItem:print("剪切单元格")selLstItems,selLstItemData = self.getSelectedeItems()if selLstItems==None: returnQTableWidgetEx.copyItems=selLstItemsQTableWidgetEx.copyItemData=copy.deepcopy(selLstItemData) #剪切前将数据先COPY备用QTableWidgetEx.bItemCopy=Truetop_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 = Signal(bool,int)# 以下这4个变量控制列头复选框的样式,位置以及大小(下面的位置刚好画在第一列上,如其他列上也要使用,需深化代码)_x_offset = 0_y_offset = 0_width = 20_height = 20def __init__(self,parent,headType=0,orientation=Qt.Horizontal):super(QHeaderViewEx, self).__init__(orientation, parent)self.isOn = Falseself.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_Activeif self.isOn:option.state |= QStyle.State_Onelse:option.state |= QStyle.State_Offself.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=0if self.isOn:self.isOn = Falseelse: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.CheckState.Checked)else:for i in self.table.all_header0_chkbox:i.setCheckState(Qt.CheckState.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 editordef 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(OwnQTextEdit, 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(OwnQLineEdit, 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=txtTypeif(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) returnelse: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 = Signal(object) #自定信号,标签被左键单击,传回参数:控件对象本身signal_Rightclicked = Signal(object) #自定信号,标签被右键单击,传回参数:控件对象本身signal_Midclicked = Signal(object) #自定信号,标签被中键单击,传回参数:控件对象本身signal_LeftDropRelease = Signal(object) #自定信号,标签被左键拖动后释放,传回参数:控件对象本身#初始化对角需传递的参数为 父类,创建矩形,内容, 控件透明度 字体 字体颜色 背景颜色 def __init__(self,parent,text='',transt=1.0,font=QFont('宋体', 11),fcolor=QColor(0,0,0)): super(QLabelEx, 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 #标签控件是否可以主窗体上拖动:对窗体元素,应设置为Falseself.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=0self.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=bkcolpalette = 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.AlignLeftif(at=='TL'): self.alignFlags=Qt.AlignTop | Qt.AlignLeftelif(at=='TC'): self.alignFlags=Qt.AlignTop | Qt.AlignHCenterelif(at=='TR'): self.alignFlags=Qt.AlignTop | Qt.AlignRightelif(at=='CL'): self.alignFlags=Qt.AlignVCenter | Qt.AlignLeftelif(at=='CC'): self.alignFlags=Qt.AlignVCenter | Qt.AlignHCenterelif(at=='CR'): self.alignFlags=Qt.AlignVCenter | Qt.AlignRightelif(at=='DL'): self.alignFlags=Qt.AlignBottom | Qt.AlignLeftelif(at=='DC'): self.alignFlags=Qt.AlignBottom | Qt.AlignHCenterelif(at=='DR'): self.alignFlags=Qt.AlignBottom | Qt.AlignRightelse:self.alignFlags=Qt.AlignVCenter | Qt.AlignLeftself.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=Falsefor exname in lst_ImgExName:if file_extension == exname:self.type='IMG'bOK=Truebreakfor exname in lst_MovExName:if file_extension == exname:self.type='MOV'bOK=Truebreakif (bOK):with open(filename, 'rb') as f:self.curData = f.read()self.image= QImage.fromData(self.curData) self.curMovFileName=filenameself.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=txtself.clear()self.type='TXT'self.setText(txt)#设置显示图片的同时,画到标签控件上的文本,传入文本为空时,同标签控件初始化时的字符串一致,图形模式下,不调用此函数,默认不会绘出文本def setDrawText(self,txt,x=0,y=0):self.bDrawTxt=Trueif(len(txt)==0):self.drawText=self.textelse:self.drawText=txtself.drawtxtX=xself.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默认播放速度为100def set_GifSpeed(self,interval=100.0):self.gifSpeed=intervalif(self.type=='MOV' and self.movie!=None):self.movie.setSpeed(interval) # 设置播放速度#设置标签控件的透明程度:对文字及图片均有效def SetTransparent(self,trans):if trans>1:trans=1elif trans<0:trans=0self.Transparent=transopacity_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()/2self.centerY=size.y()+size.height()/2return 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 = Trueself.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_Xmove_y = event.globalY() - self.globalmouse_Y# 计算窗体更新后的坐标:更新后的坐标 = 原本的坐标 + 鼠标的位移dest_x = self.origin_x + move_xdest_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=pencolself.rectPenWidth=penwidthif(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())
3、全局变量函数模块g.py
#g.py:全局变量及全局函数模块,将常用的一个通用变量,全局变量,通用函数在此模块中定义,可直接使用,如报错可使用g.变量或g.模块名也行。
#定义一全局模块,定整个应用程序用的全局变量和全局函数
"""
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
"""
import os,sys,time,math,copy,random
from PySide6 import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
#from PySide6.QtGui import * #如果运行时没有任何界面调出,也不报错,请屏蔽此行,原因不详
import PySide6.QtChartsfrom PySide6.QtCore import Signal, QEvent,Property, QSize
from PySide6.QtCore import (QDateTime, QFile,QDir, QLibraryInfo, QSysInfo, Qt,QTimer,Slot, QAbstractTableModel, QModelIndex,QPoint,QPointF,QStandardPaths, QUrl, QIODevice, QRectF,qFatal,qWarning,qVersion)
from PySide6.QtGui import (QCursor,QIcon,QImage,QPicture,QDesktopServices, QGuiApplication,QKeySequence, QShortcut, QStandardItem,QStandardItemModel)
from PySide6.QtGui import (QPen,QBrush,QColor,QFont,QPalette,QPainter,QGradient,QMatrix4x4,QPlatformSurfaceEvent, QSurface, QWindow,QSurfaceFormat)
from PySide6.QtGui import (QRhi, QRhiBuffer,QPixmap,QAction,QWheelEvent,QRhiDepthStencilClearValue,QRhiGraphicsPipeline, QRhiNullInitParams,QRhiGles2InitParams, QRhiRenderBuffer,QRhiSampler, QRhiShaderResourceBinding,QRhiShaderStage, QRhiTexture,QMovie,QRhiVertexInputAttribute, QRhiVertexInputBinding,QRhiVertexInputLayout, QRhiViewport, QShader)
from PySide6.QtWidgets import (QApplication, QDialog,QWidget, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtWidgets import (QCheckBox, QComboBox,QCommandLinkButton, QDateTimeEdit, QDial,QDialog, QDialogButtonBox, QFileSystemModel,QGridLayout, QGroupBox, QHBoxLayout, QLabel,QLineEdit, QListView, QMenu, QPlainTextEdit,QProgressBar, QPushButton, QRadioButton,QScrollBar, QSizePolicy, QSlider, QSpinBox,QStyleFactory, QTableWidget, QTabWidget,QTextBrowser, QTextEdit, QToolBox, QToolButton,QTreeView, QVBoxLayout)##########################################################################
#定义全局变更
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_apppathlst_modlist=sys.pathprint(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():global g_appPathreturn 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的文件读入内存的二进制数据为Nonedef MakeMemFile(self,fileNames,defFile='',bNew=False):if bNew: #如果新建内存文件,会用defFile文件作为KEY=0的内存文件数据self.fileCount=0self.dicMemFiles.clear()self.lstMemFileName.clear()self.lstMemFileName.append(defFile)if(defFile==''):print('新建内存文件时,必须要指定一个默认文件为作字典的默认内存数据,现将字典KEY=0的默认值为None')self.dicMemFiles[0]=Noneself.fileCount=1 #处理参数表传入的默认文件名if(defFile==''): #表示无需对默认文件进行任何操作passelse: #如有定义默认文件名,则更新字典中的KEY=0的默认文件数据self.lstMemFileName[0]=defFile #默认文件无论是否正确加载到内存文件中,列表中的值均为传入的内存文件名称if(os.path.exists(defFile)):with open(defFile, 'rb') as f:binData = f.read()self.dicMemFiles[0]=binDataprint(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=Noneif(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) #再次失败还是会得到一个Noneif(bDeepCopy):return copy.deepcopy(data)return data