当前位置: 首页 > news >正文

拿来做软件测试的网站广东东莞最新疫情

拿来做软件测试的网站,广东东莞最新疫情,亚马逊店铺网站建设费用,自己网站的登录api怎么做python实现的音乐播放器 音乐播放器,原来写过一个简陋的例子,可见 https://blog.csdn.net/cnds123/article/details/137874107 那个不能拖动播放进度条上的滑块到新的位置播放。下面介绍的可以。 简单实用的音乐播放器 这个简单实用的音乐播放器&#…

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.pixmap.isNull():self.setText("未找到封面")self.setStyleSheet("color: #fff; background: #666; border-radius: 125px; font-size: 20px;")self.timer = QTimer(self)self.timer.timeout.connect(self.rotate)self.timer.start(50)  # 旋转速度def load_cover(self, song_path, default_cover):# 1. 尝试同名jpgbase, _ = os.path.splitext(song_path)jpg_path = base + ".jpg"if os.path.exists(jpg_path):return QPixmap(jpg_path)# 2. 尝试MP3内嵌封面try:audio = MP3(song_path, ID3=ID3)for tag in audio.tags.values():if isinstance(tag, APIC):ba = QByteArray(tag.data)pixmap = QPixmap()pixmap.loadFromData(ba)if not pixmap.isNull():return pixmapexcept Exception as e:pass# 3. 默认封面if os.path.exists(default_cover):return QPixmap(default_cover)return QPixmap()  # 空pixmapdef rotate(self):if self.pixmap.isNull():returnself.angle = (self.angle + 2) % 360target_size = 250# 只旋转封面cover_scaled = self.pixmap.scaled(target_size, target_size, Qt.AspectRatioMode.KeepAspectRatioByExpanding, Qt.TransformationMode.SmoothTransformation)cover_rotated = cover_scaled.transformed(QTransform().rotate(self.angle), Qt.TransformationMode.SmoothTransformation)# 裁剪为圆形cover_circle = QPixmap(target_size, target_size)cover_circle.fill(Qt.GlobalColor.transparent)painter = QPainter(cover_circle)painter.setRenderHint(QPainter.RenderHint.Antialiasing)path = QPainterPath()path.addEllipse(0, 0, target_size, target_size)painter.setClipPath(path)x = (target_size - cover_rotated.width()) // 2y = (target_size - cover_rotated.height()) // 2painter.drawPixmap(x, y, cover_rotated)painter.end()self.setPixmap(cover_circle)def setCover(self, song_path, default_cover="fm.png"):self.pixmap = self.load_cover(song_path, default_cover)class CoverWidget(QWidget):def __init__(self, default_cover="fm.png"):super().__init__()self.setFixedSize(250, 250)self.bg_pixmap = QPixmap(default_cover) if os.path.exists(default_cover) else QPixmap()self.cover_pixmap = QPixmap()self.angle = 0self.timer = QTimer(self)self.timer.timeout.connect(self.rotate)self.timer.start(50)self.rotate()self.default_cover = default_coverdef rotate(self):self.angle = (self.angle + 2) % 360self.update()def setCover(self, song_path):pixmap = self.load_cover(song_path, self.default_cover)self.cover_pixmap = pixmapself.update()def load_cover(self, song_path, default_cover):if not song_path or not os.path.exists(song_path):return QPixmap(default_cover) if os.path.exists(default_cover) else QPixmap()# 1. 尝试同名jpgbase, _ = os.path.splitext(song_path)jpg_path = base + ".jpg"if os.path.exists(jpg_path):return QPixmap(jpg_path)# 2. 尝试MP3内嵌封面try:audio = MP3(song_path, ID3=ID3)for tag in audio.tags.values():if isinstance(tag, APIC):ba = QByteArray(tag.data)pixmap = QPixmap()pixmap.loadFromData(ba)if not pixmap.isNull():return pixmapexcept Exception as e:pass# 3. 默认封面if os.path.exists(default_cover):return QPixmap(default_cover)return QPixmap()  # 空pixmapdef paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)# 1. 画旋转后的 fm.pngif not self.bg_pixmap.isNull():bg = self.bg_pixmap.scaled(250, 250, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)# 以中心为原点旋转painter.save()painter.translate(self.width() // 2, self.height() // 2)painter.rotate(self.angle)painter.translate(-bg.width() // 2, -bg.height() // 2)painter.drawPixmap(0, 0, bg)painter.restore()# 2. 画旋转后的封面(内切圆,直径130,居中)if not self.cover_pixmap.isNull():size = 130# 1. 先裁剪为正方形src = self.cover_pixmapw, h = src.width(), src.height()if w != h:side = min(w, h)x = (w - side) // 2y = (h - side) // 2src = src.copy(x, y, side, side)# 2. 缩放cover = src.scaled(size, size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)# 3. 裁剪为圆形cover_circle = QPixmap(size, size)cover_circle.fill(Qt.GlobalColor.transparent)p = QPainter(cover_circle)p.setRenderHint(QPainter.RenderHint.Antialiasing)path = QPainterPath()path.addEllipse(0, 0, size, size)p.setClipPath(path)p.drawPixmap(0, 0, cover)p.end()# 4. 以中心为原点旋转painter.save()painter.translate(self.width() // 2, self.height() // 2)painter.rotate(self.angle)painter.translate(-size // 2, -size // 2)painter.drawPixmap(0, 0, cover_circle)painter.restore()painter.end()# 动态歌词控件(简化版)
class LyricLabel(QWidget):def __init__(self):super().__init__()self.lyrics = []  # [(time, text), ...]self.current_index = 0self.setMinimumHeight(120)self.setStyleSheet("background: transparent;")self.color_main = QColor("#fff")self.color_other = QColor("#aaa")def setDemoText(self, text):# 只显示一行小字号的提示self.lyrics = []self.current_index = 0self.demo_text = textself.update()def setLyrics(self, lyrics):self.lyrics = lyricsself.current_index = 0self.demo_text = Noneself.update()def setCurrentTime(self, cur_time):self.cur_time = cur_time  # 记录当前时间idx = 0for i, (t, _) in enumerate(self.lyrics):if cur_time >= t:idx = ielse:breakif idx != self.current_index:self.current_index = idxself.update()else:self.update()  # 即使index没变,也要刷新实现平滑def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)w, h = self.width(), self.height()# 计算要显示的歌词行lines = []for offset in range(-2, 3):idx = self.current_index + offsetif 0 <= idx < len(self.lyrics):lines.append((offset, self.lyrics[idx][1]))total_lines = len(lines)if total_lines == 0:# demo_textif hasattr(self, "demo_text") and self.demo_text:font = QFont("微软雅黑", 13)painter.setFont(font)painter.setPen(QColor("#aaa"))painter.drawText(0, 0, w, h, Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter, self.demo_text)return# 1. 先确定最大允许的行高max_line_height = h // max(total_lines, 1)# 2. 计算主行最大字号main_text = lines[[o for o, _ in lines].index(0)][1] if any(o == 0 for o, _ in lines) else lines[0][1]if not main_text.strip():main_text = "啊"  # 占位符,防止空行导致字号过大main_font_size = max_line_heightwhile main_font_size > 10:font_main = QFont("微软雅黑", main_font_size, QFont.Weight.Bold)painter.setFont(font_main)rect = painter.fontMetrics().boundingRect(main_text)if rect.width() <= w * 0.95 and rect.height() <= max_line_height * 0.95:breakmain_font_size -= 1font_main = QFont("微软雅黑", main_font_size, QFont.Weight.Bold)other_font_size = int(main_font_size * 0.7)font_other = QFont("微软雅黑", other_font_size)painter.setFont(font_main)line_height = max(painter.fontMetrics().height(), int(h / max(total_lines, 1)))# ====== 平滑滚动核心 ======# 当前歌词时间cur_time = Noneif hasattr(self, "parent") and hasattr(self.parent(), "vlc_player"):cur_time = self.parent().vlc_player.get_time() / 1000elif hasattr(self, "cur_time"):cur_time = self.cur_timeelse:cur_time = 0# 当前行和下一行的时间cur_idx = self.current_indexcur_line_time = self.lyrics[cur_idx][0] if self.lyrics else 0next_line_time = self.lyrics[cur_idx+1][0] if (self.lyrics and cur_idx+1 < len(self.lyrics)) else cur_line_time+2# 计算当前行到下一行的进度百分比if next_line_time > cur_line_time:percent = min(max((cur_time - cur_line_time) / (next_line_time - cur_line_time), 0), 1)else:percent = 0# 歌词整体Y轴平滑上移scroll_offset = -percent * line_height# 歌词整体垂直居中start_y = (h - total_lines * line_height) // 2 + scroll_offsetfor i, (offset, text) in enumerate(lines):y = start_y + i * line_height + line_height // 2if offset == 0:painter.setFont(font_main)# ====== 彩虹色高亮 ======grad = QLinearGradient(0, y-line_height//2, w, y+line_height//2)for j in range(7):grad.setColorAt(j/6, QColor.fromHsv(int((j*60 + (cur_time*60)%360)%360), 255, 255))painter.setPen(QPen(QBrush(grad), 0))else:painter.setFont(font_other)painter.setPen(self.color_other)painter.drawText(0, int(y-line_height//2), w, line_height, Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter, text)class PlaylistWidget(QListWidget):favSong = pyqtSignal(str)def __init__(self, parent=None):super().__init__(parent)self.setStyleSheet("font-size:18px;background:#222;color:#fff;")self.setDragDropMode(QListWidget.DragDropMode.InternalMove)self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)self.customContextMenuRequested.connect(self.show_menu)self.model().rowsMoved.connect(self.on_rows_moved)def show_menu(self, pos):menu = QMenu(self)act_fav = menu.addAction("收藏该歌曲")act_del = menu.addAction("删除选中项")act_clear = menu.addAction("清空列表")action = menu.exec(self.mapToGlobal(pos))if action == act_fav:self.fav_selected()elif action == act_del:self.delete_selected()elif action == act_clear:self.clear_playlist()def delete_selected(self):for item in self.selectedItems():row = self.row(item)self.takeItem(row)if hasattr(self.parent(), "sync_song_list"):self.parent().sync_song_list()def clear_playlist(self):self.clear()if hasattr(self.parent(), "sync_song_list"):self.parent().sync_song_list()def on_rows_moved(self, *args):if hasattr(self.parent(), "sync_song_list"):self.parent().sync_song_list()def fav_selected(self):for item in self.selectedItems():song = item.toolTip()self.favSong.emit(song)class FloatingLyricWindow(QWidget):EDGE_MARGIN = 8  # 边缘判定宽度def __init__(self):super().__init__()self.setWindowFlags(Qt.WindowType.FramelessWindowHint |Qt.WindowType.WindowStaysOnTopHint |Qt.WindowType.Tool)self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)self.setWindowOpacity(0.85)  # 半透明self.lyric = FloatingLyricLabel()  # 只显示2行layout = QVBoxLayout()layout.setContentsMargins(16, 16, 16, 16)layout.addWidget(self.lyric)self.setLayout(layout)self.resize(800, 100)screen = QApplication.primaryScreen()screen_geometry = screen.geometry()self.move((screen_geometry.width() - self.width()) // 2,screen_geometry.height() - 150)# 拖动和缩放相关self._move_drag = Falseself._move_DragPosition = Noneself._resize_drag = Falseself._resize_dir = Nonedef paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)rect = self.rect()color = QColor(30, 30, 30, 200)  # 深色半透明painter.setBrush(color)painter.setPen(Qt.PenStyle.NoPen)painter.drawRoundedRect(rect, 18, 18)def mousePressEvent(self, event):if event.button() == Qt.MouseButton.LeftButton:pos = event.position().toPoint()margin = self.EDGE_MARGINrect = self.rect()# 判断是否在边缘if pos.x() < margin and pos.y() < margin:self._resize_drag = Trueself._resize_dir = 'topleft'elif pos.x() > rect.width() - margin and pos.y() < margin:self._resize_drag = Trueself._resize_dir = 'topright'elif pos.x() < margin and pos.y() > rect.height() - margin:self._resize_drag = Trueself._resize_dir = 'bottomleft'elif pos.x() > rect.width() - margin and pos.y() > rect.height() - margin:self._resize_drag = Trueself._resize_dir = 'bottomright'elif pos.x() < margin:self._resize_drag = Trueself._resize_dir = 'left'elif pos.x() > rect.width() - margin:self._resize_drag = Trueself._resize_dir = 'right'elif pos.y() < margin:self._resize_drag = Trueself._resize_dir = 'top'elif pos.y() > rect.height() - margin:self._resize_drag = Trueself._resize_dir = 'bottom'else:self._move_drag = Trueself._move_DragPosition = event.globalPosition().toPoint() - self.pos()event.accept()def mouseMoveEvent(self, event):if self._move_drag and event.buttons() == Qt.MouseButton.LeftButton:self.move(event.globalPosition().toPoint() - self._move_DragPosition)event.accept()elif self._resize_drag and event.buttons() == Qt.MouseButton.LeftButton:gpos = event.globalPosition().toPoint()geo = self.geometry()minw, minh = 200, 50if self._resize_dir == 'left':diff = gpos.x() - geo.x()neww = geo.width() - diffif neww > minw:geo.setLeft(gpos.x())elif self._resize_dir == 'right':neww = gpos.x() - geo.x()if neww > minw:geo.setWidth(neww)elif self._resize_dir == 'top':diff = gpos.y() - geo.y()newh = geo.height() - diffif newh > minh:geo.setTop(gpos.y())elif self._resize_dir == 'bottom':newh = gpos.y() - geo.y()if newh > minh:geo.setHeight(newh)elif self._resize_dir == 'topleft':diffx = gpos.x() - geo.x()diffy = gpos.y() - geo.y()neww = geo.width() - diffxnewh = geo.height() - diffyif neww > minw:geo.setLeft(gpos.x())if newh > minh:geo.setTop(gpos.y())elif self._resize_dir == 'topright':diffy = gpos.y() - geo.y()neww = gpos.x() - geo.x()newh = geo.height() - diffyif neww > minw:geo.setWidth(neww)if newh > minh:geo.setTop(gpos.y())elif self._resize_dir == 'bottomleft':diffx = gpos.x() - geo.x()neww = geo.width() - diffxnewh = gpos.y() - geo.y()if neww > minw:geo.setLeft(gpos.x())if newh > minh:geo.setHeight(newh)elif self._resize_dir == 'bottomright':neww = gpos.x() - geo.x()newh = gpos.y() - geo.y()if neww > minw:geo.setWidth(neww)if newh > minh:geo.setHeight(newh)self.setGeometry(geo)event.accept()# 无论是否拖动,都要设置光标self.update_cursor(event.position().toPoint())def mouseReleaseEvent(self, event):self._move_drag = Falseself._resize_drag = Falseself._resize_dir = Nonedef setLyrics(self, lyrics):self.lyric.setLyrics(lyrics)def setCurrentTime(self, time):self.lyric.setCurrentTime(time)def update_cursor(self, pos):margin = self.EDGE_MARGINrect = self.rect()if (pos.x() < margin and pos.y() < margin) or (pos.x() > rect.width() - margin and pos.y() > rect.height() - margin):self.setCursor(Qt.CursorShape.SizeFDiagCursor)elif (pos.x() > rect.width() - margin and pos.y() < margin) or (pos.x() < margin and pos.y() > rect.height() - margin):self.setCursor(Qt.CursorShape.SizeBDiagCursor)elif pos.x() < margin or pos.x() > rect.width() - margin:self.setCursor(Qt.CursorShape.SizeHorCursor)elif pos.y() < margin or pos.y() > rect.height() - margin:self.setCursor(Qt.CursorShape.SizeVerCursor)else:self.setCursor(Qt.CursorShape.ArrowCursor)def enterEvent(self, event):self.update_cursor(self.mapFromGlobal(QCursor.pos()))def leaveEvent(self, event):self.setCursor(Qt.CursorShape.ArrowCursor)class FloatingLyricLabel(LyricLabel):def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)w, h = self.width(), self.height()# 只显示当前行和下一行,且排除空行lines = []idx = self.current_index# 找到当前行及下一个非空行count = 0while idx < len(self.lyrics) and count < 2:text = self.lyrics[idx][1].strip()if text:lines.append(text)count += 1idx += 1# 如果不足两行,补空字符串while len(lines) < 2:lines.append("")if not any(lines):# 没有歌词,显示demo_textif hasattr(self, "demo_text") and self.demo_text:font = QFont("微软雅黑", int(h * 0.35), QFont.Weight.Bold)painter.setFont(font)painter.setPen(QColor("#aaa"))painter.drawText(0, 0, w, h, Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter, self.demo_text)returnmain_text = lines[0]next_text = lines[1]# 计算主行和下一行的最大字号,兼顾宽度和高度def fit_font_size(text, max_font_size, max_width, max_height, bold=False):font_size = max_font_sizewhile font_size > 10:font = QFont("微软雅黑", font_size, QFont.Weight.Bold if bold else QFont.Weight.Normal)painter.setFont(font)rect = painter.fontMetrics().boundingRect(text)if rect.width() <= max_width * 0.95 and rect.height() <= max_height * 0.95:breakfont_size -= 1return font_size# 预设主行和下一行高度比例main_ratio = 0.58next_ratio = 0.28gap = int(h * 0.08)main_max_height = h * main_rationext_max_height = h * next_ratio# 先用最大高度和宽度分别拟合字号main_font_size = fit_font_size(main_text, int(main_max_height), w, main_max_height, bold=True)next_font_size = fit_font_size(next_text, int(next_max_height), w, next_max_height)font_main = QFont("微软雅黑", main_font_size, QFont.Weight.Bold)font_next = QFont("微软雅黑", next_font_size)# 重新计算行高painter.setFont(font_main)main_line_height = painter.fontMetrics().height()painter.setFont(font_next)next_line_height = painter.fontMetrics().height()total_height = main_line_height + next_line_height + gapstart_y = (h - total_height) // 2# 当前行:彩虹色高亮painter.setFont(font_main)grad = QLinearGradient(0, start_y, w, start_y + main_line_height)cur_time = getattr(self, "cur_time", 0)for j in range(7):grad.setColorAt(j/6, QColor.fromHsv(int((j*60 + (cur_time*60)%360)%360), 255, 255))painter.setPen(QPen(QBrush(grad), 0))painter.drawText(0, int(start_y), w, main_line_height, Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter, main_text)# 下一行:灰色painter.setFont(font_next)painter.setPen(QColor(180, 180, 180, 180))painter.drawText(0, int(start_y + main_line_height + gap), w, next_line_height, Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter, next_text)class CustomTitleBar(QWidget):def __init__(self, parent=None):super().__init__(parent)self.setFixedHeight(40)self.setStyleSheet("""background: #222;border-top-left-radius: 18px;border-top-right-radius: 18px;""")layout = QHBoxLayout(self)layout.setContentsMargins(16, 0, 8, 0)self.title = QLabel("Winamp 音乐播放器")self.title.setStyleSheet("color: #fff; font-size: 18px; font-weight: bold;")layout.addWidget(self.title)layout.addStretch()self.btn_min = QPushButton("—")self.btn_min.setFixedSize(32, 32)self.btn_min.setStyleSheet("color:#fff; background:transparent; font-size:20px; border:none;")self.btn_close = QPushButton("×")self.btn_close.setFixedSize(32, 32)self.btn_close.setStyleSheet("color:#fff; background:transparent; font-size:20px; border:none;")layout.addWidget(self.btn_min)layout.addWidget(self.btn_close)self.btn_min.clicked.connect(self.on_min)self.btn_close.clicked.connect(self.on_close)def on_min(self):self.window().showMinimized()def on_close(self):self.window().close()# 支持拖动窗口def mousePressEvent(self, event):if event.button() == Qt.MouseButton.LeftButton:self._drag_pos = event.globalPosition().toPoint() - self.window().frameGeometry().topLeft()event.accept()def mouseMoveEvent(self, event):if event.buttons() == Qt.MouseButton.LeftButton:self.window().move(event.globalPosition().toPoint() - self._drag_pos)event.accept()def paintEvent(self, event):super().paintEvent(event)painter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)pen = QPen(QColor(17, 17, 17), 2)painter.setPen(pen)y = self.height() + 4  # 向下移5像素painter.drawLine(10, y, self.width() - 10, y)class MP3Player(QWidget):def __init__(self):super().__init__()self.setWindowFlags(Qt.WindowType.FramelessWindowHint)self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)self.song_list = []  # 歌曲文件路径列表self.current_index = -1  # 当前播放索引self.player = QMediaPlayer()self.audio_output = QAudioOutput()self.player.setAudioOutput(self.audio_output)self.vlc_player = vlc.MediaPlayer()self.timer = QTimer(self)self.timer.setInterval(500)self.timer.timeout.connect(self.update_progress)self.slider_is_pressed = Falseself.loop_mode = False  # False: 顺序播放, True: 随机播放self.shuffle_mode = False  # False: 顺序播放, True: 随机播放#self.default_lyric_text = "深埋生命血脉相连\n用丝绸去润泽你的肌肤"self.default_lyric_text = "未检测到歌词\n正在播放..."self.floating_lyric = FloatingLyricWindow()self.floating_lyric_visible = Falseself.is_muted = False#self.btn_mute = QPushButton("&#128266;")self.btn_mute = QPushButton("🔊")self.btn_mute.setFixedSize(48, 48)self.btn_mute.setStyleSheet("font-size: 24px; background: #333; color: #fff; border-radius: 24px; font-weight: bold;")self.btn_mute.clicked.connect(self.toggle_mute)self.initUI()self.load_last_playlist()def initUI(self):self.song_path = "test.mp3"  # 默认MP3文件路径self.cover = CoverWidget(resource_path("fm.png"))self.lyric = LyricLabel()self.lyric.setDemoText(self.default_lyric_text)first_row = QHBoxLayout()first_row.addWidget(self.cover, 1)first_row.addWidget(self.lyric, 2)# 第二行self.slider = QSlider(Qt.Orientation.Horizontal)self.slider.setRange(0, 100)self.slider.setValue(0)self.time_label_left = QLabel("00:00")self.time_label_right = QLabel("05:27")self.time_label_left.setStyleSheet("color: #aaa;")self.time_label_right.setStyleSheet("color: #aaa;")second_row = QHBoxLayout()second_row.addWidget(self.time_label_left)second_row.addWidget(self.slider, 1)second_row.addWidget(self.time_label_right)# 第三行
##        self.btn_prev = QPushButton("&#9198;")
##        self.btn_play = QPushButton("&#9654;")
##        self.btn_next = QPushButton("&#9197;")
##        self.btn_loop = QPushButton("&#128257;")
##        self.btn_stop = QPushButton("&#9209;")self.btn_prev = QPushButton("⏮")self.btn_play = QPushButton("▶")self.btn_next = QPushButton("⏭")self.btn_loop = QPushButton("🔁;")self.btn_stop = QPushButton("⏹")        self.btn_add_file = QPushButton("添加文件")self.btn_add_dir = QPushButton("添加目录")self.btn_save_list = QPushButton("保存列表")self.btn_load_list = QPushButton("加载列表")#self.btn_mute = QPushButton("&#128266;")self.btn_mute = QPushButton("🔊")self.btn_float_lyric = QPushButton("词")        button_style = """QPushButton {font-size: 24px;background: #333;color: #fff;border-radius: 24px;font-weight: bold;border: none;}QPushButton:hover {background: #09f;color: #fff;}"""for btn in [self.btn_prev, self.btn_play, self.btn_next, self.btn_loop, self.btn_stop, self.btn_mute, self.btn_float_lyric]:btn.setFixedSize(48, 48)btn.setStyleSheet(button_style)list_button_style = """QPushButton {font-size: 16px;background: #555;color: #fff;border-radius: 8px;padding: 6px 18px;border: none;}QPushButton:hover {background: #09f;color: #fff;}"""self.btn_add_file.setFixedSize(100, 36)self.btn_add_dir.setFixedSize(100, 36)self.btn_save_list.setFixedSize(100, 36)self.btn_load_list.setFixedSize(100, 36)self.btn_add_file.setStyleSheet(list_button_style)self.btn_add_dir.setStyleSheet(list_button_style)self.btn_save_list.setStyleSheet(list_button_style)self.btn_load_list.setStyleSheet(list_button_style)self.btn_add_file.clicked.connect(self.open_file)self.btn_add_dir.clicked.connect(self.open_dir)self.btn_save_list.clicked.connect(self.save_playlist)self.btn_load_list.clicked.connect(self.load_playlist)self.btn_prev.clicked.connect(self.play_prev)self.btn_next.clicked.connect(self.play_next)# self.btn_play.clicked.connect(self.play_selected)self.btn_play.clicked.connect(self.toggle_play_pause)self.btn_loop.clicked.connect(self.toggle_shuffle_mode)self.btn_stop.clicked.connect(self.stop_play)self.btn_mute.clicked.connect(self.toggle_mute)self.btn_float_lyric.clicked.connect(self.toggle_floating_lyric)third_row = QHBoxLayout()third_row.setSpacing(8)  # 设置按钮间距third_row.addStretch()for btn in [self.btn_prev, self.btn_play, self.btn_next, self.btn_loop, self.btn_stop, self.btn_mute, self.btn_float_lyric]:third_row.addWidget(btn)third_row.addSpacing(20)third_row.addWidget(self.btn_add_file)third_row.addWidget(self.btn_add_dir)third_row.addWidget(self.btn_save_list)third_row.addWidget(self.btn_load_list)third_row.addStretch()# 新增:播放列表self.list_widget = PlaylistWidget(self)self.list_widget.itemDoubleClicked.connect(self.on_item_double_clicked)self.list_widget.favSong.connect(self.append_to_fav)# 总体布局main_widget = QWidget(self)main_widget.setObjectName("main_widget")main_widget.setStyleSheet("""#main_widget {background: #222;border-radius: 18px;}""")main_layout = QVBoxLayout(main_widget)main_layout.setContentsMargins(0, 0, 0, 0)main_layout.setSpacing(0)self.title_bar = CustomTitleBar(self)main_layout.addWidget(self.title_bar)content_layout = QVBoxLayout()content_layout.addLayout(first_row, 3)content_layout.addLayout(second_row, 1)content_layout.addLayout(third_row, 1)content_layout.addWidget(self.list_widget, 2)content_layout.setContentsMargins(16, 0, 16, 16)main_layout.addLayout(content_layout)layout = QVBoxLayout(self)layout.addWidget(main_widget)layout.setContentsMargins(0, 0, 0, 0)self.resize(1000, 700)self.slider.sliderPressed.connect(self.on_slider_pressed)self.slider.sliderReleased.connect(self.on_slider_released)self.slider.sliderMoved.connect(self.on_slider_moved)self.update_shuffle_button_style()print(f"初始按钮文本: {self.btn_loop.text()}")# 添加滚动条样式scrollbar_style = """QScrollBar:vertical {background: #222;width: 14px;margin: 4px 2px 4px 2px;border-radius: 7px;}QScrollBar::handle:vertical {background: #444;min-height: 40px;border-radius: 7px;}QScrollBar::handle:vertical:hover {background: #09f;}QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {background: none;height: 0px;}QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {background: none;}QScrollBar:horizontal {background: #222;height: 14px;margin: 2px 4px 2px 4px;border-radius: 7px;}QScrollBar::handle:horizontal {background: #444;min-width: 40px;border-radius: 7px;}QScrollBar::handle:horizontal:hover {background: #09f;}QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {background: none;width: 0px;}QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {background: none;}"""self.list_widget.verticalScrollBar().setStyleSheet(scrollbar_style)self.list_widget.horizontalScrollBar().setStyleSheet(scrollbar_style)self.load_last_playlist()def open_file(self):files, _ = QFileDialog.getOpenFileNames(self, "选择音频文件", "", "音频文件 (*.mp3 *.wav *.flac);;所有文件 (*)")for file in files:self.add_song(file)def open_dir(self):dir_path = QFileDialog.getExistingDirectory(self, "选择文件夹", "")if dir_path:for root, dirs, files in os.walk(dir_path):for fname in files:if fname.lower().endswith(('.mp3', '.wav', '.flac')):self.add_song(os.path.join(root, fname))def add_song(self, file):if file not in self.song_list:self.song_list.append(file)item = QListWidgetItem(os.path.basename(file))item.setToolTip(file)self.list_widget.addItem(item)# 如果是第一首,自动选中if len(self.song_list) == 1:self.list_widget.setCurrentRow(0)self.play_selected()def on_item_double_clicked(self, item):row = self.list_widget.row(item)self.sync_song_list()self.current_index = rowself.play_song_by_index(row)def play_selected(self):"""播放或暂停当前选中的歌曲"""row = self.list_widget.currentRow()self.sync_song_list()if row >= 0:self.current_index = rowstate = self.vlc_player.get_state()if state in (vlc.State.Playing, vlc.State.Buffering):# 如果正在播放,则暂停self.vlc_player.pause()#self.btn_play.setText("&#9654;")  # 显示播放图标self.btn_play.setText("▶")elif state == vlc.State.Paused:# 如果暂停中,则继续播放self.vlc_player.play()#self.btn_play.setText("&#9208;")  # 显示暂停图标self.btn_play.setText("⏸") elif state in (vlc.State.Stopped, vlc.State.Ended, vlc.State.NothingSpecial):# 如果停止或未开始,则播放选中歌曲self.play_song_by_index(row)def pause_play(self):"""暂停当前播放"""if self.vlc_player.is_playing():self.vlc_player.pause()#self.btn_play.setText("&#9654;")  # 显示播放图标self.btn_play.setText("▶")def resume_play(self):"""继续播放"""if self.vlc_player.get_state() == vlc.State.Paused:self.vlc_player.play()#self.btn_play.setText("&#9208;")  # 显示暂停图标self.btn_play.setText("⏸")def toggle_play_pause(self):"""切换播放/暂停状态"""if not self.song_list:# 如果播放列表为空,则提示用户添加歌曲print("播放列表为空,请先添加歌曲")returnif self.vlc_player.is_playing():self.pause_play()elif self.vlc_player.get_state() == vlc.State.Paused:self.resume_play()else:# 如果没有正在播放的歌曲,则播放当前选中的歌曲row = self.list_widget.currentRow()if row >= 0:self.play_song_by_index(row)else:# 如果没有选中歌曲,则播放第一首if self.song_list:self.list_widget.setCurrentRow(0)self.play_song_by_index(0)def keyPressEvent(self, event):"""处理键盘事件"""if event.key() == Qt.Key.Key_Space:# 空格键控制播放/暂停self.toggle_play_pause()elif event.key() == Qt.Key.Key_Left:# 左箭头键播放上一首self.play_prev()elif event.key() == Qt.Key.Key_Right:# 右箭头键播放下一首self.play_next()elif event.key() == Qt.Key.Key_Escape:# ESC键关闭窗口self.close()else:super().keyPressEvent(event)def play_song_by_index(self, idx):if 0 <= idx < len(self.song_list):self.current_index = idxsong_path = self.song_list[idx]song_name = os.path.basename(song_path)  # 获取文件名song_name_without_ext = os.path.splitext(song_name)[0]  # 去除扩展名self.cover.setCover(song_path)self.list_widget.setCurrentRow(idx)self.vlc_player.stop()self.vlc_player.set_media(vlc.Media(song_path))self.vlc_player.play()self.timer.start()self.slider.setValue(0)self.time_label_left.setText("00:00")self.time_label_right.setText("--:--")# 更新标题显示当前播放的歌曲名self.title_bar.title.setText(f"正在播放: {song_name_without_ext}")# 使用自定义的默认歌词文本,包含歌曲名custom_default_text = f"正在播放: {song_name_without_ext}\n无歌词可用"print(f"播放:{song_path}")# 加载歌词try:lrc = self.load_lrc(song_path) or self.load_embedded_lyric(song_path)if lrc:parsed = parse_lrc(lrc)if parsed:self.lyrics_parsed = parsedself.lyric.setLyrics(self.lyrics_parsed)self.floating_lyric.setLyrics(self.lyrics_parsed)  # 同步到浮动歌词else:self.lyric.setDemoText(custom_default_text)self.floating_lyric.setLyrics([])else:self.lyric.setDemoText(custom_default_text)self.floating_lyric.setLyrics([])except Exception as e:self.lyric.setDemoText(custom_default_text)self.floating_lyric.setLyrics([])def play_prev(self):if self.song_list:if self.shuffle_mode:# 随机模式:随机选择一首(避免当前曲目)import randomcandidates = [i for i in range(len(self.song_list)) if i != self.current_index]if candidates:self.current_index = random.choice(candidates)else:self.current_index = self.current_indexelse:# 顺序模式:播放上一首self.current_index = (self.current_index - 1) % len(self.song_list)self.play_song_by_index(self.current_index)def play_next(self):if self.song_list:if self.shuffle_mode:# 随机模式:随机选择一首(避免当前曲目)import randomcandidates = [i for i in range(len(self.song_list)) if i != self.current_index]if candidates:self.current_index = random.choice(candidates)else:self.current_index = self.current_indexelse:# 顺序模式:播放下一首self.current_index = (self.current_index + 1) % len(self.song_list)self.play_song_by_index(self.current_index)def on_slider_pressed(self):self.slider_is_pressed = Truedef on_slider_released(self):self.slider_is_pressed = Falsetotal = self.vlc_player.get_length()if total > 0:pos = self.slider.value() / 100self.vlc_player.set_time(int(total * pos))def on_slider_moved(self, value):total = self.vlc_player.get_length()if total > 0:cur_time = int(total * value / 100)self.time_label_left.setText(self.ms_to_mmss(cur_time))def update_progress(self):if self.vlc_player.is_playing() and not self.slider_is_pressed:total = self.vlc_player.get_length()cur = self.vlc_player.get_time()if total > 0:percent = int(cur / total * 100)self.slider.setValue(percent)self.time_label_left.setText(self.ms_to_mmss(cur))self.time_label_right.setText(self.ms_to_mmss(total))else:self.slider.setValue(0)self.time_label_right.setText("--:--")elif not self.vlc_player.is_playing():if self.vlc_player.get_state() == vlc.State.Ended:if self.shuffle_mode:import randomif self.song_list:# 避免重复播放当前曲目candidates = [i for i in range(len(self.song_list)) if i != self.current_index]if candidates:next_index = random.choice(candidates)else:next_index = self.current_indexself.play_song_by_index(next_index)else:self.play_next()# 图标联动state = self.vlc_player.get_state()if state in (vlc.State.Playing, vlc.State.Buffering):#self.btn_play.setText("&#9208;")self.btn_play.setText("⏸")else:#self.btn_play.setText("&#9654;")self.btn_play.setText("▶")if hasattr(self, "lyric") and hasattr(self, "lyrics_parsed"):cur = self.vlc_player.get_time() / 1000self.lyric.setCurrentTime(cur)self.floating_lyric.setCurrentTime(cur)  # 同步到浮动歌词def ms_to_mmss(self, ms):s = int(ms // 1000)m = s // 60s = s % 60return f"{m:02d}:{s:02d}"def save_playlist(self):self.sync_song_list()file_path, _ = QFileDialog.getSaveFileName(self, "保存播放列表", "", "播放列表文件 (*.m3u *.txt);;所有文件 (*)")if file_path:try:with open(file_path, "w", encoding="utf-8") as f:for song in self.song_list:f.write(song + "\n")except Exception as e:print("保存失败:", e)def load_playlist(self):file_path, _ = QFileDialog.getOpenFileName(self, "加载播放列表", "", "播放列表文件 (*.m3u *.txt);;所有文件 (*)")if file_path:try:with open(file_path, "r", encoding="utf-8") as f:lines = [line.strip() for line in f if line.strip()]self.song_list.clear()self.list_widget.clear()for song in lines:if os.path.exists(song):self.song_list.append(song)item = QListWidgetItem(os.path.basename(song))item.setToolTip(song)self.list_widget.addItem(item)if self.song_list:self.list_widget.setCurrentRow(0)self.play_song_by_index(0)# 保存最后一次歌单路径with open("last_playlist.txt", "w", encoding="utf-8") as f:f.write(file_path)except Exception as e:print("加载失败:", e)def sync_song_list(self):self.song_list = []for i in range(self.list_widget.count()):item = self.list_widget.item(i)if item and item.toolTip():self.song_list.append(item.toolTip())def toggle_shuffle_mode(self):self.shuffle_mode = not self.shuffle_modeself.update_shuffle_button_style()print(f"切换后模式: {self.shuffle_mode}, 按钮文本: {self.btn_loop.text()}")def stop_play(self):self.vlc_player.stop()self.timer.stop()self.slider.setValue(0)self.time_label_left.setText("00:00")self.time_label_right.setText("--:--")self.lyric.setDemoText(self.default_lyric_text)if self.song_list and 0 <= self.current_index < len(self.song_list):self.append_to_fav(self.song_list[self.current_index])def update_shuffle_button_style(self):if self.shuffle_mode:#self.btn_loop.setText("&#128256;")self.btn_loop.setText("🔄")else:#self.btn_loop.setText("&#128257;")self.btn_loop.setText("🔁")self.btn_loop.update()  # 强制刷新按钮def load_lrc(self, song_path):lrc_path = os.path.splitext(song_path)[0] + ".lrc"if os.path.exists(lrc_path):with open(lrc_path, encoding="utf-8") as f:return f.read()return Nonedef load_embedded_lyric(self, song_path):try:audio = MP3(song_path, ID3=ID3)for tag in audio.tags.values():if tag.FrameID == "USLT":return tag.textexcept Exception:passreturn Nonedef toggle_floating_lyric(self):if self.floating_lyric_visible:self.floating_lyric.hide()self.floating_lyric_visible = Falseself.btn_float_lyric.setStyleSheet("font-size: 24px; background: #333; color: #fff; border-radius: 24px; font-weight: bold;")else:self.floating_lyric.show()self.floating_lyric_visible = Trueself.btn_float_lyric.setStyleSheet("font-size: 24px; background: #09f; color: #fff; border-radius: 24px; font-weight: bold;")def toggle_mute(self):self.is_muted = not self.is_mutedself.vlc_player.audio_set_mute(self.is_muted)if self.is_muted:#self.btn_mute.setText("&#128263;")self.btn_mute.setText("🔇")self.btn_mute.setStyleSheet("font-size: 24px; background: #09f; color: #fff; border-radius: 24px; font-weight: bold;")else:#self.btn_mute.setText("&#128266;") self.btn_mute.setText("🔊") self.btn_mute.setStyleSheet("font-size: 24px; background: #333; color: #fff; border-radius: 24px; font-weight: bold;")def append_to_fav(self, song):fav_path = os.path.abspath("收藏歌单.m3u")try:need_header = not os.path.exists(fav_path)if os.path.exists(fav_path):with open(fav_path, "r", encoding="utf-8") as f:lines = [line.strip() for line in f if line.strip()]if song in lines:returnwith open(fav_path, "a", encoding="utf-8") as f:if need_header:f.write("#EXTM3U\n")f.write(song + "\n")print(f"已收藏到: {fav_path}")  # 调试用except Exception as e:print("收藏失败:", e)def load_last_playlist(self):try:if os.path.exists("last_playlist.txt"):with open("last_playlist.txt", "r", encoding="utf-8") as f:file_path = f.read().strip()if file_path and os.path.exists(file_path):with open(file_path, "r", encoding="utf-8") as f:lines = [line.strip() for line in f if line.strip()]self.song_list.clear()self.list_widget.clear()for song in lines:if os.path.exists(song):self.song_list.append(song)item = QListWidgetItem(os.path.basename(song))item.setToolTip(song)self.list_widget.addItem(item)if self.song_list:self.list_widget.setCurrentRow(0)self.play_song_by_index(0)except Exception as e:print("自动加载上次歌单失败:", e)def parse_lrc(lrc_text):pattern = re.compile(r"\[(\d+):(\d+)(?:\.(\d+))?\](.*)")result = []for line in lrc_text.splitlines():m = pattern.match(line)if m:minute = int(m.group(1))second = int(m.group(2))ms = int(m.group(3) or 0)time = minute * 60 + second + ms / 100 if ms else minute * 60 + secondtext = m.group(4).strip()result.append((time, text))result.sort()return resultif __name__ == "__main__":try:app = QApplication(sys.argv)player = MP3Player()player.show()print("当前工作目录:", os.getcwd())sys.exit(app.exec())  # PyQt6中exec_()改为exec()except Exception as e:print(f"程序发生错误: {e}")import tracebacktraceback.print_exc()

