如何使用 Python创建一个视频文件管理器并实现视频截图功能

embedded/2024/9/25 5:22:22/

在这篇博客中,我将向大家展示如何使用 wxPython 创建一个简单的图形用户界面 (GUI) 应用程序,该应用程序可以管理视频文件列表、播放视频,并生成视频截图。我们将逐步实现这些功能,并确保代码易于理解和扩展。
C:\pythoncode\new\searchmediafileinfolder.py

项目概述

本项目的目标是创建一个视频文件管理器应用,它能够:

所有代码

python">import wx
import os
import datetime
import subprocess
import sys
import cv2  # Ensure OpenCV is installed
import threadingclass FileListFrame(wx.Frame):def __init__(self):wx.Frame.__init__(self, None, title="视频文件列表", size=(600, 400))self.panel = wx.Panel(self)self.current_path = ""self.file_list_ctrl = wx.ListCtrl(self.panel, style=wx.LC_REPORT | wx.LC_SINGLE_SEL)self.file_list_ctrl.InsertColumn(0, "文件名")self.file_list_ctrl.InsertColumn(1, "大小")self.file_list_ctrl.InsertColumn(2, "修改时间")self.file_list_ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_file_selected)self.file_list_ctrl.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_file_double_clicked)self.path_label = wx.StaticText(self.panel, label="路径:")self.path_textctrl = wx.TextCtrl(self.panel, style=wx.TE_READONLY)self.path_button = wx.Button(self.panel, label="选择路径")self.path_button.Bind(wx.EVT_BUTTON, self.on_select_path)self.interval_label = wx.StaticText(self.panel, label="截图间隔(秒):")self.interval_textctrl = wx.TextCtrl(self.panel, value="1")self.capture_button = wx.Button(self.panel, label="生成截图")self.capture_button.Bind(wx.EVT_BUTTON, self.on_capture)self.export_button = wx.Button(self.panel, label="导出为文本")self.export_button.Bind(wx.EVT_BUTTON, self.on_export)self.play_button = wx.Button(self.panel, label="播放")self.play_button.Bind(wx.EVT_BUTTON, self.on_play)sizer = wx.BoxSizer(wx.VERTICAL)sizer.Add(self.path_label, 0, wx.ALL, 5)sizer.Add(self.path_textctrl, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)sizer.Add(self.path_button, 0, wx.ALL, 5)sizer.Add(self.interval_label, 0, wx.ALL, 5)sizer.Add(self.interval_textctrl, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)sizer.Add(self.capture_button, 0, wx.ALL, 5)sizer.Add(self.file_list_ctrl, 1, wx.EXPAND | wx.ALL, 5)sizer.Add(self.export_button, 0, wx.ALL, 5)sizer.Add(self.play_button, 0, wx.ALL, 5)self.panel.SetSizer(sizer)def on_select_path(self, event):dlg = wx.DirDialog(self, "选择路径", style=wx.DD_DEFAULT_STYLE)if dlg.ShowModal() == wx.ID_OK:self.current_path = dlg.GetPath()self.path_textctrl.SetValue(self.current_path)self.update_file_list()dlg.Destroy()def update_file_list(self):self.file_list_ctrl.DeleteAllItems()if not self.current_path:returnfile_list = self.search_video_files(self.current_path)for filename, file_path, file_size, modified_time in file_list:modified_time_str = datetime.datetime.fromtimestamp(modified_time).strftime("%Y-%m-%d %H:%M:%S")index = self.file_list_ctrl.InsertItem(self.file_list_ctrl.GetItemCount(), filename)self.file_list_ctrl.SetItem(index, 1, str(file_size))self.file_list_ctrl.SetItem(index, 2, modified_time_str)def search_video_files(self, directory):video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm']file_list = []for root, dirs, files in os.walk(directory):for file in files:if os.path.splitext(file)[1].lower() in video_extensions:file_path = os.path.join(root, file)file_size = os.path.getsize(file_path)modified_time = os.path.getmtime(file_path)file_list.append((file, file_path, file_size, modified_time))return file_listdef on_file_selected(self, event):selected_item = event.GetItem()file_name = selected_item.GetText()file_path = os.path.join(self.current_path, file_name)video = cv2.VideoCapture(file_path)fps = video.get(cv2.CAP_PROP_FPS)frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT))duration = frame_count / fpsvideo.release()duration_str = str(datetime.timedelta(seconds=int(duration)))wx.MessageBox(f"文件名: {file_name}\n时长: {duration_str}","视频信息", wx.OK | wx.ICON_INFORMATION)def on_file_double_clicked(self, event):self.on_play(event)def on_play(self, event):selected_item = self.file_list_ctrl.GetFirstSelected()if selected_item != -1:file_name = self.file_list_ctrl.GetItemText(selected_item)file_path = os.path.join(self.current_path, file_name)if sys.platform.startswith('win'):subprocess.Popen(['start', '', file_path], shell=True)elif sys.platform.startswith('darwin'):subprocess.Popen(['open', file_path])elif sys.platform.startswith('linux'):subprocess.Popen(['xdg-open', file_path])else:wx.MessageBox("请先选择要播放的文件", "提示", wx.OK | wx.ICON_INFORMATION)def on_export(self, event):dlg = wx.FileDialog(self, "保存为文本文件", wildcard="Text files (*.txt)|*.txt",style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)if dlg.ShowModal() == wx.ID_OK:file_path = dlg.GetPath()with open(file_path, 'w') as file:for index in range(self.file_list_ctrl.GetItemCount()):file.write(self.file_list_ctrl.GetItemText(index) + '\n')def on_capture(self, event):selected_item = self.file_list_ctrl.GetFirstSelected()if selected_item != -1:file_name = self.file_list_ctrl.GetItemText(selected_item)file_path = os.path.join(self.current_path, file_name)try:interval = int(self.interval_textctrl.GetValue())except ValueError:wx.MessageBox("请输入有效的时间间隔(秒)", "错误", wx.OK | wx.ICON_ERROR)returnthread = threading.Thread(target=self.capture_screenshots, args=(file_path, interval))thread.start()else:wx.MessageBox("请先选择要生成截图的文件", "提示", wx.OK | wx.ICON_INFORMATION)def capture_screenshots(self, file_path, interval):# 生成以视频名称命名的文件夹output_dir = os.path.join(self.current_path, os.path.splitext(os.path.basename(file_path))[0])if not os.path.exists(output_dir):os.makedirs(output_dir)# 构造 ffmpeg 命令cmd = ['ffmpeg','-i', file_path,                 # 输入视频文件'-vf', f'fps=1/{interval}',      # 每隔 {interval} 秒截取一帧os.path.join(output_dir, 'screenshot_%03d.jpg')  # 输出截图路径及文件名格式]# 执行命令subprocess.run(cmd, check=True)# 截图完成后,自动打开文件夹if sys.platform.startswith('win'):subprocess.Popen(['explorer', output_dir])elif sys.platform.startswith('darwin'):subprocess.Popen(['open', output_dir])elif sys.platform.startswith('linux'):subprocess.Popen(['xdg-open', output_dir])# def capture_screenshots(self, file_path, interval):#     video = cv2.VideoCapture(file_path)#     fps = video.get(cv2.CAP_PROP_FPS)#     frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT))#     duration = frame_count / fps#     output_dir = os.path.join(self.current_path, os.path.splitext(os.path.basename(file_path))[0])  #     if not os.path.exists(output_dir):#         os.makedirs(output_dir)#     for sec in range(0, int(duration), interval):video.set(cv2.CAP_PROP_POS_MSEC, sec * 1000)success, image = video.read()if success:cv2.imwrite(os.path.join(output_dir, f"{sec}.png"), image)video.release()wx.CallAfter(wx.MessageBox, "截图已生成", "完成", wx.OK | wx.ICON_INFORMATION)# Automatically open the folder containing screenshotsif sys.platform.startswith('win'):subprocess.Popen(['explorer', output_dir], shell=True)elif sys.platform.startswith('darwin'):subprocess.Popen(['open', output_dir])elif sys.platform.startswith('linux'):subprocess.Popen(['xdg-open', output_dir])if __name__ == "__main__":app = wx.App()frame = FileListFrame()frame.Show()app.MainLoop()

