python實現的音樂播放器
音樂播放器,原來寫過一個簡陋的例子,可見
https://blog.csdn.net/cnds123/article/details/137874107
那個不能拖動播放進度條上的滑塊到新的位置播放。下面介紹的可以拖動播放進度條上的滑塊到新的位置播放。
簡單實用的音樂播放器
這個簡單實用的音樂播放器,運行界面:
需要安裝,PyQt6這個第三方庫。
源碼如下:
import sys
import os
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel, QPushButton, QSlider, QHBoxLayout, QVBoxLayout, QFileDialog, QListWidget, QListWidgetItem
)
from PyQt6.QtCore import Qt, QTimer, QUrl
from PyQt6.QtGui import QPixmap, QPainter, QTransform, QPainterPath
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutputclass MP3Player(QWidget):def __init__(self):super().__init__()self.song_list = []self.current_index = -1self.init_ui()self.setup_player()def init_ui(self):self.setWindowTitle("音樂播放器 V1.0.2")self.setGeometry(300, 300, 600, 400)# 控件初始化self.playlist = QListWidget()self.playlist.itemDoubleClicked.connect(self.play_selected)self.play_btn = QPushButton("?")self.prev_btn = QPushButton("?")self.next_btn = QPushButton("?")self.stop_btn = QPushButton("?")self.slider = QSlider(Qt.Orientation.Horizontal)self.time_label = QLabel("00:00 / 00:00")# 按鈕樣式btn_style = """QPushButton {font-size: 20px;min-width: 40px;min-height: 40px;border-radius: 20px;background: #666;color: white;}QPushButton:hover { background: #09f; }"""for btn in [self.play_btn, self.prev_btn, self.next_btn, self.stop_btn]:btn.setStyleSheet(btn_style)# 布局control_layout = QHBoxLayout()control_layout.addWidget(self.prev_btn)control_layout.addWidget(self.play_btn)control_layout.addWidget(self.next_btn)control_layout.addWidget(self.stop_btn)main_layout = QVBoxLayout()main_layout.addWidget(self.playlist)main_layout.addWidget(self.slider)main_layout.addWidget(self.time_label)main_layout.addLayout(control_layout)# 功能按鈕buttons_layout = QHBoxLayout()# 添加文件按鈕add_file_btn = QPushButton("添加文件")add_file_btn.clicked.connect(self.add_files)buttons_layout.addWidget(add_file_btn)# 刪除文件按鈕delete_file_btn = QPushButton("刪除文件")delete_file_btn.clicked.connect(self.delete_file)buttons_layout.addWidget(delete_file_btn)# 幫助按鈕help_btn = QPushButton("幫助")help_btn.clicked.connect(self.show_help)buttons_layout.addWidget(help_btn)main_layout.addLayout(buttons_layout)# 添加音量控制self.volume_slider = QSlider(Qt.Orientation.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(70) # 默認音量70%self.volume_slider.valueChanged.connect(self.change_volume)volume_layout = QHBoxLayout()volume_layout.addWidget(QLabel("音量:"))volume_layout.addWidget(self.volume_slider)main_layout.addLayout(volume_layout)self.setLayout(main_layout)# 連接信號self.play_btn.clicked.connect(self.toggle_play)self.prev_btn.clicked.connect(self.play_prev)self.next_btn.clicked.connect(self.play_next)self.stop_btn.clicked.connect(self.stop)self.slider.sliderMoved.connect(self.seek_position)def delete_file(self):# 獲取當前選中的項目current_items = self.playlist.selectedItems()if not current_items:return# 逐一刪除所選項目for item in current_items:index = self.playlist.row(item)# 如果刪除的是正在播放的歌曲,先停止播放if index == self.current_index:self.player.stop()self.current_index = -1# 從列表和界面中刪除項目self.playlist.takeItem(index)self.song_list.pop(index)# 如果正在播放的歌曲在被刪除的歌曲之后,需要調整索引if index < self.current_index:self.current_index -= 1# 如果刪除后列表為空,重置界面if not self.song_list:self.time_label.setText("00:00 / 00:00")self.slider.setValue(0)self.update_play_button(QMediaPlayer.PlaybackState.StoppedState)def show_help(self):# 創建幫助消息help_text = """<h3>音樂播放器使用幫助</h3><p><b>播放控制:</b></p><ul><li>播放/暫停:點擊 ?/? 按鈕</li><li>上一首:點擊 ? 按鈕</li><li>下一首:點擊 ? 按鈕</li><li>停止:點擊 ? 按鈕</li></ul><p><b>播放列表:</b></p><ul><li>添加文件:點擊"添加文件"按鈕</li><li>刪除文件:選擇文件后點擊"刪除文件"按鈕</li><li>播放指定歌曲:雙擊列表中的歌曲</li></ul><p><b>其他控制:</b></p><ul><li>調整進度:拖動進度條</li><li>調整音量:拖動音量滑塊</li></ul>"""# 導入需要的組件from PyQt6.QtWidgets import QMessageBox# 顯示幫助對話框help_dialog = QMessageBox(self)help_dialog.setWindowTitle("幫助")help_dialog.setTextFormat(Qt.TextFormat.RichText)help_dialog.setText(help_text)help_dialog.setIcon(QMessageBox.Icon.Information)help_dialog.exec()def change_volume(self, value):self.audio_output.setVolume(value / 100.0)def setup_player(self):self.player = QMediaPlayer()self.audio_output = QAudioOutput()self.audio_output.setVolume(0.7) # 默認音量設置self.player.setAudioOutput(self.audio_output)# 定時器更新進度self.timer = QTimer()self.timer.timeout.connect(self.update_progress)self.timer.start(1000)# 播放狀態變化self.player.playbackStateChanged.connect(self.update_play_button)# 添加媒體結束時的信號連接self.player.mediaStatusChanged.connect(self.handle_media_status_change)# 添加媒體時長變化的信號連接self.player.durationChanged.connect(self.duration_changed)self.player.errorOccurred.connect(self.handle_error)## def handle_media_status_change(self, status):
## # 使用 QTimer.singleShot 來避免潛在的遞歸調用或信號沖突
## if status == QMediaPlayer.MediaStatus.EndOfMedia:
## QTimer.singleShot(10, self.play_next)def handle_media_status_change(self, status):# 僅當媒體結束且不是暫停狀態時處理if status == QMediaPlayer.MediaStatus.EndOfMedia:# 檢查是否只有一首歌曲if len(self.song_list) == 1:# 只有一首歌曲時,重置到開始位置而不是嘗試播放"下一首"self.player.setPosition(0)self.player.stop()self.update_play_button(QMediaPlayer.PlaybackState.StoppedState)# 重新播放if self.player.position() >= self.player.duration() - 100 and self.player.duration() > 0:self.player.setPosition(0)self.player.play()else:# 多首歌曲時,播放下一首QTimer.singleShot(10, self.play_next)def add_files(self):files, _ = QFileDialog.getOpenFileNames(self, "選擇音頻文件", "", "音頻文件 (*.mp3 *.wav *.flac)")for file in files:if file not in self.song_list:self.song_list.append(file)self.playlist.addItem(os.path.basename(file))def play_selected(self, item):self.current_index = self.playlist.row(item)self.play()def duration_changed(self, duration):self.slider.setRange(0, duration)def handle_error(self, error, error_string):print(f"播放器錯誤: {error_string}")def play(self):if self.current_index < 0 and self.song_list:self.current_index = 0if 0 <= self.current_index < len(self.song_list):# 高亮當前播放的歌曲self.playlist.setCurrentRow(self.current_index)try:file = self.song_list[self.current_index]self.player.setSource(QUrl.fromLocalFile(file))self.player.play()except Exception as e:print(f"播放錯誤: {e}")def toggle_play(self):if self.player.isPlaying():self.player.pause()else:if self.player.position() == self.player.duration():self.play()else:self.player.play()def update_play_button(self, state):if state == QMediaPlayer.PlaybackState.PlayingState:self.play_btn.setText("?")else:self.play_btn.setText("?")def update_progress(self):duration = self.player.duration()if duration > 0: # 確保時長大于0current = self.player.position()self.slider.setValue(int(current))self.time_label.setText(f"{self.format_time(current)} / {self.format_time(duration)}")def seek_position(self, position):self.player.setPosition(position)def play_prev(self):if self.song_list:self.current_index = (self.current_index - 1) % len(self.song_list)self.play()def play_next(self):if not self.song_list:return# 先停止當前播放self.player.stop()# 然后切換到下一首self.current_index = (self.current_index + 1) % len(self.song_list)# 使用短延遲來確保狀態已正確更新QTimer.singleShot(50, self.play)def stop(self):self.player.stop()self.slider.setValue(0)self.time_label.setText("00:00 / 00:00")def format_time(self, ms):seconds = ms // 1000minutes = seconds // 60seconds = seconds % 60return f"{minutes:02d}:{seconds:02d}"if __name__ == "__main__":app = QApplication(sys.argv)player = MP3Player()player.show()sys.exit(app.exec())
專業級別的音樂播放器
下面這個音樂播放器,源碼來源于網絡,適當修改,轉載記錄于此。
需要安裝PyQt6、python-vlc、mutagen 這3個第三方庫。
還需要安裝 VLC 播放器(python-vlc 是 VLC 的 Python 綁定,需依賴系統安裝的 VLC),訪問 VLC 官網Official download of VLC media player, the best Open Source player - VideoLAN ,下載并安裝對應系統的版本。否則 程序運行時提示 vlc.dll not found。
運行效果:
源碼如下:
import sys
import os
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel, QPushButton, QSlider, QHBoxLayout, QVBoxLayout, QGridLayout, QFileDialog, QListWidget, QListWidgetItem, QMenu
)
from PyQt6.QtCore import Qt, QTimer, QUrl, QByteArray, pyqtSignal
from PyQt6.QtGui import QPixmap, QPainter, QTransform, QPainterPath, QFont, QColor, QLinearGradient, QBrush, QPen, QCursor, QScreen
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
import vlc
import redef resource_path(relative_path):if hasattr(sys, '_MEIPASS'):return os.path.join(sys._MEIPASS, relative_path)return os.path.join(os.path.abspath("."), relative_path)# 旋轉封面控件
class RotatingCover(QLabel):def __init__(self, song_path, default_cover="fm.png"):super().__init__()self.angle = 0self.pixmap = self.load_cover(song_path, default_cover)if self.