Skip to content

图形和特效

使用PyQt实现的窗口样式,默认使用的是当前操作系统的原生窗口样式,我们需要定制窗口样式,实现统一美化的窗口界面。


窗口风格

获得当前平台支持的原有的QStyle样式:QStyleFactory.keys()

QApplication设置QStyle样式:QApplication.setStyle(QStyleFactory.create("WindowsXP"))

可以为每个Widget设置风格:setStyle(QStyle style)

若其他的Widget没有设置风格,默认使用QApplication设置的QStyle

案例--查看支持的窗口风格,选择一类进行设置
python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5 import QtCore 
from PyQt5.QtGui  import *

class AppWidget( QWidget):
	def __init__(self, parent=None):
		super(AppWidget, self).__init__(parent)
		self.setWindowTitle("界面风格例子")
		horizontalLayout =  QHBoxLayout()
		self.styleLabel =  QLabel("Set Style:")   # 设置提示信息
		self.styleComboBox =  QComboBox()  # 创建一个列表框
		self.styleComboBox.addItems( QStyleFactory.keys())  # 从QStyleFactory增加 styles到列表框中 
		# 选择当前界面风格
		index = self.styleComboBox.findText(
					 QApplication.style().objectName(),
					QtCore.Qt.MatchFixedString)
		# 设置当前列表框的界面风格
		self.styleComboBox.setCurrentIndex(index)
		# 通过comboBox选择界面分割
		self.styleComboBox.activated[str].connect(self.handleStyleChanged)
		horizontalLayout.addWidget(self.styleLabel)
		horizontalLayout.addWidget(self.styleComboBox)
		self.setLayout(horizontalLayout)

	# 改变界面风格
	def handleStyleChanged(self, style):
		QApplication.setStyle(style)

if __name__ == "__main__":
	app =  QApplication(sys.argv)
	widgetApp = AppWidget()
	widgetApp.show()
	sys.exit(app.exec_())

窗口样式

PyQt使用setWindowFlags(Qt.WindowFlags)函数设置窗口样式

PyQt下的基本窗口类型:

窗口类型描述
Qt.Widget默认窗口,有最小化、最大化、关闭按钮
Qt.Window普通窗口,有最小化、最大化、关闭按钮
Qt.Dialog对话框窗口,有问号和关闭按钮
Qt.Popup弹出窗口,窗口无边框
Qt.ToolTip提示窗口,窗口无边框,无任务栏
Qt.SplashScreen闪屏,窗口无边框,无任务栏
Qt.SubWindow子窗口,窗口无按钮,有标题

自定义顶层窗口外观标志:

标志描述
Qt.MSWindowsFixedSizeDialogHint窗口无法调整大小
Qt.FramelessWindowHint窗口无边框
Qt.CustomizeWindowHint有边框但无标题栏和按钮,不能移动和拖动
Qt.WindowTitleHint添加标题栏和一个关闭按钮
Qt.WindowSystemMenuHint添加系统目录和一个关闭按钮
Qt.WindowMaximizeButtonHint激活最大化和关闭按钮,禁止最小化按钮
Qt.WindowMinimizeButtonHint激活最小化和关闭按钮,禁止最大化按钮
Qt.WindowMinMaxButtonHint激活最小化、最大化和关闭按钮
Qt.WindowCloseButtonHint添加一个关闭按钮
Qt.WindowContextHelpButtonHint添加问号和关闭按钮,像对话框一样
Qt.WindowStaysOnTopHint窗口始终处于顶层位置
Qt.WindowStaysOnBottomHint窗口始终处于底层位置

设置窗口样式:如设置窗口无边框:self.setWindowFlags(Qt.FramelessWindowHint)

案例--使用自定义的无边框窗口

我们可以自定义一个无边框窗口,其可以100%占用用户的屏幕

python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QMainWindow , QApplication
from PyQt5.QtCore import Qt 

# 自定义窗口类 
class MyWindow( QMainWindow):
    def __init__(self,parent=None):
        super(MyWindow,self).__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint)  # 设置窗口标记(无边框)
        self.setStyleSheet('''background-color:blue; ''')    # 便于显示,设置窗口背景颜色(采用QSS)
    # 覆盖函数  
    def showMaximized(self):
        desktop = QApplication.desktop()   # 得到桌面控件
        rect = desktop.availableGeometry()  # 得到屏幕可显示尺寸
        self.setGeometry(rect)   # 设置窗口尺寸
        self.show()    # 设置窗口显示

