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中使用enumerate函数遍历元素实例
Jun 16 Python
python实现html转ubb代码(html2ubb)
Jul 03 Python
Python深入学习之上下文管理器
Aug 31 Python
python中安装Scrapy模块依赖包汇总
Jul 02 Python
Python使用文件锁实现进程间同步功能【基于fcntl模块】
Oct 16 Python
TensorFlow saver指定变量的存取
Mar 10 Python
Python的numpy库中将矩阵转换为列表等函数的方法
Apr 04 Python
Python 隐藏输入密码时屏幕回显的实例
Feb 19 Python
pygame实现俄罗斯方块游戏(基础篇2)
Oct 29 Python
解决django xadmin主题不显示和只显示bootstrap2的问题
Mar 30 Python
Python 捕获代码中所有异常的方法
Aug 03 Python
15个Pythonic的代码示例(值得收藏)
Oct 29 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
一个图形显示IP的PHP程序代码
2007/10/19 PHP
php jquery 实现新闻标签分类与无刷新分页
2009/12/18 PHP
Zend Studio (eclipse)使用速度优化方法
2011/03/23 PHP
Ecshop 后台添加新功能栏目及管理权限设置教程
2017/11/21 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
2019/10/12 PHP
推荐dojo学习笔记
2007/03/24 Javascript
JQuery 实现的页面滚动时浮动窗口控件
2009/07/10 Javascript
ExtJs设置GridPanel表格文本垂直居中示例
2013/07/15 Javascript
JS中的异常处理方法分享
2013/12/22 Javascript
jquery获取当前点击对象的value方法
2014/02/28 Javascript
jQuery基于cookie实现的购物车实例分析
2015/12/24 Javascript
表单input项使用label同时引用Bootstrap库导致input点击效果区增大问题
2016/10/11 Javascript
jQuery File Upload文件上传插件使用详解
2016/12/06 Javascript
jQuery实现两个select控件的互移操作
2016/12/22 Javascript
新版vue-cli模板下本地开发环境使用node服务器跨域的方法
2018/04/03 Javascript
Vue开发实现吸顶效果的示例代码
2018/08/21 Javascript
vue-cli3+typescript新建一个项目的思路分析
2019/08/06 Javascript
Vue路由模块化配置的完整步骤
2019/08/14 Javascript
python数据结构之图深度优先和广度优先实例详解
2015/07/08 Python
python中子类调用父类函数的方法示例
2017/08/18 Python
在PyCharm下使用 ipython 交互式编程的方法
2019/01/17 Python
OpenCV HSV颜色识别及HSV基本颜色分量范围
2019/03/22 Python
python项目对接钉钉SDK的实现
2019/07/15 Python
python怎么删除缓存文件
2020/07/19 Python
TensorFlow低版本代码自动升级为1.0版本
2021/02/20 Python
Bulk Powders意大利:运动补充在线商店
2019/02/09 全球购物
员工考核管理制度
2014/02/02 职场文书
财务会计大学生自我评价
2014/04/09 职场文书
团日活动总结范文
2014/04/25 职场文书
法院先进个人事迹材料
2014/05/04 职场文书
新闻专业毕业生求职信
2014/08/08 职场文书
感谢信范文大全
2015/01/23 职场文书
物业项目经理岗位职责
2015/04/01 职场文书
会议承办单位欢迎词
2015/09/30 职场文书
幼儿园教师辞职信
2019/06/21 职场文书
何时使用Map来代替普通的JS对象
2021/04/29 Javascript