第15.40节、PyQt(Python+Qt)实战:moviepy实现MP4视频转gif动图的工具

一、引言

在写《第15.39节、splitDockWidget和tabifyDockWidget嵌套布局QDockWidget的PyQt人机对话案例:笨笨机器人》的,为了说明setDockNestingEnabled的作用,使用了2个动画,当时是使用录屏软件录屏录的MP4文件,但将其转gif时遇到了困难,网上各种下载的工具都是在gif文件中加了各种LOGO图形,在线的转码操作很困难,转得慢,好不容易转完之后发现下载不下来,实在不想用了。作为一个Pythonic的人,马上想到的是“人生苦短,我用Python”,网上一查,结果发现好多大神有跟老猿一样的情况,并且还真有工具,一个是基于MoviePy 的,一个是基于OpenCV的,都还比较好使用,但MoviePy更好用,于是马上动手安装了一个。

二、MoviePy简介

MoviePy能处理的视频是ffmpeg格式的,老猿理解支持的文件类型至少包括:*.mp4 *.wmv *.rm *.avi *.flv *.webm *.wav *rmvb 。

MoviePy有很多与视频相关的功能,包括剪辑、合成、分离音视频等,在此老猿只用了其中的视频转gif的功能,老猿暂时没准备去深入研究,在此也不多介绍,大家可以参考《MoviePy - 中文文档(一个专业的python视频编辑库)教程》的介绍以及英文版官方文档https://zulko.github.io/moviepy/中文版文档:http://moviepy.cn/。在此就说明如下几点:

  1. 安装:pip安装时,请将站点指向国内的镜像站点,否则下载很慢或者下载不下来,老猿使用清华的镜像,指令是:
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple moviepy
    注意moviepy全小写,安装时会自动安装相关依赖包,这点与上面文章介绍的有出入。

  2. 模块导入:moviepy是一个包,由于只使用视频转gif,相关功能在editor模块内,因此导入使用指令:
    import moviepy.editor as mpe

  3. 视频文件装载方法:
    VideoFileClip(videoFile)
    这个方法就是构造一个VideoFileClip对象,这个对象就是视频的内容,可以通过该对象对视频进行剪辑等操作

  4. 截取视频方法:
    subclip(start,end)
    参数start和end是视频起止位置,如果是整数单位是秒,也可以是其他时间设置方法,如:2分12.5秒,表示方法可以是(2,12.5)、(0,2,12.5)或者 (00:02:12.5)。
    返回值还是一个VideoFileClip对象。

  5. 输出视频到gif文件的方法:
    write_gif(gifFileName,fps=fps)
    write_gif有很多参数,除了第一个参数是文件名外,其他参数都是关键字参数(不明白关键字参数的请参考《第5章函数进阶 第5.1节 Python函数的位置参数、关键字参数精讲》),在此老猿仅使用了fps参数,其他参数就不展开说了。fps参数是指生成GIF是每秒抽取的帧数,这个数字越大,同样视频生成的gif文件就越大,所以需要有所取舍。
    6、关闭视频缓存方法:close方法用于关闭视频缓存。

示例代码:
		import moviepy.editor as mpe
		cache = mpe.VideoFileClip(r"c:\temp\操作录屏.mp4").subclip(0,15)
        cache.write_gif(r"c:\temp\操作录屏.gif",fps=2)

三、构建MP4视频转gif工具

3.1、设计操作界面

工具的操作界面提供了选择视频文件、输出gif文件、设置输出视频段的起止时间以及转换GIF的fps,另外老猿发现moviepy的输出都是打印输出,因此将所有相关输出信息(包括自编代码输出和moviepy模块的输出)重定向到了信息输出历史窗proccessInf中,同时将最近输出的信息显示在“最近输出信息”后面的名为currentInf的label上。输入信息设置完成后,点击转换按钮即将对应视频输出到gif文件中。整体ui设计界面如下:
在这里插入图片描述

3.2、实现转换按钮点击的槽方法

为了确保转换不被异常操作干扰,开始转换后整个主窗口设置为disable,转换完成后恢复enable。

    def convert(self):
        self.setEnabled(False)
        self.proccessInf.clear()
        self.convertByMoviepy(self.videoFile.text(),self.gifFile.text())
        self.setEnabled(True)	

3.3、实现方法convertByMoviepy

方法convertByMoviepy就是取界面相关设置调用moviepy对应方法完成文件转换,为了确保转换顺利,对相关参数进行了校验,如起止位置和fps必须是整数,如果终止位置不为0则必须大于起始位置。最后就是执行视频文件加载和转换,代码可以参考上面moviepy简介部分。

3.4、重定向输出信息到信息输出历史窗proccessInf

信息输出历史窗proccessInf为一个QTextBrowser对象,要将所有print输出信息到该历史窗,需要完成如下工作:

  1. 在构造方法中备份标准输出sys.stdout
  2. 构建承接输出信息的对象赋值给sys.stdout
    承接输出对象必须是一个类似文件io的对象,Python判断对象是否支持文件IO,是个典型的鸭子类型处理方式,就是看对象是否实现了读写方法,由于标准输出无需读只需写,因此只要实现了write方法即可。
    在本工具的实现方法内,老猿将标准输出指向了主窗口,因此在主窗口中实现了write方法,在write方法中将输出信息追加显示到proccessInf中、将最新信息显示到“最近输出信息”后的currentInf标签上。
    但在此需要注意,输出到proccessInf中的信息在程序输出过程中不会即时显示,导致给人的感觉是没有输出一样,为了确保输出信息即时显示在proccessInf窗口中,需要主动调用应用的processEvents方法。
    同时为了确保信息可对比跟踪,将重定向的信息使用备份的标准输出进行了输出
重定向参考代码如下:
class mainWin(QtWidgets.QWidget,ui_mainWin.Ui_mainWin):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.stdoutbak = sys.stdout
        self.stderrbak = sys.stderr
        sys.stdout = self

	def write(self,info):
        self.proccessInf.insertPlainText(info)
        if len(str):self.currentInf.setText(str)
        QtWidgets.qApp.processEvents(QtCore.QEventLoop.ExcludeUserInputEvents|QtCore.QEventLoop.ExcludeSocketNotifiers)
        self.stdoutbak.write(info)

3.5、运行界面截图及动图

在这里插入图片描述
在这里插入图片描述

广告

老猿关于PyQt的付费专栏《使用PyQt开发图形界面Python应用》只需要9.9元,该部分与第十五章的内容基本对应,但同样内容在付费专栏上总体来说更详细、案例更多。本节内容对应付费专栏的《第三十三章、PyQt+moviepy实现的MP4视频转gif工具》。如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。

老猿Python,跟老猿学Python!

发布了760 篇原创文章 · 获赞 3457 · 访问量 41万+

猜你喜欢

转载自blog.csdn.net/LaoYuanPython/article/details/105316314