From 258b2ad3e0fc15c59e8d49ecbcd91dd5f85fd46d Mon Sep 17 00:00:00 2001 From: Cx330 <1487537121@qq.com> Date: Sun, 16 Mar 2025 15:00:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=85=E5=AE=9E=E7=8E=B0=E9=9F=B3=E4=B9=90?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E3=80=81=E6=AD=8C=E8=AF=8D=E5=92=8C=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E5=B1=95=E7=A4=BA=EF=BC=88=E9=A1=BB=E6=8C=82=E8=BD=BD?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- musicplayer.py | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 musicplayer.py diff --git a/musicplayer.py b/musicplayer.py new file mode 100644 index 0000000..a12ddea --- /dev/null +++ b/musicplayer.py @@ -0,0 +1,223 @@ +import sys +import os +import pygame +import time +import re +from PyQt6.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, + QPushButton, QSlider, QHBoxLayout +) +from PyQt6.QtCore import QTimer, Qt, QSize, QPropertyAnimation, QPoint +from PyQt6.QtGui import QPixmap, QIcon, QPalette, QColor + +class MusicPlayer(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Python 音乐播放器") + self.setGeometry(300, 200, 400, 500) + self.initUI() + pygame.mixer.init() + + self.lyrics = [] # 解析后的歌词列表 [(时间戳, 歌词)] + self.current_lyric_index = 0 # 当前歌词索引 + self.is_playing = False # 播放状态 + self.music_length = 0 # 音乐总时长(秒) + self.start_time = 0 # 播放开始时间(用于计算已播放时间) + self.pause_time = 0 # 暂停时记录的播放进度(秒) + self.animation = None # 存储滚动动画对象 + self.last_lyric = None # 上一次显示的歌词,用于避免重复启动动画 + self.load_lyrics() + + def initUI(self): + self.central_widget = QWidget() + self.setCentralWidget(self.central_widget) + self.layout = QVBoxLayout() + self.layout.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.central_widget.setLayout(self.layout) + + # 设置背景颜色 + self.set_background_color("#2E2E2E") + + # 封面 + self.cover_label = QLabel() + self.layout.addWidget(self.cover_label, alignment=Qt.AlignmentFlag.AlignCenter) + self.load_cover() + + # 歌词显示区域(固定大小容器,便于动画处理,不随内容改变大小) + self.lyrics_container = QWidget() + self.lyrics_container.setFixedSize(300, 50) + self.lyrics_container.setStyleSheet("background-color: transparent;") + # 歌词标签放置在容器中 + self.lyrics_browser = QLabel("", self.lyrics_container) + self.lyrics_browser.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.lyrics_browser.setStyleSheet("font-size: 24px; color: white; font-weight: bold;") + self.lyrics_browser.setFixedHeight(50) + self.lyrics_browser.move(0, 0) + self.layout.addWidget(self.lyrics_container, alignment=Qt.AlignmentFlag.AlignCenter) + + # 进度条 + self.slider = QSlider(Qt.Orientation.Horizontal) + self.slider.setRange(0, 100) + self.slider.sliderPressed.connect(self.pause_music) + self.slider.sliderReleased.connect(self.seek_music) + self.layout.addWidget(self.slider) + + # 播放控制区域 + self.control_layout = QHBoxLayout() + self.control_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.play_button = QPushButton() + self.play_button.setIcon(QIcon("D:/Downloads/play.png")) + self.play_button.setIconSize(QSize(52, 52)) # 明确设置图标尺寸 + self.play_button.setStyleSheet("border: none;") + self.play_button.clicked.connect(self.toggle_play_pause) + self.control_layout.addWidget(self.play_button) + self.layout.addLayout(self.control_layout) + + # 定时器,每500毫秒更新一次进度和歌词 + self.timer = QTimer(self) + self.timer.timeout.connect(self.update_lyrics_and_progress) + + def set_background_color(self, color): + palette = self.palette() + palette.setColor(QPalette.ColorRole.Window, QColor(color)) + self.setPalette(palette) + self.central_widget.setStyleSheet(f"background-color: {color};") + + def load_cover(self): + cover_path = "D:/Downloads/cover.jpg" + if os.path.exists(cover_path): + pixmap = QPixmap(cover_path) + self.cover_label.setPixmap(pixmap.scaled(256, 256, Qt.AspectRatioMode.KeepAspectRatio)) + else: + self.cover_label.setText("封面未找到") + + def load_lyrics(self): + lyrics_path = "D:/Downloads/林俊杰-光阴副本.lrc" + if os.path.exists(lyrics_path): + try: + with open(lyrics_path, 'r', encoding='utf-8') as file: + lines = file.readlines() + self.lyrics = [] + for line in lines: + match = re.match(r"\[(\d+):(\d+\.\d+)](.*)", line) + if match: + minutes = int(match.group(1)) + seconds = float(match.group(2)) + timestamp = minutes * 60 + seconds + lyrics_text = match.group(3).strip() + self.lyrics.append((timestamp, lyrics_text)) + self.lyrics.sort() + except Exception as e: + self.lyrics_browser.setText("加载歌词出错: " + str(e)) + else: + self.lyrics_browser.setText("歌词文件未找到") + + def toggle_play_pause(self): + if not self.is_playing: + if self.pause_time > 0: + self.resume_music() + else: + self.play_music() + else: + self.pause_music() + + def play_music(self): + music_path = "D:/Downloads/林俊杰-光阴副本.wav" + if os.path.exists(music_path): + try: + pygame.mixer.music.load(music_path) + pygame.mixer.music.play(start=0) + self.music_length = pygame.mixer.Sound(music_path).get_length() + self.start_time = time.time() + self.pause_time = 0 + self.timer.start(500) + self.play_button.setIcon(QIcon("D:/Downloads/pause.png")) + self.is_playing = True + except Exception as e: + print("播放音乐时出错:", e) + else: + print("音乐文件未找到:", music_path) + + def resume_music(self): + try: + pygame.mixer.music.unpause() # 直接恢复播放 + self.start_time = time.time() - self.pause_time + self.timer.start(500) + self.play_button.setIcon(QIcon("D:/Downloads/pause.png")) + self.is_playing = True + except Exception as e: + print("继续播放时出错:", e) + + def pause_music(self): + if self.is_playing: + self.pause_time = time.time() - self.start_time + pygame.mixer.music.pause() + self.timer.stop() + self.play_button.setIcon(QIcon("D:/Downloads/play.png")) + self.is_playing = False + + def seek_music(self): + if self.music_length > 0: + new_time = self.slider.value() / 100 * self.music_length + pygame.mixer.music.play(start=new_time) + self.start_time = time.time() - new_time + self.pause_time = new_time + self.current_lyric_index = self.find_lyric_index(new_time) + self.update_lyrics_display() + self.timer.start(500) + self.play_button.setIcon(QIcon("D:/Downloads/pause.png")) + self.is_playing = True + + def find_lyric_index(self, current_time): + for i, (timestamp, _) in enumerate(self.lyrics): + if current_time < timestamp: + return max(i - 1, 0) + return len(self.lyrics) - 1 + + def update_lyrics_display(self): + if self.current_lyric_index < len(self.lyrics): + _, lyric = self.lyrics[self.current_lyric_index] + # 仅当歌词发生变化时再更新(避免重复重启动画) + if self.last_lyric != lyric: + self.last_lyric = lyric + # 如果歌词宽度超过容器,则滚动显示 + if self.lyrics_browser.fontMetrics().horizontalAdvance(lyric) > self.lyrics_container.width(): + self.scroll_lyrics(lyric) + else: + if self.animation is not None: + self.animation.stop() + self.animation = None + self.lyrics_browser.setText(lyric) + self.lyrics_browser.adjustSize() + self.lyrics_browser.move((self.lyrics_container.width() - self.lyrics_browser.width()) // 2, 0) + + def scroll_lyrics(self, lyric): + self.lyrics_browser.setText(lyric) + self.lyrics_browser.adjustSize() + label_width = self.lyrics_browser.width() + container_width = self.lyrics_container.width() + start_pos = QPoint(50, 0) + end_pos = QPoint(-label_width, 0) + if self.animation is not None: + self.animation.stop() + self.animation = QPropertyAnimation(self.lyrics_browser, b"pos") + # 根据歌词长度调整滚动时间 + duration = 8000 + (label_width - container_width) * 20 + self.animation.setDuration(duration) + self.animation.setStartValue(start_pos) + self.animation.setEndValue(end_pos) + self.animation.start() + + def update_lyrics_and_progress(self): + elapsed_time = time.time() - self.start_time + self.current_lyric_index = self.find_lyric_index(elapsed_time) + self.update_lyrics_display() + if self.music_length > 0: + progress = int((elapsed_time / self.music_length) * 100) + self.slider.setValue(progress) + +if __name__ == '__main__': + app = QApplication(sys.argv) + player = MusicPlayer() + player.show() + sys.exit(app.exec())