利用PyQt5和QSS制作本地音乐播放器(初稿)

制作缘由

本来我是通过活动白嫖了某云音乐的黑胶VIP的,当时我下了很多VIP才能听整首的歌。但是VIP过期后,这音乐就播放不了了,一方面是它VIP歌曲存储为.ncm文件,另一方面是我将这ncm文件解码成MP3文件后,并将其导入我的云盘里,这软件依旧只给我播放歌的一小部分。(岂可修!!)
所以,我决定自己写个播放器,再见了某云。

图片预览及用法

如下图
ICON文件夹: 存储程序所需要的图片
list_dic文件夹: 存储我的歌单

在这里插入图片描述

见下图
各个按钮部件是干什么的大家都懂
左边的加号是创建新歌单
右边的加号是给歌单加歌

程序界面

选定某一歌单时会显示歌单内歌曲信息,并可以播放歌曲

在这里插入图片描述

新建歌单窗口

在这里插入图片描述

部分界面代码和思路

首先创建主部件
并向内添加上,左,右部件
上部件:控制窗口最小化和关闭
左部件: 创建和选择歌单
右部件: 播放歌曲和对歌单进行查看及添加歌曲
		self.setFixedSize(960, 700)
        self.main_widget = QtWidgets.QWidget()  # 创建窗口主部件
        self.main_layout = QtWidgets.QGridLayout()  # 创建主部件的网格布局
        self.main_widget.setLayout(self.main_layout)  # 设置窗口部件为网格
        # self.setWindowOpacity(1)  # 设置窗口透明度
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)  # 设置窗口背景透明度
        self.setWindowFlag(QtCore.Qt.FramelessWindowHint)  # 隐藏边框
        self.main_layout.setSpacing(0)

        self.top_widget = QtWidgets.QWidget()  # 创建上侧部件
        self.top_widget.setObjectName('top_widget')
        self.top_layout = QtWidgets.QGridLayout()
        self.top_widget.setLayout(self.top_layout)

        self.left_widget = QtWidgets.QWidget()  # 创建左侧部件
        self.left_widget.setObjectName('left_widget')
        self.left_layout = QtWidgets.QGridLayout()
        self.left_widget.setLayout(self.left_layout)

        self.right_widget = QtWidgets.QWidget()  # 创建右侧部件
        self.right_widget.setObjectName('right_widget')
        self.right_layout = QtWidgets.QGridLayout()
        self.right_widget.setLayout(self.right_layout)
        
        # 部件部署
        self.main_layout.addWidget(self.top_widget, 0, 0, 1, 12)
        self.main_layout.addWidget(self.left_widget, 1, 0, 12, 2)
        self.main_layout.addWidget(self.right_widget, 1, 2, 12, 10)
        self.setCentralWidget(self.main_widget)
上侧控件的定义和部署
		# 上侧控件定义
        self.top_close = QtWidgets.QPushButton(qtawesome.icon('fa.times', color='#FFFFFF'), '')
        self.top_mini = QtWidgets.QPushButton(qtawesome.icon('fa.window-minimize', color='#FFFFFF'), '')
        self.top_label = QtWidgets.QLabel('   本地音乐播放器')
        self.top_close.clicked.connect(self.event_close)
        self.top_mini.clicked.connect(self.showMinimized)
        self.top_close.setFixedWidth(50)
        self.top_mini.setFixedWidth(50)

        # 上侧控件部署
        self.top_layout.addWidget(self.top_label, 0, 0, 1, 7)
        self.top_layout.addWidget(self.top_close, 0, 11, 1, 1)
        self.top_layout.addWidget(self.top_mini, 0, 9, 1, 1)
左侧控件定义和部署
		# 左侧控件定义
        self.left_add = QtWidgets.QPushButton(qtawesome.icon('fa.plus', color='#FFFFFF'), '')   # 添加按钮
        self.left_label_1 = QtWidgets.QLabel("  我的歌单  ")
        self.left_list = QtWidgets.QListWidget()
        self.left_add.clicked.connect(self.add_songlist)
        self.left_list.itemDoubleClicked.connect(self.open_songlist)
        self.left_add.setFixedSize(20,20)
        for i in os.listdir('./list_dic'):
            if i.endswith('.list'):
                self.left_list.addItem(i.rstrip('.list'))

        # 左侧控件部署
        self.left_layout.addWidget(self.left_add, 0, 2, 1, 1)
        self.left_layout.addWidget(self.left_label_1, 0, 0, 1, 2)
        self.left_layout.addWidget(self.left_list, 1, 0, 11, 3)