项目实现

让我们一步一步实现这个项目。

1. 环境准备

首先,你需要确保系统中安装了以下工具:

  • Python 3.x:Python 是本项目的编程语言。
  • wxPython:用于创建图形用户界面。你可以使用以下命令安装它:
    pip install wxPython
    
  • ffmpeg:用于处理视频文件和生成截图。你可以从 ffmpeg 官网 下载并安装。
2. 创建主窗口类

首先,我们创建一个主窗口类 FileListFrame,用于展示视频文件列表并处理用户交互。

python">import wx
import os
import datetime
import subprocess
import sys
import threadingclass FileListFrame(wx.Frame):def __init__(self):wx.Frame.__init__(self, None, title="视频文件管理器", size=(600, 400))self.panel = wx.Panel(self)self.current_path = ""# 创建文件列表控件self.file_list_ctrl = wx.ListCtrl(self.panel, style=wx.LC_REPORT | wx.LC_SINGLE_SEL)self.file_list_ctrl.InsertColumn(0, "文件名")self.file_list_ctrl.InsertColumn(1, "时长")self.file_list_ctrl.InsertColumn(2, "大小")self.file_list_ctrl.InsertColumn(3, "修改时间")self.file_list_ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_file_selected)self.file_list_ctrl.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_play)# 创建路径选择控件self.path_label = wx.StaticText(self.panel, label="路径:")self.path_textctrl = wx.TextCtrl(self.panel, style=wx.TE_READONLY)self.path_button = wx.Button(self.panel, label="选择路径")self.path_button.Bind(wx.EVT_BUTTON, self.on_select_path)# 创建导出和播放按钮self.capture_button = wx.Button(self.panel, label="截图")self.capture_button.Bind(wx.EVT_BUTTON, self.on_capture)# 创建截图时间间隔输入框self.interval_label = wx.StaticText(self.panel, label="截图时间间隔(秒):")self.interval_textctrl = wx.TextCtrl(self.panel)# 创建布局sizer = wx.BoxSizer(wx.VERTICAL)sizer.Add(self.path_label, 0, wx.ALL, 5)sizer.Add(self.path_textctrl, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)sizer.Add(self.path_button, 0, wx.ALL, 5)sizer.Add(self.file_list_ctrl, 1, wx.EXPAND | wx.ALL, 5)sizer.Add(self.interval_label, 0, wx.ALL, 5)sizer.Add(self.interval_textctrl, 0, wx.EXPAND | wx.ALL, 5)sizer.Add(self.capture_button, 0, wx.ALL, 5)self.panel.SetSizer(sizer)

