使用QMediaPlayer播放音乐文件
1.QMediaPlayer类功能概述
QMediaPlayer可以播放经过压缩的音频或视频文件,如wav、mp3、wma、wmv、avi等文件,QMediaPlayer可以播放单个文件,也可以和QMediaPlaylist类结合,对一个播放列表进行播放。所以,使用QMediaPlayer和QMediaPlaylist可以轻松地设计一个自己的音乐或视频播放器。
QMediaPlayer的主要接口函数如表所示:
函数原型 | 功能描述 |
duration() | 返回当前文件播放时间总长,单位ms返回当前文件播放时间总长,单位ms |
setPosition(position) | 设置当前文件播放位置,单位ms |
setMuted(muted) | 设置是否静音,muted为bool类型 |
isMuted() | bool型返回值表示是否静音,True表示静音 |
setPlaylist(playlist) | 设置播放列表,playlist是QMediaPlaylist类型 |
playlist() | 返回设置的播放列表,返回值为QMediaPlaylist类型 |
state() | 返回播放器当前的状态,返回值是枚举类型QMediaPlayer.State |
setVolume(volume) | 设置播放音量,int型参数volume的值在0~100 |
setPlaybackRate(rate) | 设置播放速度,float型参数rate默认为1,表示正常速度 |
setMedia(media) | 设置播放媒体文件,参数media为QMediaContent类型 |
currentMedia() | 返回当前播放的媒体文件,返回值为QMediaContent类型 |
play() | 开始播放 |
pause() | 暂停播放 |
stop() | 停止播放 |
QMediaPlayer有几个有用的信号可以反映播放状态或文件信息。
(1)stateChanged(state)信号在调用play()、pause()、stop()函数时发射,反映播放器当前的状态。参数state是枚举类型QMediaPlayer.State,该枚举类型有3种取值,表示播放器的状态:
QMediaPlayer.StoppedState(停止状态);
QMediaPlayer.PlayingState(正在播放状态);
QMediaPlayer.PausedState(暂停播放状态)。
(2)durationChanged(duration)信号在文件的时间长度变化时发射,一般在切换播放文件时发射。参数duration是int类型,表示文件持续时间长度,单位ms。
(3)positionChanged(position)信号在当前文件播放位置变化时发射,可以反映文件播放进度。参数position是int类型,单位ms。
QMediaPlayer可以通过setMedia()函数设置播放单个文件,也可以通过setPlaylist()函数设置一个QMediaPlaylist类对象表示的播放列表,对列表文件进行播放,并且设置自动播放下一个文件或循环播放等。QMediaPlayer播放的文件可以是本地文件,也可以是网络上的文件。
QMediaPlaylist记录播放媒体文件信息,可以添加、移除文件,可以设置循环播放模式,在列表文件中自动切换文件,在当前播放文件切换时发射currentIndexChanged()信号和currentMediaChanged()信号。
2.窗口业务逻辑类初始化
示例就是用QMediaPlayer和QMediaPlaylist设计的一个音乐播放器,其运行时界面如图所示。
窗口中间是一个QListWidget组件,上面的3个按钮用于文件管理,为播放列表添加、移除和清空文件,列表下方是播放控制按钮、音量控制、进度显示和控制。窗口是基于QMainWindow的,MainWindow.ui的界面可视化设计结果见示例源文件。
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow,QFileDialog,QListWidgetItem
from PyQt5.QtCore import pyqtSlot,QUrl,QModelIndex,QDir,QFileInfo
from PyQt5.QtGui import QIcon
from PyQt5.QtMultimedia import QMediaPlayer, QMediaPlaylist,QMediaContent
from ui_MainWindow import Ui_MainWindow
class QmyMainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent) #调用父类构造函数,创建窗体
self.ui=Ui_MainWindow() #创建UI对象
self.ui.setupUi(self) #构造UI界面
## self.setWindowTitle("Demo10_1,音乐播放器")
self.player = QMediaPlayer(self)
self.playlist = QMediaPlaylist(self)
self.player.setPlaylist(self.playlist)
self.playlist.setPlaybackMode(QMediaPlaylist.Loop) #循环模式
self.__duration="" #文件总时间长度
self.__curPos="" #当前播放到位置
self.player.stateChanged.connect(self.do_stateChanged)
self.player.positionChanged.connect(self.do_positionChanged)
self.player.durationChanged.connect(self.do_durationChanged)
self.playlist.currentIndexChanged.connect(self.do_currentChanged)
在构造函数里创建了播放器self.player,创建了播放列表self.playlist,并为播放器设置了播放列表。
QMediaPlaylist类的setPlaybackMode(mode)函数可以设置播放列表的循环方式,参数mode是枚举类型.
QMediaPlaylist.PlaybackMode,有以下几种取值:
QMediaPlaylist.CurrentItemOnce(当前曲目只播放一次);
QMediaPlaylist.CurrentItemInLoop(当前曲目循环播放);
QMediaPlaylist.Sequential(从当前曲目开始顺序播放至列表结尾,然后结束播放);
QMediaPlaylist.Loop(列表循环播放);
QMediaPlaylist.Random(随机播放)。
构造函数里还创建了两个私有变量self.__duration和self.__curPos,分别表示文件总时间长度和当前播放的位置,用于在文件切换和播放进度变化时显示进度信息。
程序为播放器的 3 个信号设置了关联的槽函数,这 3 个信号的意义在前面已经说明。QMediaPlaylist的信号currentIndexChanged(position)在播放列表的当前曲目发生变化时发射,也为此信号设置了关联的槽函数。这4个自定义槽函数的代码:
## =============自定义槽函数===============================
def do_stateChanged(self,state): ##播放器状态变化
self.ui.btnPlay.setEnabled(state!=QMediaPlayer.PlayingState)
self.ui.btnPause.setEnabled(state==QMediaPlayer.PlayingState)
self.ui.btnStop.setEnabled(state==QMediaPlayer.PlayingState)
def do_positionChanged(self,position): ##当前文件播放位置变化,更新进度显示
if (self.ui.sliderPosition.isSliderDown()): #在拖动滑块调整进度
return
self.ui.sliderPosition.setSliderPosition(position)
secs=position/1000 #秒
mins=secs/60 #分钟
secs=secs % 60 #余数秒
self.__curPos="%d:%d"%(mins,secs)
self.ui.LabRatio.setText(self.__curPos+"/"+self.__duration)
def do_durationChanged(self,duration): ##文件时长变化
self.ui.sliderPosition.setMaximum(duration)
secs=duration/1000 #秒
mins=secs/60 #分钟
secs=secs % 60 #余数秒
self.__duration="%d:%d"%(mins,secs)
self.ui.LabRatio.setText(self.__curPos+"/"+self.__duration)
def do_currentChanged(self,position): ##playlist当前曲目变化
self.ui.listWidget.setCurrentRow(position)
item=self.ui.listWidget.currentItem() #QListWidgetItem
if (item != None):
self.ui.LabCurMedia.setText(item.text())
3.播放列表的控制
窗口中间以一个QListWidget组件显示播放的文件列表,界面上显示的文件列表与self.playlist存储的文件列表保持同步。
窗口上方有“添加”“移除”“清空”3个按钮用于播放列表管理,下方有“上一曲目”和“下一曲目”按钮用于曲目移动,在窗体中间的QListWidget组件上双击某个项可以播放该曲目。这些按钮和组件的相关槽函数的代码:
## ==========由connectSlotsByName() 自动连接的槽函数==================
# 播放列表管理
@pyqtSlot() ##添加文件
def on_btnAdd_clicked(self):
## curPath=os.getcwd() #获取系统当前目录
## curPath=QDir.homePath()
curPath=QDir.currentPath()
dlgTitle="选择音频文件"
filt="音频文件(*.mp3 *.wav *.wma);;所有文件(*.*)"
fileList,flt=QFileDialog.getOpenFileNames(self,dlgTitle,curPath,filt)
count=len(fileList)
if count<1:
return
filename=fileList[0]
fileInfo=QFileInfo(filename) #文件信息
QDir.setCurrent(fileInfo.absolutePath()) #重设当前路径
for i in range(count):
filename=fileList[i]
fileInfo.setFile(filename)
song=QMediaContent(QUrl.fromLocalFile(filename))
self.playlist.addMedia(song) #添加播放媒体
## basename=os.path.basename(filename) #文件名和后缀
basename=fileInfo.baseName()
self.ui.listWidget.addItem(basename) #添加到界面文件列表
if (self.player.state()!=QMediaPlayer.PlayingState):
self.playlist.setCurrentIndex(0)
self.player.play()
@pyqtSlot() ##移除一个文件
def on_btnRemove_clicked(self):
pos=self.ui.listWidget.currentRow()
item=self.ui.listWidget.takeItem(pos) #python会自动删除
if (self.playlist.currentIndex()==pos): #是当前播放的曲目
nextPos=0
if pos>=1:
nextPos=pos-1
self.playlist.removeMedia(pos) #从播放列表里移除
if self.ui.listWidget.count()>0: #剩余个数
self.playlist.setCurrentIndex(nextPos)
self.do_currentChanged(nextPos) #当前曲目变化
else:
self.player.stop()
self.ui.LabCurMedia.setText("无曲目")
else:
self.playlist.removeMedia(pos)
@pyqtSlot() ##清空播放列表
def on_btnClear_clicked(self):
self.playlist.clear() #清空播放列表
self.ui.listWidget.clear()
self.player.stop() #停止播放
## @pyqtSlot() ##双击时切换播放文件
def on_listWidget_doubleClicked(self,index):
rowNo=index.row() #行号
self.playlist.setCurrentIndex(rowNo)
self.player.play()
# 播放控制
@pyqtSlot() ##播放
def on_btnPlay_clicked(self):
if (self.playlist.currentIndex()<0):
self.playlist.setCurrentIndex(0)
self.player.play()
@pyqtSlot() ##暂停
def on_btnPause_clicked(self):
self.player.pause()
@pyqtSlot() ##停止
def on_btnStop_clicked(self):
self.player.stop()
@pyqtSlot() ##上一曲目
def on_btnPrevious_clicked(self):
self.playlist.previous()
@pyqtSlot() ##下一曲目
def on_btnNext_clicked(self):
self.playlist.next()
@pyqtSlot() ##静音控制
def on_btnSound_clicked(self):
mute=self.player.isMuted()
self.player.setMuted(not mute)
if mute:
self.ui.btnSound.setIcon(QIcon(":/icons/images/volumn.bmp"))
else:
self.ui.btnSound.setIcon(QIcon(":/icons/images/mute.bmp"))
@pyqtSlot(int) ##调节音量
def on_sliderVolumn_valueChanged(self,value):
self.player.setVolume(value)
@pyqtSlot(int) ##文件进度调控
def on_sliderPosition_valueChanged(self,value):
self.player.setPosition(value)
这里主要用到QMediaPlaylist类的以下几个函数。
(1)addMedia(content)函数:用于添加一个播放媒体content。
参数content是QMediaContent类型,这是用于表示媒体资源的类。一个媒体资源可以是本地音频或视频文件,也可以是网络上的资源。程序里创建播放媒体并添加到播放列表的语句是:
song=QMediaContent(QUrl.fromLocalFile(filename))
self.playlist.addMedia(song) #添加播放媒体
(2)QUrl.fromLocalFile(filename)的功能是使用QUrl的类函数fromLocalFile(),根据文件名filename创建一个指向此文件的QUrl对象。此QUrl对象传递给QMediaContent的构造函数,创建一个播放媒体song。所以song是指向本地文件的一个QMediaContent对象,再通过QMediaPlaylist.addMedia()函数添加到播放列表。
(3)removeMedia(pos)函数:从播放列表中移除序号为pos的一个项。
(4)clear()函数,用于清空播放列表。
(5)setCurrentIndex(pos)函数:设置序号pos的项为当前播放的媒体。
(6)previous()函数和 next()函数:在播放列表中前移和后移,移动时播放列表会发射信号currentIndexChanged(),从而自动更新界面组件listWidget里的当前条目。
4.播放控制
播放、暂停或停止播放器,只需调用QMediaPlayer相应的函数即可,界面上3个按钮的槽函数代码:
播放器的播放状态变化时会发射stateChanged()信号,在关联的自定义槽函数do_stateChanged()里更新3个按钮的使能状态。
音量控制由一个“静音”按钮和音量滑动条控制。
文件播放进度条在do_durationChanged()和do_positionChanged()两个自定义槽函数里会更新,显示当前文件播放进度。拖动滑动条的滑块可以设置文件播放位置。
程序运行测试时发现,如果正在播放音乐时退出程序,并不会自动停止播放(用Qt C++编写的相同程序是自动停止的),所以再为窗口的closeEvent()事件函数填写代码,停止音乐播放.
## ===============event 处理函数==========
def closeEvent(self,event): ##窗体关闭时
## 窗口关闭时不能自动停止播放,需手动停止
if (self.player.state()==QMediaPlayer.PlayingState):
self.player.stop()