右侧控件的定义和部署
		# 右侧控件定义
        self.right_layout.setSpacing(0)
        # 歌单控件
        self.right_songlist_widget = QtWidgets.QWidget()
        self.right_songlist_layout = QtWidgets.QGridLayout()
        self.right_songlist_widget.setLayout(self.right_songlist_layout)
        self.songlist_add = QtWidgets.QPushButton('+')  # 添加按钮
        self.songlist_add.clicked.connect(self.add_song)
        self.songlist_label = QtWidgets.QLabel("  正在播放:  无")
        self.songlist_table = QtWidgets.QTableWidget(100, 3)
        self.songlist_table.itemDoubleClicked.connect(self.now_play)
        self.songlist_add.setFixedSize(20,20)
        self.songlist_table.setHorizontalHeaderLabels(['音乐标题', '歌手', '时长'])
        self.songlist_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)  # 表格禁止编辑
        self.songlist_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)  # 设置整行选中
        self.songlist_table.setColumnWidth(0, 300)
        self.songlist_table.setColumnWidth(1, 150)
        self.songlist_table.setColumnWidth(2, 100)
        
        # 进度条控件
        self.right_process_widget = QtWidgets.QWidget()
        self.right_process_layout = QtWidgets.QGridLayout()
        self.right_process_widget.setLayout(self.right_process_layout)
        self.process_label = QtWidgets.QLabel("00:00/05:00")
        self.right_process_bar = progressSlider(QtCore.Qt.Horizontal)
        self.right_process_bar.clicked.connect(self.click_process_slider)
        self.right_process_bar.setFixedHeight(30)
        self.right_process_bar.setMinimum(0)
        self.right_process_bar.setMaximum(self.process_max)
        self.right_process_bar.setSingleStep(1)
        self.right_process_bar.setValue(0)
        self.right_process_bar.setFixedHeight(12)
        # 播放控件
        self.right_playconsole_widget = QtWidgets.QWidget()
        self.right_playconsole_layout = QtWidgets.QGridLayout()
        self.right_playconsole_widget.setLayout(self.right_playconsole_layout)
        self.console_button_1 = QtWidgets.QPushButton(qtawesome.icon('fa.step-backward', color='#F76677'), '')
        self.console_button_2 = QtWidgets.QPushButton(qtawesome.icon('fa.step-forward', color='#F76677'), '')
        self.console_button_3 = QtWidgets.QPushButton(qtawesome.icon('fa.play', color='#F76677', font=18), '')
        self.console_button_3.setIconSize(QtCore.QSize(30,30))
        self.console_button_4 = QtWidgets.QPushButton(qtawesome.icon('fa.volume-up'), '')
        self.console_button_5 = QtWidgets.QPushButton(qtawesome.icon('fa.random', color='#F76677'), '')
        self.console_button_1.clicked.connect(self.prev_song)
        self.console_button_2.clicked.connect(self.next_song)
        self.console_button_3.clicked.connect(self.play_song)
        self.console_button_4.clicked.connect(self.play_volume)
        self.console_button_5.clicked.connect(self.play_mode)
        self.console_slide = progressSlider(QtCore.Qt.Horizontal)
        self.console_slide.clicked.connect(self.click_console_slider)
        self.console_slide.setFixedSize(100, 30)
        self.console_slide.setMaximum(100)
        self.console_slide.setMinimum(0)
        self.console_slide.setSingleStep(1)
        self.console_slide.setValue(self.Volume)

        # 右侧控件部署
        self.right_songlist_layout.addWidget(self.songlist_label, 0, 0, 1, 2)
        self.right_songlist_layout.addWidget(self.songlist_add, 0, 8, 1, 1)
        self.right_songlist_layout.addWidget(self.songlist_table, 1, 0, 7, 9)
        self.right_playconsole_layout.addWidget(self.console_button_1, 0, 1, 1, 1)
        self.right_playconsole_layout.addWidget(self.console_button_2, 0, 3, 1, 1)
        self.right_playconsole_layout.addWidget(self.console_button_3, 0, 2, 1, 1)
        self.right_playconsole_layout.addWidget(self.console_button_4, 0, 5, 1, 1)
        self.right_playconsole_layout.addWidget(self.console_button_5, 0, 0, 1, 1)
        self.right_playconsole_layout.addWidget(self.console_slide, 0, 4, 1, 1)
        # self.right_playconsole_layout.setAlignment(QtCore.Qt.AlignCenter)  # 设置布局内部件居中显示
        self.right_process_layout.addWidget(self.right_process_bar, 0, 0, 1, 7)
        self.right_process_layout.addWidget(self.process_label, 0, 8, 1, 1)
        self.right_layout.addWidget(self.right_process_widget, 9, 0, 1, 9)
        self.right_layout.addWidget(self.right_songlist_widget, 0, 0, 9, 9)
        self.right_layout.addWidget(self.right_playconsole_widget, 10, 0, 1, 9)

