在寻找如何使用Pyqt做一个播放器时首先找到的是openCV2
openCV2 貌似太强大了,各种关于图像处理的事情它都能完成,如 读取摄像头、图像识别、人脸识别、 图像灰度处理 、 播放视频等,强大的让你想不到!
openCV2 播放视频也很简单:
1 #coding=utf-8 2 3 import cv2.cv as cv 4 filename = "cn.avi" 5 win_name = "video player" 6 capture = cv.CaptureFromFile(filename) 7 cv.NamedWindow(win_name, cv.CV_WINDOW_AUTOSIZE) 8 9 # 定义一个无限循环 10 while 1: 11 12 # 每次从视频数据流框架中抓取一帧图片 13 image = cv.QueryFrame(capture) 14 15 # 将图片显示在特定窗口上 16 cv.ShowImage(win_name, image) 17 18 # 当安县Esc键时退出循环 19 c = cv.WaitKey(33) 20 if c == 27: 21 break 22 23 # 退出循环后销毁显示窗口 24 cv.DestroyWindow(win_name)
效果:
在这里也提供以些openCV的信息
下载地址:http://opencv.org/downloads.html 我使用的版本(V2.4.10) time:2015-02-10
下载完成后解压文件,找到opencv目录下的build-->python->cv2.pyd, 复制cv2.pyd到python的安装目录,此时运行脚本会报错,因为还要安装numpy,下载地址:https://pypi.python.org/pypi/numpy/1.9.1
再次运行不报错了但播放不了视频文件,为什么呢? 因为缺少解码器下载video codec解码器,http://www.xvidmovies.com/codec/
现在运行就OK了!
关于更多的openCV信息参考:
http://blog.sina.com.cn/s/blog_5562b0440102uw7g.html
-------------------------------------------------------------------------------
言归正传, 我们这里要讲的是如何用Pyqt 做一个音视频播放器。
使用openCV2播放视频,但openCV2只提供图像处理,没有音频处理,所以即使Pyqt集成openCV2也只能播放视频而没有声音。
现在我们使用Phonon 来完成这个功能。 话说 Phonon不属于QT, 是QT集成了Phonon
下面我们来讲讲用Phonon实现的过程。
在上一篇我转载过一篇关于Phonon的文章http://www.cnblogs.com/dcb3688/p/4283222.html
里面介绍了Phonon的结构、安装 、使用以及详细的例子,今天我们就在做一个例子
第一步:创建UI
老方法,先创建UI文件
video.ui XML代码:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ui version="4.0"> 3 <class>videofrom</class> 4 <widget class="QWidget" name="videofrom"> 5 <property name="geometry"> 6 <rect> 7 <x>0</x> 8 <y>0</y> 9 <width>880</width> 10 <height>572</height> 11 </rect> 12 </property> 13 <property name="windowTitle"> 14 <string>Form</string> 15 </property> 16 <widget class="QWidget" name="verticalLayoutWidget_2"> 17 <property name="geometry"> 18 <rect> 19 <x>10</x> 20 <y>10</y> 21 <width>861</width> 22 <height>551</height> 23 </rect> 24 </property> 25 <layout class="QVBoxLayout" name="verticalLayout_main" stretch="2"> 26 <item> 27 <layout class="QVBoxLayout" name="verticalLayout" stretch="8,1,1"> 28 <item> 29 <layout class="QVBoxLayout" name="verticalLayout_player" stretch=""> 30 <property name="spacing"> 31 <number>6</number> 32 </property> 33 </layout> 34 </item> 35 <item> 36 <widget class="Phonon::SeekSlider" name="seekSlider"/> 37 </item> 38 <item> 39 <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0,2,0,5,0,2"> 40 <item> 41 <widget class="QPushButton" name="BtnOpen"> 42 <property name="contextMenuPolicy"> 43 <enum>Qt::CustomContextMenu</enum> 44 </property> 45 <property name="toolTip"> 46 <string><html><head/><body><p>选择文件,右键选择音频or 视频</p></body></html></string> 47 </property> 48 <property name="text"> 49 <string>选择文件</string> 50 </property> 51 </widget> 52 </item> 53 <item> 54 <widget class="Line" name="line"> 55 <property name="orientation"> 56 <enum>Qt::Vertical</enum> 57 </property> 58 </widget> 59 </item> 60 <item> 61 <layout class="QHBoxLayout" name="horizontalLayout_btn"/> 62 </item> 63 <item> 64 <widget class="Line" name="line_2"> 65 <property name="orientation"> 66 <enum>Qt::Vertical</enum> 67 </property> 68 </widget> 69 </item> 70 <item> 71 <widget class="Phonon::VolumeSlider" name="volumeSlider"/> 72 </item> 73 <item> 74 <widget class="Line" name="line_3"> 75 <property name="orientation"> 76 <enum>Qt::Vertical</enum> 77 </property> 78 </widget> 79 </item> 80 <item> 81 <widget class="QLCDNumber" name="lcdNumber"/> 82 </item> 83 </layout> 84 </item> 85 </layout> 86 </item> 87 </layout> 88 </widget> 89 </widget> 90 <customwidgets> 91 <customwidget> 92 <class>Phonon::SeekSlider</class> 93 <extends>QWidget</extends> 94 <header location="global">phonon/seekslider.h</header> 95 </customwidget> 96 <customwidget> 97 <class>Phonon::VolumeSlider</class> 98 <extends>QWidget</extends> 99 <header location="global">phonon/volumeslider.h</header> 100 </customwidget> 101 </customwidgets> 102 <resources/> 103 <connections/> 104 </ui>
转换为py文件
video.py :
1 # -*- coding: utf-8 -*- 2 3 # Form implementation generated from reading ui file ‘video.ui‘ 4 # 5 # Created: Thu Feb 12 17:25:10 2015 6 # by: PyQt4 UI code generator 4.10.3 7 # 8 # WARNING! All changes made in this file will be lost! 9 10 from PyQt4 import QtCore, QtGui 11 12 try: 13 _fromUtf8 = QtCore.QString.fromUtf8 14 except AttributeError: 15 def _fromUtf8(s): 16 return s 17 18 try: 19 _encoding = QtGui.QApplication.UnicodeUTF8 20 def _translate(context, text, disambig): 21 return QtGui.QApplication.translate(context, text, disambig, _encoding) 22 except AttributeError: 23 def _translate(context, text, disambig): 24 return QtGui.QApplication.translate(context, text, disambig) 25 26 class Ui_videofrom(object): 27 def setupUi(self, videofrom): 28 videofrom.setObjectName(_fromUtf8("videofrom")) 29 videofrom.resize(880, 572) 30 self.verticalLayoutWidget_2 = QtGui.QWidget(videofrom) 31 self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(10, 10, 861, 551)) 32 self.verticalLayoutWidget_2.setObjectName(_fromUtf8("verticalLayoutWidget_2")) 33 self.verticalLayout_main = QtGui.QVBoxLayout(self.verticalLayoutWidget_2) 34 self.verticalLayout_main.setMargin(0) 35 self.verticalLayout_main.setObjectName(_fromUtf8("verticalLayout_main")) 36 self.verticalLayout = QtGui.QVBoxLayout() 37 self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) 38 self.verticalLayout_player = QtGui.QVBoxLayout() 39 self.verticalLayout_player.setObjectName(_fromUtf8("verticalLayout_player")) 40 self.verticalLayout.addLayout(self.verticalLayout_player) 41 self.seekSlider = phonon.Phonon.SeekSlider(self.verticalLayoutWidget_2) 42 self.seekSlider.setObjectName(_fromUtf8("seekSlider")) 43 self.verticalLayout.addWidget(self.seekSlider) 44 self.horizontalLayout = QtGui.QHBoxLayout() 45 self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) 46 self.BtnOpen = QtGui.QPushButton(self.verticalLayoutWidget_2) 47 self.BtnOpen.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 48 self.BtnOpen.setObjectName(_fromUtf8("BtnOpen")) 49 self.horizontalLayout.addWidget(self.BtnOpen) 50 self.line = QtGui.QFrame(self.verticalLayoutWidget_2) 51 self.line.setFrameShape(QtGui.QFrame.VLine) 52 self.line.setFrameShadow(QtGui.QFrame.Sunken) 53 self.line.setObjectName(_fromUtf8("line")) 54 self.horizontalLayout.addWidget(self.line) 55 self.horizontalLayout_btn = QtGui.QHBoxLayout() 56 self.horizontalLayout_btn.setObjectName(_fromUtf8("horizontalLayout_btn")) 57 self.horizontalLayout.addLayout(self.horizontalLayout_btn) 58 self.line_2 = QtGui.QFrame(self.verticalLayoutWidget_2) 59 self.line_2.setFrameShape(QtGui.QFrame.VLine) 60 self.line_2.setFrameShadow(QtGui.QFrame.Sunken) 61 self.line_2.setObjectName(_fromUtf8("line_2")) 62 self.horizontalLayout.addWidget(self.line_2) 63 self.volumeSlider = phonon.Phonon.VolumeSlider(self.verticalLayoutWidget_2) 64 self.volumeSlider.setObjectName(_fromUtf8("volumeSlider")) 65 self.horizontalLayout.addWidget(self.volumeSlider) 66 self.line_3 = QtGui.QFrame(self.verticalLayoutWidget_2) 67 self.line_3.setFrameShape(QtGui.QFrame.VLine) 68 self.line_3.setFrameShadow(QtGui.QFrame.Sunken) 69 self.line_3.setObjectName(_fromUtf8("line_3")) 70 self.horizontalLayout.addWidget(self.line_3) 71 self.lcdNumber = QtGui.QLCDNumber(self.verticalLayoutWidget_2) 72 self.lcdNumber.setObjectName(_fromUtf8("lcdNumber")) 73 self.horizontalLayout.addWidget(self.lcdNumber) 74 self.horizontalLayout.setStretch(0, 1) 75 self.horizontalLayout.setStretch(2, 2) 76 self.horizontalLayout.setStretch(4, 5) 77 self.horizontalLayout.setStretch(6, 2) 78 self.verticalLayout.addLayout(self.horizontalLayout) 79 self.verticalLayout.setStretch(0, 8) 80 self.verticalLayout.setStretch(1, 1) 81 self.verticalLayout.setStretch(2, 1) 82 self.verticalLayout_main.addLayout(self.verticalLayout) 83 self.verticalLayout_main.setStretch(0, 2) 84 85 self.retranslateUi(videofrom) 86 QtCore.QMetaObject.connectSlotsByName(videofrom) 87 88 def retranslateUi(self, videofrom): 89 videofrom.setWindowTitle(_translate("videofrom", "Form", None)) 90 self.BtnOpen.setToolTip(_translate("videofrom", "<html><head/><body><p>选择文件,右键选择音频or 视频</p></body></html>", None)) 91 self.BtnOpen.setText(_translate("videofrom", "选择文件", None)) 92 93 from PyQt4 import phonon 94 95 if __name__ == "__main__": 96 import sys 97 app = QtGui.QApplication(sys.argv) 98 videofrom = QtGui.QWidget() 99 ui = Ui_videofrom() 100 ui.setupUi(videofrom) 101 videofrom.show() 102 sys.exit(app.exec_())
运行效果:
看起来很乱的布局是吧,在这理因为的是layout里面的setStretch 方法,该方法可以按照百分比的比例来显示控件的坐标,空的地方我们预留了一个 QToolBar 和 VideoWidget。
为什么是VideoWidget 而不是VideoPlayer 呢? 因为VideoPlayer 提供的方法太少。只有播放 暂停 停止等,而VideoWidget 提供了更多的方法和功能。
在Qt designer中我一直在找Phonon的VideoWidget 控件一直找不到,只有一个VideoPlayer, 所以只能在逻辑页面 addWidget了。
第二步: 编写逻辑页面
细节不讲了,直接贴出代码:
1 # -*- coding: utf-8 -*- 2 3 4 5 from PyQt4 import QtCore, QtGui 6 from PyQt4 import phonon 7 from video import Ui_videofrom 8 import sys 9 import icoqrc 10 11 class mainvideo(QtGui.QWidget): 12 def __init__(self): 13 super(mainvideo, self).__init__() 14 self.UI=Ui_videofrom() 15 self.UI.setupUi(self) 16 self.setWindowTitle(u‘Pyqt 音视频播放器‘) 17 self.setWindowIcon(QtGui.QIcon(‘:flash.ico‘)) 18 self.mediaObject = phonon.Phonon.MediaObject(self) 19 self.mediaObject.stateChanged.connect(self.stateChanged) # 对象改变时 20 self.mediaObject.tick.connect(self.tick) # 链接到时间 21 self.setupUi() 22 self.connect(self.UI.BtnOpen, QtCore.SIGNAL(‘customContextMenuRequested (const QPoint&)‘), self.openright) 23 self.connect(self.UI.BtnOpen, QtCore.SIGNAL(‘clicked()‘), self.alert) 24 25 self.UI.videoPlayer =phonon.Phonon.VideoWidget(self) 26 self.UI.verticalLayout_player.addWidget(self.UI.videoPlayer) 27 28 29 def setupUi(self): 30 self.playAction = QtGui.QAction(self.style().standardIcon(QtGui.QStyle.SP_MediaPlay), "Play",self, shortcut="Ctrl+P", enabled=False, triggered=self.mediaObject.play) 31 self.pauseAction = QtGui.QAction(self.style().standardIcon(QtGui.QStyle.SP_MediaPause), "Pause", self, shortcut="Ctrl+A", enabled=False, triggered=self.mediaObject.pause) 32 self.stopAction = QtGui.QAction(self.style().standardIcon(QtGui.QStyle.SP_MediaStop), "Stop", self, shortcut="Ctrl+S", enabled=False,triggered=self.mediaObject.stop) 33 # 添加工具条 包含 播放, 暂停, 重新开始 34 bar = QtGui.QToolBar() 35 bar.addAction(self.playAction) 36 bar.addAction(self.pauseAction) 37 bar.addAction(self.stopAction) 38 self.UI.horizontalLayout_btn.addWidget(bar) 39 # 显示LED时间 40 palette = QtGui.QPalette() 41 palette.setBrush(QtGui.QPalette.Light, QtCore.Qt.darkGray) 42 self.timeLcd = self.UI.lcdNumber 43 self.timeLcd.setPalette(palette) 44 self.timeLcd.display(‘00:00‘) 45 self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint) # PyQT禁止窗口最大化按钮: 46 self.setFixedSize(self.width(), self.height()) # PyQT禁止调整窗口大小: 47 48 49 50 51 52 # button 右键菜单 53 def openright(self): 54 popMenu = QtGui.QMenu() 55 popMenu.addAction(QtGui.QAction(QtGui.QIcon(‘:chrome.ico‘), u‘音频文件‘, self, enabled=True, triggered=self.openaudio)) 56 popMenu.addAction(QtGui.QAction(QtGui.QIcon(‘:myfavicon.ico‘), u‘视频文件‘, self, enabled=True, triggered=self.openvideo)) 57 popMenu.exec_(QtGui.QCursor.pos()) 58 59 60 # 选择打开音频 61 def openaudio(self): 62 file = self.addFiles(‘audio‘) 63 self.mediaObject.setCurrentSource(phonon.Phonon.MediaSource(file)) 64 # 初始化音频的输出按钮 65 self.audioOutput = phonon.Phonon.AudioOutput(phonon.Phonon.VideoCategory, self) 66 phonon.Phonon.createPath(self.mediaObject, self.audioOutput) 67 # 连接到音量 68 self.UI.volumeSlider.setAudioOutput(self.audioOutput) 69 self.UI.seekSlider.setMediaObject(self.mediaObject) 70 self.mediaObject.play() 71 72 73 # 选择打开视频文件 74 def openvideo(self): 75 file = self.addFiles(‘video‘) 76 self.mediaObject.setCurrentSource(phonon.Phonon.MediaSource(file)) # 加载当前的源文件 77 phonon.Phonon.createPath(self.mediaObject, self.UI.videoPlayer) 78 # 初始化视频输出 79 self.UI.videoPlayer.setAspectRatio(phonon.Phonon.VideoWidget.AspectRatioAuto) 80 # 初始化音频的输出按钮 81 self.audioOutput =phonon.Phonon.AudioOutput(phonon.Phonon.VideoCategory, self) 82 phonon.Phonon.createPath(self.mediaObject, self.audioOutput) 83 # 连接到音量按钮 84 self.UI.volumeSlider.setAudioOutput(self.audioOutput) 85 self.UI.seekSlider.setMediaObject(self.mediaObject) 86 self.mediaObject.play() 87 88 89 90 def alert(self): 91 QtGui.QMessageBox.question(self, (u‘提示‘),(u‘请右键选择打开文件!‘),QtGui.QMessageBox.Ok) 92 93 # 选择文件 94 def addFiles(self,filetype=‘all‘): 95 if filetype==‘audio‘: 96 tips=u‘选择音频文件‘ 97 expand = ‘Image Files(*.mp3 *.wav)‘ 98 elif filetype==‘video‘: 99 tips = u‘选择视频文件‘ 100 expand = ‘Image Files(*.mp4 *.avi)‘ 101 else: 102 tips =u‘请选择播放文件‘ 103 expand = ‘Image Files(*.mp3 *.wav *.mp4 *.avi)‘ 104 # getOpenFileName 只能选择一个 getOpenFileNames 可多个选择 105 files = QtGui.QFileDialog.getOpenFileName(self, tips,QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MusicLocation), expand) # QStringList getOpenFileNames (QWidget parent = None, QString caption = QString(), QString directory = QString(), QString filter = QString(), Options options = 0) 106 107 if not files: 108 return ‘‘ 109 110 return files 111 # 改变状态 112 def stateChanged(self, newState): 113 114 if newState == phonon.Phonon.ErrorState: 115 if self.mediaObject.errorType() == phonon.Phonon.FatalError: 116 QtGui.QMessageBox.warning(self, "Fatal Error", 117 self.mediaObject.errorString()) 118 else: 119 QtGui.QMessageBox.warning(self, "Error", 120 self.mediaObject.errorString()) 121 122 elif newState == phonon.Phonon.PlayingState: 123 self.playAction.setEnabled(False) 124 self.pauseAction.setEnabled(True) 125 self.stopAction.setEnabled(True) 126 127 elif newState == phonon.Phonon.StoppedState: 128 self.stopAction.setEnabled(False) 129 self.playAction.setEnabled(True) 130 self.pauseAction.setEnabled(False) 131 self.timeLcd.display("00:00") 132 133 134 elif newState == phonon.Phonon.PausedState: 135 self.pauseAction.setEnabled(False) 136 self.stopAction.setEnabled(True) 137 self.playAction.setEnabled(True) 138 # 时间显示 139 def tick(self, time): 140 displayTime = QtCore.QTime(0, (time / 60000) % 60, (time / 1000) % 60) 141 self.timeLcd.display(displayTime.toString(‘mm:ss‘)) 142 143 144 145 def keyPressEvent(self, event): 146 if event.key() ==QtCore.Qt.Key_Escape: 147 self.close() 148 149 150 if __name__ == ‘__main__‘: 151 app=QtGui.QApplication(sys.argv) 152 mainapp = mainvideo() 153 app.setQuitOnLastWindowClosed(True) 154 mainapp.show() 155 sys.exit(app.exec_())
第三步: 运行和排除问题
按理来说,应该是先排除问题再运行。
在完成这个Pyqt的播放器中过程中遇到了N多问题
一个大问题是:没有声音和视频画面, 问题google后很简单: 没有解码器! 下载视频解码器喽!
好!现在我们运行看效果: