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中的类与对象之描述符详解
Mar 27 Python
11个并不被常用但对开发非常有帮助的Python库
Mar 31 Python
Python中防止sql注入的方法详解
Feb 25 Python
python对离散变量的one-hot编码方法
Jul 11 Python
python+numpy+matplotalib实现梯度下降法
Aug 31 Python
Python使用itchat模块实现简单的微信控制电脑功能示例
Aug 26 Python
使用 Supervisor 监控 Python3 进程方式
Dec 05 Python
python保存log日志,实现用log日志画图
Dec 24 Python
Jupyter Notebook添加代码自动补全功能的实现
Jan 07 Python
Python中相见恨晚的技巧
Apr 13 Python
Django如何与Ajax交互
Apr 29 Python
关于python类SortedList详解
Sep 04 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
table标签的结构与合并单元格的实现方法
2013/07/24 PHP
php生成验证码函数
2015/10/20 PHP
WordPress开发中短代码的实现及相关函数使用技巧
2016/01/05 PHP
php实现多维数组排序的方法示例
2017/03/23 PHP
一段好玩的JavaScript代码
2006/12/01 Javascript
Javascript 文件夹选择框的两种解决方案
2009/07/01 Javascript
js window.onload 加载多个函数和追加函数详解
2014/01/08 Javascript
深入理解JavaScript系列(31):设计模式之代理模式详解
2015/03/03 Javascript
JavaScript动态改变表格单元格内容的方法
2015/03/30 Javascript
javascript函数式编程实例分析
2015/04/25 Javascript
jQuery中cookie插件用法实例分析
2015/12/04 Javascript
Nodejs学习item【入门手上】
2016/05/05 NodeJs
JS实现图片上传预览功能
2016/11/21 Javascript
Bootstrop实现多级下拉菜单功能
2016/11/24 Javascript
微信小程序加载更多 点击查看更多
2016/11/29 Javascript
JS实现类似百叶窗下拉菜单效果
2016/12/30 Javascript
使用ionic在首页新闻中应用到的跑马灯效果的实现方法
2017/02/13 Javascript
vue3.0 CLI - 2.6 - 组件的复用入门教程
2018/09/14 Javascript
用Vue.js方法创建模板并使用多个模板合成
2019/06/28 Javascript
jQuery zTree插件快速实现目录树
2019/08/16 jQuery
OpenLayers3实现图层控件功能
2020/09/25 Javascript
[43:49]LGD vs CHAOS 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
django 开发忘记密码通过邮箱找回功能示例
2018/04/17 Python
Python 通过调用接口获取公交信息的实例
2018/12/17 Python
对Python正则匹配IP、Url、Mail的方法详解
2018/12/25 Python
使用Rasterio读取栅格数据的实例讲解
2019/11/26 Python
Python 实现平台类游戏添加跳跃功能
2020/03/27 Python
python实现二分类和多分类的ROC曲线教程
2020/06/15 Python
Python3实现英文字母转换哥特式字体实例代码
2020/09/01 Python
HTML5添加鼠标悬浮音响效果不使用FLASH
2014/04/23 HTML / CSS
HTML里显示pdf、word、xls、ppt的方法示例
2020/04/14 HTML / CSS
CK美国官网:Calvin Klein
2016/08/26 全球购物
美体小铺波兰官方网站:The Body Shop波兰
2019/09/03 全球购物
18岁生日感言
2014/01/12 职场文书
微笑服务演讲稿
2014/05/13 职场文书
使用jpa之动态插入与修改(重写save)
2021/11/23 Java/Android