### 主函数     
if __name__ == "__main__":
    app =  QApplication(sys.argv)  # 声明变量
    window = MyWindow()   # 创建窗口
    window.showMaximized()  # 调用最大化显示
    sys.exit(app.exec_())  # 应用程序事件循环

绘图

图像类

PyQt中常用的图像类有四个:QPixmapQImageQPictureQBitmap

  • QPixmap:专门为绘图设计,在绘制图片时要使用。
  • QImage:提供一个与硬件无关的图像表示函数,可以用于图片的像素级访问。
  • QPicture:绘图设备类,继承自QPainter,使用QPainterbegin()函数在QPicture上绘图,使用end()函数结束绘图,使用QPicture的save()函数将QPainter所使用过的绘图指令保存到文件中。
  • QBitmap:是一个继承自QPixmap的简单类,提供了1bit深度的二值图像的类。
案例--实现基本的画线功能
python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication  ,QWidget 
from PyQt5.QtGui import   QPainter ,QPixmap
from PyQt5.QtCore import Qt , QPoint

class Winform(QWidget):
	def __init__(self,parent=None):
		super(Winform,self).__init__(parent)
		self.setWindowTitle("绘图例子") 
		self.pix =  QPixmap()
		self.lastPoint =  QPoint()
		self.endPoint =  QPoint()
		self.initUi()
		
	def initUi(self):
		self.resize(600, 500)   # 窗口大小设置为600*500
		# 画布大小为400*400,背景为白色
		self.pix = QPixmap(400, 400)
		self.pix.fill(Qt.white)
         
	def paintEvent(self,event):   # 重构paintEvent函数
		pp = QPainter( self.pix)
		pp.drawLine( self.lastPoint, self.endPoint)   # 根据鼠标指针前后两个位置绘制直线
		self.lastPoint = self.endPoint   # 让前一个坐标值等于后一个坐标值,这样就能实现画出连续的线
		painter = QPainter(self)
		painter.drawPixmap(0, 0, self.pix)	

	def mousePressEvent(self, event) :    # 重构mousePressEvent函数,使用两个点来绘制线条,点从鼠标事件中获取
		# 鼠标左键按下  
		if event.button() == Qt.LeftButton :
			self.lastPoint = event.pos()   
			self.endPoint = self.lastPoint
	
	def mouseMoveEvent(self, event):	  # 重构mouseMoveEvent函数
		# 鼠标左键按下的同时移动鼠标
		if event.buttons() and Qt.LeftButton :
			self.endPoint = event.pos()
			# 进行重新绘制
			self.update()

	def mouseReleaseEvent( self, event):
		# 鼠标左键释放   
		if event.button() == Qt.LeftButton :
			self.endPoint = event.pos()
			# 进行重新绘制
			self.update()
			
if __name__ == "__main__":  
		app = QApplication(sys.argv) 
		form = Winform()
		form.show()
		sys.exit(app.exec_())

程序运行结果:按住鼠标左键在画板上进行绘画,释放鼠标左键结束绘画。


QSSUI美化

QSSQt样式表,用来自定义控件外观的一种机制,QSS使界面美化跟代码层分开,利于维护。

QSS的语法规则

QSS样式由两部分组成,其中一部分是选择器,指定受影响的控件;另一部分是声明,指定哪些属性应该在控件上进行设置,声明部分是一系列的“属性:值”对,使用(;)分隔各个不同的属性值对,使用({})将所有的声明包括在内。

如设置QPushButton 类及其子类的所有实例的前景色是红色:QPushButton {color:red}

QPushButton 表示选择器,指定所有的QPushButton 类及其子类都会受到影响;{color:red}是规则的定义,表示前景色为红色。

python
# -*- coding: utf-8 -*-

from PyQt5.QtWidgets import *
import sys  
    
