python+ffpyplayer制作的视频播放程序第九篇

这章我分享一下系统主界面代码,定义了类 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("系统推出")

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值