为了让界面更好看一点,所以下面还有QSS样式代码

上侧控件QSS
self.top_widget.setStyleSheet('''
            QWidget#top_widget{
                background:#EC4141;
                border-top:1px solid white;
                border-left:1px solid white;
                border-right:1px solid white;
                border-top-left-radius:10px;
                border-top-right-radius:10px;
                }
            QPushButton{
                background:#EC4141;
                border-radius:5px;
                color:white;
                }
            QLabel{
                border:none;
                color:white;
                font-size:24px;
                font-family:STHUPO;
                }    
        ''')
左侧控件QSS
self.left_widget.setStyleSheet('''
            QWidget#left_widget{
                background:gray;
                border-bottom:1px solid white;
                border-left:1px solid white;
                border-bottom-left-radius:10px;
                }
            QPushButton{background:gray;border-radius:5px;border:none;color:white;}
            QPushButton:hover{background:red}
            QLabel{
                border:none;
                color:white;
                font-size:18px;
                font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
                }
            QListWidget{border:none; border-radius:5px;color:white;font-size:18px;
                font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
                background:gray;
             }
            QListWidget::Item{padding-top:20px; padding-bottom:4px; }
            QListWidget::Item:hover{background:lightgray; }
            QListWidget::item:selected{background:lightgray; color:white; }
            QListWidget::item:selected:!active{border-width:0px; background:lightgray; }
        ''')
右侧控件
self.right_widget.setStyleSheet('''
            QWidget#right_widget{
                color:#232C51;
                background:white;
                border-bottom:1px solid darkGray;
                border-right:1px solid darkGray;
                border-bottom-right-radius:10px;
                }
            QLabel#right_lable{
                border:none;
                font-size:16px;
                font-weight:700;
                font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
                }
            QPushButton{
                border:none;
                color:gray;
                font-size:14px;
                height:40px;
                }
            QPushButton:hover{
                color:black;
                border:none;
                border-radius:10px;
                background:LightGray;
                }
            QTableWidget{
                color:black;
                background:white;
                selection-background-color:lightgray;
                border:none;
                }
            QSlider::add-page:horizontal{
                background-color:lightgray;
                height:4px;
                }
            QSlider::sub-page:horizontal{
                background-color:red;
                height:4px;
                }
            QSlider::groove:horizontal{
                background:transparent;
                height:6px;
                }
            QSlider::handle:horizontal{
                height: 13px;
                width: 13px;
                border-image:url(ICON/dot.png);
                margin: -4 -0px;
                }
        ''')
Qt自带滑动条只能点击圆钮拖动
所以给滑动条增加点哪,圆钮到哪的功能
class progressSlider(QtWidgets.QSlider):
    clicked = QtCore.pyqtSignal(int, int)

    def __init__(self, orientation, parent=None):
        super(progressSlider, self).__init__(orientation, parent)

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            super().mousePressEvent(event)  # 调用父级的单击事件,听说这样能不影响进度条原来的拖动
            val_por = event.pos().x() / self.width()  # 获取鼠标在进度条的相对位置
            now = self.value()
            self.setValue(int(val_por * self.maximum()))  # 改变进度条的值
            self.clicked.emit(self.value(), self.value()-now)