class WindowDemo(QWidget):  
	def __init__(self ):  
		super().__init__()
		# 创建相关控件
		btn1 = QPushButton(self )  
		btn1.setText('按钮1')
		btn2 = QPushButton(self )  
		btn2.setText('按钮2')	
		# 布局
		vbox=QVBoxLayout()
		vbox.addWidget(btn1)
		vbox.addWidget(btn2)	  
		self.setLayout(vbox)
		self.setWindowTitle("QSS样式")
  
if __name__ == "__main__":  
	app = QApplication(sys.argv)  
	win = WindowDemo()  
    # 加载了自定义的QSS样式,窗口按钮的背景颜色为红色,字体颜色为蓝色
	qssStyle = '''      			  			
			QPushButton {background-color: red}	
            QPushButton {color: blue}	
		'''
	win.setStyleSheet( qssStyle ) 				
	win.show()  
	sys.exit(app.exec_())

还可以使用多个选择器指定相关声明:QPushButton, QLineEdit, QComboBox {color:bule}

QSS选择器类型

  1. 通配选择器:* 匹配所以控件

  2. 类型选择器:QPushButton 匹配所有的QPushButton类及其子类的实例

  3. 属性选择器:QPushButton[name = "myBtn"] 匹配所有的name属性是myBtnQPushButton实例

python
# 给按钮btn2设置属性名
btn2 = QPushButton(self )  
btn2.setProperty( 'name' , 'myBtn' )
btn2.setText('按钮2')
# 修改属性名为myBtn的QPushButton,改变其背景颜色
qssStyle = '''      			  			
			QPushButton[name = "myBtn"] {background-color: red}		
		'''
  1. 类选择器: .QPushButton 匹配所有的QPushButton 实例,但是不匹配其子类。
  2. ID选择器:#myButton 匹配所有IDmyButton的控件,IDobjectName指定的值。
  3. 后代选择器:QDialog QPushButton 匹配所有的QDialog容器中包含的QPushButton,不管是直接还是间接的。
  4. 子选择器:QDialog > QPushButton 匹配所有的QDialog容器中包含的QPushButton,其中要求QPushButton的直接父容器是QDialog

QSS子控件

QSS子控件实际上也是一种选择器,应用在一些复合控件上,典型的如:QComboBox

子控件列表:

子控件描述
::add-line用于添加 QScrollBar 行的按钮
::add-page手柄(滑块)和 QScrollBar 的添加行之间的区域
::branchQTreeView 的分支指示器
::chunkQProgressBar 的进度块
::close-buttonQDockWidget 的关闭按钮或 QTabBar 的选项卡
::cornerQAbstractScrollArea 中两个滚动条之间的角
::down-arrowQComboBox,QHeaderView(排序指示器),QScrollBar或QSpinBox的向下箭头
::down-buttonQScrollBar 或 QSpinBox 的向下按钮
::drop-downQComboBox 的下拉按钮
::float-buttonQDockWidget 的浮点按钮
::grooveQSlider的凹槽
::indicatorQAbstractItemView、QCheckBox、QRadioButton、可检查QMenu项目或可检查QGroupBox的指标
::handleQScrollBar、QSplitter 或 QSlider 的手柄(滑块)
::iconQAbstractItemView 或 QMenu 的图标
::itemQAbstractItemView、QMenuBar、QMenu 或 QStatusBar 的项
::left-arrowQScrollBar 的左箭头
::left-cornerQTabWidget 的左上角。例如,此控件可用于控制 QTabWidget 中左上角小部件的位置
::menu-arrow带有菜单的 QToolButton 的箭头
::menu-buttonQToolButton 的菜单按钮
::menu-indicatorQPush按钮的菜单指示器
::right-arrowQMenu 或 QScrollBar 的右箭头
::paneQTabWidget 的窗格(框架)
::right-cornerQTabWidget 的右上角。例如,此控件可用于控制 QTabWidget 中右上角小部件的位置
::scrollerQMenu 或 QTabBar 的滚动条
::sectionQHeaderView 的部分
::separatorQMenu 或 QMainWindow 中的分隔符
::sub-line用于减去 QScrollBar 的一行的按钮
::sub-page控点(滑块)和 QScrollBar 的子行之间的区域
::tabQTabBar 或 QToolBox 的选项卡
::tab-barQTabWidget 的标签栏。此子控件仅用于控制 QTabWidget 中 QTabBar 的位置。使用 ::tab 子控件设置选项卡样式
::tearQTabBar 的撕裂指示器
::tearoffQMenu 的撕下指示器
::textQAbstractItemView 的文本
::titleQGroupBox 或 QDockWidget 的标题
::up-arrowQHeaderView(排序指示器)、QScrollBar 或 QSpinBox 的向上箭头
::up-buttonQSpinBox 的向上按钮

