Python应用03 使用PyQT制作视频播放器实例


Posted in Python onDecember 07, 2016

最近研究了Python的两个GUI包,Tkinter和PyQT。这两个GUI包的底层分别是Tcl/Tk和QT。相比之下,我觉得PyQT使用起来更加方便,功能也相对丰富。这一篇用PyQT实现一个视频播放器,并借此来说明PyQT的基本用法。

 视频播放器

先把已经完成的代码放出来。代码基于Python 3.5:

import time
import sys

from PyQt4 import QtGui, QtCore
from PyQt4.phonon import Phonon


class PollTimeThread(QtCore.QThread):
  """
  This thread works as a timer.
  """
  update = QtCore.pyqtSignal()

  def __init__(self, parent):
    super(PollTimeThread, self).__init__(parent)

  def run(self):
    while True:
      time.sleep(1)
      if self.isRunning():
        # emit signal
        self.update.emit()
      else:
        return

class Window(QtGui.QWidget):
  def __init__(self):
    QtGui.QWidget.__init__(self)

    # media
    self.media = Phonon.MediaObject(self)
    self.media.stateChanged.connect(self.handleStateChanged)
    self.video = Phonon.VideoWidget(self)
    self.video.setMinimumSize(200, 200)
    self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self)
    Phonon.createPath(self.media, self.audio)
    Phonon.createPath(self.media, self.video)

    # control button
    self.button = QtGui.QPushButton('选择文件', self)
    self.button.clicked.connect(self.handleButton)

    # for display of time lapse
    self.info = QtGui.QLabel(self)

    # layout
    layout = QtGui.QGridLayout(self)
    layout.addWidget(self.video, 1, 1, 3, 3)
    layout.addWidget(self.info, 4, 1, 1, 3)
    layout.addWidget(self.button, 5, 1, 1, 3)

    # signal-slot, for time lapse
    self.thread = PollTimeThread(self)
    self.thread.update.connect(self.update)

  def update(self):
    # slot
    lapse = self.media.currentTime()/1000.0
    self.info.setText("%4.2f 秒" % lapse)

  def startPlay(self):
    if self.path:
      self.media.setCurrentSource(Phonon.MediaSource(self.path))

      # use a thread as a timer
      self.thread = PollTimeThread(self)
      self.thread.update.connect(self.update)
      self.thread.start()
      self.media.play()

  def handleButton(self):
    if self.media.state() == Phonon.PlayingState:
      self.media.stop()
      self.thread.terminate()
    else:
      self.path = QtGui.QFileDialog.getOpenFileName(self, self.button.text())
      self.startPlay()

  def handleStateChanged(self, newstate, oldstate):
    if newstate == Phonon.PlayingState:
      self.button.setText('停止')
    elif (newstate != Phonon.LoadingState and
       newstate != Phonon.BufferingState):
      self.button.setText('选择文件')
      if newstate == Phonon.ErrorState:
        source = self.media.currentSource().fileName()
        print ('错误:不能播放:', source.toLocal8Bit().data())
        print (' %s' % self.media.errorString().toLocal8Bit().data())


if __name__ == '__main__':
  app = QtGui.QApplication(sys.argv)
  app.setApplicationName('视频播放')
  window = Window()
  window.show()
  sys.exit(app.exec_())

代码实现了一个有GUI窗口的应用,用来播放视频文件。视频播放利用了PyQT中的Phonon模块。此外,还有一个进程每隔一秒发出一个信号。窗口在接收到信号后,更新视频播放的时间。这个应用的效果如下:
测试运行环境为Mac OSX El Capitan。

 Python应用03 使用PyQT制作视频播放器实例

视图部分

写完这个代码之后,我发现这个代码虽然简单,但涉及了几个重要机制,可以用PyQT的练习题。下面对代码进行一些简要的说明,首先是主程序部分:

app = QtGui.QApplication(sys.argv)
...
window = Window()
window.show()
sys.exit(app.exec_())

在PyQT程序中,QApplication是最上层的对象,指代整个GUI应用。我们在程序的一开始创建了一个应用对象,在程序最后调用exec_()来运行这个应用。sys.exit()用来要求应用的主循环结束后干净地退出程序。PyQT程序的开始和结尾都是类似的固定套路。关键就在于其间定义的QWidget对象。

我们自定义的Window类继承自QWidget。其实QWidget是所有用户界面对象的基类,并不单单指代一个窗口。表格、输入框、按钮都继承自QWidget。在一个Window对象中,我们还组合有QPushButton和QLabel这样的对象,分别代表一个按钮和一个文本框。它们通过QGridLayout的方式,布局在Window的界面上,即下面一部分代码:

# layout
layout = QtGui.QGridLayout(self)
...
layout.addWidget(self.info, 4, 1, 1, 3)
layout.addWidget(self.button, 5, 1, 1, 3)

QGridLayout把界面分成网格,并把某个视图对象附着在特定的网格位置。比如说,addWidget()(self.info, 4, 1, 1, 3)表示把一个文本框对象放在第4排、第1列的位置。该文本框纵向将占据1排,横向占据3列。这样,上下层视图的位置关系就通过布局确定了下来。除了网格式的布局,PyQT还支持其他形式的布局,如横向堆砌、纵向堆砌等等,可以进一步了解。

 除了QWidget,PyQT还提供了常用的对话框,如:

self.path = QtGui.QFileDialog.getOpenFileName(self, self.button.text())

这里的QFileDialog对话框用于选择文件。对话框将访问所选文件的路径。除了文件选择,对话框还有确认对话框、文件输入对话框、色彩对话框。这些对话框实现了不少常用的GUI输入功能。通过利用这些对话框,可以减少程序员从头开发的工作量。

 多线程

