文章目录
- 一.前言
- 二.环境
- 1.开发环境
- 2.打包环境
- 3.运行环境
- 三.软件截图
- 1.启动页
- 2.视频播放
- 3.音频播放
- 4.其他
- 1.托盘
- 2.对话框
- 四.功能总览
- 五.代码展示&心得
- 1.UI设计
- 2.如何防止卡顿
- 3.如何自定义组件
- 五.思考
- 1.解码器
- 2.播放列表
- 六.总结
一.前言
前面做过一款python3GUI–做一款弹幕视频播放器(第二弹v1.5)By:PyQt5(附下载地址),仿作了一款支持播放在线、本地音乐、视频的多功能播放器,本次仍然使用PyQt5制作一款音视频播放器、UI风格参考了暴风影音,下面我将详细介绍一下我开发的这款“风暴影音”播放器。放个图比较一下。
二.环境
1.开发环境
本次开发是在window11上使用pycharm开发工具开发的,详细开发环境如下:
- 系统:Windows 11 家庭中文版
- 处理器:AMD Ryzen 7 7735H with Radeon Graphics 3.20 GHz
- 开发工具:PyCharm 2021.3
- Python版本:Python3.8
- PyQt版本:PyQt5 5.15.9
- pip版本:pip 24.0
2.打包环境
nuitka打包,版本:2.0.5
详细参数见下图
3.运行环境
对于已打包好的exe文件,需要安装对应版本的解码器才能正常使用本播放器,解码器下载地址放在了下面:
https://wwt.lanzoul.com/ickJ81rkm50b
大家下载安装好解码器后再打开本播放器,即可正常使用播放器。
三.软件截图
1.启动页
启动页是我们打开软件后看到的第一个页面,它大致展示了软件的基本功能,
主界面分为四个区域,最顶端为标题区域,中间左边为主播放区域,内部包含欢迎页面、视频页面、音频页面,中间右侧为播放列表区域,最下方为播放控制区域。
2.视频播放
通过在主界面或播放列表的+号添加本地视频文件到播放列表,选择播放视频文件,本播放器支持多种视频格式,包括不限于"mp4", “rmbv”, “mkv”, “3gp”, “m3u8”, “avi”, “m4v”, “ts”, “flv”, “m4s”。
3.音频播放
添加方式和添加视频类似,支持的播放的音频类别包括不限于"mp3", “m4a”, “aac”, “flac”,在播放列表项目右击展示右击菜单,右击菜单包括对单文件的操作和对播放列表的操作。
4.其他
1.托盘
本次自定义了托盘的样式,托盘的逻辑目前只有“退出”能够响应,其他操作均为跳转到笔者博客主页。
2.对话框
本次自定义了对话框的样式,这样可以使得我们的整体UI风格比较统一,对话框之间通过使用继承的方式实现不同的效果,提高了代码复用。
信息对话框
询问对话框
输入对话框
四.功能总览
博文不如视频说的清楚、明白,遂备一张思维导图以详细展示本软件所有功能。
五.代码展示&心得
每次做新的项目都会有点收货,在此模块记录下自己的心得体会,和大家分享一下代码思路。
1.UI设计
本次采用组件化思想,对整个项目进行拆分,按照UI布局整体拆分成了4个部分,对每一部分的组件继续拆分,直到无法再分,拆分到单个组件的最小单位。本次UI设计并没有使用Designer进行布局,使得本次项目UI设计比较抽象,但是如果自己心里清楚每一部分的话,就有一个整体全局观。
在下图的代码中,我们能看到主界面被分成了四个部分,整体采用垂直布局,内部采用一个QSplitter将主播放区域和播放列表区域水平分开,这样设计的好处是:用户可以自行手动调节中间区域的大小,给予用户较大的灵活性。
2.如何防止卡顿
软件使用过程中卡顿是用户不能容忍的,当UI卡顿时,用户会感觉到程序响应缓慢,操作不流畅。长时间等待和界面无响应会让用户感到不耐烦,降低对程序的满意度。
本次采用多线程的方式处理耗时操作,本软件的耗时操作主要是读取本地文件的基本信息,我们可以把这个耗时操作放到子线程中,当读取完后,使用发射信号的方式把数据发射到主程序中,最后使用槽函数接收信号,处理数据。
上面的思路体现到代码中就是:通过继承QThread的方式来处理耗时操作,具体使用方法见下图:
下面也有一些能够解决PyQt5 UI卡顿的思路,大家可以尝试
- 优化代码逻辑:检查程序中是否存在长时间运行的操作或大量计算,尝试将这些操作放在单独的线程中执行,以避免阻塞主线程。
- 使用合适的布局和控件:选择适合程序需求的布局和控件,避免使用过于复杂或不必要的布局和控件,以减少渲染和计算负担。
- 减少界面元素:适当减少界面上的元素数量,特别是动态加载的元素,以降低界面渲染的复杂度。
- 使用缓存和异步加载:对于需要频繁加载的数据或图片等资源,可以使用缓存和异步加载技术,提高加载速度和效率。
- 升级硬件和软件:如果程序对硬件要求较高,可以考虑升级计算机硬件。同时,保持PyQt5和相关依赖库的版本更新,以利用最新的性能优化和修复。
3.如何自定义组件
这个问题比较基础,在此说下我的思路,就拿下面音量调节组件来说,它的基类是一个横向的QSlider,通过重写鼠标事件来控制展示的样式。
相关代码如下
import sys
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QSlider
class NormalHorizontalSlider(QSlider):
"""
通用水平进度条
"""
def __init__(self, p=None):
super(NormalHorizontalSlider, self).__init__(p)
self.setOrientation(Qt.Horizontal)
self.setMinimum(0)
class VolumeSlider(NormalHorizontalSlider):
"""
音量调节slider
"""
horizontal_scrollBar_normal_style = """
QSlider::groove:horizontal {
border: 1px solid rgb(50,50,50);
background: rgb(50,50,50);
height:4px;
border-radius: 1px;
padding-left:-1px;
padding-right:-1px;
padding-top:-2px;
padding-bottom:-2px;
}
QSlider::handle:hover{
width: 5px;
height: 5px;
}
QSlider::sub-page:horizontal {
background: rgb(27,157,255);
border: 1px solid rgb(27,157,255);
height: 10px;
border-radius: 1px;
}
QSlider::handle:horizontal
{
background: #09a1ff;
border: 0px solid #09a1ff;
width: 0px;
height: 0px;
margin-top: 0px;
margin-bottom: 0px;
border-radius: 6px;
}
"""
horizontal_scrollBar_style = """
QSlider::groove:horizontal {
border: 1px solid rgb(50,50,50);
background: rgb(50,50,50);
height:4px;
border-radius: 1px;
padding-left:-1px;
padding-right:-1px;
padding-top:-2px;
padding-bottom:-2px;
}
QSlider::handle:hover{
width: 5px;
height: 5px;
}
QSlider::sub-page:horizontal {
background: rgb(27,157,255);
border: 1px solid rgb(27,157,255);
height: 10px;
border-radius: 1px;
}
QSlider::handle:horizontal
{
background: #09a1ff;
border: 1px solid #09a1ff;
width: 10px;
height: 10px;
margin-top: -4px;
margin-bottom: -4px;
border-radius: 6px;
}
"""
def __init__(self, p=None):
super(VolumeSlider, self).__init__(p)
self.setSingleStep(5)
self.setFixedWidth(70)
self.setMaximum(100)
self.setValue(100)
self.setCursor(Qt.PointingHandCursor)
self.setStyleSheet(self.horizontal_scrollBar_normal_style)
def enterEvent(self, a0: QtCore.QEvent) -> None:
self.setStyleSheet(self.horizontal_scrollBar_style)
super(VolumeSlider, self).enterEvent(a0)
def leaveEvent(self, a0: QtCore.QEvent) -> None:
self.setStyleSheet(self.horizontal_scrollBar_normal_style)
super(VolumeSlider, self).leaveEvent(a0)
if __name__ == '__main__':
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication(sys.argv)
app.setAttribute(Qt.AA_DontCreateNativeWidgetSiblings)
win = VolumeSlider()
win.show()
sys.exit(app.exec_())
五.思考
1.解码器
如果遇到了DirectShowPlayerService::doSetUrlSource: Unresolved error code 0x80004005 (δ???),90%是因为没有安装或正确安装解码器。
大家在使用本软件之前切记要下载对应的解码器,以保证视频的正常播放。
2.播放列表
本次播放列表的实现是通过QMediaPlayer+QMediaPlaylist的方式实现的,但是写到播放列表“删除”操作的时候,发现只要是当前正在播放视频,删除当前视频前面的视频,就会导致播放器停止播放。这里我思考了,是因为播放列表移除项目后会导致QMediaPlaylist的索引发生改变,这样会很影响体验。
为此笔者对上面思路进行了改进,即不使用QMediaPlaylist,通过手写播放模式的方式控制当前播放列表,每次保证播放器的播放列表只有一个媒体,如果在列表中移除了非当前播放的项目,并不会引起播放器状态改变。
下载地址:https://wwt.lanzoul.com/ikucA1rmby0b
六.总结
本次使用PyQt5最做了一款仿暴风影音的音视频播放器,支持多种文件格式,相关功能丰富,在博文中详细介绍了软件开发的设计思路也贴出了部分代码与大家讨论,提出了我的思考,欢迎大家在评论区和我讨论交流!