为其下来箭头自定义图片:

python
# 创建下拉文本框控件
combo = QComboBox(self)
combo.setObjectName('myQComboBox')
combo.addItem('Window')
combo.addItem('Ubuntu')
combo.addItem('Red Hat')

# 子控件选择器是选择复合控件的一部分,对复合控件的一部分应用样式,指定的是下拉箭头,而不是下拉框本身
qssStyle = '''      			  			
			QComboBox#myQComboBox::drop-down {image: url( ./images/dropdown.png)}		
		'''

QSS伪状态

QSS伪状态选择器是以冒号开头的一个表达式,如:hover,表示当鼠标指针经过时的状态,伪状态选择器限制了当控件处于某种状态时才可以使用QSS规则。

伪状态只能描述一个控件或者一个复合控件的子控件的状态,所以它只能放在选择器的最后面。

如:QPushButton:hover{background-color: red} 当鼠标经过时,该控件的背景颜色变成红色。

伪状态还可以描述子控件选择的复合控件的子控件的状态,但必须放在最后:

QComboBox::drop-down:hover{background-color: red} 当鼠标经过下拉箭头时,该下拉箭头背景变成红色。

QSS的伪状态:多种伪状态可以同时组合使用,如:hover:checked :!hover 表示其逆状态 一些特定的伪状态只能用在特定的控件上

伪状态描述
:active当小组件驻留在活动窗口中时,将设置此状态
:adjoins-item当 QTreeView 的 ::branch与一个item相邻时,将设置此状态。
:alternate当 QAbstractItemView::alternatingRowColors() 设置为 true 时,将为绘制 QAbstractItemView 行的每个交替行设置此状态
:bottom该项目位于底部。例如,其选项卡位于底部的 QTabBar
:checked该项目已选中。例如,QAbstractButton 的已检查状态
:closable可以关闭这些项目。例如,QDockWidget 打开了 QDockWidget:😄 ockWidgetClosable 功能
:closed项目处于关闭状态。例如,QTreeView 中的非展开项
:default该项为默认值。例如,默认 QPushButton 或 QMenu 中的默认操作
:disabled该项目已禁用
:editableQComboBox 是可编辑的
:edit-focus该项目具有编辑焦点(请参阅 QStyle::State_HasEditFocus)。此状态仅适用于 Qt 扩展应用程序
:enabled该项目已启用
:exclusive该物料是独占物料组的一部分。例如,独占 QActionGroup 中的菜单项
:first该项是第一个(在列表中)。例如,QTabBar 中的第一个选项卡
:flat该项目是平坦的。例如,一个平面的QPushButton
:floatable这些项目可以浮动。例如,QDockWidget 打开了 QDockWidget:😄 ockWidgetFloatable 功能
:focus该项目具有输入焦点
:has-children项目具有子项。例如,QTreeView 中具有子项的项
:has-siblings该项目有同级。例如,QTreeView 中的同级项目
:horizontal项目具有水平方向
:hover鼠标悬停在项目上
:indeterminate项目具有不确定状态。例如,QCheckBox 或 QRadioButton 被部分选中
:last该项是最后一个(在列表中)。例如,QTabBar 中的最后一个选项卡
:left该项目位于左侧。例如,选项卡位于左侧的 QTabBar
:maximized项目最大化。例如,最大化的 QMdiSubWindow
:middle该项位于中间(在列表中)。例如,不在 QTabBar 的开头或结尾的选项卡
:minimized该项目已最小化。例如,最小化的 QMdiSubWindow
:movable该项目可以四处移动。例如,QDockWidget 打开了 QDockWidget:😄 ockWidgetMovable 功能
:no-frame项目没有框架。例如,无框QSpinBox或QLineEdit
:non-exclusive物料是非独占物料组的一部分。例如,非独占 QActionGroup 中的菜单项
:off对于可以切换的项目,这适用于处于“关闭”状态的项目
:on对于可以切换的项目,这适用于处于“打开”状态的小部件
:only-one该项是唯一的项(在列表中)。例如,QTabBar 中的单个选项卡
:open项目处于打开状态。例如,QTreeView 中的展开项,或者带有打开菜单的 QComboBox 或 QPushButton
:next-selected下一个项目(在列表中)被选中。例如,QTabBar 的选定选项卡位于此项旁边
:pressed正在使用鼠标按下该项目
:previous-selected上一项(在列表中)处于选中状态。例如,QTabBar 中所选选项卡旁边的选项卡
:read-only项目标记为只读或不可编辑。例如,只读 QLineEdit 或不可编辑的 QComboBox
:right该项目位于右侧。例如,选项卡位于右侧的 QTabBar
:selected项目处于选中状态。例如,QTabBar 中的选定选项卡或 QMenu 中的选定项
:top该项目位于顶部。例如,其选项卡位于顶部的 QTabBar
:unchecked该项目处于未选中状态
:vertical该项目具有垂直方向
:window小部件是一个窗口(即顶级小部件)

