python Pyqt5制作音乐播放器(第二版)

更新功能:随机播放/收藏列表

暂存bug:未使用数据库、无翻页等

import fnmatch
import os
import random
import sys
import logging

from PyQt5.QtGui import QPalette, QBrush, QPixmap, QLinearGradient, QColor
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QSlider, QLabel, \
    QTableWidget, QLineEdit, QTableWidgetItem
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl, Qt, QTimer

logging.basicConfig(filename="music.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s",
                    datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO)
logger = logging.getLogger("music")
KZT = logging.StreamHandler()
KZT.setLevel(logging.INFO)
logger.addHandler(KZT)


class MusicPlayer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.music_path = "F://wyy"  # 音乐文件夹,可以有子文件夹
        self.start_x = 10
        self.x_step = 100
        self.start_y = 10
        self.y_step = 40
        self.window_w, self.window_h = 500, 800
        self.music_list = self.find_mp3_files()
        self.show_list = self.music_list
        self.random_list = []

        self.duration = 1
        self.position = 0
        self.text = "歌曲未加载"

        self.current_index = -1
        self.current_music = self.show_list[self.current_index]['music']
        self.current_singer = self.show_list[self.current_index]['singer']
        self.current_path = self.show_list[self.current_index]['path']

        self.status = True  # 音乐是否在播放
        self.update_label = True  # 音乐是否在播放-》是否更新进度条
        self.random_play_status = False  # 是否处于随机播放状态
        self.show_favorite_status = False  # 是否展示收藏
        self.init_ui()
        logger.info("配置加载完成")

    def init_ui(self):
        self.setWindowTitle("音乐播放器")
        self.resize(self.window_w, self.window_h)
        self.setMinimumSize(self.window_w, self.window_h)
        self.setMaximumSize(self.window_w, self.window_h)
        palette = QPalette()  # 主界面背景
        palette.setBrush(QPalette.Background, QBrush(QPixmap("imgs/21.jpg")))
        self.setPalette(palette)

        self.player = QMediaPlayer()

        self.play_button = QPushButton("播放", self)
        self.play_button.clicked.connect(self.play_music)
        self.play_button.move(self.start_x, self.start_y)
        self.change_button = QPushButton("暂停", self)
        self.change_button.clicked.connect(self.change_music_status)
        self.change_button.move(self.start_x + self.x_step, self.start_y)
        self.play_pre_button = QPushButton("上一首", self)
        self.play_pre_button.clicked.connect(self.play_pre)
        self.play_pre_button.move(self.start_x, self.start_y + self.y_step)
        self.play_next_button = QPushButton("下一首", self)
        self.play_next_button.clicked.connect(self.play_next)
        self.play_next_button.move(self.start_x + self.x_step, self.start_y + self.y_step)
        self.play_random_btn = QPushButton("随机播放", self)
        self.play_random_btn.clicked.connect(self.change_random_status)
        self.play_random_btn.move(self.start_x + 2 * self.x_step, self.start_y + self.y_step)
        self.favorite_btn = QPushButton("我的收藏", self)
        self.favorite_btn.clicked.connect(self.show_favorite)
        self.favorite_btn.move(self.start_x + 3 * self.x_step, self.start_y + self.y_step)

        self.volume_slider = QSlider(Qt.Horizontal, self)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(50)
        self.volume_slider.setTickPosition(QSlider.TicksBelow)
        self.volume_slider.setTickInterval(10)
        self.volume_slider.resize(200, 30)
        self.volume_slider.sliderReleased.connect(self.change_volume)
        # self.volume_slider.sliderPressed.connect(self.volume_pressed)
        self.volume_slider.move(self.start_x, self.start_y + 2 * self.y_step)

        self.music_pro_bar = QLabel(self)
        self.music_pro_width = self.window_w - self.start_x - 210
        self.music_pro_bar.resize(self.music_pro_width, 10)
        self.music_pro_bar.move(self.start_x + 210, self.start_y + 2 * self.y_step + 10)
        # self.music_pro_bar.setStyleSheet("background:'#123456'")
        gradient = QLinearGradient(0, 0, self.music_pro_width, 0)
        gradient.setColorAt(0, QColor(0, 0, 0))
        gradient.setColorAt(1, QColor(255, 255, 255))
        music_pro_bar_palette = QPalette()
        music_pro_bar_palette.setBrush(QPalette.Background, gradient)
        self.music_pro_bar.setAutoFillBackground(True)
        self.music_pro_bar.setPalette(music_pro_bar_palette)

        self.table_widget = QTableWidget(0, 3, parent=self)  # 检测结果列表
        self.table_widget.setHorizontalHeaderLabels(['歌手', '歌曲', '♥'])  # 设置列表头
        self.table_widget.verticalHeader().hide()  # 隐藏行索引
        self.table_widget.move(self.start_x, self.start_y + 4 * self.y_step)
        self.table_widget.resize(self.window_w - self.start_x, self.window_h - self.start_y - 4 * self.y_step)
        self.table_widget.cellClicked.connect(self.cell_clicked)
        self.table_widget.cellDoubleClicked.connect(self.play_cell)
        self.table_widget.setColumnWidth(0, 150)
        self.table_widget.setColumnWidth(1, 250)
        self.table_widget.setColumnWidth(2, 70)
        self.table_widget.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table_widget.setStyleSheet("QTableWidget {background-image: url('imgs/11.jpg');}")
        self.load_music()

        self.search_btn = QPushButton("搜索", self)
        self.search_btn.move(self.start_x + 2 * self.x_step, self.start_y)
        self.search_btn.clicked.connect(self.search_music)

        self.search_text = QLineEdit(self)
        self.search_text.resize(self.window_w - self.start_x - 3 * self.x_step - 10, 30)
        self.search_text.move(self.start_x + 3 * self.x_step, self.start_y)
        self.search_text.setStyleSheet("QLineEdit {border-radius: 5px;}")
        self.search_text.returnPressed.connect(self.search_music)

        self.music_label = QLabel(self.text, self)
        self.music_label.resize(self.window_w - self.start_x, 30)
        self.music_label.move(self.start_x, self.start_y + 3 * self.y_step)
        self.music_label.setAlignment(Qt.AlignCenter)

        self.timer = QTimer()
        self.timer.timeout.connect(self.scroll_text)
        self.timer.start(500)

        self.player.stateChanged.connect(self.handle_player_status_changed)
        self.player.mediaStatusChanged.connect(self.handle_media_status_changed)
        self.player.durationChanged.connect(self.update_duration)
        self.player.positionChanged.connect(self.update_position)

    def play_music(self):
        self.player.setMedia(QMediaContent(QUrl.fromLocalFile(self.current_path)))
        self.player.play()
        self.text = f"当前播放:{self.current_music} —— {self.current_singer} "
        self.music_label.setText(self.text)
        logger.info("正在播放:%s - %s", self.current_music, self.current_singer)

    def change_music_status(self):
        if self.status:
            self.player.pause()
            self.status = False
            self.change_button.setText('继续')
            logger.info("已暂停当前音乐:%s", self.current_music)
        else:
            self.status = True
            self.player.play()
            self.change_button.setText('暂停')
            logger.info("已继续当前音乐:%s", self.current_music)

    def handle_media_status_changed(self, status):
        if status == QMediaPlayer.EndOfMedia:
            logger.info("播放完毕: %s - %s", self.current_music, self.current_singer)
            if not self.show_list:  # 优先判断是否存在音乐
                logger.info("当前列表没有歌曲")
                self.reload_music()
            if self.random_play_status:  # 再判断是否随机播放
                self.play_random()
            else:
                self.play_next()

    def handle_player_status_changed(self, state):
        if state == QMediaPlayer.PlayingState:
            self.update_label = True
        elif state == QMediaPlayer.StoppedState:
            self.update_label = False

    def search_music(self):
        text = self.search_text.text()
        n = 0
        if text:
            for i in self.show_list:
                if text in i.get('singer') or text in i.get('music'):
                    if self.show_favorite_status and i.get('like'):
                        i['show'] = True
                    else:
                        i['show'] = True
                    n += 1
                else:
                    i['show'] = False
            self.load_music()
            logger.info("已查询'%s'相关内容,总计 %d 条", text, n)
        else:
            logger.info('未查询到相关音乐')
            for i in self.music_list:
                i['show'] = True
            self.load_music()
        self.search_text.setText('')

    def clear_table(self):
        self.table_widget.clearContents()
        self.table_widget.setHorizontalHeaderLabels(['歌手', '歌曲'])  # 设置列表头
        self.table_widget.setRowCount(0)
        logger.info("已更新列表")

    def cell_clicked(self, row, column):
        self.current_index = row
        self.show_list = [d for d in self.music_list if d['like']] \
            if self.show_favorite_status else [d for d in self.music_list if d['show']]
        if column == 0:
            logger.info("点击了:%s", self.current_singer)
        if column == 1:
            logger.info("点击了:%s", self.current_music)
        if column == 2:
            if not self.show_list[self.current_index].get('like'):
                self.show_list[self.current_index]['like'] = True
                self.table_widget.setItem(self.current_index, 2, QTableWidgetItem('已收藏'))
                logger.info("收藏音乐:%s - %s", self.show_list[self.current_index]['music'],
                            self.show_list[self.current_index]['singer'])
            else:
                self.show_list[self.current_index]['like'] = False
                self.table_widget.setItem(self.current_index, 2, QTableWidgetItem('收藏'))
                logger.info("取消收藏:%s - %s", self.show_list[self.current_index]['music'],
                            self.show_list[self.current_index]['singer'])

    def play_cell(self, row, column):
        if column == 1:
            self.status = True
            self.change_button.setText('暂停')
            self.current_index = row
            self.update_current_music()
            logger.info("双击播放:%s - %s", self.current_music, self.current_singer)
            self.play_music()

    def play_random(self):
        if self.random_play_status:
            self.random_list = [d for d in self.music_list if d['like']] \
                if self.show_favorite_status else [d for d in self.music_list if d['show']]
        while True:
            n = random.randint(0, len(self.random_list))
            if not self.show_favorite_status:
                if self.music_list[n] in self.random_list:
                    self.random_list.remove(self.music_list[n])
                    break
            else:
                if self.music_list[n] in self.random_list and self.music_list[n]['like']:
                    self.random_list.remove(self.music_list[n])
                    break
        self.current_index = self.music_list[n]['id']
        self.update_current_music()
        self.play_music()

    def change_random_status(self):
        if not self.random_play_status:
            self.random_play_status = True
            self.play_random_btn.setText("取消随机")
            logger.info("切换为随机播放")
            self.play_random()  # 切换时启动一次
        else:
            self.random_play_status = False
            self.play_random_btn.setText("随机播放")
            self.random_list.clear()
            logger.info("取消随机播放")

    def load_music(self):
        self.clear_table()
        n = 0
        for i in self.music_list:
            if self.show_favorite_status:
                if not i.get('like'):
                    continue
                else:
                    i['show'] = True
            if not i.get('show'):
                continue
            self.table_widget.setRowCount(n + 1)
            self.table_widget.setItem(n, 0, QTableWidgetItem(i['singer']))
            self.table_widget.setItem(n, 1, QTableWidgetItem(i['music']))
            like = '已收藏' if i.get('like') else '收藏'
            self.table_widget.setItem(n, 2, QTableWidgetItem(like))
            n += 1
        logger.info('加载音乐列表完成')

    def reload_music(self):
        logger.info('重新加载列表')
        self.music_list = self.find_mp3_files()  # 从本地重新加载
        self.show_list = self.music_list
        self.load_music()

    def show_favorite(self):
        if not self.show_favorite_status:
            logger.info('已进入我的收藏列表')
            self.show_favorite_status = True
            self.favorite_btn.setText('返回列表')
            self.load_music()
        else:
            logger.info('已返回所有列表')
            self.show_favorite_status = False
            self.favorite_btn.setText('我的收藏')
            self.load_music()

    def update_current_music(self):
        self.current_index = self.show_list[self.current_index]['id']
        self.current_path = self.show_list[self.current_index]['path']
        self.current_singer = self.show_list[self.current_index]['singer']
        self.current_music = self.show_list[self.current_index]['music']

    def play_pre(self):
        logger.info("播放上一首")
        if all(d['show'] == False for d in self.show_list):
            logger.info("当前列表没有音乐")
            return
        n = 1
        while True:
            if self.show_list[self.current_index - n]['show']:
                self.current_index = self.current_index - n
                self.update_current_music()
                break
            n += 1
        self.status = True
        self.change_button.setText('暂停')
        logger.info("切换音乐:%s - %s", self.current_music, self.current_singer)
        self.play_music()

    def play_next(self):
        logger.info("播放下一首")
        if all(d['show'] == False for d in self.show_list):
            logger.info("当前列表没有音乐")
            return
        n = 1
        while True:
            if self.show_list[self.current_index + n]['show']:
                self.current_index = self.current_index + n
                self.update_current_music()
                break
            n += 1
        self.status = True
        self.change_button.setText('暂停')
        logger.info("切换音乐为:%s - %s", self.current_music, self.current_singer)
        self.play_music()

    def change_volume(self):
        value = self.volume_slider.value()
        self.player.setVolume(value)
        logger.info("滑动设置音量为%s", value)

    def set_position(self):
        self.music_pro_bar.resize(int(self.position / self.duration * self.music_pro_width), 10)

    def update_duration(self, duration):
        if not self.update_label:
            return
        self.duration = duration

    def update_position(self, position):
        if not self.update_label:
            return
        self.position = position
        self.set_position()

    def scroll_text(self):
        current_text = self.music_label.text()
        scroll_text = current_text[1:] + current_text[0]
        self.music_label.setText(scroll_text)
        self.music_label.setStyleSheet(f"color:{self.rgb2hex(*self.random_color())}")

    def rgb2hex(self, r, g, b):
        return "#{:02x}{:02x}{:02x}".format(r, g, b)

    def find_mp3_files(self):  # 生成音乐文件列表,根据需求自定义
        mp3_files = []
        n = 0
        for root, dirs, files in os.walk(self.music_path):
            for file in files:
                if fnmatch.fnmatch(file, '*.mp3'):
                    if '_' in file:
                        r = {
                            'id': n,
                            'music': file.split('_')[0],
                            'singer': file.split('_')[1].split('.')[0],
                            'path': os.path.join(root, file),
                            'like': False,  # 默认不收藏
                            'show': True,  # 默认展示
                        }
                        mp3_files.append(r)
                        n += 1
        return mp3_files

    def random_color(self):  # 随机颜色
        r, g, b = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
        return r, g, b


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值