OK!

http://www.lryc.cn/news/616687.html

相关文章:

  • 网站建设 发票网络口碑营销名词解释
  • 有个新网站专门做外贸的叫什么软文编辑器
  • wordpress标签怎么做静态化建站seo推广
  • 15年做哪些网站致富抖音网络营销案例分析
  • 天水市秦州区建设局网站seo云优化是什么意思
  • 一流的镇江网站建设免费营销软件网站
  • 用node做的网站排名优化价格
  • 做微信公众号网站新东方教育培训机构
  • 深圳福田区区住房和建设局网站武汉seo招聘
  • 如何做网站推广页面seo优化一般包括哪些
  • 要个网站游戏推广话术
  • 贵州省建设厅城乡建设网站网络平台运营是做什么的
  • 网站建设要素网络销售怎么做才能做好
  • 网站如何做快照建一个外贸独立站大约多少钱
  • 烟台网站排名优化全自动引流推广软件免费
  • 销售网站建设怎么做欧洲站fba
  • 360全景网站怎么做最新百度快速收录技术
  • 钓鱼网站制作者杭州网站推广优化公司
  • 建站宝盒如何使用营销型网站建设托管
  • 做营销网站的企业推广网站公司
  • 医院哪个科室负责网站建设申请一个网站
  • mac做网站设计百度优化教程
  • 什么网站比谷歌还好同城引流用什么软件
  • 带会员注册企业网站源码网站推广优化排名教程
  • 人气页游排行榜前十名seo搜索价格
  • 太原便宜做网站的公司哪家好seo顾问是什么职业
  • 自考网站建设与管理下拉框关键词软件
  • 徐州网站设计价位优化大师官方
  • 网站建设企业资质西安网络优化大的公司
  • 常州做网站的企业近期的重大新闻