设置窗口背景

窗口背景包括背景色和背景图片

使用QSS设置窗口背景

QSS中,可以通过background或者background-color的方式来设置背景色,设置窗口的背景色后,子控件会默认继承父窗口的背景颜色,要想为控件设置背景图片或图标,可以使用setPixmap或者setIcon来完成。

使用setStyleSheet()设置窗口背景图片

使用setStyleSheet()来添加背景图片

python
win = QMainWindow()
# 设置窗口名
win.setObjectName("MainWindow")
# 设置图片的相对路径添加,当前文件夹下有images文件夹,images文件夹中有python.jpg图片
win.setStyleSheet("#MainWindow{border-image:url(images/python.jpg);}")
# 设置图片的绝对路径,从c盘/d盘或其他盘开始,可以点击图片的详情查看
win.setStyleSheet("#MainWindow{border-image:url(c:/images/python.jpg);}")
使用setStyleSheet()设置窗口背景颜色
python
win = QMainWindow()
# 设置窗口名
win.setObjectName("MainWindow")
# 设置窗口的背景颜色
win.setStyleSheet("#MainWindow{background-color: yellow}")

使用QPalette设置窗口背景

使用QPalette(调色板)设置窗口背景图片

使用QPalette设置背景图片时,要考虑背景图片的尺寸,当背景图片的宽度和高度大于窗口的宽度和高度时,背景图片会平铺整个背景;当背景图片的宽度和高度小于窗口的宽度和高度时,会加载多个背景图片。给窗口设置背景时,要看图片的分辨率和设置的窗口大小win.resize(460,255)进行比较。

python
win = QMainWindow()
palette	= QPalette()
palette.setBrush(QPalette.Background,QBrush(QPixmap("./images/python.jpg")))
win.setPalette(palette)  
win.resize(460,  255 )
使用QPalette(调色板)设置窗口背景颜色
python
win = QMainWindow() 
palette	= QPalette()
palette.setColor(QPalette.Background , Qt.red)
win.setPalette(palette)

使用paintEvent设置窗口背景

使用paintEvent设置窗口背景
python
def paintEvent(self,event):    # 重载paintEvent,不需要调用,作为方法直接放入
    painter = QPainter(self)
    pixmap = QPixmap("./images/screen1.jpg")
    # 绘制窗口背景,平铺到整个窗口,随着窗口改变而改变
    painter.drawPixmap(self.rect(),pixmap)
使用paintEvent设置窗口背景颜色
python
def paintEvent(self,event):   # 重载paintEvent,不需要调用,作为方法直接放入
    painter = QPainter(self)
    painter.setBrush(Qt.yellow );
    painter.drawRect(self.rect());

不规则窗口的显示

QWidget类中重要的绘图函数:

函数描述
setMask(self, QBitmap) setMask(self, QRegion)setMask()的作用是为调用它的控件增加一个遮罩,遮住所选区域外的部分,使看起来透明,它的参数可以为QBitmap或QRegion对象,调用QPixmap的mask()函数获得图片自身的遮罩,是一个QBitmap对象
paintEvent(self, QPaintEvent)通过重载paintEvent()函数绘制窗口背景
案例--实现不规则窗口的最简单方式是图片素材既当遮罩层又当背景图片

