Python中如何使用PyQt5创建一个支持播放任意格式音乐的播放器?

screenshot on GitHub

特性

  • 可以在 Linux,macOS,Windows 上使用
  • 安装简单,新手友好,默认提供国内各音乐平台插件(网易云、虾米、QQ )
  • 较强的可扩展性可以满足大家折腾的欲望 | MPRIS2 插件 | 和 Emacs 集成、纯命令行使用 视频 DEMO
  • 核心模块有较好文档和测试覆盖,欢迎大家参与开发 | 详细文档 | 开发者 /用户交流群

安装使用

安装过程十分简单,这里以 Ubuntu 为例。其他系统安装使用方法请参考 Quick Start

# Ubuntu 用户可以依次执行以下命令进行安装
sudo apt-get install python3-pyqt5  # 安装 Python PyQt5 依赖包
sudo apt-get install libmpv1        # 安装 libmpv1 系统依赖

pip3 install ‘feeluown>=3.0’ --user -i https://pypi.org/simple/

feeluown-genicon # 生成图标,点击桌面图标就可以运行 feeluown # 不想生成图标,也可以选择直接在命令行中运行


欢迎对此项目有兴趣的童鞋加入开发

这是今天发帖的重点,嘿嘿 ~

  • 如果你了解 Python,对 Python 最佳实践感兴趣
  • 如果你对怎样开发一个音乐播放器感兴趣
  • 如果你对怎样写一门自己的玩具语言感兴趣
  • 如果你对初级网络编程感兴趣
  • ...

欢迎加入我们这个小群体 ~ 目前,我们这里有刚毕业的高中生,有刚毕业的研究生,也有已经工作的 Python 工程师...

在这里,你 可能 可以收获:

  • 一群友善、有趣的朋友
  • 一个好的 Python 学习交流环境
  • 在实际项目提升自己 Python 技能
  • 定制一个自己喜欢的播放器
  • ...

如果你对项目、我们还心存疑惑,你可以阅读文档 或者加入开发者 /用户交流群来了解更多


Python中如何使用PyQt5创建一个支持播放任意格式音乐的播放器?

42 回复

这个太厉害了!


import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, 
                             QLabel, QFileDialog, QSlider, QHBoxLayout, 
                             QVBoxLayout, QWidget)
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtGui import QFont

class MusicPlayer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.player = QMediaPlayer()
        self.init_ui()
        self.connect_signals()
        
    def init_ui(self):
        self.setWindowTitle('PyQt5音乐播放器')
        self.setGeometry(300, 300, 400, 200)
        
        # 创建控件
        self.title_label = QLabel('未选择文件')
        self.title_label.setFont(QFont('Arial', 12))
        self.title_label.setAlignment(Qt.AlignCenter)
        
        self.play_btn = QPushButton('播放')
        self.pause_btn = QPushButton('暂停')
        self.stop_btn = QPushButton('停止')
        self.open_btn = QPushButton('打开文件')
        
        self.volume_slider = QSlider(Qt.Horizontal)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(50)
        self.volume_label = QLabel('音量: 50%')
        
        self.position_slider = QSlider(Qt.Horizontal)
        self.position_slider.setRange(0, 100)
        
        # 布局
        control_layout = QHBoxLayout()
        control_layout.addWidget(self.play_btn)
        control_layout.addWidget(self.pause_btn)
        control_layout.addWidget(self.stop_btn)
        control_layout.addWidget(self.open_btn)
        
        volume_layout = QHBoxLayout()
        volume_layout.addWidget(self.volume_label)
        volume_layout.addWidget(self.volume_slider)
        
        main_layout = QVBoxLayout()
        main_layout.addWidget(self.title_label)
        main_layout.addWidget(self.position_slider)
        main_layout.addLayout(control_layout)
        main_layout.addLayout(volume_layout)
        
        container = QWidget()
        container.setLayout(main_layout)
        self.setCentralWidget(container)
        
    def connect_signals(self):
        self.play_btn.clicked.connect(self.player.play)
        self.pause_btn.clicked.connect(self.player.pause)
        self.stop_btn.clicked.connect(self.player.stop)
        self.open_btn.clicked.connect(self.open_file)
        self.volume_slider.valueChanged.connect(self.set_volume)
        self.position_slider.sliderMoved.connect(self.set_position)
        
        self.player.positionChanged.connect(self.update_position)
        self.player.durationChanged.connect(self.update_duration)
        self.player.stateChanged.connect(self.update_buttons)
        
    def open_file(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, 
            '选择音乐文件',
            '',
            '音频文件 (*.mp3 *.wav *.flac *.ogg *.m4a *.aac);;所有文件 (*.*)'
        )
        
        if file_path:
            media_content = QMediaContent(QUrl.fromLocalFile(file_path))
            self.player.setMedia(media_content)
            self.title_label.setText(file_path.split('/')[-1])
            self.play_btn.setEnabled(True)
            
    def set_volume(self, value):
        self.player.setVolume(value)
        self.volume_label.setText(f'音量: {value}%')
        
    def set_position(self, position):
        if self.player.duration() > 0:
            self.player.setPosition(position)
            
    def update_position(self, position):
        if self.player.duration() > 0:
            self.position_slider.setValue(position)
            
    def update_duration(self, duration):
        self.position_slider.setRange(0, duration)
        
    def update_buttons(self, state):
        self.play_btn.setEnabled(state != QMediaPlayer.PlayingState)
        self.pause_btn.setEnabled(state == QMediaPlayer.PlayingState)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    player = MusicPlayer()
    player.show()
    sys.exit(app.exec_())

