这次接入了数据库,增加了翻页模式,更新了功能跳转之间的细节
数据设计:
收藏 like=1时表示被收藏,展示show=0的时候表示表数据被搜索
from peewee import Model, PrimaryKeyField, CharField, BooleanField, MySQLDatabase,IntegerField
database = MySQLDatabase("czh", host="localhost", port=3306,
user="root", password="123456")
class BaseModel(Model):
class Meta:
database = database
class 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,翻页后上下切换已经翻页自动播放等问题
7.26: 新增加载本地音乐和展示音量值的功能,修复无法enter搜索的问题
import fnmatch
import os
import random
import sys
import logging
from 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, QTimer
from database import Music
logging.basicConfig(filename="music.log", filemode="a", 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.random_play_list = []
self.duration = 1
self.position = 0
self.text = "歌曲未加载"
self.total_pages = 0
self.page_size = 20
self.current_page = 1 # 翻页时所在页面
self.play_page = 1 # 正在播放音乐的所在页
self.current_index = 1 # 数据库id
self.current_music_info = Music.select().where(Music.id == self.current_index)[0]
self.current_music = self.current_music_info.song
self.current_singer = self.current_music_info.singer
self.current_path = self.current_music_info.path
self.status = True # 音乐是否在播放
self.update_label = True # 音乐是否在播放-》是否更新进度条
self.random_play_status = False # 是否处于随机播放状态
self.show_favorite_status = False # 是否展示收藏
self.search_status = False # 查询状态
self.load_local = False # 是否加载本地音乐
self.con1 = lambda x: x >= 0 # 普通查询条件
self.con2 = lambda x: x >= 0 # 搜索查询条件
self.con3 = lambda x: x >= 0 # 收藏查询条件
self.load_local_music() # 加载本地音乐文件
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.reload_btn = QPushButton("加载本地", self)
self.reload_btn.clicked.connect(self.confirm_load_local_status)
self.reload_btn.move(self.start_x + 4 * 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.valueChanged.connect(self.update_volume)
self.volume_slider.move(self.start_x, self.start_y + 2 * self.y_step)
self.volume_value = QLabel('50', self)
self.volume_value.resize(25, 20)
self.volume_value.move(self.start_x + int(200 * (50 / 100)*0.9), self.start_y + 2 * self.y_step + 20)
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)
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.change_search_status)
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 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 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.like
if like == 1:
query.like = 0
logger.info("取消收藏:%s %s", query.singer, query.song)
self.table_widget.setItem(row, column, QTableWidgetItem('收藏'))
else:
query.like = 1
logger.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 = True
self.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].id
self.play_page = self.current_page
self.update_current_music()
logger.info("双击播放:%s - %s", self.current_music, self.current_singer)
self.play_music()
def change_search_status(self):
self.current_page = 1
if not self.search_status:
self.search_status = True
self.search_btn.setText("返回列表")
logger.info("点击搜索")
self.con2 = lambda x: x == 0
self.search_music()
else:
self.search_status = False
self.search_btn.setText("搜索")
query = Music.select().where(Music.show == 0)
for q in query:
q.show = 1
q.save()
self.con2 = lambda x: x >= 0
self.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 = False
q.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 = True
self.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 = False
self.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_size
self.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 = i
record.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 confirm_load_local_status(self):
self.load_local = True
self.load_local_music()
def load_local_music(self):
logger.info('开始加载本地音乐')
self.find_mp3_files() if self.load_local else None # 从本地重新加载
def show_favorite(self):
self.current_page = 1
if not self.show_favorite_status:
logger.info('已进入我的收藏列表')
self.show_favorite_status = True
self.favorite_btn.setText('返回列表')
self.con3 = lambda x: x == 1
self.change_search_status() if self.search_status else None
self.load_music()
else:
logger.info('已返回所有列表')
self.show_favorite_status = False
self.favorite_btn.setText('我的收藏')
self.con3 = lambda x: x >= 0
self.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.song
self.current_singer = self.current_music_info.singer
self.current_path = self.current_music_info.path
def play_pre(self):
logger.info("播放上一首")
row = self.current_music_info.row
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)
if row == 0:
self.play_page = self.total_pages if self.play_page == 1 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)
row = len(query) - 1
logger.info(f"已播放至第一首,切换至最后一首")
else:
row -= 1
self.current_index = query[row].id
self.update_current_music()
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("播放下一首")
row = self.current_music_info.row
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)
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 += 1
self.current_index = query[row].id
self.update_current_music()
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 update_volume(self, value):
self.volume_value.setText(str(value))
self.volume_value.move(self.start_x + int(200 * (value / 100) * 0.9),
self.start_y + 2 * self.y_step + 20)
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 click_prev_page(self):
if self.current_page > 1:
logger.info('跳转上一页')
self.current_page -= 1
self.load_music()
def click_next_page(self):
if self.current_page < self.total_pages:
logger.info('跳转点击下一页')
self.current_page += 1
self.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): # 生成音乐文件列表,根据需求自定义
n, m = 0, 0
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:
continue
else:
singer = file.split('_')[1].split('.')[0]
song = file.split('_')[0]
tp = Music(singer=singer, song=song,
path=path, like=False, show=True)
tp.save()
logger.info("新增:%s - %s", singer, song)
m += 1
n += 1
logger.info("加载完成,新增%d首歌曲,总计%d首歌曲", m, n)
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_())