通过重载paintEvent()函数绘制窗口背景

python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication  ,QWidget 
from PyQt5.QtGui import  QPixmap,   QPainter , QBitmap

class Winform(QWidget):
	def __init__(self,parent=None):
		super(Winform,self).__init__(parent)
		self.setWindowTitle("不规则窗体的实现例子") 
		self.resize(600, 400)
        
	def paintEvent(self,event):
		painter = QPainter(self)
		painter.drawPixmap(0,0,280,390,QPixmap(r"./images/dog.jpg"))  # 获取图片
		painter.drawPixmap(300,0,280,390,QBitmap(r"./images/dog.jpg")) # 获取图片遮罩
         
if __name__ == "__main__":  
	app = QApplication(sys.argv)  
	form = Winform()
	form.show()
	sys.exit(app.exec_())
案例--使用一张遮罩图片来控制窗口大小,再用paintEvent()函数重绘窗口背景图
python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication  ,QWidget 
from PyQt5.QtGui import  QPixmap,   QPainter , QBitmap

class Winform(QWidget):
	def __init__(self,parent=None):
		super(Winform,self).__init__(parent)
		self.setWindowTitle("不规则窗体的实现例子") 

		self.pix = QBitmap("./images/mask.png")
		self.resize(self.pix.size())
		self.setMask(self.pix)
         
	def paintEvent(self,event):
		painter = QPainter(self)
        # 在指定区域直接绘制窗口背景
		painter.drawPixmap(0,0,self.pix.width(),self.pix.height(),QPixmap("./images/screen1.jpg"))
        
if __name__ == "__main__":  
		app = QApplication(sys.argv) 
		form = Winform()
		form.show()
		sys.exit(app.exec_())

程序运行结果:窗口背景图片是不能移动的

案例--实现可以拖动的不规则窗口
python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication  ,QWidget 
from PyQt5.QtGui import  QPixmap,   QPainter  ,  QCursor , QBitmap
from PyQt5.QtCore import Qt 

class ShapeWidget(QWidget):  
	def __init__(self,parent=None):  
		super(ShapeWidget,self).__init__(parent)
		self.setWindowTitle("不规则的,可以拖动的窗体实现例子") 
		self.mypix()	

    # 显示不规则 pic
	def mypix(self):
		self.pix = QBitmap( "./images/mask.png" )
		self.resize(self.pix.size())       
		self.setMask(self.pix)
		print( self.pix.size())
		self.dragPosition = None

	# 重定义鼠标按下响应函数mousePressEvent(QMouseEvent)和鼠标移动响应函数mouseMoveEvent(QMouseEvent),使不规则窗体能响应鼠标事件,随意拖动。
	def mousePressEvent(self, event):
		if event.button() == Qt.LeftButton:
			self.m_drag=True
			self.m_DragPosition=event.globalPos()-self.pos()
			event.accept()
			self.setCursor(QCursor(Qt.OpenHandCursor))
		if event.button()==Qt.RightButton:  
			self.close()  
			
	def mouseMoveEvent(self, QMouseEvent):
		if Qt.LeftButton and self.m_drag:
		    # 当左键移动窗体修改偏移值
			self.move(QMouseEvent.globalPos()- self.m_DragPosition )
			QMouseEvent.accept()
	
	def mouseReleaseEvent(self, QMouseEvent):
		self.m_drag=False
		self.setCursor(QCursor(Qt.ArrowCursor))
    
    # 一般 paintEvent 在窗体首次绘制加载, 要重新加载paintEvent 需要重新加载窗口使用 self.update() or  self.repaint() 
	def paintEvent(self, event):
		painter = QPainter(self)
		painter.drawPixmap(0,0,self.width(),self.height(),QPixmap("./images/boy.png"))
			
if __name__ == '__main__':
    app=QApplication(sys.argv)
    form=ShapeWidget()
    form.show()
    app.exec_()

程序运行结果:生成背景图片,图片可以鼠标左键按住移动位置。


不规则窗口实现动画效果

