实战PyQt5: 122-使用QMovie实现GIF动画播放

QMovie简介

QMovie类用于显示没有声音的简单动画。可以通过文件名来创建一个QMovie对象,在播放前,可以调用函数isValid()来检测图像文件是否有效。QMovie提供了一些枚举和函数来控制播放。

枚举量QMovie.CacheMode控制缓冲模式:

  • QMovie.CacheNone (0): 不缓存任何帧(默认)。
  • QMovie.CacheAll (1): 缓存所有帧。

枚举量QMovie.MovieState 控制播放状态:

  • QMovie.NotRuning (0): 未运行状态。这是QMovie的初始状态,在调用stop()或播放结束后也进入该状态。
  • QMovie.Paused (1): 暂停状态,QMovie暂停在当前的播放帧上。
  • QMovie.Running(2): 正在播放状态。

QMovie的常用函数有:

  • setFilename(self, filename: str): 设置要播放的文件名称。
  • filename(self): 返回播放文件名称。
  • setScaledSize(self, size: QSize): 设置帧的缩放大小。
  • scaledSize(self): 返回帧的缩放大小。
  • setSpeed(self, percentSpeed: int):按正常速度的百分比控制播出。
  • speed(self): 返回当前的播放速度。
  • setCacheMode(self, mode: 'QMovie.CacheMode'): 设置缓存模式。
  • cacheMode(self): 返回缓存模式。
  • start(self): 开始播放。
  • stop(self): 停止播放。
  • setPaused(self, paused: bool): 设置暂停。
  • jumpToNextFrame(self): 跳到下一帧,成功返回True,否则,返回False。
  • jumpToFrame(self, frameNumber: int):跳转到指定帧frameNumber, 成功返回True,否则,返回False。
  • nextFrameDelay(self): 返回更新动画中的下一帧之前将要等待的毫秒数。
  • isValid(self): 如果动画有效(例如,图像数据可读并且支持图像格式)则返回True,否则返回False。
  • currentPixmap(self): 以QPixmap形式返回当前帧。
  • curentImage(self): 以QImage形式返回当前帧。
  • state(self): 返回QMovie当前的状态。
  • setBackgroundColor(self, color): 设置背景色。
  • backgroundColor(self): 返回设置的背景色。
  • frameCount(self): 返回动画中的帧数。
  • loopCount(self): 返回完成播放之前将循环播放的次数。如果只播放一次,返回0, 如果一直循环播放,则返回-1。

QMovie 常用信号:

  • error(self, error): 当播放过程中发生错误时发射此信号。
  • finished(self): 播放结束后发射此信号。
  • frameChanged(self, frameNumber: int):当帧号发生改变为新的frameNumber时,发射此信号。
  • resized(self, size: QSize):当当前帧大小发生调整时,将发射此信号。
  • started(self): 调用start()函数,并进入播放状态时,将发出此信号。
  • stateChanged(self, state: 'QMovie.MovieState'): 每当动画状态更改时都会发出此信号。新状态由state指定。
  • updated(self, rect: QRect): 当前帧的rect更新后,将发出此信号。

测试

测试代码演示加载一个动画,控制动画的播出,并可以将动画帧导出图像保存。可以在此基础上,做一个功能更完善的动画浏览器。完整代码如下:

import sys
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QDir
from PyQt5.QtGui import (QIcon, QMovie, QPalette, QImage)
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel, QMenuBar, 
                             QToolButton, QStyle, QMenu, QAction, QScrollArea,
                             QHBoxLayout, QVBoxLayout, QSizePolicy, QGroupBox,
                             QFileDialog, QMessageBox)
 