创建歌单界面
class AddListUI(QtWidgets.QWidget):
    _signal = QtCore.pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.list_name = ''
        self.setFixedSize(500, 300)
        self.add_list_layout = QtWidgets.QVBoxLayout()
        self.setLayout(self.add_list_layout)
        self.setWindowFlag(QtCore.Qt.FramelessWindowHint)  # 隐藏边框

        self.close_btn = QtWidgets.QPushButton(qtawesome.icon('fa.times', color='#000000'), '')
        self.create_btn = QtWidgets.QPushButton('创 建')
        self.add_list_edit = QtWidgets.QLineEdit()
        self.add_list_edit.setPlaceholderText('请输入新歌单标题')
        self.add_list_label = QtWidgets.QLabel('新建歌单')
        self.close_btn.clicked.connect(self.close)
        self.create_btn.clicked.connect(self.add_list)

        self.add_list_layout.addWidget(self.close_btn, 0, QtCore.Qt.AlignRight)
        self.add_list_layout.addWidget(self.add_list_label, 1, QtCore.Qt.AlignCenter)
        self.add_list_layout.addWidget(self.add_list_edit, 2, QtCore.Qt.AlignCenter)
        self.add_list_layout.addWidget(self.create_btn, 3, QtCore.Qt.AlignCenter)

        self.close_btn.setFixedSize(50,50)
        self.create_btn.setFixedSize(70,40)
        # self.add_list_label.setFixedHeight(80)
        self.add_list_edit.setFixedHeight(40)
        self.close_btn.setStyleSheet('''
            QPushButton{
                background:white;
                border-radius:5px;
                border:none;
                }
        ''')
        self.setStyleSheet('''
            QWidget{
                background:white;
                border:none;
                border-radius:10px;
                }
            QLabel{
                border:none;
                color:black;
                font-size:24px;
                font-family:STHUPO;
                }
            QPushButton{
                color:white;
                border:none;
                border-radius:10px;
                background:red;
                font-size:22px;
                font-family:STHUPO;
                }
            QLineEdit{
                border:1px solid gray;
                width:400px;
                border-radius:10px;
                padding:2px 4px;
                }
        ''')

    def add_list(self):
        self.list_name = self.add_list_edit.text()
        if self.list_name and not os.path.isfile('./list_dic/'+self.list_name+'.list'):
            a_dict = {'songlist_meta': []}
            f = open('./list_dic/'+self.list_name+'.list', 'w')
            a_dict = add_songs(a_dict)
            f.write(dumps(a_dict))
            self._signal.emit(self.list_name)
            f.close()
            self.close()
        else:
            self.close()

功能代码及思路

一些散装函数
获取歌曲信息
如歌曲名,歌曲长度(ts),歌手信息等
def get_media_info(media_name):
    media_info = MediaInfo.parse(media_name)
    media_data = loads(media_info.to_json())["tracks"]
    a = []
    if 'title' in media_data[0].keys():
        a.append(media_data[0]['title'])
    else:
        a.append(media_data[0]['file_name'].split('/')[-1])
    if 'performer' in media_data[0].keys():
        a.append(media_data[0]['performer'])
    else:
        a.append('无歌手名')
    if media_data[0]['duration']:
        a.append(media_data[0]['duration'])
        a.append(m_s(media_data[0]['duration']))
    else:
        a.append(0)
        a.append('')
    a.append(media_name)
    return a
将ts转化成00:00的时间格式
def m_s(ts):
    s = ts//1000 + 1
    m, s = s//60, s % 60
    return str(m).rjust(2, '0')+':'+str(s).rjust(2, '0')

添加歌曲文件是出现文件选择框
def add_songs(a_dict):
    list_meta=filedialog.askopenfilenames\
                (filetypes=[('音频文件', ('*.mp3', '*.ogg'))])
    for i in list_meta:
        a_dict['songlist_meta'].append(get_media_info(i))
    return a_dict
类内函数
打开歌单
由于打开歌单后就可以进行歌曲的播放了,
而播放音乐的一系列操作(比如监听歌曲是否结束,以及进度条),
我目前只会用循环来解决
所以这里选择开一条播放线程来解决这件事
    def open_songlist(self, item):
        self.list_path = './list_dic/' + str(item.text()) + '.list'
        f = open(self.list_path, 'r')
        self.songlist = loads(f.read())
        self.songlist_num = len(self.songlist["songlist_meta"])
        self.songlist_table.setRowCount(self.songlist_num)
        flag = 0
        for i in self.songlist["songlist_meta"]:
            self.songlist_table.setItem(flag, 0, QtWidgets.QTableWidgetItem(i[0]))
            self.songlist_table.setItem(flag, 1, QtWidgets.QTableWidgetItem(i[1]))
            self.songlist_table.setItem(flag, 2, QtWidgets.QTableWidgetItem(i[3]))
            flag += 1
        f.close()
        if self.thread_play == 0:
            self.thread_play = threading.Thread(target=self.play)
            self.thread_play.start()