通过PyQt设计不规则窗口的动画效果,显示不规则图片需注意:

  1. pixmap.setMask()函数的作用是为调用它的控件添加一个遮罩,遮住所选的区域外的部分,使控件看起来是透明的。
  2. paintEvent()函数每次初始化窗口只调用一次,所以每加载一次图片就要重新调用一次paintEvent()函数
python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication  ,QWidget 
from PyQt5.QtGui import  QPixmap,   QPainter ,  QCursor 
from PyQt5.QtCore import Qt, QTimer

class ShapeWidget(QWidget):  
	def __init__(self,parent=None):  
		super(ShapeWidget,self).__init__(parent)
		self.i = 1
		self.mypix()
		self.timer = QTimer()
		self.timer.setInterval(500)  # 500毫秒
		self.timer.timeout.connect(self.timeChange)   
		self.timer.start()

    # 显示不规则 pic
	def mypix(self):
		self.update()
		if self.i == 5:
			self.i = 1
		self.mypic = {1: './images/left.png', 2: "./images/up.png", 3: './images/right.png', 4: './images/down.png'}
		self.pix = QPixmap(self.mypic[self.i], "0", Qt.AvoidDither | Qt.ThresholdDither | Qt.ThresholdAlphaDither)   
		self.resize(self.pix.size())
		self.setMask(self.pix.mask())  
		self.dragPosition = None

	def mousePressEvent(self, event):
		if event.button() == Qt.LeftButton:
			self.m_drag=True
			self.m_DragPosition=event.globalPos()-self.pos()
			event.accept()
			self.setCursor(QCursor(Qt.OpenHandCursor))

	def mouseMoveEvent(self, QMouseEvent):
		if Qt.LeftButton and self.m_drag:
			self.move(QMouseEvent.globalPos()- self.m_DragPosition )
			QMouseEvent.accept()
	
	def mouseReleaseEvent(self, QMouseEvent):
		self.m_drag=False
		self.setCursor(QCursor(Qt.ArrowCursor))
        
	def paintEvent(self, event):
		painter = QPainter(self)
		painter.drawPixmap(0, 0, self.pix.width(),self.pix.height(),self.pix)
    
	# 鼠标双击事件
	def mouseDoubleClickEvent(self, event):
		if event.button() == 1:
			self.i += 1
			self.mypix()

    # 每500毫秒修改paint
	def timeChange(self):
		self.i += 1
		self.mypix()

if __name__ == '__main__':
	app = QApplication(sys.argv)
	form = ShapeWidget()
	form.show()
	sys.exit(app.exec_())

运行上述例子,会弹出一个窗口,显示不同方向的箭头,每500毫秒改变一次,按照上,右,下,左方向转动

案例--加载GIF动画效果
python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication,  QLabel  ,QWidget 
from PyQt5.QtCore import Qt 
from PyQt5.QtGui import QMovie

class LoadingGifWin( QWidget):
    def __init__(self,parent=None):
        super(LoadingGifWin, self).__init__(parent)
        self.label =  QLabel('', self)
        self.setFixedSize(128,128)
        self.setWindowFlags( Qt.Dialog| Qt.CustomizeWindowHint)
        self.movie =  QMovie("./images/loading.gif")
        self.label.setMovie(self.movie)
        self.movie.start()

if __name__ == '__main__':
    app =  QApplication(sys.argv)
    loadingGitWin = LoadingGifWin()
    loadingGitWin.show()
    sys.exit(app.exec_())

设置样式

为标签添加背景图片

python
label1 = QLabel(self)
label1.setToolTip('这是一个文本标签')
label1.setStyleSheet("QLabel{border-image: url(./images/python.jpg);}")
# 设置标签的宽度和高度,设置的大小会影响窗口的大小,但是图片是全部填充的
label1.setFixedWidth(476)
label1.setFixedHeight(259)

为按钮添加背景图片

对所有的按钮同时生效:

python
btn1 = QPushButton(self )  
btn1.setMaximumSize(64, 64)
btn1.setMinimumSize(64, 64)
style = '''
            QPushButton{
                border-radius: 30px;
                background-image: url('./images/left.png');
                }
        '''
btn1.setStyleSheet(style)

对指定的按钮设置背景图片,要调用QPushButton对象的setObjectName()函数,为对象设置名字,指定的按钮时btn1

