当前位置: 首页 > news >正文

pyside6学习专栏(三):自定义QLabel标签扩展类QLabelEx

标签是界面设计中最常用的控件,本文演示了如何基于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到代码目录下即可

相关文章:

  • 复制所绑定元素文本的vue自定义指令
  • 【论文解析】Fast prediction mode selection and CU partition for HEVC intra coding
  • flink-cdc同步数据到doris中
  • 算法的复杂性分析以及时间复杂度的表示方法
  • JavaSE学习笔记25-反射(reflection)
  • 顺序表和STL——vector【 复习笔记】
  • C++ IDE设置 visual studio 2010安装、注册、使用
  • 一周学会Flask3 Python Web开发-flask3模块化blueprint配置
  • 【Go语言快速上手】第二部分:Go语言进阶之工具与框架
  • L2-【英音】地道语音语调
  • 自由学习记录(37)
  • python学智能算法(二)|模拟退火算法:进阶分析
  • PHP 会话(Session)实现用户登陆功能
  • Flutter CupertinoNavigationBar iOS 风格导航栏的组件
  • 10-R数组
  • LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_34_中等_C++)(二分查找)(一次二分查找+挨个搜索;两次二分查找)
  • 独立开发者如何寻找产品设计灵感
  • 大规模 RDMA AI 组网技术创新:算法和可编程硬件的深度融合
  • 基于Spring Boot的兴顺物流管理系统设计与实现(LW+源码+讲解)
  • eclips 快捷键
  • 西藏日喀则市拉孜县发生5.5级地震,震源深度10公里
  • 人民空军:网上出现的“运-20向外方运送物资”为不实消息
  • 俄乌释放停火和谈信号,克宫:将组建“相应级别”谈判代表团
  • 新修订的《婚姻登记条例》明起施行,领证不用户口本了
  • 上海“世行对标改革”的税务样本:设立全国首个税务审判庭、制定首个税务行政复议简易程序
  • 央行谈MLF:逐步退出政策利率属性回归流动性投放工具