控制播放的循环,即线程内容
其实可以多开几个线程的,但是我懒得
自上而下的每一个外部的if语句功能为:
1、控制Play关闭的(程序关闭的时候)
2、控制下一首歌是什么(随机播放和顺序播放,单曲循环找不到图标,所以就不做了)
3、让程序一开始就载入一首歌
4、控制播放进度条
5、控制歌曲快播放结束时,载入下一首歌
    def play(self):  # 监视变量NextSong,以达到控制播放的效果
        while 1:
            if self.stop:
                self.Player.stop()
                break
            if self.NextSong == -1:
                if self.PlayMode:
                    self.NextSong = RandInt(0, self.songlist_num-1,self.NowSong)
                else:
                    self.NextSong = (self.NowSong + 1) % self.songlist_num
                continue
            if self.NowSong == -1:
                self.NowSong = self.NextSong
                self.NextSong = -1
                self.Player.load(self.songlist["songlist_meta"][self.NowSong][4])
                self.Player.play()
                self.Player.pause()
            if self.Player.get_busy():
                self.songlist_label.setText('  正在播放:  '+self.songlist["songlist_meta"][self.NowSong][0])
                self.process = self.Player.get_pos() + self.process_add
                self.process_label.setText(m_s(self.process)+'/'+self.songlist["songlist_meta"][self.NowSong][3])
                self.right_process_bar.setValue(self.process)
                sleep(0.9)
            if self.process >= self.process_max-3000:
                sleep(2)
                self.next_song()
函数  下一首歌
本程序所有的切歌操作都由其承担
 def next_song(self):
        self.PrevSong = self.NowSong
        self.NowSong = self.NextSong
        self.NextSong = -1
        self.Player.load(self.songlist["songlist_meta"][self.NowSong][4])
        self.process_add = 0
        self.process_max = self.songlist["songlist_meta"][self.NowSong][2]
        self.right_process_bar.setMaximum(self.process_max)
        self.Player.play()
剩下的函数
    def add_songlist(self):  # 添加歌单
        self.add_list = AddListUI()
        self.add_list.show()
        self.add_list._signal.connect(self.left_list.addItem)

    def add_song(self):  #添加音乐
        a_dict = add_songs(self.songlist)
        f = open(self.list_path, 'w')
        f.write(dumps(a_dict))
        f.close()
        f = open(self.list_path, 'r')
        self.songlist = loads(f.read())
        self.songlist_num = len(self.songlist["songlist_meta"])
        self.songlist_table.setRowCount(self.songlist_num)
        flag = 0
        for i in self.songlist["songlist_meta"]:
            self.songlist_table.setItem(flag, 0, QtWidgets.QTableWidgetItem(i[0]))
            self.songlist_table.setItem(flag, 1, QtWidgets.QTableWidgetItem(i[1]))
            self.songlist_table.setItem(flag, 2, QtWidgets.QTableWidgetItem(i[3]))
            flag += 1
        f.close()

    def prev_song(self):  # 上一首歌
        self.NextSong = self.PrevSong
        self.next_song()

    def play_song(self):
        if self.PlayState:  # 开始播放
            self.PlayState = 0
            self.console_button_3.setIcon(qtawesome.icon('fa.pause', color='#F76677', font=18))
            self.Player.unpause()

        else:  # 暂停播放
            self.PlayState = 1
            self.console_button_3.setIcon(qtawesome.icon('fa.play', color='#F76677', font=18))
            self.Player.pause()

    def play_volume(self):  # 控制是否静音
        if self.HaveVolume:
            self.HaveVolume = 0
            self.console_button_4.setIcon(qtawesome.icon('fa.volume-off', color='#F76677'))
            self.console_slide.setValue(0)
            self.Player.set_volume(0)
        else:
            self.HaveVolume = 1
            self.console_button_4.setIcon(qtawesome.icon('fa.volume-up', color='#F76677'))
            self.console_slide.setValue(self.Volume)
            self.Player.set_volume(self.Volume)

    def play_mode(self):  # 控制随机和循环播放
        if self.PlayMode:
            self.PlayMode = 0
            self.console_button_5.setIcon(qtawesome.icon('fa.retweet', color='#F76677'))
            self.NextSong = -1
        else:
            self.PlayMode = 1
            self.console_button_5.setIcon(qtawesome.icon('fa.random', color='#F76677'))
            self.NextSong = -1

    def now_play(self, item):  # 双击歌单内歌曲即播放该歌曲
        self.PlayState = 1
        self.console_button_3.setIcon(qtawesome.icon('fa.pause', color='#F76677', font=18))
        self.NextSong = item.row()
        self.next_song()

    def click_process_slider(self, a, b):  # 播放进度条控制
        self.process_add += b
        self.Player.set_pos(a/1000)
        # self.right_process_bar.setValue(a)

    def click_console_slider(self, a, b):  # 音量控制
        self.Player.set_volume(a/100)

结尾

这个播放器功能实现还不够完善,且有些许操作bug还未解决,以后会添加别的功能(在以后的文章里)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值