__init__ 方法中,我们初始化了主窗口,并创建了一个 ListCtrl 控件用于显示视频文件列表。还添加了用于选择路径的按钮和输入截图时间间隔的文本框。

3. 列出视频文件

接下来,我们实现选择路径和列出视频文件的功能:

python">    # 处理选择路径事件def on_select_path(self, event):dlg = wx.DirDialog(self, "选择路径", style=wx.DD_DEFAULT_STYLE)if dlg.ShowModal() == wx.ID_OK:self.current_path = dlg.GetPath()self.path_textctrl.SetValue(self.current_path)self.update_file_list()dlg.Destroy()# 更新文件列表def update_file_list(self):self.file_list_ctrl.DeleteAllItems()if not self.current_path:returnfile_list = self.search_video_files(self.current_path)for filename, file_path, file_size, modified_time in file_list:modified_time_str = datetime.datetime.fromtimestamp(modified_time).strftime("%Y-%m-%d %H:%M:%S")index = self.file_list_ctrl.InsertItem(self.file_list_ctrl.GetItemCount(), filename)self.file_list_ctrl.SetItem(index, 1, "待获取")self.file_list_ctrl.SetItem(index, 2, str(file_size))self.file_list_ctrl.SetItem(index, 3, modified_time_str)# 搜索视频文件def search_video_files(self, directory):video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm']file_list = []for root, dirs, files in os.walk(directory):for file in files:if os.path.splitext(file)[1].lower() in video_extensions:file_path = os.path.join(root, file)file_size = os.path.getsize(file_path)modified_time = os.path.getmtime(file_path)file_list.append((file, file_path, file_size, modified_time))return file_list

