|
|
|
@ -0,0 +1,878 @@
|
|
|
|
|
import os
|
|
|
|
|
import re
|
|
|
|
|
import threading
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
import wx
|
|
|
|
|
import wx.adv
|
|
|
|
|
import wx.lib.mixins.listctrl
|
|
|
|
|
from pygame import mixer
|
|
|
|
|
|
|
|
|
|
import ruijia
|
|
|
|
|
|
|
|
|
|
user_music_folder = os.path.expanduser(r'~\Music')
|
|
|
|
|
DEFAULT_SETTING = { # 默认设置
|
|
|
|
|
# 用户可设置
|
|
|
|
|
'bg_color': '#88fefe',
|
|
|
|
|
'font_size': 13,
|
|
|
|
|
'text_color': '#000000',
|
|
|
|
|
'lyrics_fg_color': '#ff1122',
|
|
|
|
|
'lyrics_bg_color': '#000000',
|
|
|
|
|
'volume': 0.3, # 自动保存
|
|
|
|
|
'font': '宋体',
|
|
|
|
|
'underline': False,
|
|
|
|
|
'music_folder_type': 0, # 0表示默认目录,1表示用户音乐路径,2表示自定义路径
|
|
|
|
|
'music_folder_list': ['music_folder', user_music_folder, ''],
|
|
|
|
|
# 用户不可设置
|
|
|
|
|
'max_music_name_len': 30,
|
|
|
|
|
'app_title': 'music_player',
|
|
|
|
|
}
|
|
|
|
|
FONT_LIST = ['黑体', '楷体', '宋体', '仿宋', 'Arial', '等线', '华文琥珀', '华文行楷', '华文新魏'] # 字体列表
|
|
|
|
|
FONT_LIST = [font for font in FONT_LIST if wx.Font().SetFaceName(font)] # 字体可能会不存在
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_lyrics(lyric_path: str) -> list:
|
|
|
|
|
"""
|
|
|
|
|
从歌词文件中读取歌词返回列表
|
|
|
|
|
:param lyric_path:歌词路径str
|
|
|
|
|
:return:歌词列表(时间,文字)元组
|
|
|
|
|
"""
|
|
|
|
|
lyric_row_reg = '\[[0-9]{2}:[0-9]{2}.[0-9]{2,}\]'
|
|
|
|
|
if os.path.exists(lyric_path):
|
|
|
|
|
with open(lyric_path, 'r', encoding="utf-8") as f:
|
|
|
|
|
content_list = [line.strip() for line in f.readlines()] # 去除每行的\n
|
|
|
|
|
lyrics_list = []
|
|
|
|
|
for content in content_list:
|
|
|
|
|
while re.match(lyric_row_reg, content): # 一句歌词可能有多个时间
|
|
|
|
|
lyric_time = float(content[1:3]) * 60 + float(content[4:6]) + float(content[7:9]) / 100
|
|
|
|
|
lyric = content[content.rindex(']') + 1:]
|
|
|
|
|
lyrics_list.append((lyric_time, lyric))
|
|
|
|
|
content = content[content.index(']') + 1:]
|
|
|
|
|
lyrics_list.sort(key=lambda x: x[0]) # 按时间排序
|
|
|
|
|
return lyrics_list
|
|
|
|
|
else:
|
|
|
|
|
return [(0, '纯音乐或无歌词')]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_setting() -> dict:
|
|
|
|
|
"""
|
|
|
|
|
从设置文件中读取设置,如不存在则使用默认设置
|
|
|
|
|
:return: 设置项目的字典
|
|
|
|
|
"""
|
|
|
|
|
if os.path.exists('setting.txt'):
|
|
|
|
|
with open('setting.txt', 'r', encoding='utf-8') as f:
|
|
|
|
|
setting_dic = eval(f.read())
|
|
|
|
|
else:
|
|
|
|
|
setting_dic = DEFAULT_SETTING
|
|
|
|
|
return setting_dic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RankListNotebook(wx.Notebook):
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent):
|
|
|
|
|
wx.Notebook.__init__(self, parent)
|
|
|
|
|
self.SetFont(parent.user_font)
|
|
|
|
|
self.text_color = parent.setting_dic['text_color']
|
|
|
|
|
url_dic = {
|
|
|
|
|
'酷狗飙升榜': 'http://www.kugou.com/yy/rank/home/{}-6666.html',
|
|
|
|
|
'酷狗TOP500': 'http://www.kugou.com/yy/rank/home/{}-8888.html',
|
|
|
|
|
'华语新歌榜': 'http://www.kugou.com/yy/rank/home/{}-31308.html',
|
|
|
|
|
'酷狗雷达榜': 'https://www.kugou.com/yy/rank/home/{}-37361.html'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 进度dialog初始化
|
|
|
|
|
max_ = 200*len(url_dic)
|
|
|
|
|
dlg = wx.ProgressDialog("处理中", "获取数据中", maximum=max_, parent=self,
|
|
|
|
|
style=wx.PD_AUTO_HIDE | wx.PD_CAN_ABORT | wx.PD_APP_MODAL)
|
|
|
|
|
count = 0
|
|
|
|
|
|
|
|
|
|
for name in url_dic: # 每个排行榜为一页
|
|
|
|
|
page = wx.ScrolledWindow(self)
|
|
|
|
|
self.AddPage(page, name) # TODO 页面描述文字不可设置字体颜色
|
|
|
|
|
page.SetScrollbars(10, 10, 1, 1)
|
|
|
|
|
|
|
|
|
|
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
|
page.SetSizer(sizer)
|
|
|
|
|
sizer.Add((200, -1), 0) # 设置榜单左方空白区域
|
|
|
|
|
|
|
|
|
|
column_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
|
sizer.Add(column_sizer, proportion=1)
|
|
|
|
|
column_sizer.Add((0, 50), 0) # 榜单上方空白
|
|
|
|
|
|
|
|
|
|
for i in range(50): # 进度条更新
|
|
|
|
|
dlg.Update(count := count + 1)
|
|
|
|
|
if dlg.WasCancelled(): # 当dialog取消时退出循环
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
ranking_list = ruijia.main(url_dic[name]) # 从ruijia.py获取榜单
|
|
|
|
|
for line in range(100):
|
|
|
|
|
song = str(line+1) + ranking_list[line]['song'] # 排名加上歌名——歌手
|
|
|
|
|
st = wx.StaticText(page, -1, song)
|
|
|
|
|
st.SetFont(parent.user_font)
|
|
|
|
|
st.SetForegroundColour(self.text_color)
|
|
|
|
|
column_sizer.Add(st, 1, flag=wx.TOP, border=5)
|
|
|
|
|
dlg.Update(count := count + 1) # 进度条更新
|
|
|
|
|
|
|
|
|
|
column_sizer.Add((0, 50), 0) # 榜单下方空白
|
|
|
|
|
for i in range(50): # 进度条更新
|
|
|
|
|
dlg.Update(count := count + 1)
|
|
|
|
|
if dlg.WasCancelled(): # 当dialog取消时退出循环
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingPanel(wx.Panel):
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent):
|
|
|
|
|
wx.Panel.__init__(self, parent)
|
|
|
|
|
self.parent = parent
|
|
|
|
|
self.present_setting = parent.setting_dic # 当前(未修改)的设置
|
|
|
|
|
self.unsaved_setting = parent.unsaved_setting # 修改后未保存的设置
|
|
|
|
|
self.bg_color = self.present_setting['bg_color']
|
|
|
|
|
self.text_color = self.present_setting['text_color']
|
|
|
|
|
|
|
|
|
|
# 使设置panel居中显示
|
|
|
|
|
setting_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
|
self.SetSizer(setting_sizer)
|
|
|
|
|
setting_sizer.AddStretchSpacer(1)
|
|
|
|
|
|
|
|
|
|
self.main_panel = wx.Panel(self, size=(800, 550))
|
|
|
|
|
self.main_panel.SetFont(parent.user_font)
|
|
|
|
|
self.main_panel.SetForegroundColour(self.text_color)
|
|
|
|
|
setting_sizer.Add(self.main_panel, 0, wx.ALIGN_CENTRE)
|
|
|
|
|
|
|
|
|
|
# 标题----------------------------------------------------------------------------------------------------------
|
|
|
|
|
title = wx.StaticText(self.main_panel, -1, '设置(重启后生效)')
|
|
|
|
|
title.SetFont(wx.Font(pointSize=23, family=wx.FONTFAMILY_DEFAULT, style=wx.FONTSTYLE_NORMAL,
|
|
|
|
|
weight=wx.FONTWEIGHT_NORMAL, faceName='黑体')) # title字体及颜色不可更改
|
|
|
|
|
title.SetForegroundColour('#000')
|
|
|
|
|
# 重置按钮
|
|
|
|
|
reset_button = wx.Button(self.main_panel, -1, '重置', pos=(300, 5))
|
|
|
|
|
reset_button.SetForegroundColour(self.text_color)
|
|
|
|
|
reset_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
reset_button.Bind(wx.EVT_LEFT_DOWN, self.reset)
|
|
|
|
|
|
|
|
|
|
# 歌词前景背景颜色 ------------------------------------------------------------------------------------------------
|
|
|
|
|
self.box1 = wx.StaticBox(self.main_panel, -1, "歌词设置", pos=(50, 50), size=(700, 70))
|
|
|
|
|
|
|
|
|
|
# 歌词前景色文字及按钮
|
|
|
|
|
text1 = f"歌词前景色:{self.present_setting['lyrics_fg_color']}"
|
|
|
|
|
self.lyrics_fg_color_text = wx.StaticText(self.box1, -1, text1, pos=(30, 30))
|
|
|
|
|
self.lyrics_fg_color_text.SetForegroundColour(self.present_setting['lyrics_fg_color'])
|
|
|
|
|
self.lyrics_fg_color_button = wx.Button(self.box1, -1, '修改', pos=(230, 25))
|
|
|
|
|
self.lyrics_fg_color_button.SetForegroundColour(self.text_color)
|
|
|
|
|
self.lyrics_fg_color_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.lyrics_fg_color_button.Bind(wx.EVT_LEFT_DOWN, self.change_lyrics_fg_color)
|
|
|
|
|
|
|
|
|
|
# 歌词背景色文字及按钮
|
|
|
|
|
text2 = f"歌词背景色:{self.present_setting['lyrics_bg_color']}"
|
|
|
|
|
self.lyrics_bg_color_text = wx.StaticText(self.box1, -1, text2, pos=(400, 30))
|
|
|
|
|
self.lyrics_bg_color_text.SetForegroundColour(self.present_setting['lyrics_bg_color'])
|
|
|
|
|
self.lyrics_bg_color_button = wx.Button(self.box1, -1, '修改', pos=(600, 25))
|
|
|
|
|
self.lyrics_bg_color_button.SetForegroundColour(self.text_color)
|
|
|
|
|
self.lyrics_bg_color_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.lyrics_bg_color_button.Bind(wx.EVT_LEFT_DOWN, self.change_lyrics_bg_color)
|
|
|
|
|
|
|
|
|
|
# 界面颜色,字体设置 ----------------------------------------------------------------------------------------------
|
|
|
|
|
self.box2 = wx.StaticBox(self.main_panel, -1, "界面设置", pos=(50, 145), size=(700, 150))
|
|
|
|
|
|
|
|
|
|
# 页面背景颜色文字及按钮
|
|
|
|
|
text3 = f"页面背景色:{self.bg_color}"
|
|
|
|
|
self.bg_color_text = wx.StaticText(self.box2, -1, text3, pos=(30, 30))
|
|
|
|
|
self.bg_color_text.SetForegroundColour(self.text_color)
|
|
|
|
|
self.bg_color_button = wx.Button(self.box2, -1, '修改', pos=(230, 25))
|
|
|
|
|
self.bg_color_button.SetForegroundColour(self.text_color)
|
|
|
|
|
self.bg_color_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.bg_color_button.Bind(wx.EVT_LEFT_DOWN, self.change_bg_color)
|
|
|
|
|
|
|
|
|
|
# 文字颜色文字及按钮
|
|
|
|
|
text4 = f" 文字颜色 :{self.text_color}"
|
|
|
|
|
self.text_color_text = wx.StaticText(self.box2, -1, text4, pos=(400, 30))
|
|
|
|
|
self.text_color_text.SetForegroundColour(self.text_color)
|
|
|
|
|
self.text_color_button = wx.Button(self.box2, -1, '修改', pos=(600, 25))
|
|
|
|
|
self.text_color_button.SetForegroundColour(self.text_color)
|
|
|
|
|
self.text_color_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.text_color_button.Bind(wx.EVT_LEFT_DOWN, self.change_text_color)
|
|
|
|
|
|
|
|
|
|
# 文字大小文字及text ctrl和spin button
|
|
|
|
|
self.font_size_text = wx.StaticText(self.box2, -1, '文字大小:', pos=(30, 90))
|
|
|
|
|
font_size = self.present_setting['font_size']
|
|
|
|
|
self.font_size = wx.TextCtrl(self.box2, -1, str(font_size), pos=(120, 85),
|
|
|
|
|
size=(40, -1), style=wx.TE_READONLY | wx.TE_CENTRE)
|
|
|
|
|
self.font_size.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.font_size.SetForegroundColour(self.text_color)
|
|
|
|
|
font_size_width = self.font_size.GetSize()[1]
|
|
|
|
|
self.font_size_spin = wx.SpinButton(self.box2, -1, pos=(160, 85), size=(15, font_size_width))
|
|
|
|
|
self.font_size_spin.SetRange(9, 15)
|
|
|
|
|
self.font_size_spin.SetValue(font_size)
|
|
|
|
|
self.font_size_spin.Bind(wx.EVT_SPIN, self.change_font_size)
|
|
|
|
|
|
|
|
|
|
# 文字字体combobox
|
|
|
|
|
self.font_name_text = wx.StaticText(self.box2, -1, '当前字体:', pos=(280, 90))
|
|
|
|
|
self.font_choice = wx.ComboBox(self.box2, -1, pos=(380, 85),
|
|
|
|
|
size=(150, -1), choices=FONT_LIST)
|
|
|
|
|
self.font_choice.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.font_choice.SetForegroundColour(self.text_color)
|
|
|
|
|
self.font_choice.SetSelection(FONT_LIST.index(self.present_setting['font']))
|
|
|
|
|
self.font_choice.Bind(wx.EVT_TEXT, self.change_font)
|
|
|
|
|
|
|
|
|
|
# 是否显示下划线的checkbox
|
|
|
|
|
self.underline = wx.CheckBox(self.box2, -1, '下划线', pos=(600, 88))
|
|
|
|
|
self.underline.SetValue(self.present_setting['underline'])
|
|
|
|
|
self.underline.Bind(wx.EVT_CHECKBOX, self.is_underline)
|
|
|
|
|
|
|
|
|
|
# 播放路径设置 ---------------------------------------------------------------------------------------------------
|
|
|
|
|
folder_type = self.present_setting['music_folder_type']
|
|
|
|
|
self.box3_text = f"歌曲路径选择({['默认路径','用户音乐目录','自定义路径'][folder_type]})"
|
|
|
|
|
self.box3 = wx.StaticBox(self.main_panel, -1, self.box3_text,
|
|
|
|
|
pos=(50, 320), size=(700, 100))
|
|
|
|
|
wx.StaticText(self.box3, -1, '当前路径:', pos=(10, 70))
|
|
|
|
|
|
|
|
|
|
# 路径textctrl
|
|
|
|
|
path = os.path.abspath(self.present_setting['music_folder_list'][folder_type])
|
|
|
|
|
self.path_text_ctrl = wx.TextCtrl(self.box3, -1, path, pos=(100, 65), size=(480, -1))
|
|
|
|
|
self.path_text_ctrl.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.path_text_ctrl.SetForegroundColour(self.text_color)
|
|
|
|
|
|
|
|
|
|
# 默认路径按钮
|
|
|
|
|
self.folder_button1 = wx.Button(self.box3, -1, label="默认路径", pos=(100, 27))
|
|
|
|
|
self.folder_button1.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.folder_button1.SetForegroundColour(self.text_color)
|
|
|
|
|
self.folder_button1.Bind(wx.EVT_LEFT_DOWN, self.select_path_type1)
|
|
|
|
|
|
|
|
|
|
# 用户音乐目录按钮
|
|
|
|
|
self.folder_button2 = wx.Button(self.box3, -1, label="用户音乐目录", pos=(233, 27))
|
|
|
|
|
self.folder_button2.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.folder_button2.SetForegroundColour(self.text_color)
|
|
|
|
|
self.folder_button2.Bind(wx.EVT_LEFT_DOWN, self.select_path_type2)
|
|
|
|
|
|
|
|
|
|
# 自定义路径按钮
|
|
|
|
|
self.button3 = wx.Button(self.box3, -1, label="自定义路径", pos=(400, 27))
|
|
|
|
|
self.button3.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.button3.SetForegroundColour(self.text_color)
|
|
|
|
|
self.button3.Bind(wx.EVT_LEFT_DOWN, self.select_path_type3)
|
|
|
|
|
|
|
|
|
|
# 路径选择按钮(自定义路径时显示)
|
|
|
|
|
self.path_select_button = wx.Button(self.box3, label="选择路径", pos=(580, 27))
|
|
|
|
|
self.path_select_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.path_select_button.SetForegroundColour(self.text_color)
|
|
|
|
|
self.path_select_button.Bind(wx.EVT_LEFT_DOWN, self.select_path)
|
|
|
|
|
|
|
|
|
|
# 确认路径按钮(自定义路径时显示)
|
|
|
|
|
self.folder_confirm_button = wx.Button(self.box3, label="确认", pos=(607, 65))
|
|
|
|
|
self.folder_confirm_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.folder_confirm_button.SetForegroundColour(self.text_color)
|
|
|
|
|
self.folder_confirm_button.Bind(wx.EVT_LEFT_DOWN, self.folder_confirm)
|
|
|
|
|
self.hide_show_button(folder_type == 2) # 是否显示路径选择按钮和确认路径按钮
|
|
|
|
|
|
|
|
|
|
setting_sizer.AddStretchSpacer(1)
|
|
|
|
|
|
|
|
|
|
def reset(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
重置为默认设置
|
|
|
|
|
"""
|
|
|
|
|
for i in DEFAULT_SETTING:
|
|
|
|
|
self.unsaved_setting[i] = DEFAULT_SETTING[i]
|
|
|
|
|
dlg = wx.MessageDialog(self, r'成功重置所有设置,重启后生效', '成功', wx.OK)
|
|
|
|
|
dlg.ShowModal()
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def change_lyrics_fg_color(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改歌词前景色
|
|
|
|
|
"""
|
|
|
|
|
dlg = wx.ColourDialog(self)
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
color = dlg.GetColourData().GetColour().GetAsString(flags=wx.C2S_HTML_SYNTAX)
|
|
|
|
|
self.unsaved_setting['lyrics_fg_color'] = color
|
|
|
|
|
self.lyrics_fg_color_text.SetForegroundColour(color)
|
|
|
|
|
self.lyrics_fg_color_text.SetLabel(f'歌词前景色:{color}')
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def change_lyrics_bg_color(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改歌词背景色
|
|
|
|
|
"""
|
|
|
|
|
dlg = wx.ColourDialog(self)
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
color = dlg.GetColourData().GetColour().GetAsString(flags=wx.C2S_HTML_SYNTAX)
|
|
|
|
|
self.unsaved_setting['lyrics_bg_color'] = color
|
|
|
|
|
self.lyrics_bg_color_text.SetForegroundColour(color)
|
|
|
|
|
self.lyrics_bg_color_text.SetLabel(f'歌词背景色:{color}')
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def change_bg_color(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改界面背景色
|
|
|
|
|
"""
|
|
|
|
|
dlg = wx.ColourDialog(self)
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
color = dlg.GetColourData().GetColour().GetAsString(flags=wx.C2S_HTML_SYNTAX)
|
|
|
|
|
self.unsaved_setting['bg_color'] = color
|
|
|
|
|
self.bg_color_text.SetBackgroundColour(color)
|
|
|
|
|
self.bg_color_text.SetLabel(f'页面背景色:{color}')
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def change_text_color(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改文字颜色(不包括歌词)
|
|
|
|
|
"""
|
|
|
|
|
dlg = wx.ColourDialog(self)
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
color = dlg.GetColourData().GetColour().GetAsString(flags=wx.C2S_HTML_SYNTAX)
|
|
|
|
|
self.unsaved_setting['text_color'] = color
|
|
|
|
|
self.text_color_text.SetForegroundColour(color)
|
|
|
|
|
self.text_color_text.SetLabel(f' 文字颜色 :{color}')
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def change_font_size(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改文字大小
|
|
|
|
|
"""
|
|
|
|
|
size = self.font_size_spin.GetValue()
|
|
|
|
|
self.font_size.SetLabel(str(size))
|
|
|
|
|
self.unsaved_setting['font_size'] = size
|
|
|
|
|
|
|
|
|
|
def change_font(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改文字字体
|
|
|
|
|
"""
|
|
|
|
|
font_name = evt.GetEventObject().GetValue()
|
|
|
|
|
if font_name in FONT_LIST:
|
|
|
|
|
self.unsaved_setting['font'] = font_name
|
|
|
|
|
# font = self.parent.user_font 此方式会在后续设置中更改父类中的原字体
|
|
|
|
|
font = wx.Font(self.parent.user_font)
|
|
|
|
|
font.SetFaceName(font_name)
|
|
|
|
|
self.font_name_text.SetFont(font)
|
|
|
|
|
self.font_choice.SetFont(font)
|
|
|
|
|
|
|
|
|
|
def is_underline(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改是否显示下划线
|
|
|
|
|
"""
|
|
|
|
|
underline = self.underline.GetValue()
|
|
|
|
|
# font = self.parent.user_font 此方式会在后续设置中更改父类中的原字体
|
|
|
|
|
font = wx.Font(self.parent.user_font)
|
|
|
|
|
font.SetUnderlined(underline)
|
|
|
|
|
self.underline.SetFont(font)
|
|
|
|
|
self.unsaved_setting['underline'] = underline
|
|
|
|
|
|
|
|
|
|
def select_path_type1(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改路径类型(默认路径)
|
|
|
|
|
"""
|
|
|
|
|
self.hide_show_button(0)
|
|
|
|
|
self.box3.SetLabel('歌曲路径选择(默认路径)')
|
|
|
|
|
self.box3.Refresh()
|
|
|
|
|
self.unsaved_setting['music_folder_type'] = 0
|
|
|
|
|
folder_path = os.path.abspath('music_folder')
|
|
|
|
|
self.path_text_ctrl.SetLabel(folder_path)
|
|
|
|
|
dlg = wx.MessageDialog(self, r'成功设置路径:默认路径(.\music_folder)', '成功', wx.OK)
|
|
|
|
|
dlg.ShowModal()
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def select_path_type2(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改路径类型(用户音乐目录)
|
|
|
|
|
"""
|
|
|
|
|
self.hide_show_button(0)
|
|
|
|
|
self.box3.SetLabel('歌曲路径选择(用户音乐目录)')
|
|
|
|
|
self.box3.Refresh()
|
|
|
|
|
self.unsaved_setting['music_folder_type'] = 1
|
|
|
|
|
folder_path = os.path.expanduser('~\Music')
|
|
|
|
|
self.path_text_ctrl.SetLabel(folder_path)
|
|
|
|
|
dlg = wx.MessageDialog(self, rf'成功设置路径:用户音乐目录({folder_path})', '成功', wx.OK)
|
|
|
|
|
dlg.ShowModal()
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def select_path_type3(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
修改路径类型(自定义路径)并显示选择路径及确认按钮
|
|
|
|
|
"""
|
|
|
|
|
self.hide_show_button(1)
|
|
|
|
|
self.box3.SetLabel('歌曲路径选择(自定义路径)')
|
|
|
|
|
self.box3.Refresh()
|
|
|
|
|
|
|
|
|
|
def hide_show_button(self, op: int):
|
|
|
|
|
"""
|
|
|
|
|
是否显示选择路径及确认按钮及路径textctrl是否可修改
|
|
|
|
|
"""
|
|
|
|
|
if op == 0:
|
|
|
|
|
self.path_select_button.Hide()
|
|
|
|
|
self.folder_confirm_button.Hide()
|
|
|
|
|
self.path_text_ctrl.SetEditable(False)
|
|
|
|
|
elif op == 1:
|
|
|
|
|
self.path_select_button.Show()
|
|
|
|
|
self.folder_confirm_button.Show()
|
|
|
|
|
self.path_text_ctrl.SetEditable(True)
|
|
|
|
|
|
|
|
|
|
def select_path(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
选择路径将其显示在路径textctrl中
|
|
|
|
|
"""
|
|
|
|
|
dlg = wx.DirDialog(self, "选择音乐文件夹", defaultPath=os.getcwd(), style=wx.DD_DEFAULT_STYLE)
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
path = dlg.GetPath()
|
|
|
|
|
self.path_text_ctrl.SetLabel(path)
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
def folder_confirm(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
确认路径
|
|
|
|
|
"""
|
|
|
|
|
path = self.path_text_ctrl.GetValue()
|
|
|
|
|
if path == 'fly': # fly
|
|
|
|
|
dlg = wx.Dialog(self, size=(333, 280), title='fly')
|
|
|
|
|
wx.adv.AnimationCtrl(dlg, -1, wx.adv.Animation(r'icon\fly')).Play()
|
|
|
|
|
elif os.path.exists(path): # 路径存在
|
|
|
|
|
self.unsaved_setting['music_folder_list'][2] = path
|
|
|
|
|
dlg = wx.MessageDialog(self, '成功设置路径:'+path, '成功', wx.OK)
|
|
|
|
|
self.unsaved_setting['music_folder_type'] = 2
|
|
|
|
|
else: # 路径不存在
|
|
|
|
|
dlg = wx.MessageDialog(self, '请输入正确的路径', '错误', wx.OK | wx.ICON_ERROR)
|
|
|
|
|
dlg.ShowModal()
|
|
|
|
|
dlg.Destroy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AutoWidthListCtrl(wx.ListCtrl, wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin):
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent, *args, **kw):
|
|
|
|
|
# 设置歌曲列表list ctrl的列随sizer的改变而自动改变宽度
|
|
|
|
|
wx.ListCtrl.__init__(self, parent, *args, **kw)
|
|
|
|
|
wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MainFrame(wx.Frame):
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.setting_dic = get_setting()
|
|
|
|
|
wx.Frame.__init__(self, None, -1, self.setting_dic['app_title'], pos=(50, 50))
|
|
|
|
|
self.SetSize(1280, 720)
|
|
|
|
|
self.SetSizeHints((1080, 600)) # 设置页面最小大小
|
|
|
|
|
|
|
|
|
|
self.bg_color = self.setting_dic['bg_color']
|
|
|
|
|
self.font_size = self.setting_dic['font_size']
|
|
|
|
|
self.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.play_png = wx.Image("icon/tiny-start.png", wx.BITMAP_TYPE_PNG).\
|
|
|
|
|
Rescale(30, 30).ConvertToBitmap()
|
|
|
|
|
self.stop_png = wx.Image("icon/tiny-pause.png", wx.BITMAP_TYPE_PNG).\
|
|
|
|
|
Rescale(30, 30).ConvertToBitmap()
|
|
|
|
|
|
|
|
|
|
# 字体设置
|
|
|
|
|
self.user_font = wx.Font(pointSize=self.font_size, family=wx.FONTFAMILY_DEFAULT,
|
|
|
|
|
style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_NORMAL)
|
|
|
|
|
self.user_font.SetFaceName(self.setting_dic['font'])
|
|
|
|
|
self.user_font.SetUnderlined(self.setting_dic['underline'])
|
|
|
|
|
|
|
|
|
|
# 绑定窗口关闭事件
|
|
|
|
|
self.is_running = True
|
|
|
|
|
self.Bind(wx.EVT_CLOSE, self.close_window)
|
|
|
|
|
|
|
|
|
|
# 从设置中读取歌曲路径并读取歌曲
|
|
|
|
|
folder_type = self.setting_dic['music_folder_type']
|
|
|
|
|
self.local_music_folder = self.setting_dic['music_folder_list'][folder_type]
|
|
|
|
|
self.local_music_name_list = []
|
|
|
|
|
self.get_local_music_list()
|
|
|
|
|
self.lyrics_static_text_list = []
|
|
|
|
|
|
|
|
|
|
# 音乐播放初始化
|
|
|
|
|
mixer.init()
|
|
|
|
|
self.volume = self.setting_dic['volume']
|
|
|
|
|
self.music = mixer.music
|
|
|
|
|
self.music.set_volume(self.volume)
|
|
|
|
|
self.current_music_state = 0 # 0表示未播放,1表示播放中,2表示暂停中
|
|
|
|
|
self.current_music_index = 0
|
|
|
|
|
|
|
|
|
|
# base_sizer将界面分为左右两部分,左侧为导航栏sizer,右侧初始为主界面
|
|
|
|
|
self.base_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
|
self.unsaved_setting = self.setting_dic.copy()
|
|
|
|
|
navi_sizer = self.draw_navigator()
|
|
|
|
|
self.base_sizer.Add(navi_sizer, 0, wx.EXPAND)
|
|
|
|
|
|
|
|
|
|
self.main_interface_panel = None
|
|
|
|
|
self.music_list_ctrl = None
|
|
|
|
|
self.lyrics_panel = None
|
|
|
|
|
self.setting_panel = None
|
|
|
|
|
self.ranking_notebook = None
|
|
|
|
|
self.draw_main_interface()
|
|
|
|
|
|
|
|
|
|
self.SetSizer(self.base_sizer)
|
|
|
|
|
self.Show()
|
|
|
|
|
|
|
|
|
|
def draw_navigator(self):
|
|
|
|
|
"""
|
|
|
|
|
绘制导航栏,按钮切换界面
|
|
|
|
|
:return: 导航栏sizer
|
|
|
|
|
"""
|
|
|
|
|
navi_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
|
|
|
|
|
|
local_music_button = wx.Button(self, label="本地音乐", size=(200, 100))
|
|
|
|
|
navi_sizer.Add(local_music_button)
|
|
|
|
|
local_music_button.SetFont(self.user_font)
|
|
|
|
|
local_music_button.SetForegroundColour(self.setting_dic['text_color'])
|
|
|
|
|
local_music_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
local_music_button.Bind(wx.EVT_LEFT_DOWN, self.main_interface_button_evt)
|
|
|
|
|
|
|
|
|
|
navi_sizer.Add((0, 0), 1)
|
|
|
|
|
|
|
|
|
|
ranking_button = wx.Button(self, label="排行榜", size=(200, 100))
|
|
|
|
|
navi_sizer.Add(ranking_button)
|
|
|
|
|
ranking_button.SetFont(self.user_font)
|
|
|
|
|
ranking_button.SetForegroundColour(self.setting_dic['text_color'])
|
|
|
|
|
ranking_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
ranking_button.Bind(wx.EVT_LEFT_DOWN, self.ranking_button_evt)
|
|
|
|
|
|
|
|
|
|
navi_sizer.Add((0, 0), 1)
|
|
|
|
|
|
|
|
|
|
setting_button = wx.Button(self, label="设置", size=(200, 100))
|
|
|
|
|
navi_sizer.Add(setting_button)
|
|
|
|
|
setting_button.SetFont(self.user_font)
|
|
|
|
|
setting_button.SetForegroundColour(self.setting_dic['text_color'])
|
|
|
|
|
setting_button.SetBackgroundColour(self.bg_color)
|
|
|
|
|
setting_button.Bind(wx.EVT_LEFT_DOWN, self.setting_button_evt)
|
|
|
|
|
|
|
|
|
|
navi_sizer.Add((0, 0), 3)
|
|
|
|
|
return navi_sizer
|
|
|
|
|
|
|
|
|
|
def main_interface_button_evt(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
当设置界面或排行榜界面存在时将其隐藏,在主界面未显示时显示主界面
|
|
|
|
|
"""
|
|
|
|
|
if self.setting_panel and self.setting_panel.IsShown():
|
|
|
|
|
self.setting_panel.Hide()
|
|
|
|
|
if self.ranking_notebook and self.ranking_notebook.IsShown():
|
|
|
|
|
self.ranking_notebook.Hide()
|
|
|
|
|
if not self.main_interface_panel.IsShown():
|
|
|
|
|
self.main_interface_panel.Show()
|
|
|
|
|
|
|
|
|
|
def draw_main_interface(self):
|
|
|
|
|
"""
|
|
|
|
|
绘制主界面
|
|
|
|
|
"""
|
|
|
|
|
# 主界面panel设置了main_interface_sizer,main_interface_sizer将主界面分为上下两部分,下面部分为控制区域,
|
|
|
|
|
# 高度始终不变;上面部分为sizer1,大小可变,包含歌曲列表及歌词面板部分。
|
|
|
|
|
self.main_interface_panel = wx.Panel(self)
|
|
|
|
|
self.base_sizer.Add(self.main_interface_panel, 1, wx.EXPAND)
|
|
|
|
|
main_interface_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
|
self.main_interface_panel.SetSizer(main_interface_sizer)
|
|
|
|
|
|
|
|
|
|
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
|
main_interface_sizer.Add(sizer1, 1, wx.EXPAND)
|
|
|
|
|
self.get_local_music_list()
|
|
|
|
|
|
|
|
|
|
# 歌曲列表list ctrl----------------------------------------------------------------------------------------------
|
|
|
|
|
self.music_list_ctrl = AutoWidthListCtrl(self.main_interface_panel, -1,
|
|
|
|
|
style=wx.LC_REPORT
|
|
|
|
|
# | wx.LC_NO_HEADER
|
|
|
|
|
)
|
|
|
|
|
self.music_list_ctrl.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.list_ctrl_evt)
|
|
|
|
|
self.music_list_ctrl.SetBackgroundColour(self.bg_color)
|
|
|
|
|
self.music_list_ctrl.SetFont(self.user_font)
|
|
|
|
|
self.music_list_ctrl.SetForegroundColour(self.setting_dic['text_color'])
|
|
|
|
|
# TODO: header背景颜色无法设置,若使用no header的风格则list ctrl宽度有问题
|
|
|
|
|
self.music_list_ctrl.InsertColumn(0, '歌曲列表')
|
|
|
|
|
self.music_list_ctrl.SetHeaderAttr(wx.ItemAttr(self.setting_dic['text_color'], None, self.user_font))
|
|
|
|
|
sizer1.Add(self.music_list_ctrl, 2, wx.EXPAND)
|
|
|
|
|
for music in self.local_music_name_list:
|
|
|
|
|
music_full_name = music.replace(".mp3", "") # 删除后缀
|
|
|
|
|
self.music_list_ctrl.InsertItem(0, music_full_name, 0)
|
|
|
|
|
|
|
|
|
|
# 歌词面板-------------------------------------------------------------------------------------------------------
|
|
|
|
|
self.lyrics_panel = wx.Panel(self.main_interface_panel)
|
|
|
|
|
sizer1.Add(self.lyrics_panel, 9, wx.EXPAND)
|
|
|
|
|
|
|
|
|
|
# 控制区域-------------------------------------------------------------------------------------------------------
|
|
|
|
|
control_panel = wx.Panel(self.main_interface_panel)
|
|
|
|
|
control_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
|
control_panel.SetSizer(control_sizer)
|
|
|
|
|
control_panel.Fit()
|
|
|
|
|
main_interface_sizer.Add(control_panel, 0, wx.EXPAND)
|
|
|
|
|
|
|
|
|
|
# 上一首按钮
|
|
|
|
|
last_music_png = wx.Image("icon/media-skip-backward.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()
|
|
|
|
|
next_music_png = wx.Image("icon/media-skip-forward.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()
|
|
|
|
|
last_button = wx.BitmapButton(control_panel, -1, last_music_png, size=(30, 30))
|
|
|
|
|
last_button.SetToolTip('上一首音乐')
|
|
|
|
|
last_button.Bind(wx.EVT_LEFT_DOWN, self.play_last_music)
|
|
|
|
|
|
|
|
|
|
# 播放暂停按钮
|
|
|
|
|
if self.current_music_state == 1:
|
|
|
|
|
current_png = self.stop_png
|
|
|
|
|
else:
|
|
|
|
|
current_png = self.play_png
|
|
|
|
|
self.play_stop_button = wx.BitmapButton(control_panel, -1, current_png, size=(30, 30))
|
|
|
|
|
self.play_stop_button.SetToolTip('播放/暂停音乐')
|
|
|
|
|
self.play_stop_button.Bind(wx.EVT_LEFT_DOWN, self.play_stop_music)
|
|
|
|
|
|
|
|
|
|
# 下一首按钮
|
|
|
|
|
next_button = wx.BitmapButton(control_panel, -1, next_music_png, size=(30, 30))
|
|
|
|
|
next_button.SetToolTip('下一首音乐')
|
|
|
|
|
next_button.Bind(wx.EVT_LEFT_DOWN, self.play_next_music)
|
|
|
|
|
|
|
|
|
|
control_sizer.Add(last_button, 0, wx.EXPAND | wx.ALL, border=10)
|
|
|
|
|
control_sizer.Add(self.play_stop_button, 0, wx.EXPAND | wx.ALL, border=10)
|
|
|
|
|
control_sizer.Add(next_button, 0, wx.EXPAND | wx.ALL, border=10)
|
|
|
|
|
|
|
|
|
|
# 正在播放的歌曲名
|
|
|
|
|
self.current_music_static_text = wx.StaticText(control_panel, -1, '未选择')
|
|
|
|
|
self.current_music_static_text.SetFont(self.user_font)
|
|
|
|
|
self.current_music_static_text.SetForegroundColour(self.setting_dic['text_color'])
|
|
|
|
|
control_sizer.Add(self.current_music_static_text, 0, wx.ALIGN_CENTER_VERTICAL, border=10)
|
|
|
|
|
|
|
|
|
|
control_sizer.Add(0, 1, wx.EXPAND) # 增加空白
|
|
|
|
|
|
|
|
|
|
# 音量滑动条及当前音量大小文字
|
|
|
|
|
volume_slider = wx.Slider(control_panel, -1, int(self.volume * 100), 0, 100)
|
|
|
|
|
volume_slider.Bind(wx.EVT_SLIDER, self.change_volume)
|
|
|
|
|
self.volume_text = wx.StaticText(control_panel, -1, f'音量{int(self.volume * 100)}%')
|
|
|
|
|
self.volume_text.SetFont(self.user_font)
|
|
|
|
|
self.volume_text.SetForegroundColour(self.setting_dic['text_color'])
|
|
|
|
|
control_sizer.Add(self.volume_text, 0, wx.ALIGN_CENTER_VERTICAL, border=10)
|
|
|
|
|
control_sizer.Add(volume_slider, 0, wx.EXPAND | wx.ALL, border=10)
|
|
|
|
|
|
|
|
|
|
self.base_sizer.Layout()
|
|
|
|
|
|
|
|
|
|
def get_local_music_list(self):
|
|
|
|
|
"""
|
|
|
|
|
获取歌词列表
|
|
|
|
|
"""
|
|
|
|
|
self.local_music_name_list.clear()
|
|
|
|
|
for local_music_file_name in os.listdir(self.local_music_folder):
|
|
|
|
|
if local_music_file_name.endswith(".mp3"):
|
|
|
|
|
self.local_music_name_list.append(local_music_file_name)
|
|
|
|
|
|
|
|
|
|
def list_ctrl_evt(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
在歌曲列表双击时播放歌曲
|
|
|
|
|
"""
|
|
|
|
|
self.current_music_name = evt.GetItem().GetText()
|
|
|
|
|
self.play_music()
|
|
|
|
|
|
|
|
|
|
def play_stop_music(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
播放暂停按钮事件
|
|
|
|
|
"""
|
|
|
|
|
if 1 == self.current_music_state:
|
|
|
|
|
self.music.pause()
|
|
|
|
|
self.current_music_state = 2
|
|
|
|
|
self.play_stop_button.SetBitmap(self.play_png)
|
|
|
|
|
elif 2 == self.current_music_state:
|
|
|
|
|
self.music.unpause()
|
|
|
|
|
self.current_music_state = 1
|
|
|
|
|
self.play_stop_button.SetBitmap(self.stop_png)
|
|
|
|
|
elif 0 == self.current_music_state:
|
|
|
|
|
self.current_music_name = self.local_music_name_list[0].replace('.mp3', '')
|
|
|
|
|
self.play_music()
|
|
|
|
|
|
|
|
|
|
def play_music(self):
|
|
|
|
|
"""
|
|
|
|
|
播放歌曲
|
|
|
|
|
"""
|
|
|
|
|
self.lyrics_panel.DestroyChildren()
|
|
|
|
|
full_path = os.path.join(self.local_music_folder, self.current_music_name) + '.mp3'
|
|
|
|
|
self.music.load(full_path)
|
|
|
|
|
self.music.play()
|
|
|
|
|
self.current_music_state = 1
|
|
|
|
|
self.current_music_index = self.local_music_name_list.index(self.current_music_name + '.mp3')
|
|
|
|
|
self.play_stop_button.SetBitmap(self.stop_png)
|
|
|
|
|
lyric_path = os.path.join(self.local_music_folder, self.current_music_name) + '.lrc'
|
|
|
|
|
self.lyrics_list = get_lyrics(lyric_path)
|
|
|
|
|
self.draw_music_lyrics_panel()
|
|
|
|
|
|
|
|
|
|
# 歌词刷新线程
|
|
|
|
|
lyric_refresh_thread = threading.Thread(target=self.refresh_lyrics)
|
|
|
|
|
lyric_refresh_thread.start()
|
|
|
|
|
|
|
|
|
|
# 当前播放音乐名(控制区域)
|
|
|
|
|
current_music_name = self.local_music_name_list[self.current_music_index].replace(".mp3", "")
|
|
|
|
|
max_music_name_len = self.setting_dic['max_music_name_len']
|
|
|
|
|
if len(current_music_name) > max_music_name_len:
|
|
|
|
|
current_music_name = self.current_music_name[0:max_music_name_len] + "..."
|
|
|
|
|
self.current_music_static_text.SetLabelText('正在播放:' + current_music_name)
|
|
|
|
|
self.current_music_static_text.SetToolTip(self.current_music_name)
|
|
|
|
|
|
|
|
|
|
def draw_music_lyrics_panel(self):
|
|
|
|
|
"""
|
|
|
|
|
绘制歌词面板
|
|
|
|
|
"""
|
|
|
|
|
# 清空歌词static text列表
|
|
|
|
|
self.lyrics_static_text_list.clear()
|
|
|
|
|
length, width = self.lyrics_panel.GetSize()
|
|
|
|
|
total_lyrics_row = int(1080 / 30) # 页面最多歌词行数(仅支持1080p及以下屏幕)
|
|
|
|
|
max_lyrics_row = int(width / 30)
|
|
|
|
|
for lyric_index in range(total_lyrics_row):
|
|
|
|
|
if lyric_index < max_lyrics_row and lyric_index < len(self.lyrics_list):
|
|
|
|
|
time_lyric_tuple = self.lyrics_list[lyric_index]
|
|
|
|
|
else:
|
|
|
|
|
time_lyric_tuple = (-1, '') # 时间为-1表示空行
|
|
|
|
|
lyric_row = wx.StaticText(self.lyrics_panel, -1, time_lyric_tuple[1],
|
|
|
|
|
pos=(int((length - 400) * 0.8 / 2), 30 * lyric_index + 10),
|
|
|
|
|
size=(400, 30), style=wx.ALIGN_CENTER_HORIZONTAL)
|
|
|
|
|
lyric_row.SetForegroundColour(self.setting_dic['lyrics_bg_color'])
|
|
|
|
|
lyric_row.SetFont(self.user_font)
|
|
|
|
|
self.lyrics_static_text_list.append((time_lyric_tuple[0], lyric_row))
|
|
|
|
|
|
|
|
|
|
def refresh_lyrics(self):
|
|
|
|
|
"""
|
|
|
|
|
刷新歌词线程
|
|
|
|
|
"""
|
|
|
|
|
music_index = self.current_music_index
|
|
|
|
|
length, width = self.lyrics_panel.GetSize()
|
|
|
|
|
# 当前显示的歌词行数
|
|
|
|
|
current_shown_lyrics_num = len(list(filter(lambda x: x != '',
|
|
|
|
|
(i[1].GetLabel() for i in self.lyrics_static_text_list))))
|
|
|
|
|
current_start_lyrics_index = 0
|
|
|
|
|
while music_index == self.current_music_index and self.is_running:
|
|
|
|
|
|
|
|
|
|
# 刷新歌词颜色
|
|
|
|
|
lyrics_fg_color = self.setting_dic['lyrics_fg_color']
|
|
|
|
|
for lyric_time, lyric_row in self.lyrics_static_text_list:
|
|
|
|
|
# 当歌词时间大于0(-1为空行)小于当前时间,且颜色不为前景色时将其设置为前景色
|
|
|
|
|
if 0 <= lyric_time <= self.music.get_pos()/1000 and \
|
|
|
|
|
lyric_row.ForegroundColour != wx.Colour(lyrics_fg_color):
|
|
|
|
|
lyric_row.SetForegroundColour(lyrics_fg_color)
|
|
|
|
|
lyric_row.Refresh()
|
|
|
|
|
|
|
|
|
|
# 刷新歌词位置
|
|
|
|
|
if length != self.lyrics_panel.GetSize()[0]:
|
|
|
|
|
length = self.lyrics_panel.GetSize()[0]
|
|
|
|
|
for ind, (_, row) in enumerate(self.lyrics_static_text_list):
|
|
|
|
|
row.SetPosition((int((length - 400) * 0.8 / 2), 30 * ind + 10))
|
|
|
|
|
|
|
|
|
|
# 刷新歌词行数
|
|
|
|
|
if width != self.lyrics_panel.GetSize()[1]:
|
|
|
|
|
width = self.lyrics_panel.GetSize()[1]
|
|
|
|
|
max_lyrics_row = int(width / 30)
|
|
|
|
|
# 可显示行数小于已显示行数,将多余行设为空行
|
|
|
|
|
if max_lyrics_row < current_shown_lyrics_num:
|
|
|
|
|
for ind, (_, lyric_row) in enumerate(self.lyrics_static_text_list[max_lyrics_row:]):
|
|
|
|
|
lyric_row.SetLabel('')
|
|
|
|
|
self.lyrics_static_text_list[max_lyrics_row + ind] = (-1, lyric_row)
|
|
|
|
|
# 可显示行数大于已显示行数,显示不足行
|
|
|
|
|
else:
|
|
|
|
|
lst = self.lyrics_static_text_list[current_shown_lyrics_num:max_lyrics_row] # 需增加的歌词
|
|
|
|
|
for ind, (_, lyric_row) in enumerate(lst):
|
|
|
|
|
start_index = current_start_lyrics_index + current_shown_lyrics_num # 需增加的歌词在歌词列表中的起始位置
|
|
|
|
|
if start_index + ind < len(self.lyrics_list):
|
|
|
|
|
lyric_time, lyric_text = self.lyrics_list[start_index + ind]
|
|
|
|
|
lyric_row.SetLabel(lyric_text)
|
|
|
|
|
# 当设置完歌曲最后一行歌词后将其余行设置为空
|
|
|
|
|
else:
|
|
|
|
|
lyric_time = -1
|
|
|
|
|
lyric_row.SetLabel('')
|
|
|
|
|
self.lyrics_static_text_list[current_shown_lyrics_num+ind] = (lyric_time, lyric_row)
|
|
|
|
|
current_shown_lyrics_num = max_lyrics_row # 当前显示的歌词行数
|
|
|
|
|
|
|
|
|
|
# 歌词到底后刷新下一页
|
|
|
|
|
new_page_index = int(width / 30) + current_start_lyrics_index # 下一页起始索引
|
|
|
|
|
# 当下一页起始歌词索引小于歌词总行数,且离下一页第一句歌词时间小于0.5s时绘制下一页
|
|
|
|
|
if new_page_index < len(self.lyrics_list) and \
|
|
|
|
|
abs(self.lyrics_list[new_page_index][0] - self.music.get_pos()/1000) < 0.5:
|
|
|
|
|
current_start_lyrics_index = new_page_index
|
|
|
|
|
max_lyrics_row = int(width / 30)
|
|
|
|
|
for ind, (_, lyric_row) in enumerate(self.lyrics_static_text_list):
|
|
|
|
|
lyric_row.SetForegroundColour(self.setting_dic['lyrics_bg_color'])
|
|
|
|
|
if ind < max_lyrics_row and new_page_index + ind < len(self.lyrics_list):
|
|
|
|
|
lyric_time, lyric_text = self.lyrics_list[new_page_index + ind]
|
|
|
|
|
else:
|
|
|
|
|
lyric_time, lyric_text = (-1, '')
|
|
|
|
|
lyric_row.SetLabel(lyric_text)
|
|
|
|
|
self.lyrics_static_text_list[ind] = (lyric_time, lyric_row)
|
|
|
|
|
|
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
|
|
|
|
def play_last_music(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
上一首按钮事件
|
|
|
|
|
"""
|
|
|
|
|
if self.current_music_state == 0: # 未开始播放时按钮无效
|
|
|
|
|
return
|
|
|
|
|
elif self.current_music_index > 1:
|
|
|
|
|
self.current_music_index -= 1
|
|
|
|
|
else:
|
|
|
|
|
self.current_music_index = len(self.local_music_name_list) - 1
|
|
|
|
|
self.current_music_name = self.local_music_name_list[self.current_music_index].replace('.mp3', '')
|
|
|
|
|
self.play_music()
|
|
|
|
|
|
|
|
|
|
def play_next_music(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
下一首按钮事件
|
|
|
|
|
"""
|
|
|
|
|
if self.current_music_state == 0: # 未开始播放时按钮无效
|
|
|
|
|
return
|
|
|
|
|
elif self.current_music_index < len(self.local_music_name_list) - 1:
|
|
|
|
|
self.current_music_index += 1
|
|
|
|
|
else:
|
|
|
|
|
self.current_music_index = 0
|
|
|
|
|
self.current_music_name = self.local_music_name_list[self.current_music_index].replace('.mp3', '')
|
|
|
|
|
self.play_music()
|
|
|
|
|
|
|
|
|
|
def change_volume(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
音量滑动条事件
|
|
|
|
|
"""
|
|
|
|
|
obj = evt.GetEventObject()
|
|
|
|
|
val = obj.GetValue()
|
|
|
|
|
self.volume = float(val / 100)
|
|
|
|
|
self.music.set_volume(self.volume)
|
|
|
|
|
self.volume_text.SetLabel(f'音量{int(self.volume * 100)}%')
|
|
|
|
|
self.unsaved_setting['volume'] = self.volume
|
|
|
|
|
|
|
|
|
|
def setting_button_evt(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
当主界面或排行榜界面显示时将其隐藏,在设置界面不存在时绘制界面,存在时显示设置界面
|
|
|
|
|
"""
|
|
|
|
|
if self.main_interface_panel.IsShown():
|
|
|
|
|
self.main_interface_panel.Hide()
|
|
|
|
|
if self.ranking_notebook and self.ranking_notebook.IsShown():
|
|
|
|
|
self.ranking_notebook.Hide()
|
|
|
|
|
if not self.setting_panel:
|
|
|
|
|
self.draw_setting_interface()
|
|
|
|
|
if not self.setting_panel.IsShown():
|
|
|
|
|
self.setting_panel.Show()
|
|
|
|
|
|
|
|
|
|
def ranking_button_evt(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
当主界面或设置界面显示时将其隐藏,在排行榜界面不存在时绘制界面,存在时显示排行榜界面
|
|
|
|
|
"""
|
|
|
|
|
if self.main_interface_panel.IsShown():
|
|
|
|
|
self.main_interface_panel.Hide()
|
|
|
|
|
if self.setting_panel and self.setting_panel.IsShown():
|
|
|
|
|
self.setting_panel.Hide()
|
|
|
|
|
if not self.ranking_notebook:
|
|
|
|
|
self.draw_ranking_notebook()
|
|
|
|
|
if not self.ranking_notebook.IsShown():
|
|
|
|
|
self.ranking_notebook.Show()
|
|
|
|
|
|
|
|
|
|
def draw_setting_interface(self):
|
|
|
|
|
"""
|
|
|
|
|
绘制设置界面
|
|
|
|
|
"""
|
|
|
|
|
self.setting_panel = SettingPanel(self)
|
|
|
|
|
self.base_sizer.Add(self.setting_panel, 1, wx.EXPAND)
|
|
|
|
|
self.base_sizer.Layout()
|
|
|
|
|
|
|
|
|
|
def draw_ranking_notebook(self):
|
|
|
|
|
"""
|
|
|
|
|
绘制排行榜界面
|
|
|
|
|
"""
|
|
|
|
|
self.ranking_notebook = RankListNotebook(self)
|
|
|
|
|
self.base_sizer.Add(self.ranking_notebook, 1, wx.EXPAND)
|
|
|
|
|
self.base_sizer.Layout()
|
|
|
|
|
|
|
|
|
|
def close_window(self, evt):
|
|
|
|
|
"""
|
|
|
|
|
关闭界面事件
|
|
|
|
|
"""
|
|
|
|
|
self.is_running = False # 刷新歌词线程终止的条件之一
|
|
|
|
|
# 退出时保存设置
|
|
|
|
|
with open('setting.txt', 'w', encoding='utf-8') as f:
|
|
|
|
|
f.writelines(str(self.unsaved_setting))
|
|
|
|
|
evt.Skip() # 继续关闭事件
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
app = wx.App()
|
|
|
|
|
frame = MainFrame()
|
|
|
|
|
app.MainLoop()
|