这个播放器核心是QMediaPlayer,它通过系统底层多媒体框架支持多种格式。关键点:

  1. 格式支持QMediaPlayer依赖本地系统的解码器,在Windows上通过DirectShow、Linux通过GStreamer、macOS通过AVFoundation来解码。只要系统能播放的格式(如MP3、WAV、FLAC、OGG、AAC等),播放器就能支持。

  2. 文件选择:使用QFileDialoggetOpenFileName方法,过滤器设置为常见的音频格式,同时保留*.*选项尝试播放任意文件。

  3. 播放控制:基础的播放/暂停/停止功能直接调用QMediaPlayer的对应方法。

  4. 进度和音量:通过两个QSlider分别控制播放进度和音量,实时更新显示。

  5. 状态同步:连接positionChangeddurationChanged信号来同步进度条,stateChanged信号用于更新按钮状态。

要增加更多功能(如播放列表、均衡器等),可以在此基础上扩展。注意不同系统对音频格式的支持可能略有差异。

一句话建议:用QMediaPlayer配合系统解码器是最简单的跨平台方案。

能播放 qq 音乐的付费歌曲吗?比如杰伦的

不错哦
pyqt5 好学吗?

杰伦的可以… 嘘,比如晴天,告白气球

图片里面正好是杰伦的

ummm,怎么说呢,PyQt5 入门比 Python 入门差不多吧~ 我感觉不难,但是要深入的话,我感觉是有挑战的。

赶紧顶一顶

可以试用起来~ 有问题随时联系我 ~

赞~ 开源不?来个 GitHub 链接?

补充了链接,开始忘记放了 ~

之前都有类似这种,好像都会有法律函过来。。。

有没有云村日推

暂时还不支持 =。=

很早以前有,不过后来在一次更新中删掉了… 如果会写 Python,加上去应该只有个把小时

嗯,我之前看过几个项目,确实遇到了这个问题…

在 FeelUOwn 这个项目里面,我们有想过怎样应对这个问题:

一方面是从版权方面考虑,我们尽量少损害产商的利益:
1. 我们会声明每首歌的来源地,如上截图,每首歌都有来源
2. 优先用 web 的资源,人家收费的资源,我们 尽量 不使用

另一方面是技术方面:
1. FeelUOwn 项目本身并没有包含 QQ/虾米 /网易云 等厂商的任何资源或者敏感信息,这些东西都是以插件的形式存在于民间
2. 如果厂商真的发送法律函等资料过来,我们可以取消使用这一个插件,到时,也不会太影响整体使用

前段时间刚好要用 python 搞个界面,学的也是 pyqt,现在看见这个界面控件风格好眼熟哈哈哈

这个厉害了!我也有过类似的想法,不过初衷是想在各大平台上训练一个自己的推荐算法

我曾经想给自己定制个推荐算法,但是…不太懂推荐系统那一套…也没怎么调研过 =。=

windows 党安装劝退…就不能用 pyinstaller 打个包么?

可以,但是目前还没有足够时间做这个工作…之前有朋友试过 pyinstaller,确实可以打包成功 ~


