社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python使用PyQT制作视频播放器

python • 6 年前 • 372 次点击  


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


视频播放器

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

  1. import time

  2. import sys

  3. from PyQt4 import QtGui, QtCore

  4. from PyQt4.phonon import Phonon

  5. class PollTimeThread(QtCore.QThread):

  6.    """

  7.    This thread works as a timer.

  8.    """

  9.    update = QtCore.pyqtSignal()

  10.    def __init__(self, parent):

  11.        super(PollTimeThread, self).__init__(parent )

  12.    def run(self):

  13.        while True:

  14.            time.sleep(1)

  15.            if self.isRunning():

  16.                # emit signal

  17.                self.update.emit()

  18.             else:

  19.                return

  20. class Window(QtGui.QWidget):

  21.    def __init__(self):

  22.        QtGui.QWidget.__init__(self)

  23.        # media

  24.        self.media = Phonon.MediaObject(self)

  25.        self.media.stateChanged.connect(self.handleStateChanged)

  26.        self.video = Phonon.VideoWidget(self)

  27.        self.video.setMinimumSize(200, 200)

  28.        self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self)

  29.        Phonon.createPath(self.media, self.audio)

  30.        Phonon.createPath(self.media, self.video)

  31.        # control button

  32.        self .button = QtGui.QPushButton('选择文件', self)

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

  34.        # for display of time lapse

  35.        self.info = QtGui.QLabel(self)

  36.         # layout

  37.        layout = QtGui.QGridLayout(self)

  38.        layout.addWidget(self.video, 1, 1, 3, 3)

  39.        layout.addWidget(self.info, 4, 1, 1, 3)

  40.        layout.addWidget(self.button, 5, 1, 1, 3)

  41.        # signal-slot, for time lapse

  42.        self.thread = PollTimeThread(self)

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

  44.    def update(self):

  45.        # slot

  46.        lapse = self.media.currentTime()/1000.0

  47.        self.info.setText("%4.2f 秒" % lapse)

  48.    def startPlay(self):

  49.        if self.path:

  50.            self.media.setCurrentSource(Phonon.MediaSource(self.path))

  51.            # use a thread as a timer

  52.            self.thread = PollTimeThread(self)

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

  54.            self.thread.start()

  55.            self.media.play()

  56.    def handleButton(self):

  57.        if self.media.state() == Phonon.PlayingState:

  58.            self. media.stop()

  59.            self.thread.terminate()

  60.        else:

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

  62.            self.startPlay()

  63.     def handleStateChanged(self, newstate, oldstate):

  64.        if newstate == Phonon.PlayingState:

  65.            self.button.setText('停止')

  66.        elif (newstate != Phonon.LoadingState and

  67.              newstate != Phonon.BufferingState):

  68.            self.button.setText('选择文件')

  69.            if newstate == Phonon.ErrorState:

  70.                source = self.media.currentSource().fileName()

  71.                print ('错误:不能播放:', source.toLocal8Bit().data())

  72.                print ('  %s' % self.media.errorString().toLocal8Bit().data())

  73. if __name__ == '__main__':

  74.    app = QtGui.QApplication(sys.argv)

  75.    app.setApplicationName('视频播放')

  76.    window = Window()

  77.    window.show()

  78.    sys.exit(app.exec_())


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

测试运行环境为Mac OSX El Capitan。

视图部分

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

  1. app = QtGui.QApplication(sys.argv)

  2. ...

  3. window = Window()

  4. window.show()

  5. sys.exit(app.exec_())

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

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

  1. # layout

  2. layout = QtGui.QGridLayout(self)

  3. ...

  4. layout.addWidget(self.info, 4, 1, 1, 3 )

  5. 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()方法就可以了:


  1. class PollTimeThread(QtCore.QThread):

  2.    def __init__(self, parent ):

  3.        super(PollTimeThread, self).__init__(parent)

  4.    def run(self):

  5.        ...


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


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


信号与槽

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

  1. class PollTimeThread(QtCore.QThread):

  2.    """

  3.    This thread works as a timer.

  4.    """

  5.    update = QtCore.pyqtSignal()

  6.    def __init__(self, parent):

  7.        super(PollTimeThread, self).__init__(parent)

  8.    def run(self ):

  9.        while True:

  10.            time.sleep(1)

  11.            if self.isRunning():

  12.                # emit signal

  13.                self.update.emit()

  14.            else:

  15.                return

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

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

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

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

 

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

作者:Vamei

源自:

http://www.cnblogs.com/vamei/p/6139513.html


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/e0pEmyJUBt
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/12281
 
372 次点击