【开源工具】视频分割神器:PyQt6+FFmpeg多线程优化设计实现

🎬 【开源工具】视频分割神器:PyQt6+FFmpeg多线程优化设计实现

请添加图片描述

🌈 个人主页:创客白泽 - CSDN博客
🔥 系列专栏:🐍《Python开源项目实战》
💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦

更新日期:2023年11月15日
技术栈:Python3 + PyQt6 + FFmpeg
关键词视频处理 Qt6开发 Python GUI FFmpeg 开源工具

请添加图片描述
在这里插入图片描述

🌟 一、开篇碎碎念

最近在整理旅行视频时,发现需要把长视频按场景分割成小片段。试了几款工具都不够顺手,要么操作复杂,要么界面丑陋。作为技术人,当然要自己造轮子!💪

本文将带你用PyQt6打造一款支持拖拽操作三种分割模式高颜值界面的视频分割神器。最重要的是——完整开源!文末提供源码下载~

(效果预览图)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


🛠️ 二、功能全景图

2.1 核心功能清单

功能模块 技术实现 亮点
拖拽文件支持 Qt6 DnD API 拖拽即识别,边框动态反馈
三种分割模式 FFmpeg子进程 按秒/按段/区间三种策略
实时进度展示 QProgressBar 平滑动画+百分比计算
智能时长解析 FFprobe调用 自动识别视频元数据
跨平台支持 Python封装 Win/macOS/Linux全兼容

2.2 技术选型对比

45% 35% 15% 5% 技术栈构成 PyQt6界面 FFmpeg处理 多线程控制 异常处理

🎨 三、UI设计详解

3.1 色彩方案

采用蓝绿渐变科技风配色:

COLOR_SCHEME = {
   
   
    'primary': '#5D9CEC',  # 主按钮蓝
    'success': '#4CAF50',  # 成功绿
    'danger': '#F44336',   # 警告红
    'background': '#F0F7FA' # 背景浅蓝
}

3.2 关键界面组件

  1. 拖拽文件区:虚线边框+悬浮高亮
   QLabel {
   
   
       border: 2px dashed #aaa;
       border-radius: 5px;
       background: rgba(255,255,255,0.7);
   }
   QLabel:hover {
   
    border-color: #5D9CEC; }
  1. 动态进度条
   self.progress.setStyleSheet("""
       QProgressBar::chunk {
           background: qlineargradient(
               x1:0, y1:0, x2:1, y2:0,
               stop:0 #4CAF50, stop:1 #8BC34A);
       }
   """)

🚀 四、手把手教学:从安装到使用

4.1 环境准备

# 必备依赖
pip install PyQt6 moviepy
# FFmpeg安装(Windows示例)
choco install ffmpeg

4.2 视频分割处理流程

在这里插入图片描述


💻 五、核心代码解析

5.1 拖拽处理三件套

def dragEnterEvent(self, event):
    """文件拖入时触发"""
    if event.mimeData().hasUrls():
        event.acceptProposedAction()

def dropEvent(self, event):
    """文件放下时处理"""
    file_path = event.mimeData().urls()[0].toLocalFile()
    self.process_video(file_path)

5.2 FFmpeg多线程调用

Thread(target=lambda: 
    subprocess.run([
        'ffmpeg', '-ss', start_time,
        '-i', input_path,
        '-t', duration,
        '-c', 'copy', output_path
    ], check=True)
).start()

5.3 智能区间计算

def calculate_intervals(self):
    """自动计算合理分段"""
    duration = self.get_duration()
    if mode == "seconds":
        return [(i*interval, (i+1)*interval) 
               for i in range(math.ceil(duration/interval))]

📦 六、源码下载

import os
import math
import subprocess
import json
import re
from threading import Thread
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                            QLabel, QPushButton, QLineEdit, QRadioButton, QButtonGroup,
                            QProgressBar, QListWidget, QFileDialog, QMessageBox,
                            QDialog, QFrame, QListWidgetItem)
from PyQt6.QtCore import Qt, QSize, QMimeData, QUrl
from PyQt6.QtGui import QIcon, QFont, QColor, QDragEnterEvent, QDropEvent


class VideoProcessor:
    def __init__(self):
        self.source = ""
        self.running = True

    def set_source(self, path):
        """设置视频源文件"""
        if not os.path.exists(path):
            raise FileNotFoundError("文件不存在")
        self.source = path

    def start(self, mode, param, progress_callback, status_callback):
        """处理入口"""
        duration = self.get_duration()

        if mode == "seconds":
            self.split_by_seconds(param, duration, progress_callback, status_callback)
        elif mode == "segments":
            self.split_by_segments(param, duration, progress_callback, status_callback)
        elif mode == "intervals":
            self.split_by_intervals(param, duration, progress_callback, status_callback)
        else:
            raise ValueError(f"不支持的分割模式: {
     
     mode}")

    def split_by_seconds(self, interval, duration, progress_callback, status_callback):
        """秒级分割逻辑"""
        full_segments = int(duration // interval)
        remaining = duration % interval

        actual_segments = full_segments + (1 if remaining >= 1 else 0)
        output_dir = self.create_output_dir("秒分割")

        base_name, ext = os.path.splitext(os.path.basename(self.source))
        for i in range(actual_segments):
            if not self.running:
                break
            start = i * interval
            end = (i + 1) * interval if i < actual_segments - 1 else duration

            output_name = f"{
     
     base_name}_副本_{
     
     i + 1}{
     
     ext}"
            subprocess.run([
                'ffmpeg', '-y', '-ss', str(start), '-i', self.source,
                '-t', str(end - start), '-c', 'copy',
                os.path.join(output_dir, output_name)
            ], check=True)
            progress_callback((i + 1) / actual_segments * 100)
            status_callback(f"生成分段 {
     
     i + 1}/{
     
     actual_segments}")

    def split_by_segments(self, count, duration, progress_callback, status_callback):
        """分段模式处理"""
        interval = duration / count
        output_dir = self.create_output_dir("段分割")

        base_name, ext = os.path.splitext(os.path.basename(self.source))
        for i in range(count):
            if not self.running:
                break
            start = i * interval
            end = duration if i == count - 1 else (i + 1) * interval

            output_name = f"{
     
     base_name}_副本_{
     
     i + 1}{
     
     ext}"
            subprocess.run([
                'ffmpeg', '-y', '-ss', str(start), '-i', self.source,
                '-t', str(end - start), '-c', 'copy',
                os.path.join(output_dir, output_name)
            ], check=True)
            progress_callback((i + 1) / count * 100)
            status_callback(f"处理进度 {
     
     i + 1}/{
     
     count}")

    def split_by_intervals(self, intervals, duration, progress_callback, status_callback):
        """区间提取逻辑"""
        valid_intervals = []
        for interval in intervals:
            start, end = interval
            if 0 <= start < end <= duration and end - start >= 0.5:
                valid_intervals.append((start, end))

        if not valid_intervals:
            raise ValueError("没有有效的提取区间")

        output_dir = self.create_output_dir("区间提取")

        base_name, ext = os.path.splitext(os.path.basename(self.source))
        total = len(valid_intervals)
        for i, (start, end) in enumerate(valid_intervals):
            if not self.running:
                break

            start_hour, start_minute, start_second = self.seconds_to_hms(start)
            end_hour, end_minute, end_second = self.seconds_to_hms(end)
            output_name = f"
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创客白泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值