这次接入了数据库,增加了翻页模式,更新了功能跳转之间的细节
数据设计:
收藏 like=1时表示被收藏,展示show=0的时候表示表数据被搜索
from peewee import Model, PrimaryKeyField, CharField, BooleanField, MySQLDatabase,IntegerFielddatabase = MySQLDatabase("czh", host="localhost", port=3306,user="root", password="123456")class BaseModel(Model):class Meta:database = databaseclass Music(BaseModel):id = PrimaryKeyField()singer = CharField(max_length=264, help_text='歌手')song = CharField(max_length=264, help_text='歌曲')path = CharField(max_length=264, help_text='路径')like = BooleanField(default=False, help_text='收藏')show = BooleanField(default=True, help_text='展示')row = IntegerField(help_text='行数')
在load_music中想到了lambda来传条件参数,分别给三个字段设计了对应的条件con1,con2,con3,只有在使用相应功能时修改条件即可实现函数复用了
records = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like)).offset(start_index).limit(self.page_size)
代码:
注:
7.25:修改了上下切换音乐的BUG,翻页后上下切换已经翻页自动播放等问题
import fnmatch
import os
import random
import sys
import loggingfrom PyQt5.QtGui import QPalette, QBrush, QPixmap, QLinearGradient, QColor, QIntValidator
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QSlider, QLabel, \QTableWidget, QLineEdit, QTableWidgetItem
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl, Qt, QTimerfrom database import Musiclogging.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 = 10self.x_step = 100self.start_y = 10self.y_step = 40self.window_w, self.window_h = 500, 800self.duration = 1self.position = 0self.text = "歌曲未加载"self.random_play_list= []self.total_pages = 0self.page_size = 20self.current_page = 1 # 翻页时所在页面self.play_page = 1 # 正在播放音乐的所在页self.current_index = 1 # 数据库idself.current_music_info = Music.select().where(Music.id == self.current_index)[0]self.current_music = self.current_music_info.songself.current_singer = self.current_music_info.singerself.current_path = self.current_music_info.pathself.status = True # 音乐是否在播放self.update_label = True # 音乐是否在播放-》是否更新进度条self.random_play_status = False # 是否处于随机播放状态self.show_favorite_status = False # 是否展示收藏self.search_status = False # 查询状态self.con1 = lambda x: x >= 0 # 普通查询条件self.con2 = lambda x: x >= 0 # 搜索查询条件self.con3 = lambda x: x >= 0 # 收藏查询条件self.load_local_music(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 - 210self.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)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.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 - 5 * 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.prev_button = QPushButton("上一页", self)self.next_button = QPushButton("下一页", self)self.prev_button.clicked.connect(self.click_prev_page)self.next_button.clicked.connect(self.click_next_page)self.prev_button.move(100, self.window_h - self.y_step)self.next_button.move(200, self.window_h - self.y_step)self.int_validator = QIntValidator(0, self.total_pages)self.jump_page_btn = QPushButton("跳转", self)self.jump_page_btn.clicked.connect(self.jump_page)self.jump_page_btn.move(300, self.window_h - self.y_step)self.jump_page_text = QLineEdit(self)self.jump_page_text.setValidator(self.int_validator)self.jump_page_text.move(400, self.window_h - self.y_step)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.change_search_status)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 = Falseself.change_button.setText('继续')logger.info("已暂停当前音乐:%s", self.current_music)else:self.status = Trueself.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 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 = Trueelif state == QMediaPlayer.StoppedState:self.update_label = Falsedef clear_table(self):self.table_widget.clearContents()self.table_widget.setHorizontalHeaderLabels(['歌手', '歌曲']) # 设置列表头self.table_widget.setRowCount(0)logger.info("已更新列表")def cell_clicked(self, row, column):if column != 2:text = self.table_widget.item(row, column).text()logger.info("点击了:%s", text)if column == 2:song = self.table_widget.item(row, column - 1).text()singer = self.table_widget.item(row, column - 2).text()query = Music.select().where((Music.song == song) & (Music.singer == singer))[0]like = query.likeif like == 1:query.like = 0logger.info("取消收藏:%s %s", query.singer, query.song)self.table_widget.setItem(row, column, QTableWidgetItem('收藏'))else:query.like = 1logger.info("收藏音乐:%s %s", query.singer, query.song)self.table_widget.setItem(row, column, QTableWidgetItem('已收藏'))query.save()def play_cell(self, row, column):if column == 1:self.status = Trueself.change_button.setText('暂停')song = self.table_widget.item(row, column).text()singer = self.table_widget.item(row, column - 1).text()query = Music.select().where((Music.song == song) & (Music.singer == singer))if query:self.current_index = query[0].idself.play_page = self.current_pageself.update_current_music()logger.info("双击播放:%s - %s", self.current_music, self.current_singer)self.play_music()def change_search_status(self):self.current_page = 1if not self.search_status:self.search_status = Trueself.search_btn.setText("返回列表")logger.info("点击搜索")self.con2 = lambda x: x == 0self.search_music()else:self.search_status = Falseself.search_btn.setText("搜索")query = Music.select().where(Music.show == 0)for q in query:q.show = 1q.save()self.con2 = lambda x: x >= 0self.load_music()self.search_text.setText('')logger.info("取消查询")def search_music(self):text = self.search_text.text()logger.info("点击查询:%s", text)if text:query = Music.select().where((Music.singer.contains(text)) | (Music.song.contains(text)))for q in query:q.show = Falseq.save()logger.info("查询到 %d条 相关内容", len(query))self.load_music()else:logger.info('未查询到相关音乐')def change_random_status(self):if not self.random_play_status:self.random_play_status = Trueself.play_random_btn.setText("取消随机")logger.info("切换为随机播放")query = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like))self.random_play_list= [q.id for q in query]logger.info("已加载随机列表")self.play_random() # 切换时启动一次else:self.random_play_status = Falseself.play_random_btn.setText("随机播放")logger.info("取消随机播放")def play_random(self):if not self.random_play_list:self.change_random_status() # 取消随机self.change_random_status() # 重新随机self.current_index = random.choice(self.random_play_list)self.random_play_list.remove(self.current_index)logger.info("已获取随机音乐,剩余 %d 首", len(self.random_play_list))self.update_current_music()self.play_music()def load_music(self):total_records = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like)).count()self.total_pages = (total_records + self.page_size - 1) // self.page_sizeself.int_validator = QIntValidator(0, self.total_pages)records = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like)).offset((self.current_page - 1) * self.page_size).limit(self.page_size)self.table_widget.setRowCount(len(records))self.table_widget.setColumnCount(3)for i, record in enumerate(records):record.row = irecord.save()like = "收藏" if record.like == 0 else "已收藏"self.table_widget.setItem(i, 0, QTableWidgetItem(record.singer))self.table_widget.setItem(i, 1, QTableWidgetItem(record.song))self.table_widget.setItem(i, 2, QTableWidgetItem(like))self.table_widget.setHorizontalHeaderLabels(['歌手', '歌曲', '♥'])self.statusBar().showMessage(f'{self.current_page}/{self.total_pages}')logger.info('已加载音乐列表')def load_local_music(self, ensure):logger.info('重新加载列表')self.find_mp3_files() if ensure else None # 从本地重新加载def show_favorite(self):self.current_page = 1if not self.show_favorite_status:logger.info('已进入我的收藏列表')self.show_favorite_status = Trueself.favorite_btn.setText('返回列表')self.con3 = lambda x: x == 1self.change_search_status() if self.search_status else Noneself.load_music()else:logger.info('已返回所有列表')self.show_favorite_status = Falseself.favorite_btn.setText('我的收藏')self.con3 = lambda x: x >= 0self.load_music()def update_current_music(self):self.current_music_info = Music.select().where(Music.id == self.current_index)[0]self.current_music = self.current_music_info.songself.current_singer = self.current_music_info.singerself.current_path = self.current_music_info.pathdef play_pre(self):logger.info("播放上一首")row = self.current_music_info.rowquery = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like)).offset((self.play_page - 1) * self.page_size).limit(self.page_size)if row == 0:self.play_page = self.total_pages if self.play_page == 1 else self.play_page - 1query = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like)).offset((self.play_page - 1) * self.page_size).limit(self.page_size)row = len(query) - 1logger.info(f"已播放至第一首,切换至最后一首")else:row -= 1self.current_index = query[row].idself.update_current_music()self.status = Trueself.change_button.setText('暂停')logger.info("切换音乐为:%s - %s", self.current_music, self.current_singer)self.play_music()def play_next(self):logger.info("播放下一首")row = self.current_music_info.rowquery = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like)).offset((self.play_page - 1) * self.page_size).limit(self.page_size)if row == len(query) - 1: # 该页最后一条数据row = 0 # 切换至首条数据self.play_page = 1 if self.play_page == self.total_pages else self.play_page + 1 # 若为最后一页切换至首页query = Music.select().where(self.con1(Music.id) & self.con2(Music.show) & self.con3(Music.like)).offset((self.play_page - 1) * self.page_size).limit(self.page_size) # 重新查询logger.info(f"已播放至最后一首,切换至第一首")else:row += 1self.current_index = query[row].idself.update_current_music()self.status = Trueself.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:returnself.duration = durationdef update_position(self, position):if not self.update_label:returnself.position = positionself.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 click_prev_page(self):if self.current_page > 1:logger.info('跳转上一页')self.current_page -= 1self.load_music()def click_next_page(self):if self.current_page < self.total_pages:logger.info('跳转点击下一页')self.current_page += 1self.load_music()def jump_page(self):self.current_page = int(self.jump_page_text.text())logger.info('输入跳转 %d 页', self.current_page)self.load_music()def rgb2hex(self, r, g, b):return "#{:02x}{:02x}{:02x}".format(r, g, b)def find_mp3_files(self): # 生成音乐文件列表,根据需求自定义for root, dirs, files in os.walk(self.music_path):for file in files:if fnmatch.fnmatch(file, '*.mp3'):if '_' in file:path = os.path.join(root, file)s = Music.select().where(Music.path == path)if s:continueelse:tp = Music(singer=file.split('_')[1].split('.')[0], song=file.split('_')[0],path=path, like=False, show=True)tp.save()def random_color(self): # 随机颜色r, g, b = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)return r, g, bif __name__ == "__main__":app = QApplication(sys.argv)player = MusicPlayer()player.show()sys.exit(app.exec_())