因PYQT5的标签类QLabel可支持显示透明图象,GIF动画等,用此类扩展继承类,封装好继承类以实现低代码就可以完成实现用标签显示图片,动画,文字,模仿图形按纽等,同时扩展来代替按纽,多选按纽,单选按纽的功能,下面以QLabel作为基类编写其扩展子类QLabelEx类(用于显示图片,动画等基本功能),在以QLabelEx类为基类,扩展出以下4个子类(QLabel的孙类)
QLabelExLstImg: 继承QLabelEx类的扩展类,用于定时显示指定的图象序列,显示类似于GIF动画效果
QLabelExBtn: 继承QLabelEx类的扩展类,用于模仿图象按纽,移入移出单击控件时可以用不同的透明图象显示效果
QLabelExBtnCheck: 继承QLabelEx类的扩展类,用于模仿check多选图象按纽
QLabelExBtnRadio: 继承QLabelEx类的扩展类,用于模仿Radio单选图象按纽
示例运行效果如下:(注:代码中用到的1.png等图片请自行找几小透明图片改名到模块目前录中即可,博客中我上传这几个图片可截屏保存,对异型图片要想显示异型按纽效果需要用firwork软件处理成透明图片即可)
原计划封装GIF动画时,用缓冲方式,即先将要用的GIF文件全部加载到内存中,以后不用再在硬盘上读取文件了,但没有成功,只能采用要使用的时候现读取和加载指定文件,如有成功的可以评论区贴下代码,供大家学习。
测试QtLabelEx.py模块的主窗体代码如下:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QByteArray
from QtLabelEx import * #导入自定义的标签扩展类
#测试标签扩展类的主窗体
class MyWidget(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('测试自定义继承QT的常用控件类')
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.btn_Test=QPushButton('测试...',self)
self.btn_Test.setGeometry(500,50,100,50)
self.btn_Test.clicked.connect(self.test)
font=QFont('宋体',9)
font.setBold(True)
font.setItalic(True)
font.setUnderline(True)
lst=['1.png','2.png','3.png','4.png'] #和模块文件同一目录的几个测试用小图片文件(最好用透明的PNG图)
self.myLabel01 = QLabelEx(self,50,50,150,150,'图象标签',0.8,font,QColor(255,0,0))
self.myLabel01.SetAlign('DR')
self.myLabel01.bDrawRect=True #是否画出控件边框
self.myLabel01.bChgCtlRect=False #控件是否会自动变化其矩形区,以适应当前图象大小
self.myLabel01.bZoomImgSize=True #是否将图片放缩填满到控件的当前矩形区域
self.myLabel01.LoadFile('1.gif') #和模块文件同一目录的1个测试用GIF动画文件
self.myLabel01.set_GifSpeed(100)
self.myLabel01.DrawObjRect(QColor(0,0,255),2)
self.myLabel02 = QLabelExLstImg(self,250,50,150,150,'播放图象列表标签',0.8,font,QColor(255,0,0))
self.myLabel02.setLstImg(lst,100)
self.myLabel02.bDrawRect=False #是否画出控件边框
lstbtn=['btn_on.png','btn_on.png','btn_off.png','btn_disable.png'] #和模块文件同一目录的几个测试用小图片文件(最好用透明的PNG图)
self.myLabel03 = QLabelExBtn(self,50,250,202,73,'按纽标签')
self.myLabel03.bDrawRect=False #不画外框
self.myLabel03.setBtnImg(lstbtn)
self.myLabel03.showMemImg(0)
self.myLabel04 = QLabelExBtnCheck(self,50,450,150,150,'多选按纽标签')
self.myLabel05 = QLabelExBtnCheck(self,250,450,150,150,'多选按纽标签')
self.myLabel04.setBtnImg(lst)
self.myLabel04.bZoomImgSize=False #是否将图缩片放填满到控件的当前矩形区域
self.myLabel04.showMemImg(0)
self.myLabel05.setBtnImg(lst)
self.myLabel05.bZoomImgSize=False #是否将图片缩放填满到控件的当前矩形区域
self.myLabel05.showMemImg(0)
self.myLabel06 = QLabelExBtnRadio(self,50,650,150,150,'单选按纽标签')
self.myLabel06.setBtnGroup(0,0) #仅用于QLabelExBtnRadio
self.myLabel07 = QLabelExBtnRadio(self,250,650,150,150,'单选按纽标签')
self.myLabel07.setBtnGroup(0,1) #仅用于QLabelExBtnRadio
self.myLabel06.setBtnImg(lst)
self.myLabel07.setBtnImg(lst)
self.myLabel06.showMemImg(0)
self.myLabel07.showMemImg(0)
self.myLabel01.signal_Leftclicked.connect(self.lab_LeftClick)
self.myLabel01.signal_Rightclicked.connect(self.lab_RightClick)
self.myLabel01.signal_Midclicked.connect(self.lab_MidClick)
self.myLabel01.signal_LeftDropRelease.connect(self.lab_LeftDropClick)
self.myLabel02.signal_Leftclicked.connect(self.lab_LeftClick)
self.myLabel02.signal_Rightclicked.connect(self.lab_RightClick)
self.myLabel02.signal_Midclicked.connect(self.lab_MidClick)
self.myLabel02.signal_LeftDropRelease.connect(self.lab_LeftDropClick)
self.myLabel03.signal_Leftclicked.connect(self.lab_LeftClick)
self.myLabel03.signal_Rightclicked.connect(self.lab_RightClick)
self.myLabel03.signal_Midclicked.connect(self.lab_MidClick)
self.myLabel03.signal_LeftDropRelease.connect(self.lab_LeftDropClick)
self.myLabel04.signal_Leftclicked.connect(self.lab_LeftClick)
self.myLabel04.signal_Rightclicked.connect(self.lab_RightClick)
self.myLabel04.signal_Midclicked.connect(self.lab_MidClick)
self.myLabel04.signal_LeftDropRelease.connect(self.lab_LeftDropClick)
self.myLabel05.signal_Leftclicked.connect(self.lab_LeftClick)
self.myLabel05.signal_Rightclicked.connect(self.lab_RightClick)
self.myLabel05.signal_Midclicked.connect(self.lab_MidClick)
self.myLabel05.signal_LeftDropRelease.connect(self.lab_LeftDropClick)
self.myLabel06.signal_Leftclicked.connect(self.lab_LeftClick)
self.myLabel06.signal_Rightclicked.connect(self.lab_RightClick)
self.myLabel06.signal_Midclicked.connect(self.lab_MidClick)
self.myLabel06.signal_LeftDropRelease.connect(self.lab_LeftDropClick)
self.myLabel07.signal_Leftclicked.connect(self.lab_LeftClick)
self.myLabel07.signal_Rightclicked.connect(self.lab_RightClick)
self.myLabel07.signal_Midclicked.connect(self.lab_MidClick)
self.myLabel07.signal_LeftDropRelease.connect(self.lab_LeftDropClick)
self.myLabel06.signal_RadioBntSelected.connect(self.lab_RadioClick) #仅用于QLabelExBtnRadio
self.myLabel07.signal_RadioBntSelected.connect(self.lab_RadioClick) #仅用于QLabelExBtnRadio
#接收Radio标签按纽被单击时的信号 #仅用于QLabelExBtnRadio
def lab_RadioClick(self,groupID,obj):
print('Radio标签按纽被单击反馈到主窗体的槽函数')
#标签被单击时的信号槽函数
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 test(self):
self.myLabel01.set_GifSpeed(200) #为标签1的GIF播放速度快一倍
self.myLabel02.setBkCol(QColor(0,255,0)) #为标签2为标签图片设置底色
#self.myLabel03.ReshowText('abcd1234')
self.myLabel03.setBtnEnable(False) #为标签3为按纽设置为失效状态
self.myLabel04.bDrawTxt=True #图片和文字同时出现
self.myLabel04.bDrawRect=False #不画出控件外框
self.myLabel05.bDrawTxt=True
self.myLabel04.RefreshLable()
self.myLabel05.RefreshLable()
self.myLabel06.bDrawTxt=True #图片和文字同时出现
self.myLabel06.bDrawRect=False #不画出控件外框
self.myLabel06.move_Flag=True #使控件06也支持可拖动了
self.myLabel07.bDrawTxt=True
self.myLabel06.RefreshLable()
self.myLabel07.RefreshLable()
#########################################################################################
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec())
自定义的扩展QLabel的模块文件QtLabel.py文件代码如下:
#模块名:QtLabelEx.py:将QT5/6的标签等常规控件类再扩展对应的子类,更方便快速使用
#包含类名: QLabelEx: 继承QtLable类的扩展类:支持低代码显示图片,动画等功能
# QLabelExLstImg: 继承QLabelEx类的扩展类,用于可以定时显示指定的图象列表
# QLabelExBtn: 继承QLabelEx类的扩展类,用于模仿图象按纽,移入移出单击控件时可以用不同的透明图象显示
# QLabelExBtnCheck: 继承QLabelEx类的扩展类,用于模仿check多选图象按纽
# QLabelExBtnRadio: 继承QLabelEx类的扩展类,用于模仿Radio单选图象按纽
# :
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.QtNetwork import QNetworkAccessManager, QNetworkRequest
import time
import math
import copy
import random
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,x,y,w,h,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 = True #是否在标签控件外边画出矩形框
self.rectCol=QColor(255,0,0) #画矩形边框的颜色
self.rectPenWidth=2 #画矩形边框的线宽度
self.bChgCtlRect=False #如果self.ctlRect不能满足文字、图象的矩形区域时,是否允许控件变化其矩形来适应文字或图象要求的矩形区域
self.move_Flag = True #标签控件是否可以主窗体上拖动:对窗体元素,应设置为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'):
"""
#用内存文件来播放GIF没成功??
#从bytes创建一个QBuffer对象
buffer=QBuffer()
buffer.open(QBuffer.ReadOnly)
buffer.write(self.curData)
#self.movie = QMovie(self)
#self.movie.setDevice(QByteArray(self.curData))
# 使用QMovie来播放GIF
#self.movie = QMovie(buffer)
#self.movie.setSpeed(10)
# 将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(2.0) #设置播放动画GIF的整速度:方法接受的是每1000毫秒播放的帧数比例,如是1:表示,一秒显示全部帧数,0.5表示一秒显示半数的帧数。
self.movie.start()
#self.setLabelLayer(True)
"""
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())
#定义可用计时器播放连续图片以形成动画的标签扩展类
class QLabelExLstImg(QLabelEx):
def __init__(self, parent,x,y,w,h,text='',transt=1.0,font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super().__init__(parent,x,y,w,h,text,transt,font,fcolor)
self.type='IMG'
self.memImgFile=[]
self.curtimespeed=100
self.curindex = 0
self.curPlayCount=0
self.imgPlayCount = 0 #图象列表播放遍数,0=循环播放,大于0时为播放的遍数后,自动停止到最后一帖位置
self.bStop=False #指定遍数放完后,此开关为True
# 创建计时器,设置时间间隔为100毫秒(1秒)
self.timer = QTimer()
# 计时器信号连接到timeout_slot槽函数
self.timer.timeout.connect(self.time_objmove_slot)
self.timer.start(self.curtimespeed) # 开始计时器:
#设置要播放的图片列表,及图象列表间播放的间隔
def setLstImg(self,lstfilename,timesleep=50,playcount=0):
self.bStop=False
self.curPlayCount=0
self.lstImgFile=lstfilename
self.curtimespeed=timesleep
self.imgPlayCount = playcount
self.makeMemFile()
#创建图片的内存文件
def makeMemFile(self):
self.memImgFile.clear()
for imgFilename in self.lstImgFile:
# 打开图片
if(os.path.exists(imgFilename)):
with open(imgFilename, 'rb') as f:
img = f.read()
self.memImgFile.append(img)
#得到对象的文件内存数据:序号
def getImgData(self,index):
count=len(self.memImgFile)
if(count>0 and index>=0 and index<count):
return self.memImgFile[index]
else:
print(f'返回一空图象{index},{count}')
return None
#显示内存文件数据:序号
def showMemImg(self,index):
if(self.getImgData(index)!=None):
self.curData = self.getImgData(index)
self.RefreshLable()
#在标签控件中播放下一帧
def next_frame(self):
count=len(self.memImgFile)
if(count==0 or self.bStop):return
if(self.curindex>=(count-1)): #已循环一遍
if(self.imgPlayCount>=0):
self.curPlayCount+=1
if(self.imgPlayCount>0 and self.curPlayCount>=self.imgPlayCount):
self.curPlayCount = self.imgPlayCount+1
self.curData = self.getImgData(count-1)
self.bStop=True
return
self.curData = self.getImgData(self.curindex)
self.curindex=0
else:
self.curData = self.getImgData(self.curindex)
self.curindex = (self.curindex + 1) % count
self.timer.setInterval(self.curtimespeed)
#对象计时器来显示动画效果
def time_objmove_slot(self):
self.next_frame() #得到下一帖图象
#移动前对图象进行方向进行处理
if (self.curData==None): # 没图片,则不执行任何操作
return
size = self.geometry()
fx = size.x()+size.width()/2 #对象矩形中心的坐标
fy = size.y()+size.height()/2
if(self.type=='IMG'): #对GIF,计时器会会造成播放过快无法控制
self.RefreshLable()
#定义标签类按纽扩展类
class QLabelExBtn(QLabelEx):
def __init__(self, parent,x,y,w,h,text='',transt=1.0,font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super().__init__(parent,x,y,w,h,text,transt,font,fcolor)
self.type='IMG'
self.memImgFile=[]
self.bEnable=True
#设置要图片列表:0=按纽常规状态下的图片,1=鼠标移入控件时的图片,2=鼠标点击按纽时的图片,3=按纽失效时的图片
def setBtnImg(self,lstfilename):
self.lstImgFile=lstfilename
self.makeMemFile()
#创建图片的内存文件
def makeMemFile(self):
self.memImgFile.clear()
for imgFilename in self.lstImgFile:
# 打开图片
if(os.path.exists(imgFilename)):
with open(imgFilename, 'rb') as f:
img = f.read()
self.memImgFile.append(img)
#得到对象的文件内存数据:序号
def getImgData(self,index):
count=len(self.memImgFile)
if(count>0 and index>=0 and index<count):
return self.memImgFile[index]
else:
print(f'返回一空图象{index},{count}')
return None
#显示内存文件数据:序号
def showMemImg(self,index):
if(self.getImgData(index)!=None):
self.curData = self.getImgData(index)
self.RefreshLable()
#移入控件事件
def enterEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
self.showMemImg(1)
#移出控件事件
def leaveEvent(self,event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
self.showMemImg(0)
#鼠标按下事件重载
def mousePressEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
if (event.button() == Qt.LeftButton):
self.showMemImg(2)
return super().mousePressEvent(event)
#鼠标移动事件重载
def mouseMoveEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
return super().mouseMoveEvent(event)
# 鼠标左键释放
def mouseReleaseEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
return super().mouseReleaseEvent(event)
def setBtnEnable(self,enable):
self.bEnable = enable
print(f'self.bEnable={self.bEnable}')
if(enable):self.showMemImg(0)
else:self.showMemImg(3)
#定义标签CHECK类按纽扩展类
class QLabelExBtnCheck(QLabelEx):
def __init__(self, parent,x,y,w,h,text='',transt=1.0,font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super().__init__(parent,x,y,w,h,text,transt,font,fcolor)
self.type='IMG'
self.groupID=0 #check按纽所属组数
self.move_Flag=False #控件不可在主窗体上被拖动
self.memImgFile=[]
self.bEnable=True #是否可用
self.bChecked=False #是否被选中
#设置按纽所属组
def setBtnGroup(self,group):
self.groupID=group
#设置要图片列表:0=按纽未被选中时的图片,1=按纽被选中时的图片,2=按纽未被选中失效时的图片,3=按纽被选中失效时的图片
def setBtnImg(self,lstfilename):
self.lstImgFile=lstfilename
self.makeMemFile()
#创建图片的内存文件
def makeMemFile(self):
self.memImgFile.clear()
for imgFilename in self.lstImgFile:
# 打开图片
if(os.path.exists(imgFilename)):
with open(imgFilename, 'rb') as f:
img = f.read()
self.memImgFile.append(img)
#得到对象的文件内存数据:序号
def getImgData(self,index):
count=len(self.memImgFile)
if(count>0 and index>=0 and index<count):
return self.memImgFile[index]
else:
print(f'返回一空图象{index},{count}')
return None
#显示内存文件数据:序号
def showMemImg(self,index):
if(self.getImgData(index)!=None):
self.curData = self.getImgData(index)
self.RefreshLable()
#鼠标按下事件重载
def mousePressEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
if (event.button() == Qt.LeftButton):
self.bChecked=not self.bChecked
if(self.bChecked):
self.showMemImg(1)
else:
self.showMemImg(0)
return super().mousePressEvent(event)
#鼠标移动事件重载
def mouseMoveEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
return super().mouseMoveEvent(event)
# 鼠标左键释放
def mouseReleaseEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
return super().mouseReleaseEvent(event)
def setBtnEnable(self,enable):
self.bEnable = enable
if(enable):
if(self.bChecked):
self.showMemImg(1)
else:
self.showMemImg(0)
else:
if(self.bChecked):
self.showMemImg(2)
else:
self.showMemImg(3)
#定义标签Radio类按纽扩展类
class QLabelExBtnRadio(QLabelEx):
lst_btnRadio=[] #定义到类构造外所有的类成员共用,每增加一个类成员实例化对象,同时加到此列表中以处理类似radio单选按纽的效果
signal_RadioBntSelected = QtCore.pyqtSignal(int,object) #自定信号,radio类按纽被单击时,通知主窗口,所有本组中的其他同类radio按结全部去除被选择状态,传回参数:所属组号
def __init__(self, parent,x,y,w,h,text='',transt=1.0,font=QFont('宋体', 11),fcolor=QColor(0,0,0)):
super().__init__(parent,x,y,w,h,text,transt,font,fcolor)
self.type='IMG'
self.groupID=0 #单选按纽所属组数
self.ID=0 #单选按纽在组中的序号:同组不可重复
self.move_Flag=False #控件不可在主窗体上被拖动
self.memImgFile=[]
self.bEnable=True #是否可用
self.bChecked=False #是否被选中
QLabelExBtnRadio.lst_btnRadio.append(self) #单选按纽本身加入公用列表
#设置按纽所属组和在组中的编号ID
def setBtnGroup(self,group,ID):
self.groupID=group
self.ID=ID
#设置要图片列表:0=按纽未被选中时的图片,1=按纽被选中时的图片,2=按纽未被选中失效时的图片,3=按纽被选中失效时的图片
def setBtnImg(self,lstfilename):
self.lstImgFile=lstfilename
self.makeMemFile()
#创建图片的内存文件
def makeMemFile(self):
self.memImgFile.clear()
for imgFilename in self.lstImgFile:
# 打开图片
if(os.path.exists(imgFilename)):
with open(imgFilename, 'rb') as f:
img = f.read()
self.memImgFile.append(img)
#得到对象的文件内存数据:序号
def getImgData(self,index):
count=len(self.memImgFile)
if(count>0 and index>=0 and index<count):
return self.memImgFile[index]
else:
print(f'返回一空图象{index},{count}')
return None
#显示内存文件数据:序号
def showMemImg(self,index):
if(self.getImgData(index)!=None):
self.curData = self.getImgData(index)
self.RefreshLable()
#鼠标按下事件重载
def mousePressEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
if (event.button() == Qt.LeftButton):
self.setBtnChecked(True) #本对象被选中,同组内的其他成员去除选中状态,一组成员中只能有一个被选中
for obj in QLabelExBtnRadio.lst_btnRadio:
if(self.groupID==obj.groupID and self.ID!=obj.ID): #是同一组其他对象
obj.setBtnChecked(False)
self.signal_RadioBntSelected.emit(self.groupID,self)
return super().mousePressEvent(event) #调用父类的按下事件
#鼠标移动事件重载
def mouseMoveEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
return super().mouseMoveEvent(event)
# 鼠标左键释放
def mouseReleaseEvent(self, event):
if(not self.bEnable): return #当按纽状记为不可用时,不接受鼠标事件
return super().mouseReleaseEvent(event)
def setBtnEnable(self,enable):
self.bEnable = enable
if(enable):
if(self.bChecked):
self.showMemImg(1)
else:
self.showMemImg(0)
else:
if(self.bChecked):
self.showMemImg(2)
else:
self.showMemImg(3)
#设置Radio按纽的显示状态
def setBtnChecked(self,checked):
if(not self.bEnable): return #当按纽标记为不可用时,不接受鼠标事件
self.bChecked = checked
if(self.bChecked):
self.showMemImg(1)
else:
self.showMemImg(0)
代码中用到的几个图片粘到文章中,方便小白截屏使用看效果(不加工成透明,无法演示图片周围异形外的透时效果哦)
以下图片依次为'1.png','2.png','3.png','4.png'
以下图片依次为
'btn_on.png','btn_on.png','btn_off.png','btn_disable.png'
主窗体用底图'1.jpg', 任意COPY个屏保底图改名成1.jpg即可
动画用'1.gif'用的下面的GIF