python
btn1 = QPushButton(self )  
btn1.setObjectName('btn1')
btn1.setMaximumSize(64, 64)
btn1.setMinimumSize(64, 64)
style = '''
            #btn1{
                border-radius: 30px;
                background-image: url('./images/left.png');
                }
            #btn1:hover{
                border-radius: 30px;
                background-image: url('./images/leftHover.png');
                }
            #btn1:Pressed{
                border-radius: 30px;
                background-image: url('./images/leftPressed.png');
                }
        '''
btn1.setStyleSheet(style)

缩放图片

使图片大小固定,不管窗口大小怎么变化,图片都保持原始大小:

python
# -*- coding: utf-8 -*-

from PyQt5.QtWidgets import QApplication,  QLabel  ,QWidget, QVBoxLayout 
from PyQt5.QtGui   import QImage , QPixmap 
from PyQt5.QtCore import Qt 
import sys

class WindowDemo(QWidget):  
    def __init__(self ):  
        super().__init__()
        filename = r".\images\Cloudy_72px.png"
        img = QImage( filename )   # 原始图片的大小高度,宽度是72像素,需要进行缩放处理
               
        label1 = QLabel(self)
        # 设置宽度,高度为120像素,所加载的图片按照标签的高度和宽度等比例缩放
        label1.setFixedWidth(120)
        label1.setFixedHeight(120)
        # 缩放图片,以固定大小显示
        result = img.scaled(label1.width(), label1.height(),Qt.IgnoreAspectRatio, Qt.SmoothTransformation);
        # 在标签控件上显示图片
        label1.setPixmap(QPixmap.fromImage(result))
        
        vbox=QVBoxLayout()
        vbox.addWidget(label1)
      
        self.setLayout(vbox)
        self.setWindowTitle("图片大小缩放例子")
  
if __name__ == "__main__":  
    app = QApplication(sys.argv)  
    win = WindowDemo()  
    win.show()  
    sys.exit(app.exec_())

设置窗口透明

窗口透明,就可以通过窗口看到桌面的背景,要想窗口透明,需要修改窗口的透明度:

python
win.QMainWindow()
win.setWindowOpacity(0.5);  #透明度:0.0(全透明)-1.0(不透明),默认为1.0

加载QSS

Qt中使用样式,为了降低耦合性(与逻辑代码分离),我们需要定义一个QSS文件,用来编写各种控件(如QLableQLineEditQPushButton)的样式,最后使用QApplicationQMainWindow来加载样式,从而使整个应用程序共享一种样式。

在编写QSS时要新建一个拓展名为.qss的文件,如style.qss,再将其加入资源文件(.qrc)中,

编写QSS

style.qss的样式代码如下:

python
QMainWindow{
		border-image:url(./images/python.jpg);
}

QToolTip{
		border: 1px solid rgb(45, 45, 45);
		background: white;
		color: red;
}
加载QSS

为了方便调用,编写一个加载样式的公共类CommonHelper放在CommonHelper.py文件中。

python
# -*- coding: utf-8 -*-

class CommonHelper :
    def __init__(self ) :
        pass
         
    @staticmethod    
    def readQss( style):
        with open( style , 'r') as f:
           return f.read()

最后在主函数中进行加载:

python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QMainWindow , QApplication,  QVBoxLayout , QPushButton
from CommonHelper import CommonHelper

class MainWindow(QMainWindow):
	def __init__(self,parent=None):
		super(MainWindow,self).__init__(parent)
		self.resize(477, 258) 
		self.setWindowTitle("加载QSS文件") 
		btn1 = QPushButton( self)  
		btn1.setText('添加')
		btn1.setToolTip('测试提示')
		vbox = QVBoxLayout()
		vbox.addWidget( btn1 )
      
		self.setLayout(vbox) 
       
if __name__ == "__main__": 
	app = QApplication(sys.argv)
	win = MainWindow() 
	styleFile = './style.qss'   # 换肤时进行全局修改,只需修改不同的QSS文件
	qssStyle = CommonHelper.readQss( styleFile )	
	win.setStyleSheet( qssStyle ) 
	win.show()     
	sys.exit(app.exec_())

程序运行结果:背景图片和测试提示界面都是通过QSS加载得到的

Released under the MIT License.