GUI界面的主线程通常留给应用做主循环。其他的很多工作要通过其他的线程来完成。PyQT多线程编程很简单,只需要重写QThread的run()方法就可以了:

class PollTimeThread(QtCore.QThread):
  def __init__(self, parent):
    super(PollTimeThread, self).__init__(parent)

  def run(self):
    ...

创建线程后,只需要调用start()方法,就可以运行:

self.thread = PollTimeThread()
...
self.thread.start()    # 启动线程
...
self.thread.terminate()  # 终止线程

信号与槽

GUI经常要用到异步处理。比如说点击某个按钮,然后调用相应的回调函数。QT的“信号与槽”(signal-slot)机制就是为了解决异步处理问题。我们在线程中创建了信号,并通过emit()方法来发出信号:

class PollTimeThread(QtCore.QThread):
  """
  This thread works as a timer.
  """
  update = QtCore.pyqtSignal()

  def __init__(self, parent):
    super(PollTimeThread, self).__init__(parent)

  def run(self):
    while True:
      time.sleep(1)
      if self.isRunning():
        # emit signal
        self.update.emit()
      else:
        return

有了信号,我们就可以给该信号连接到一个“槽”,其实就是对应于该信号的回调函数:

self.thread.update.connect(self.update)

每当信号被发出时,“槽”就会被调用。在这个例子中,就是更新视频播放时间。QT中的“信号与槽”是普遍存在的机制。一些组建如按键,预设了“点击”这样的信号,可以直接对应到“槽”。如代码中的:

self.button.clicked.connect(self.handleButton)

 此外,Phonon是一个很好用的多媒体模块,使用方法也很简单,可以参考代码本身,这里不再赘述。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python操作xml文件示例
Apr 07 Python
Python Queue模块详解
Nov 30 Python
python求解水仙花数的方法
May 11 Python
Java实现的执行python脚本工具类示例【使用jython.jar】
Mar 29 Python
python 对给定可迭代集合统计出现频率,并排序的方法
Oct 18 Python
python 实现视频流下载保存MP4的方法
Jan 09 Python
python PIL/cv2/base64相互转换实例
Jan 09 Python
keras训练曲线,混淆矩阵,CNN层输出可视化实例
Jun 15 Python
OpenCV 使用imread()函数读取图片的六种正确姿势
Jul 09 Python
python中strip(),lstrip(),rstrip()函数的使用讲解
Nov 17 Python
Pycharm创建python文件自动添加日期作者等信息(步骤详解)
Feb 03 Python
对Pytorch 中的contiguous理解说明
Mar 03 Python
Python 实现一个颜色色值转换的小工具
Dec 06 #Python
python 线程的暂停, 恢复, 退出详解及实例
Dec 06 #Python
python 实现删除文件或文件夹实例详解
Dec 04 #Python
python 根据正则表达式提取指定的内容实例详解
Dec 04 #Python
python xml.etree.ElementTree遍历xml所有节点实例详解
Dec 04 #Python
Python性能提升之延迟初始化
Dec 04 #Python
python中redis的安装和使用
Dec 04 #Python
You might like
一个用php实现的获取URL信息的类
2007/01/02 PHP
PHP生成随机用户名和密码的实现代码
2013/02/27 PHP
php生成固定长度纯数字编码的方法
2015/07/09 PHP
PHP中功能强大却很少使用的函数实例小结
2016/11/10 PHP
php 三元运算符实例详细介绍
2016/12/15 PHP
JavaScript 注册事件代码
2011/01/27 Javascript
JS正则中的RegExp对象对象
2012/11/07 Javascript
JS实现仿QQ效果的三级竖向菜单
2015/09/25 Javascript
详解JavaScript的AngularJS框架中的表达式与指令
2016/03/05 Javascript
jquery分页插件jquery.pagination.js实现无刷新分页
2016/04/01 Javascript
easyui window refresh 刷新两次的解决方法(推荐)
2016/05/18 Javascript
AngularJS中实现动画效果的方法
2016/07/28 Javascript
Vue.js 父子组件通讯开发实例
2016/09/06 Javascript
JavaScript 字符串常用操作小结(非常实用)
2016/11/30 Javascript
javascript基础练习之翻转字符串与回文
2017/02/20 Javascript
使用react-router4.0实现重定向和404功能的方法
2017/08/28 Javascript
基于vue.js的分页插件详解
2017/11/27 Javascript
JS实现倒计时图文效果
2018/11/17 Javascript
今天,小程序正式支持 SVG
2019/04/20 Javascript
layui关闭层级、简单监听的实例
2019/09/06 Javascript
JS实现纵向轮播图(初级版)
2020/01/18 Javascript
vue.js watch经常失效的场景与解决方案
2021/01/07 Vue.js
Python实现一个简单的验证码程序
2017/11/03 Python
Python中利用aiohttp制作异步爬虫及简单应用
2018/11/29 Python
解决sublime+python3无法输出中文的问题
2018/12/12 Python
Python爬虫——爬取豆瓣电影Top250代码实例
2019/04/17 Python
python英语单词测试小程序代码实例
2019/09/09 Python
在pycharm中配置Anaconda以及pip源配置详解
2019/09/09 Python
python调用HEG工具批量处理MODIS数据的方法及注意事项
2020/02/18 Python
python实现简单猜单词游戏
2020/12/24 Python
携程旅行网:中国领先的在线旅行服务公司
2017/02/17 全球购物
.NET笔试题(20个问题)
2016/02/02 面试题
电子商务自荐书范文
2014/01/04 职场文书
考核工作实施方案
2014/03/30 职场文书
师德师风自我剖析材料
2014/09/27 职场文书
项目经理岗位职责范本
2015/04/01 职场文书