class GifPlayer(QMainWindow):
    def __init__(self, parent=None):
        super(GifPlayer, self).__init__(parent)
        
        # 设置窗口标题
        self.setWindowTitle("实战 Qt for Python: QMovie实现一个GIF动画播放")      
        # 设置窗口大小
        self.resize(500, 400)
        
        # 播放状态记录变量
        self.is_playing = False
        self.is_pause = False
        self.total_frame = 0
        self.cur_frame = 0
 
        self.initUi()
    
    def initUi(self):
        
        #创建显示图片的窗口 
        self.imgLabel = QLabel()
        self.imgLabel.setBackgroundRole(QPalette.Base)
        self.imgLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imgLabel.setScaledContents(True)
        
        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imgLabel)
        
        self.createPlayCtlr()
                        
        mainLayout = QVBoxLayout()
        mainLayout.setContentsMargins(0,0,0,0)
        mainLayout.addWidget(self.scrollArea)
        mainLayout.addWidget(self.grpButtons)
        
        mainWidget = QWidget()
        mainWidget.setLayout(mainLayout)
        self.setCentralWidget(mainWidget)
        
        self.initMenuBar()
        
        #创建动画播放器
        self.movie = QMovie()
        self.movie.setCacheMode(QMovie.CacheAll)   #支持回卷
        
        '''       
        self.movie = QMovie(os.path.dirname(__file__) + "/use-python.gif")
        self.movie.setScaledSize(QtCore.QSize(img_width, img_height))
        
        
        label = QLabel(self)
        label.move(20, 10)
        label.setFixedSize(img_width, img_height)
        label.setMovie(self.movie)
        self.movie.jumpToFrame(0)      
        '''
 
    def initMenuBar(self):
        menuBar = self.menuBar() 
        menuFile = menuBar.addMenu('文件(&F)')
        menuView = menuBar.addMenu('视图(&V)')
        
        actionOpen = QAction('打开(&O)...', self, shortcut='Ctrl+O', triggered=self.onFileOpen)
        self.actionExport = QAction('导出(&E)...', self, shortcut='Ctrl+E', enabled=False, triggered=self.onFileExport)  
        actionExit = QAction('退出(&X)', self, shortcut='Ctrl+Q', triggered=QApplication.instance().quit)
 
        self.actionFitToWindow = QAction('适应窗口(&F)', self, shortcut='Ctrl+F', enabled=False, checkable=True, triggered=self.onViewFitToWindow)
 
        menuFile.addAction(actionOpen)
        menuFile.addAction(self.actionExport)
        menuFile.addSeparator()
        menuFile.addAction(actionExit)  
        
        menuView.addAction(self.actionFitToWindow)
    
    def createPlayCtlr(self):      
        #播放按钮控制部分
        self.btn_play = QToolButton()
        self.btn_play.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.btn_play.clicked.connect(lambda: self.play(100))
        # 创建三种不同速率播放的Action
        action_slow = QAction('慢速播放', self)
        action_slow.triggered.connect(lambda: self.play(25))
        action_normal = QAction('常速播放', self)
        action_normal.triggered.connect(lambda: self.play(100))
        action_fast = QAction('快速播放', self)
        action_fast.triggered.connect(lambda: self.play(400))        
        #弹出菜单
        self.popup_menu = QMenu(self)
        self.popup_menu.addAction(action_slow)
        self.popup_menu.addAction(action_normal)
        self.popup_menu.addAction(action_fast)
        self.btn_play.setPopupMode(QToolButton.MenuButtonPopup)
        self.btn_play.setAutoRaise(True)
        self.btn_play.setMenu(self.popup_menu)
        
        self.setPlayButtonState()
        
        self.btn_first = QToolButton()
        self.btn_first.setIcon(QApplication.style().standardIcon(QStyle.SP_MediaSkipBackward))
        self.btn_first.setText("到头")
        self.btn_first.setToolTip("到第一帧")
        self.btn_first.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.btn_first.setStyleSheet('border: 0px')
        self.btn_first.clicked.connect(self.firstFrame)
        
        self.btn_last = QToolButton()
        self.btn_last.setIcon(QApplication.style().standardIcon(QStyle.SP_MediaSkipForward))
        self.btn_last.setText("到尾")
        self.btn_last.setToolTip("到最后一帧")
        self.btn_last.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.btn_last.setStyleSheet('border: 0px')
        self.btn_last.clicked.connect(self.lastFrame)
        
        self.btn_prev = QToolButton()
        self.btn_prev.setIcon(QApplication.style().standardIcon(QStyle.SP_MediaSeekBackward))
        self.btn_prev.setText("前帧")
        self.btn_prev.setToolTip("到前一帧")
        self.btn_prev.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.btn_prev.setStyleSheet('border: 0px')
        self.btn_prev.clicked.connect(self.prevFrame)
        
        self.btn_next = QToolButton()
        self.btn_next.setIcon(QApplication.style().standardIcon(QStyle.SP_MediaSeekForward))
        self.btn_next.setText("后帧")
        self.btn_next.setToolTip("到后一帧")
        self.btn_next.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.btn_next.setStyleSheet('border: 0px')
        self.btn_next.clicked.connect(self.nextFrame)
 
        btnLayout = QHBoxLayout()
        btnLayout.setContentsMargins(0, 0, 0, 0)
        btnLayout.addWidget(self.btn_play)
        btnLayout.addWidget(self.btn_first)
        btnLayout.addWidget(self.btn_last)
        btnLayout.addWidget(self.btn_prev)
        btnLayout.addWidget(self.btn_next)
        
        self.grpButtons = QGroupBox()
        self.grpButtons.setStyleSheet('border: 0px')
        self.grpButtons.setLayout(btnLayout)
        
        self.grpButtons.setEnabled(False)
        
    #打开文件
    def onFileOpen(self):
        filename,_ = QFileDialog.getOpenFileName(self, '打开动画', QDir.currentPath(),
                                                 'GIF Files(*.gif);;All Files(*)') 
        
        if filename:
            self.movie.setFileName(filename)
            if not self.movie.isValid():
                QMessageBox.information(self, '动画播放', '不能加载文件%s.' % filename)
                return
            
            self.total_frame = self.movie.frameCount() #保存总帧数
            self.movie.stop()
            self.is_playing = False
            
            self.imgLabel.setMovie(self.movie)
            self.movie.jumpToFrame(0)
            
            self.actionExport.setEnabled(True)
            self.actionFitToWindow.setEnabled(True)
            self.grpButtons.setEnabled(True)
            
            if not self.actionFitToWindow.isChecked():
                self.imgLabel.adjustSize()
                
            self.play(100)
            self.setPlayButtonState()
                         
    def onFileExport(self):
        path,_ = QFileDialog.getSaveFileName(self, '导出图像文件', '', '图像文件 (*.png)')
        if not path:
            return
        
        isRunning = self.movie.state()
        if isRunning:
            self.movie.setPaused(True)
        img = self.movie.currentImage()
        img.save(path, 'PNG', 100)
        if isRunning:
            self.movie.setPaused(False)
    
    def onViewFitToWindow(self):
        fitToWindow = self.actionFitToWindow.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.imgLabel.adjustSize()
 
            
    #修改播放按钮的状态
    def setPlayButtonState(self):
        if self.is_playing :
            self.btn_play.setIcon(QApplication.style().standardIcon(QStyle.SP_MediaStop))
            self.btn_play.setText("停止") 
            self.btn_play.setToolTip("点击按钮停止播放")  
            self.popup_menu.setEnabled(False)
        else:
            self.btn_play.setIcon(QApplication.style().standardIcon(QStyle.SP_MediaPlay))
            self.btn_play.setText("播放")
            self.btn_play.setToolTip("点击按钮开始播放")
            self.popup_menu.setEnabled(True)
                       
    #播放按钮的槽函数
    def play(self, speed):
        if self.is_playing:            
            self.movie.stop()
            self.movie.jumpToFrame(0) #回到第一帧
            self.is_playing = False         
        else:          
            self.movie.start()
            self.movie.setSpeed(speed)
            self.is_playing = True
        self.setPlayButtonState()
    
    #到设定的当前帧    
    def toFrame(self):
        if self.is_playing:
            self.movie.stop()
            self.is_playing = False
            self.setPlayButtonState()                 
        self.movie.jumpToFrame(self.cur_frame)
       
    #到第一帧
    def firstFrame(self):
        self.cur_frame = 0
        self.toFrame()
            
    #到最后一帧
    def lastFrame(self):
        self.cur_frame = self.total_frame - 1
        self.toFrame()
    
    #到前一帧
    def prevFrame(self):
        if self.cur_frame <= 0:
            self.cur_frame = self.total_frame - 1
        else:
            self.cur_frame = self.cur_frame - 1
        self.toFrame()
        
    #到后一帧
    def nextFrame(self):
        if self.cur_frame >= self.total_frame - 1:
            self.cur_frame = 0
        else:
            self.cur_frame = self.cur_frame + 1
        self.toFrame()
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = GifPlayer()
    window.show()
    sys.exit(app.exec())        
 

运行结果如下图:

实战PyQt5: 122-使用QMovie实现GIF动画播放_第1张图片

使用QMovie生成一个动画播放器

本文知识点

  • QMovie只适合播放无声音的简单动画,不适合处理视频和多媒体。
  • QMovie 加载和播放动画。
  • QMovie 导出动画帧。

前一篇: 实战PyQt5: 121-使用QImage实现一个看图应用

你可能感兴趣的:(编码,python,pyqt5,pyside2,qt)