标签是界面设计中最常用的控件,本文演示了如何基于PySide6的QLabex控件类扩展定义QLabelEX类,以实现更少的编码完成各种图像、彩色文本、动画的加载和显示,丰富界面显示
本示例演示了QLabel和其扩展类QLabelEx分别显示文本、图像、动画的使用方法
示例主窗口模块代码如下:
# -*- coding:utf-8 -*-
import sys
from PySide6 import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
#from PySide6.QtGui import * #如果运行时没有任何界面调出,也不报错,请屏蔽此行,原因不详
import PySide6.QtCharts
from 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, 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 QLabelEx import * #导入标签扩展类
################################################################################
class mainWindow(QWidget):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.resize(500, 800)
self.setWindowTitle("PySide QLabel及扩展标签类QLabelEx的几种用法示例")
# 全局布局(1个):水平
wlayout = QHBoxLayout()
# 局部布局(2个):竖直
self.layout1 = QVBoxLayout()
self.layout1.setSpacing(10)
self.layout2 = QVBoxLayout()
self.layout2.setSpacing(10)
'''配置'''
# 配置文本内容
label1 = QLabel(self)
label1.setText("本列为原始PySide QLabel示例")
# 设置图片
label2 = QLabel(self)
label2.setPixmap(QPixmap("2.png"))
# 限制图片大小,并允许图片自适应限制
label3 = QLabel(self)
label3.setPixmap(QPixmap("2.png"))
label3.setFixedSize(40, 40) # 限制图片大小
label3.setScaledContents(True) # 图片自适应限制
# 设置居中对齐
label4 = QLabel(self)
label4.setText("设置居中对齐")
label4.setAlignment(Qt.AlignmentFlag.AlignCenter)
# 设置缩进
label5 = QLabel(self)
label5.setText("设置缩进")
label5.setIndent(20)
# 设置边距;setStyleSheet("border:边框粗细 实体 颜色;")
label6 = QLabel(self)
label6.setText('文本边框显示,边框2倍框,实体边框,红色')
label6.setStyleSheet("border:2px solid red;")
# 文本内容距离边框的间距
label7 = QLabel(self)
label7.setText('文本内容距离边框的间距')
label7.setStyleSheet("border:1px solid;")
label7.setMargin(10)
# 设置文本格式
label8 = QLabel(self)
label8.setText('设置文本格式为超文本')
label8.setTextFormat(Qt.TextFormat.RichText)
# 允许文本被编辑和选中
label9 = QLabel(self)
label9.setText('允许文本被编辑和选中')
label9.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse | Qt.TextInteractionFlag.TextEditable)
# 打开外部链接(可选择交互)
label10 = QLabel(self)
label10.setText("<a href='www.baidu.com' target='_blank'>超链接:百度</a>")
label10.setOpenExternalLinks(True) # 允许打开链接
# 画图案,drawEllipse(第1、2个参数是矩形的坐标原点,第3、4个参数是矩形的长和宽)
label11 = QLabel(self)
pic = QPicture() # 图片对象
painter = QPainter(pic) # 画家对象
painter.setBrush(QBrush(QColor(100, 120, 155))) # 设置画刷
painter.drawEllipse(0, 0, 50, 100)
label11.setPicture(pic)
# 展示动图
label12 = QLabel(self)
movie = QMovie("1.gif")
label12.setMovie(movie)
label12.setFixedSize(100, 100) # 限制图片大小
label12.setScaledContents(True) # 图片自适应限制
movie.start() # !! 开始动画
movie.setSpeed(100) # 设置动画的速度100%
# movie.stop() # 关闭动画
# 设计标签并清空
label13 = QLabel(self)
label13.setText('清空')
label13.clear()
# 字体
label14 = QLabel(self)
label14.setText('字体加粗,14号,黑体')
label14.setFont(QFont('Bold', 14, QFont.Black))
'''布局'''
self.layout1.addWidget(label1)
self.layout1.addWidget(label2)
self.layout1.addWidget(label3)
self.layout1.addWidget(label4)
self.layout1.addWidget(label5)
self.layout1.addWidget(label6)
self.layout1.addWidget(label7)
self.layout1.addWidget(label8)
self.layout1.addWidget(label9)
self.layout1.addWidget(label10)
self.layout1.addWidget(label11)
self.layout1.addWidget(label12)
self.layout1.addWidget(label13)
self.layout1.addWidget(label14)
# 文本类扩展标签
labelEx01 = QLabelEx(self,0,0,0,0,'本列为原始PySide QLabel类继承扩展QLabelEx类示例',0.5,QFont('黑体',18),QColor(255,0,0))
self.layout2.addWidget(labelEx01)
# 图像类扩展标签
labelEx02 = QLabelEx(self,0,0,0,0,'图片扩展标签02') #默认是自动缩放装满标签矩形框:默认左中对齐
labelEx02.LoadFile("2.png")
self.layout2.addWidget(labelEx02)
labelEx03 = QLabelEx(self,0,0,0,0,'图片扩展标签03') #默认是自动缩放装满标签矩形框
labelEx03.bZoomImgSize=False #原图大小不缩放
labelEx03.bDrawRect=True #画出控件矩形区域线框
labelEx03.SetAlign('DR') #图片右下角对齐显示
labelEx03.LoadFile("2.png")
self.layout2.addWidget(labelEx03)
# 动画类扩展标签
labelEx04 = QLabelEx(self,0,0,0,0,'动画扩展标签04') #默认是自动缩放装满标签矩形框
labelEx04.LoadFile("1.gif")
self.layout2.addWidget(labelEx04)
labelEx05 = QLabelEx(self,0,0,0,0,'动画扩展标签05') #默认是自动缩放装满标签矩形框
labelEx05.bZoomImgSize=False #原图大小不进行缩放
labelEx05.bDrawRect=True #画出控件矩形区域线框
labelEx05.SetAlign('CC') #动画上下左右居中显示
labelEx05.LoadFile("2.gif")
self.layout2.addWidget(labelEx05)
# 准备2个子窗体部件,分别定义上面准备布局的2个区域
hwg = QWidget()
vwg = QWidget()
hwg.setLayout(self.layout1)
vwg.setLayout(self.layout2)
# 四个部件加至全局布局(沿水平方向,竖向7等分平区为8块,水平占3等份网格占3等份,其他占1等份)
wlayout.addWidget(hwg)
wlayout.addWidget(vwg)
# 窗体本体设置全局布局
self.setLayout(wlayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwin = mainWindow()
mainwin.show()
sys.exit(app.exec_())
自定义标签扩展类模块QLabeEx.py代码如下
#模块名:QLabelEx.py:将PySide6的标签、编辑框、按纽等常规控件类再扩展对应的子类,更方便快速使用
#包含类名: QLabelEx: 继承QtLable类的扩展类:支持低代码显示图片,动画等功能
# QLabelExLstImg: 继承QLabelEx类的扩展类,用于可以定时显示指定的图象列表
# QLabelExBtn: 继承QLabelEx类的扩展类,用于模仿图象按纽,移入移出单击控件时可以用不同的透明图象显示
# QLabelExBtnCheck: 继承QLabelEx类的扩展类,用于模仿check多选图象按纽
# QLabelExBtnRadio: 继承QLabelEx类的扩展类,用于模仿Radio单选图象按纽
# :
import os,sys,time,math,copy,random
"""已不需要PyQt5,转为PySide6支持了
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
"""
from PySide6 import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
#from PySide6.QtGui import * #如果运行时没有任何界面调出,也不报错,请屏蔽此行,原因不详
import PySide6.QtCharts
from 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)
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=None,x=0,y=0,w=0,h=0,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 #标签控件是否可以主窗体上拖动:对窗体元素,应设置为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.ColorRole.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.ColorRole.Window, 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)
self.RefreshLable()
#清除图象,重新显示标签的文本
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.ColorRole.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 = Signal(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 #check按纽所属组数
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 1.jpg 2.png 1.gif 2.gif等文件自行找些改名后,copy到代码目录下即可