这里我们通过遍历用户选择的路径,查找所有视频文件,并将其添加到列表控件中。视频文件的时长将在用户点击时获取。

4. 显示视频时长

我们使用 ffmpeg 提供的功能来获取视频文件的时长。

python">    # 处理文件选择事件def on_file_selected(self, event):selected_item = event.GetItem()file_name = selected_item.GetText()file_path = os.path.join(self.current_path, file_name)duration = self.get_video_duration(file_path)self.file_list_ctrl.SetItem(selected_item.GetId(), 1, duration)# 获取视频时长def get_video_duration(self, file_path):cmd = ['ffmpeg','-i', file_path,'-hide_banner','-loglevel', 'error','-show_entries', 'format=duration','-of', 'default=noprint_wrappers=1:nokey=1']result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)duration = float(result.stdout.strip())return str(datetime.timedelta(seconds=int(duration)))

在用户选择视频文件时,on_file_selected 事件被触发,应用会调用 ffmpeg 命令获取视频时长并显示在列表中。

5. 播放视频

用户可以通过双击视频文件来播放视频。我们使用默认的媒体播放器来实现播放功能:

python">    # 处理播放事件def on_play(self, event):selected_item = self.file_list_ctrl.GetFirstSelected()if selected_item != -1:file_name = self.file_list_ctrl.GetItemText(selected_item)file_path = os.path.join(self.current_path, file_name)if sys.platform.startswith('win'):subprocess.Popen(['start', '', file_path], shell=True)elif sys.platform.startswith('darwin'):subprocess.Popen(['open', file_path])elif sys.platform.startswith('linux'):subprocess.Popen(['xdg-open', file_path])else:wx.MessageBox("请先选择要播放的文件", "提示", wx.OK | wx.ICON_INFORMATION)

通过调用系统命令,我们让视频文件可以使用系统默认的播放器进行播放。

6. 截取视频截图

用户可以设置时间间隔,并对视频进行截图截图将保存到以视频文件名命名的文件夹中:

python">    # 处理截图事件def on_capture(self, event):selected_item = self.file_list_ctrl.GetFirstSelected()if selected_item != -1:file_name = self.file_list_ctrl.GetItemText(selected_item)file_path = os.path.join(self.current_path, file_name)interval = int(self.interval_textctrl.GetValue())thread = threading.Thread(target=self.capture_screenshots, args=(file_path, interval))thread.start()else:wx.MessageBox("请先选择要截图视频文件", "提示", wx.OK | wx.ICON_INFORMATION)# 截取视频截图def capture_screenshots(self, file_path, interval):# 生成以视频名称命名的文件夹output_dir= os.path.join(self.current_path, os.path.splitext(os.path.basename(file_path))[0])if not os.path.exists(output_dir):os.makedirs(output_dir)# 构造 ffmpeg 命令cmd = ['ffmpeg','-i', file_path,'-vf', f'fps=1/{interval}',os.path.join(output_dir, 'screenshot_%03d.jpg')]# 执行命令subprocess.run(cmd, check=True)# 截图完成后,自动打开文件夹if sys.platform.startswith('win'):subprocess.Popen(['explorer', output_dir])elif sys.platform.startswith('darwin'):subprocess.Popen(['open', output_dir])elif sys.platform.startswith('linux'):subprocess.Popen(['xdg-open', output_dir])
7. 运行应用

