DreamLife_MusicPlayer/musicplayer.py

224 lines
9.2 KiB
Python
Raw Normal View History

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__()
2025-03-19 18:51:25 +08:00
self.setWindowTitle("DreamLife|MusicPlayer")
self.setGeometry(300, 200, 400, 500)
self.initUI()
pygame.mixer.init()
2025-03-19 18:51:25 +08:00
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()
2025-03-19 18:51:25 +08:00
self.play_button.setIcon(QIcon("file/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):
2025-03-19 18:51:25 +08:00
cover_path = "file/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):
2025-03-19 18:51:25 +08:00
lyrics_path = "file/林俊杰-光阴副本.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):
2025-03-19 18:51:25 +08:00
music_path = "file/林俊杰-光阴副本.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)
2025-03-19 18:51:25 +08:00
self.play_button.setIcon(QIcon("file/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)
2025-03-19 18:51:25 +08:00
self.play_button.setIcon(QIcon("file/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()
2025-03-19 18:51:25 +08:00
self.play_button.setIcon(QIcon("file/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)
2025-03-19 18:51:25 +08:00
self.play_button.setIcon(QIcon("file/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())