然后你会发现,我还是下载个网易云音乐好了( qt 依赖打包起来简直和 electron 差不多)

嗯,这也是一个问题。项目依赖了 PyQt 和 mpv,打包之后会有大约 150MB 左右~ 会比较大

通过打包来安装的话,就意味着以后每次更新都要打包,用户每次都要重新下载整个包,更新相对来说那么不方便,所以综合考虑,暂时只能让大家安装的时候折腾一下了~

File “\fuocore<a target=”_blank" href=“http://mpvplayer.py” rel=“nofollow noopener”>mpvplayer.py", line 4, in <module>
from mpv import (
File “<a target=”_blank" href=“http://mpv.py” rel=“nofollow noopener”>mpv.py", line 39, in <module>
backend = CDLL(‘mpv-1.dll’)
File <a target="_blank" href=“http://init.py” rel=“nofollow noopener”>init.py", line 348, in init
self._handle = _dlopen(self._name, mode)
OSError: [WinError 126] 找不到指定的模块。

好像因为安装顺序的原因(最后一步才放了 dll ),无法运行了怎么办……

如果确认 mpv-1.dll 放对位置了,那只需要重新运行就好了~

好像前几年搜索网易云音乐 linux 版搜出了这个,没想到这么些年了还在蓬勃发展

你木有记错,哈哈哈,不过它也不是蓬勃发展啦。ummm,我想想,“垂死挣扎”可能更形象。

好评!海外自动 geoblock 解锁

资源来源跟 Listen1 比起来如何。


pyqt 虽然简单,但打包太大真的很头痛。我试过纯 qt,但效率太低也弃坑了。最终还是选择了 js+electron

qt5.13 增加了 wasm 的支持,可以在 web 端运行 qt,不知道 pyqt 后续会不会增加支持

看了下 listen1 的来源,它的更多一些 ~ 它多支持了两个来源:酷狗和酷我。这边目前只支持 QQ/虾米 /网易云(都是插件形式),但是我想这三个平台应该能覆盖几乎所有的歌曲了吧(毕竟酷狗和酷我都是 tencent 家的,另外,要新增来源是一件相对容易的事情,如果真的需要的话)。

这个我觉得自己玩玩还可以,做大了必然被告,厂商甘心买版权为其他人做嫁衣么?

大佬🐂🍺


嗯嗯,是这样子的,如果你侵犯了产商的权益,他就来搞你,但是如果你尊重他,他就睁一只眼,闭一只眼。
当然,什么事情都有例外和意外。

针对这些例外和意外,我们在 FeelUOwn 上也想了一些办法:FeelUOwn 本身只是个播放器,不过它可以支持多个源,每个源都是一个插件。当一个源不行的时候,我们可以就放弃他。目前 FeelUOwn 支持国内三个源,以后还可以考虑 iTunes, Spotify, Google Music 等。所以除非每个源都来搞我们,不然这个播放器就不会死(另外值得一说的是:我们确实有注意 尽量不过分 的侵犯他们的权益,我在上面一个回答里面也解释了这个问题)。

最差最差的情况:每个源都不能用,那时候,我们还可以听本地音乐了;可以通过搜索引擎来找资源。

最后,值得一说的是,支持多资源只是 FeelUOwn 众多特性中的一个。

老师好 ~

ubuntu19.04,apt 装 pyqt5 报错 ModuleNotFoundError: No module named ‘PyQt5.QtOpenGL’,应该是版本太老,pip3 install pyqt5 --upgrade 可以解决

RuntimeError: generator raised StopIteration 这个报错应该解决一下。。

装 fcitx-frontend-qt5,fcitx 输入法也用不了

你那个群咋回事 刚加就 T



不好意思,这里忘记写这个步骤了 sudo apt-get install python3-pyqt5.qtopengl,另外,在 Ubuntu 上,更推荐使用 apt 来安装 PyQt5。

> 装 fcitx-frontend-qt5,fcitx 输入法也用不了

ummm,这个问题,我也不是很确定。
我觉得可以先尝试用 apt 安装试试,把 pip 安装的给卸载了。如果还是不行的话,我今天装个虚拟机也试试。

不好意思,你是 id 为 4a6f5a1**** 的那位童鞋么,我以为这个 ID 是个 spam… 我拉您回来 ~

咦,不对,我没有权限拉您回来… =。= 不好意思,能麻烦您再加一下吗 ~

回到顶部