最后,添加主函数以启动应用:

python">if __name__ == "__main__":app = wx.App(False)frame = FileListFrame()frame.Show()app.MainLoop()

效果如下

在这里插入图片描述

总结

在这篇博客中,我们使用 wxPython 和 ffmpeg 创建了一个视频文件管理器。它不仅能管理和播放视频,还能生成视频截图,并将截图存放到特定的文件夹中。你可以根据需要进一步扩展这个应用程序,例如增加视频剪辑、视频合并等功能。希望这篇博客能帮助你更好地理解 wxPython 和 ffmpeg 的使用方法,并激发你在项目中应用这些技术的兴趣。


http://www.ppmy.cn/embedded/98232.html

相关文章

Python编码系列—Python机器学习库scikit-learn:解锁高效数据处理与智能应用

🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…

Apache CloudStack Official Document 翻译节选(三)

关于 Apache CloudStack 的 概念和专用术语 (三) About Pods 豆荚舱通常代表着一个单独的机柜,同一个豆荚舱中的宿主机处于同一个分支网络中。在部署Apache CloudStack时豆荚舱是第三大的资源管理单位。豆荚舱隶属于专职地带,每一…

CPU内部单总线数据通路各阶段的微操作序列利控制信号

1.内部总线与系统总线 内部总线是指同一部件,如CPU内部连接各寄存器及运算部件之间的总线; 系统总线是指同一台计算机系统的各部件,如CPU、内存、通道和各类/0接口间互相连接的总线。 2.寄存器之间数据传送 比如把PC内容送至MAR&#xff…

一款免费的开源支付网关系统,支持X宝、某信、云闪付等多种支付方式,提供收单、退款、聚合支付、对账、分账等功能(附源码)

前言 在数字化浪潮中,电子-商务和移动支付迅速崛起,支付系统成为企业运营的核心组件。然而,现有支付处理方案常面临成本高、集成复杂、系统耦合度高等挑战。 这些问题不仅增加了企业负担,也制约了业务的快速迭代和创新。市场迫切…

基于GPT-SoVITS的API实现批量克隆声音

目标是将每一段声音通过GPT-SoVITS的API的API进行克隆,因为拼在一起的整个片段处理会造成内存或者缓存溢出。 将目录下的音频文件生成到指定目录下,然后再进行拼接。 通过AI工具箱生成的数据文件是这样的结构,temp目录下是没个片段生成的部分,connect_是正常拼接的音频文件…

Milvus向量数据库-内存中索引简介

以下内容是自己在学习Milvus向量数据库时,在Milvus官方网站文档库中对索引的学习整理和翻译,通过自己和借助翻译软件进行了理解整合翻译,有可能有一些地方理解整理的不到位,还望大家理解。 一、内存中索引 本文列出了 Milvus 支…

搭建个人网站

一 个人搭建网站需要进行的操作 详细步骤: 1 网站目标:搭建在线查看法拍房拍卖价格的预测模型,输出预测结果 2 实际功能:在线爬取 阿里法拍网站的信息 3 根据实时模型建模预测法拍价格和成交概率 要搭建一个能够在线查看法拍房拍卖…

stm32—ADC

1. 什么是ADC 生活中我们经常会用到ADC这种器件,比如说,当我们在使用手机进行语音通信时,ADC器件会将我们的声信号转换为电信号 (模拟信号 ---> 数字信号) 模拟信号: 模拟信号是指用连续变化的物理量表示的信息,其信…