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进行数据科学工作的简单入门教程
Apr 01 Python
python内存管理分析
Apr 08 Python
在Python程序中进行文件读取和写入操作的教程
Apr 28 Python
Python存取XML的常见方法实例分析
Mar 21 Python
基于python中pygame模块的Linux下安装过程(详解)
Nov 09 Python
全面了解Nginx, WSGI, Flask之间的关系
Jan 09 Python
对Python正则匹配IP、Url、Mail的方法详解
Dec 25 Python
python学生信息管理系统实现代码
Dec 17 Python
使用python实现多维数据降维操作
Feb 24 Python
Python中常见的数制转换有哪些
May 27 Python
Python爬取豆瓣数据实现过程解析
Oct 27 Python
python+selenium爬取微博热搜存入Mysql的实现方法
Jan 27 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
JS中encodeURIComponent函数用php解码的代码
2012/03/01 PHP
php中flush()、ob_flush()、ob_end_flush()的区别介绍
2013/02/17 PHP
PHP容易忘记的知识点分享
2013/04/30 PHP
WordPress自定义时间显示格式
2015/03/27 PHP
Laravel搭建后台登录系统步骤详解
2016/07/26 PHP
php微信公众号开发之微信企业付款给个人
2018/10/04 PHP
php xhprof使用实例详解
2019/04/15 PHP
基于JQuery的数字改变的动画效果--可用来做计数器
2010/08/11 Javascript
jQuery插件开发的两种方法及$.fn.extend的详解
2014/01/16 Javascript
setTimeout()递归调用不加引号出错的解决方法
2014/09/05 Javascript
jQuery DOM插入节点操作指南
2015/03/03 Javascript
html、css和jquery相结合实现简单的进度条效果实例代码
2016/10/24 Javascript
浅析vue数据绑定
2017/01/17 Javascript
浅谈angularjs $http提交数据探索
2017/01/20 Javascript
react-native DatePicker日期选择组件的实现代码
2017/09/12 Javascript
vue项目中v-model父子组件通信的实现详解
2017/12/10 Javascript
如何编写一个d.ts文件的步骤详解
2018/04/13 Javascript
JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例
2018/07/27 Javascript
微信小程序实现搜索功能并跳转搜索结果页面
2019/05/18 Javascript
node.js命令行教程图文详解
2019/05/27 Javascript
ionic4+angular7+cordova上传图片功能的实例代码
2019/06/19 Javascript
layer.js open 隐藏滚动条的例子
2019/09/05 Javascript
Python中os和shutil模块实用方法集锦
2014/05/13 Python
python调用Moxa PCOMM Lite通过串口Ymodem协议实现发送文件
2014/08/15 Python
python抓取百度首页的方法
2015/05/19 Python
python解决汉字编码问题:Unicode Decode Error
2017/01/19 Python
python操作excel的包(openpyxl、xlsxwriter)
2018/06/11 Python
linux mint中搜狗输入法导致pycharm卡死的问题
2020/10/28 Python
收集的22款给力的HTML5和CSS3帮助工具
2012/09/14 HTML / CSS
美国在线购买空气净化器、除湿器、加湿器网站:AllergyBuyersClub
2021/03/16 全球购物
托管代码(Managed Code)和非托管代码(Unmanaged Code)有什么区别
2014/09/29 面试题
大学生的网络创业计划书
2013/12/26 职场文书
抽奖活动主持词
2014/03/31 职场文书
中队活动总结
2014/08/27 职场文书
区政府领导班子个人对照检查材料
2014/09/25 职场文书
苹果发布了MagSafe固件更新,可以不外接电源实现最高7.5W充电
2022/04/21 数码科技