標簽是界面設計中最常用的控件,本文演示了如何基于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.QtChartsfrom PySide6.QtCore import Signal, QEvent,Property, QSize
from PySide6.QtCore import (QDateTime, QFile,QDir, QLibraryInfo, QSysInfo, Qt,QTimer,Slot, QAbstractTableModel, QModelIndex,QPoint,QPointF,QStandardPaths, QUrl, QIODevice, QRectF,qFatal,qWarning,qVersion)
from PySide6.QtGui import (QCursor,QIcon,QImage,QPicture,QDesktopServices, QGuiApplication,QKeySequence, QShortcut, QStandardItem,QStandardItemModel)
from PySide6.QtGui import (QPen,QBrush,QColor,QFont, 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.QtChartsfrom PySide6.QtCore import Signal, QEvent,Property, QSize
from PySide6.QtCore import (QDateTime, QFile,QDir, QLibraryInfo, QSysInfo, Qt,QTimer,Slot, QAbstractTableModel, QModelIndex,QPoint,QPointF,QStandardPaths, QUrl, QIODevice, QRectF,qFatal,qWarning,qVersion)
from PySide6.QtGui import (QCursor,QIcon,QImage,QPicture,QDesktopServices, QGuiApplication,QKeySequence, QShortcut, QStandardItem,QStandardItemModel)
from PySide6.QtGui import (QPen,QBrush,QColor,QFont,QPalette,QPainter,QGradient,QMatrix4x4,QPlatformSurfaceEvent, QSurface, QWindow,QSurfaceFormat)
from PySide6.QtGui import (QRhi, QRhiBuffer,QPixmap,QAction,QWheelEvent,QRhiDepthStencilClearValue,QRhiGraphicsPipeline, QRhiNullInitParams,QRhiGles2InitParams, QRhiRenderBuffer,QRhiSampler, QRhiShaderResourceBinding,QRhiShaderStage, QRhiTexture,QMovie,QRhiVertexInputAttribute, QRhiVertexInputBinding,QRhiVertexInputLayout, QRhiViewport, QShader)
from PySide6.QtWidgets import (QApplication, QDialog,QWidget, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtWidgets import (QCheckBox, QComboBox,QCommandLinkButton, QDateTimeEdit, QDial,QDialog, QDialogButtonBox, QFileSystemModel,QGridLayout, QGroupBox, QHBoxLayout, QLabel,QLineEdit, QListView, QMenu, QPlainTextEdit,QProgressBar, QPushButton, QRadioButton,QScrollBar, QSizePolicy, QSlider, QSpinBox,QStyleFactory, QTableWidget, QTabWidget,QTextBrowser, QTextEdit, QToolBox, QToolButton,QTreeView, QVBoxLayout)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 #標簽控件是否可以主窗體上拖動:對窗體元素,應設置為Falseself.bZoomImgSize=True #控件的矩形區同圖象的矩形區不相符時,是否允許圖象或視頻自動縮放以適應控件矩形區self.setScaledContents(self.bZoomImgSize) # 設置標簽的圖片,設置True時圖片自適應控件,為False時,只顯示控件范圍圖片self.setAutoFillBackground(False) #不允許自動填充背景底色self.text=text #標簽是文本類型時顯示的內容self.drawText=text #標簽是圖片或視頻類型時顯示的內容self.alignFlags=Qt.AlignTop | Qt.AlignLeft #對齊方式self.bDrawTxt = False #顯示圖片的同時,是否將self.drawText畫到圖象上self.fontCol=fcolor #字體顏色self.bkCol=QColor(255,255,255) #如設置不透明時的標簽背景顏色self.setFont(font)palette = QPalette()palette.setColor(QPalette.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=0self.globalmouse_X=self.globalmouse_Y=0 #鼠標在標簽控件上相對屏幕左上點(0,0)的坐標self.oldPos=QPoint() #移動前標簽控件的位置self.curImgfilename=''self.curMovFileName=''self.curData=None #當標簽是加載的圖片或動畫時,將文件同容加載到內存中再顯示,避免頻繁讀寫文件self.image=QImage()self.curRotAngle=0.0 #圖片當前旋轉角度(角度,非弧度,順時針為正)self.gifSpeed=200 #當前要播放的GIF動畫的速度self.drawtxtX=self.drawtxtY=0#如要要不透明的標簽,設置標簽背景色def setBkCol(self,bkcol=QColor(255,255,255)):self.bkCol=bkcolpalette = QPalette()self.setAutoFillBackground(True) palette.setColor(QPalette.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.AlignLeftif(at=='TL'): self.alignFlags=Qt.AlignTop | Qt.AlignLeftelif(at=='TC'): self.alignFlags=Qt.AlignTop | Qt.AlignHCenterelif(at=='TR'): self.alignFlags=Qt.AlignTop | Qt.AlignRightelif(at=='CL'): self.alignFlags=Qt.AlignVCenter | Qt.AlignLeftelif(at=='CC'): self.alignFlags=Qt.AlignVCenter | Qt.AlignHCenterelif(at=='CR'): self.alignFlags=Qt.AlignVCenter | Qt.AlignRightelif(at=='DL'): self.alignFlags=Qt.AlignBottom | Qt.AlignLeftelif(at=='DC'): self.alignFlags=Qt.AlignBottom | Qt.AlignHCenterelif(at=='DR'): self.alignFlags=Qt.AlignBottom | Qt.AlignRightelse:self.alignFlags=Qt.AlignVCenter | Qt.AlignLeftself.setAlignment(self.alignFlags)self.setText(self.text) #有時并沒有出現對齊效果,只能采用先清除再重加載的方式#旋轉控件中的圖片一指定的角度:角度為正東向,向順時針旋轉的角度為正,反之為負(非弧度)def RotateImg(self,angle): if(self.type=='IMG' and self.curData!=None):transform = QTransform() transform.rotate(angle) self.image=self.image.transformed(transform); self.setPixmap(QPixmap.fromImage(self.image)) # 顯示圖片到Qlabel控件if(self.bChgCtlRect): #為真時,旋轉后同時調整控件大小self.resize(self.image.width(),self.image.height())#設置標簽控件在加載圖片時,控件尺寸同圖片尺寸不符時,是否允許控件調整自身的矩形區域,以適應1:1的圖象顯示def ObjToImgSize(self):self.setScaledContents(self.bZoomImgSize) #不允許自適應控件,只1:1顯示到控件中,同時調整控件大小if(self.bChgCtlRect): #只有先設置此屬性為真時,才允許變化控件尺寸if(self.curData!=None): image= QImage.fromData(self.curData)self.resize(image.width(),image.height()) #用下行后用設置參數中的矩形,用本行就是圖片本身的尺寸self.ctlRect=QRect(self.x(),self.y(),self.width(),self.height())#設置標簽加載的文件名稱,可以是圖片也可以是動畫GIF或視頻def LoadFile(self,filename=''):if(os.path.exists(filename)):file_extension = str(filename.split(".")[-1]).upper()bOK=Falsefor exname in lst_ImgExName:if file_extension == exname:self.type='IMG'bOK=Truebreakfor exname in lst_MovExName:if file_extension == exname:self.type='MOV'bOK=Truebreakif (bOK):with open(filename, 'rb') as f:self.curData = f.read()self.image= QImage.fromData(self.curData) self.curMovFileName=filenameself.RefreshLable()else:print(f'沒有找到對應擴展名: {file_extension} 的分類')self.type='TXT'self.ReshowText(self.text)self.ObjToImgSize()else:self.type='TXT'self.ReshowText(self.text)self.RefreshLable() #清除圖象,重新顯示標簽的文本def ReshowText(self,txt):self.text=txtself.clear()self.type='TXT'self.setText(txt)#設置顯示圖片的同時,畫到標簽控件上的文本,傳入文本為空時,同標簽控件初始化時的字符串一致,圖形模式下,不調用此函數,默認不會繪出文本def setDrawText(self,txt,x=0,y=0):self.bDrawTxt=Trueif(len(txt)==0):self.drawText=self.textelse:self.drawText=txtself.drawtxtX=xself.drawtxtY=y#重新顯示標簽(在用了LoadFile后)def RefreshLable(self): #如果圖片被調整亂了,且不想要控件尺寸同圖片尺寸self.setScaledContents(self.bZoomImgSize) #不允許自適應控件,只1:1顯示到控件中,同時調整控件大小if(self.type=='IMG'):self.image= QImage.fromData(self.curData)self.setPixmap(QPixmap.fromImage(self.image)) # 顯示圖片到Qlabel控件self.imgRect=QRect(self.x(),self.y(),self.image.width(),self.image.height())if(self.bChgCtlRect):self.resize(self.image.width(),self.image.height()) #用下行后用設置參數中的矩形,用本行就是圖片本身的尺寸 elif(self.type=='MOV'):"""#用內存文件來播放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默認播放速度為100def set_GifSpeed(self,interval=100.0):self.gifSpeed=intervalif(self.type=='MOV' and self.movie!=None):self.movie.setSpeed(interval) # 設置播放速度#設置標簽控件的透明程度:對文字及圖片均有效def SetTransparent(self,trans):if trans>1:trans=1elif trans<0:trans=0self.Transparent=transopacity_effect = QGraphicsOpacityEffect(parent=self)opacity_effect.setOpacity(trans) # 設置透明度self.setGraphicsEffect(opacity_effect) # 將透明度效果應用到標簽上#self.setWindowOpacity(self.Transparent)#設置本標簽對象是在最上方還是在最下方 def setLabelLayer(self,bTop=True):if(bTop):self.raise_()else:self.lower()#設置標簽顯示的文本的字體def setTextFont(self,fontname='宋體',fontsize=11,bBold=False,bItalic=False,bUnderline=False):font = QFont()font.setFamily(fontname) # 設置字體名稱font.setPointSize(fontsize) # 設置字體大小font.setBold(bBold) # 設置字體加粗font.setItalic(False)font.setUnderline(False)self.setFont(font)#設置標簽顯示的文本的字體def setTextCol(self,fcol=QColor(0,0,0)):self.setStyleSheet(f"QLabel {{ color: {fcol.name()}; }}")#設置標簽顯示的文本的字體def setTextBkCol(self,bkcol=QColor(255,255,255)):if( not self.bTransparent): #對非透明模式才支持設置標簽背景顏色self.setAutoFillBackground(True) # 確保背景自動填充palette = self.palette()palette.setColor(QPalette.ColorRole.Window, bkcol) self.setPalette(palette)#得到標簽矩形中心位置 def getObjRect(self):size = self.geometry()self.centerX=size.x()+size.width()/2self.centerY=size.y()+size.height()/2return size.x(),size.y(),size.width(),size.height()#鼠標按下事件重載 def mousePressEvent(self, event): self.startPoint=event.pos()self.oldPos=QPoint(event.globalX(),event.globalY()) # 核心部分: 當鼠標點擊是左鍵 并且 在top控件內點擊時候觸發 if (event.button() == Qt.LeftButton and self.move_Flag): #and self.top.underMouse():self.setCursor(Qt.OpenHandCursor) #移動時設置成手型光標# 但判斷條件滿足時候, 把拖動標識位設定為真#self.move_Flag = Trueself.globalmouse_X = event.globalX()self.globalmouse_Y = event.globalY()# 獲取窗體當前坐標self.origin_x = self.x()self.origin_y = self.y()#鼠標移動事件重載 def mouseMoveEvent(self, event): # 拖動標識位設定為真時, 進入移動事件if self.move_Flag:# 計算鼠標移動的x,y位移move_x = event.globalX() - self.globalmouse_Xmove_y = event.globalY() - self.globalmouse_Y# 計算窗體更新后的坐標:更新后的坐標 = 原本的坐標 + 鼠標的位移dest_x = self.origin_x + move_xdest_y = self.origin_y + move_y# 移動本標簽控件size = self.geometry()self.move(dest_x, dest_y)self.ctlRect=QRect(self.x(),self.y(),self.width(),self.height())self.setLabelLayer(True) #拖動的標簽控件角色在最頂端顯示# 鼠標左鍵釋放 def mouseReleaseEvent(self, event):self.endPoint=event.pos()newPos=QPoint(event.globalX(),event.globalY()) if (event.button() == Qt.LeftButton and self.move_Flag): self.setCursor(Qt.ArrowCursor) # 設定鼠標為普通狀態: 箭頭if(self.move_Flag==False): #非對象拖動狀態,鼠標在控件區域移動一位置if(abs(self.endPoint.x()-self.startPoint.x())<2 and abs(self.endPoint.y()-self.startPoint.y())<2 ): #判斷是否單擊if(event.button() == Qt.LeftButton):print('非拖動狀態下:是左鍵單擊不是拖動')self.signal_Leftclicked.emit(self)elif(event.button() == Qt.RightButton):print('非拖動狀態下:是右鍵單擊不是拖動')self.signal_Rightclicked.emit(self)elif(event.button() == Qt.MidButton):print('非拖動狀態下:是中鍵單擊不是拖動')self.signal_Midclicked.emit(self)else:print('非拖動狀態下,鼠標在控件上移動了一位置')else: #拖動對象狀態,除非一點也沒拖動,否則不對單位處理if(abs(self.oldPos.x()-newPos.x())<2 and abs(self.oldPos.y()-newPos.y())<2 ):print('雖是拖動狀態但是并沒拖動對象')if(event.button() == Qt.LeftButton):print('拖動狀態下:是左鍵單擊不是拖動')self.signal_Leftclicked.emit(self)elif(event.button() == Qt.RightButton):print('拖動狀態下:是右鍵單擊不是拖動')self.signal_Rightclicked.emit(self)elif(event.button() == Qt.MidButton):print('拖動狀態下:是中鍵單擊不是拖動')self.signal_Midclicked.emit(self)else: #拖動對象移動了位置 print('拖動狀態下:左鍵拖動控件移動了位置')self.signal_LeftDropRelease.emit(self)#重載繪圖函數:def paintEvent(self, event):if (self.bDrawRect): #標簽控件是否繪制邊框架self.DrawObjRect(self.rectCol,self.rectPenWidth) #為控件畫出外邊框if(self.bDrawTxt): #是否在標簽控件上畫出文本pen = QPen() # 創建畫筆對象painter = QPainter(self) # 此QPainter只能在paintEvent中定義,不能定義成類的self成員對象,也不能在其地方(如其他窗口,線程中)定義,否則沒有繪畫功能顯示#繪制pen.setColor(self.fontCol) painter.drawText(self.drawtxtX,self.drawtxtY,self.width(),self.height(),self.alignFlags,self.drawText) return super().paintEvent(event) #調用主窗口的重繪事件,不用不會加載動畫只顯示第一帖,用了動畫加載正常,但又多了一靜態圖第一帖#畫出當前控件的矩形框(用于對象被選擇時)def DrawObjRect(self,pencol,penwidth):self.rectCol=pencolself.rectPenWidth=penwidthif(self.bDrawRect):pen = QPen() # 創建畫筆對象brush = QBrush() # 創建畫刷對象painter = QPainter(self) # 此QPainter只能在paintEvent中定義,不能定義成類的self成員對象,也不能在其地方(如其他窗口,線程中)定義,否則沒有繪畫功能顯示#繪制pen.setColor(pencol) pen.setStyle(Qt.SolidLine) pen.setWidth(penwidth) # 設置畫筆寬度painter.setPen(pen) # 設置畫筆self.pixmap = QPixmap.fromImage(self.image)#painter.drawPixmap(0, 0, self.pixmap)painter.drawRect(0,0,self.width(),self.height()) #定義可用計時器播放連續圖片以形成動畫的標簽擴展類
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=100self.curindex = 0self.curPlayCount=0self.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=Falseself.curPlayCount=0self.lstImgFile=lstfilenameself.curtimespeed=timesleepself.imgPlayCount = playcountself.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):returnif(self.curindex>=(count-1)): #已循環一遍if(self.imgPlayCount>=0):self.curPlayCount+=1if(self.imgPlayCount>0 and self.curPlayCount>=self.imgPlayCount):self.curPlayCount = self.imgPlayCount+1self.curData = self.getImgData(count-1)self.bStop=Truereturnself.curData = self.getImgData(self.curindex)self.curindex=0else: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): # 沒圖片,則不執行任何操作returnsize = 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=lstfilenameself.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 = enableprint(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=lstfilenameself.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.bCheckedif(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 = enableif(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=0self.move_Flag=False #控件不可在主窗體上被拖動self.memImgFile=[]self.bEnable=True #是否可用self.bChecked=False #是否被選中QLabelExBtnRadio.lst_btnRadio.append(self) #單選按紐本身加入公用列表#設置按紐所屬組和在組中的編號IDdef setBtnGroup(self,group,ID):self.groupID=groupself.ID=ID#設置要圖片列表:0=按紐未被選中時的圖片,1=按紐被選中時的圖片,2=按紐未被選中失效時的圖片,3=按紐被選中失效時的圖片def setBtnImg(self,lstfilename):self.lstImgFile=lstfilenameself.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 = enableif(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 = checkedif(self.bChecked):self.showMemImg(1)else:self.showMemImg(0)
本示例用到圖像文件:1.png? 1.jpg? 2.png 1.gif 2.gif等文件自行找些改名后,copy到代碼目錄下即可