python+ffpyplayer制作的视频播放程序第九篇
其中Ui_VideoMainWindow是通过QTDesigner设置页面后使用代码生成工具导出的代码,这样使得View和service的分离,我们再修改页面后避免覆盖一些业务逻辑。至此,关于视频播放的代码基本全了,有些小功能还不完善,大家可以自由发挥,后面我会把代码共享给大家下载。这章我分享一下系统主界面代码,定义了类 Ui_VideoMainWindowService(Ui_VideoMain
·
这章我分享一下系统主界面代码,定义了类 Ui_VideoMainWindowService(Ui_VideoMainWindow)
其中Ui_VideoMainWindow是通过QTDesigner设置页面后使用代码生成工具导出的代码,这样使得View和service的分离,我们再修改页面后避免覆盖一些业务逻辑。至此,关于视频播放的代码基本全了,有些小功能还不完善,大家可以自由发挥,后面我会把代码共享给大家下载。
import cv2
from PySide6 import QtCore
from PySide6.QtGui import QImage, qRgb, QPixmap
from PySide6.QtWidgets import QApplication, QSizePolicy, QFileDialog, QMessageBox
from com.xx.util.datatimetools import TimeUtil
from com.xx.util.exception_handler import exceptionHandler
from com.xx.video.tools.EventFilter import SliderEventFilter
from com.xx.video.tools.VideoPlayerRun import VideoPlayerRun
from com.xx.video.ui.sunwayui.PlayStreaming import PlayStreaming
from com.xx.video.ui.Ui_video_MainWindow import Ui_VideoMainWindow
from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed
import time
from com.xx.video.tools.FileTools import is_path_os
from com.xx.util.style_sheet import setStyleSheet
import ffpyplayer.player
#
from enum import Enum
from qfluentwidgets import (
MessageBox,
)
import com.xx.log as logs
# log的作用域在当前模块
log = logs.get_logger(__name__)
print('log id:',id(log))
defaultPath = "G:\\王郑非\\软考\\2024年中级信管W-持续更新"
class Ui_VideoMainWindowService(Ui_VideoMainWindow):
def __init__(self):
super(Ui_VideoMainWindow, self).__init__()
self.setAcceptDrops(True)
# self.setStyleSheet(qdarkstyle.load_stylesheet())
# StyleSheet.WINDOW.apply(self)
self.setupUi(self) # 创建窗体对象
log.info("初始化页面")
self.setQss()
# QFrame用于拓展一些样式(如:设置边框形状,阴影,),也可以放置其他控件,此处用于放置视频图片。
self.playerFrame.setFrameStyle(2) # 设置风格:边框形状和阴影的组合。
# 这里设置为圆形,你可以改变为其他形状
self.playerFrame.setStyleSheet(
"QFrame {"
" background-color: cyan;"
" border-radius: 10px;" # 圆形的半径大于或等于宽度和高度的一半
"}"
)
# self.frame_2.setContentsMargins(QMargins(1, 1, 1, 1))
# self.frame_3.setContentsMargins(QMargins(1, 1, 1, 1))
self.playerFrame.setLineWidth(1) # 设置边框线宽
self.playerFrame.setMidLineWidth(1) # 设置边框中线线宽
self.labelPalyer = PlayStreaming(self.playerFrame) # 自定义标签实例
self.labelPalyer.setScaledContents(True) # scaledContents,它允许图片可以随意拉伸
# self.labelPalyer.setAlignment(Qt.AlignCenter)
self.playerFrame.setStyleSheet(
"QLabel {"
" background-color: red;" # 设置背景色为青色
" color: black;" # 设置文本颜色为黑色
" background-image: url(:/backimg/backimg.jpg);"
" background-position: center;"
" background->attachment:fixed; "
" background-repeat: repeat; /* 是否重复 repeat-y是重复*"
"}"
)
self.labelPalyer.setSizePolicy(
QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored
)
# self.labelPalyer.set(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # 对于 PyQt5,需要这样设置大小策略
# self.labelPalyer.setMaximumSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # 允许标签增长以填充空间
self.horizontalLayout_4.addWidget(self.labelPalyer) # 布局是设置在playerFrame上
#为滑动条添加事件过滤器
event_filter = SliderEventFilter(self)
self.viderPalyerSlider.installEventFilter(event_filter)
self.playBtn.clicked.connect(self.play)
self.stopBtn.clicked.connect(self.close)
self.pauseBtn.clicked.connect(self.pause)
self.seek_pBtn.clicked.connect(self.seek_p)
self.seek_bBtn.clicked.connect(self.seek_m)
self.muteBtn.clicked.connect(self.mute)
self.vol_pBtn.clicked.connect(self.vol_p)
self.vol_bBtn.clicked.connect(self.vol_m)
self.actionquite.triggered.connect(self.closeWindow)
self.openFile.triggered.connect(self.selectFile)
self.playState = False # 判断播放按钮是否点击
self.filePath = ""
# 定义一个信息对话框
self.msgbox = QMessageBox()
# 创建一个最大容纳数量为2的线程池
self.pool= ThreadPoolExecutor(max_workers=2)
self.futures = []
print("初始化视频控件")
"""
播放事件,使用一个线程进行播放视频
exceptionHandler:添加的装饰器,用于处理异常信息
"""
@exceptionHandler(__name__)
def play(self):
# print(dir(print))
# raise Exception("发生了一个异常")#主动抛出异常的方法
#判断播放文件路径是否正确
if not is_path_os(self.filePath):
w = MessageBox("警告对话框", "请打开视频文件!", self)
w.yesButton.setText('确定')
w.cancelButton.hide()
# 下面是针对按钮的回调函数
if w.exec():
print("确认")
else:
print("取消")
self.playBtn.setEnabled(True)
return
#判断是否存在player属性(播放线程对象实例)
#如果不存在player属性,或者播放对象是关闭的状态
if not hasattr(self, "player") or not self.player.is_running:
# if not self.playState : # 如果按钮未被点击过
# self.playState = True
self.playBtn.setEnabled(False)
self.play2()
else:
log.debug(f"是否关闭:{self.player.close}")
def play2(self):
log.debug(f"播放器大小:宽度{ self.labelPalyer.width()},高度:{self.labelPalyer.height()}")
# 开启线程
# print(hasattr(self, "player"))
# hasattr() 是一个内置函数,用于检查对象是否具有给定的属性
#正在播放的时候,拖入新的播放文件,则需要先终止当前播放
if hasattr(self, "player") and self.player.is_running==True:
self.player.is_running=False #设置为False,以终止正在执行的任务
time.sleep(0.2)
for future in as_completed(self.futures):
result = future.result()
if not result:
print(f"Function {result} returned {result}")
self.futures.remove(future)
self.player = VideoPlayerRun(self.filePath, self.labelPalyer)#构造播放任务实例
self.player.changePixmap.connect(self.labelPalyer.setImage) #为播放图片信号绑定更新事件
self.player.sliderValue.connect(self.update_slider) #滑块更新
self.viderPalyerSlider.setRange(0,self.player.duration) #设置滑动条区间,0到播放时间
self.videoTimelabel.setText(TimeUtil.getHHMMSS_from_Seconds(self.player.duration))
self.viderPalyerSlider.clicked.connect(self.sliderMove) #滑块点击事件
self.viderPalyerSlider.sliderMoved.connect(self.sliderMove)#当滑动条上的滑动块移动时事件处理
# self.player.player.set_callback(self.update_slider, ffpyplayer.player.PY_CALLBACK_ON_DURATION)
# self.labelPalyer.reSize.connect(self.player.scaled)#
#Future对象表示任务的执行结果
future =self.pool.submit(self.player.run)
self.futures.append(future)
print(f'run:{future.done()}')
# print(f'结果:{future.result()}')
# self.player.start()
# self.player.l = self.labelPalyer # 此处的label用于播放视频
def sliderMove(self):
value = self.viderPalyerSlider.value()
# print(value)
self.player.seek_by_slider(value)
def sliderClicked(self):
value = self.viderPalyerSlider.value()
# print(value)#更新滑动条的位置
@QtCore.Slot(float)
def update_slider(self,length):
self.viderPalyerSlider.setValue(length)
if length==0:
self.playBtn.setEnabled(True)
#文件拖拽
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
def dropEvent(self, event):
for url in event.mimeData().urls():
self.filePath = url.toLocalFile()
self.play2()
def pause(self):
self.player.player.toggle_pause()
#前进
def seek_p(self):
self.player.seek_p()
#后退
def seek_m(self):
self.player.seek_m()
#快进条
def set_position(self, position):
self.player.player.setPosition(position)
#静音
def mute(self):
try:
if self.player.player.get_volume() > 0.0:
self.player.player.set_volume(0.0)
else:
self.player.player.set_volume(1.0)
except:
pass
def vol_p(self):
self.player.player.set_volume(self.player.player.get_volume() + 0.1)
print(self.player.player.get_volume())
def vol_m(self):
self.player.player.set_volume(self.player.player.get_volume() - 0.1)
print(self.player.player.get_volume())
@exceptionHandler(__name__)
def selectFile(self): # print("选择文件")
fileName, fileType = QFileDialog.getOpenFileName(
self, "打开视频文件", defaultPath, "视频 文件 (*.mp4 *.avi)"
)
self.filePath = fileName
try:
# 文件重新选择后,重新打开文件
if hasattr(self, "player") and not self.player.close:
path = r"H:\de\bm.jpg"
self.load_image(path)
# self.labelPalyer.setImage(myqimage)
# self.close()
else:
pass
except Exception as e:
self.close()
print(e)
def load_image(self, image_path):
self.labelPalyer.label.setPixmap(QPixmap("")) #
self.labelPalyer.update()
pixmap = QPixmap(image_path) # 从文件加载图片到 QPixmap 对象
if pixmap.isNull(): # 如果图片加载失败,可以处理错误或跳过更新
print("无法加载图片")
return
print("更新图片")
# self.labelPalyer.label.setPixmap(pixmap) # 将 QPixmap 设置到 QLabel 上,刷新显示图片
def close(self):
try:
print("视频播放停止")
self.player.close = True
time.sleep(1)
path = r"H:\de\3Dqj\sun.jpg"
img = cv2.imread(path)
# ... 省略其他代码 ...
myqimage = QImage(
img.data,
img.shape[1],
img.shape[0],
3 * img.shape[1],
QImage.Format_RGB888,
)
self.player.changePixmap.emit(myqimage)
# path = r'H:\de\bm.jpg'
# self.load_image(path)
except Exception as e:
print(e)
pass
def closeEvent(self, event):
try:
# self.destroy() # 窗口关闭销毁
if hasattr(self, "player"): # 线程停止
self.player.player.close_player()
self.player.stop()
self.player.close = True
self.pool.shutdown()
# time.sleep(1)
log.debug("窗口关闭")
except Exception as e:
print(e)
def closeWindow(self):
log.debug("退出程序")
if hasattr(self, "player"): # 线程停止
self.player.player.close_player()
self.player.stop()
self.player.close = True
self.pool.shutdown()
app = QApplication.instance() # 获取当前应用程序对象
app.quit() # 关闭应用程序
def setQss(self):
"""set style sheet"""
self.setObjectName("mainWindow")
self.setProperty("useAcrylic", True)
# self.subMainWindow.setObjectName("subMainWindow")
# self.subStackWidget.setObjectName("subStackWidget")
# self.totalStackWidget.setObjectName('totalStackWidget')
# self.playingInterface.setObjectName("playingInterface")
# self.myMusicInterface.setObjectName('myMusicInterface')
setStyleSheet(self, "main_window")
# if __name__ == "__main__":
# app = QApplication(sys.argv)
# # 获取显示器分辨率
# # desk = QApplication.desktop()
# # width = desk.width() # 宽度
# # height = desk.height() # 高度
# window = Ui_VideoMainWindowService() # 生成主窗口的实例,并将实例传到登陆界面
# window.show()
# # 关闭程序,释放资源
# sys.exit(app.exec_())
# print("系统推出")
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献1条内容
所有评论(0)