使用Python創(chuàng)建一個(gè)視頻管理器并實(shí)現(xiàn)視頻截圖功能
項(xiàng)目概述
本項(xiàng)目的目標(biāo)是創(chuàng)建一個(gè)視頻文件管理器應(yīng)用,它能夠:
- 列出視頻文件:用戶可以選擇一個(gè)文件夾,應(yīng)用會(huì)顯示該文件夾中的所有視頻文件。
- 顯示視頻時(shí)長(zhǎng):用戶點(diǎn)擊視頻文件后,可以查看視頻的時(shí)長(zhǎng)信息。
- 播放視頻:用戶雙擊視頻文件,應(yīng)用將調(diào)用默認(rèn)的媒體播放器播放視頻。
- 生成視頻截圖:用戶可以選擇視頻并設(shè)定截圖時(shí)間間隔,應(yīng)用將生成視頻截圖,并將截圖存放在以視頻文件命名的文件夾中。
- 自動(dòng)打開截圖文件夾:截圖生成后,應(yīng)用會(huì)自動(dòng)打開截圖文件夾以方便用戶查看。
所有代碼
import wx
import os
import datetime
import subprocess
import sys
import cv2 # Ensure OpenCV is installed
import threading
class 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, "修改時(shí)間")
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="導(dǎo)出為文本")
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:
return
file_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_list
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)
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
video.release()
duration_str = str(datetime.timedelta(seconds=int(duration)))
wx.MessageBox(
f"文件名: {file_name}\n時(shí)長(zhǎng): {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("請(qǐng)先選擇要播放的文件", "提示", 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("請(qǐng)輸入有效的時(shí)間間隔(秒)", "錯(cuò)誤", wx.OK | wx.ICON_ERROR)
return
thread = threading.Thread(target=self.capture_screenshots, args=(file_path, interval))
thread.start()
else:
wx.MessageBox("請(qǐng)先選擇要生成截圖的文件", "提示", 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)
# 構(gòu)造 ffmpeg 命令
cmd = [
'ffmpeg',
'-i', file_path, # 輸入視頻文件
'-vf', f'fps=1/{interval}', # 每隔 {interval} 秒截取一幀
os.path.join(output_dir, 'screenshot_%03d.jpg') # 輸出截圖路徑及文件名格式
]
# 執(zhí)行命令
subprocess.run(cmd, check=True)
# 截圖完成后,自動(dòng)打開文件夾
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 screenshots
if 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()
項(xiàng)目實(shí)現(xiàn)
讓我們一步一步實(shí)現(xiàn)這個(gè)項(xiàng)目。
1. 環(huán)境準(zhǔn)備
首先,你需要確保系統(tǒng)中安裝了以下工具:
- Python 3.x:Python 是本項(xiàng)目的編程語言。
- wxPython:用于創(chuàng)建圖形用戶界面。你可以使用以下命令安裝它:
pip install wxPython
- ffmpeg:用于處理視頻文件和生成截圖。你可以從 ffmpeg 官網(wǎng) 下載并安裝。
2. 創(chuàng)建主窗口類
首先,我們創(chuàng)建一個(gè)主窗口類 FileListFrame,用于展示視頻文件列表并處理用戶交互。
import wx
import os
import datetime
import subprocess
import sys
import threading
class FileListFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="視頻文件管理器", size=(600, 400))
self.panel = wx.Panel(self)
self.current_path = ""
# 創(chuàng)建文件列表控件
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, "時(shí)長(zhǎng)")
self.file_list_ctrl.InsertColumn(2, "大小")
self.file_list_ctrl.InsertColumn(3, "修改時(shí)間")
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)
# 創(chuàng)建路徑選擇控件
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)
# 創(chuàng)建導(dǎo)出和播放按鈕
self.capture_button = wx.Button(self.panel, label="截圖")
self.capture_button.Bind(wx.EVT_BUTTON, self.on_capture)
# 創(chuàng)建截圖時(shí)間間隔輸入框
self.interval_label = wx.StaticText(self.panel, label="截圖時(shí)間間隔(秒):")
self.interval_textctrl = wx.TextCtrl(self.panel)
# 創(chuàng)建布局
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__ 方法中,我們初始化了主窗口,并創(chuàng)建了一個(gè) ListCtrl 控件用于顯示視頻文件列表。還添加了用于選擇路徑的按鈕和輸入截圖時(shí)間間隔的文本框。
3. 列出視頻文件
接下來,我們實(shí)現(xiàn)選擇路徑和列出視頻文件的功能:
# 處理選擇路徑事件
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:
return
file_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
這里我們通過遍歷用戶選擇的路徑,查找所有視頻文件,并將其添加到列表控件中。視頻文件的時(shí)長(zhǎng)將在用戶點(diǎn)擊時(shí)獲取。
4. 顯示視頻時(shí)長(zhǎng)
我們使用 ffmpeg 提供的功能來獲取視頻文件的時(shí)長(zhǎng)。
# 處理文件選擇事件
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)
# 獲取視頻時(shí)長(zhǎng)
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)))
在用戶選擇視頻文件時(shí),on_file_selected 事件被觸發(fā),應(yīng)用會(huì)調(diào)用 ffmpeg 命令獲取視頻時(shí)長(zhǎng)并顯示在列表中。
5. 播放視頻
用戶可以通過雙擊視頻文件來播放視頻。我們使用默認(rèn)的媒體播放器來實(shí)現(xiàn)播放功能:
# 處理播放事件
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("請(qǐng)先選擇要播放的文件", "提示", wx.OK | wx.ICON_INFORMATION)
通過調(diào)用系統(tǒng)命令,我們讓視頻文件可以使用系統(tǒng)默認(rèn)的播放器進(jìn)行播放。
6. 截取視頻截圖
用戶可以設(shè)置時(shí)間間隔,并對(duì)視頻進(jì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)
interval = int(self.interval_textctrl.GetValue())
thread = threading.Thread(target=self.capture_screenshots, args=(file_path, interval))
thread.start()
else:
wx.MessageBox("請(qǐng)先選擇要截圖的視頻文件", "提示", 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)
# 構(gòu)造 ffmpeg 命令
cmd = [
'ffmpeg',
'-i', file_path,
'-vf', f'fps=1/{interval}',
os.path.join(output_dir, 'screenshot_%03d.jpg')
]
# 執(zhí)行命令
subprocess.run(cmd, check=True)
# 截圖完成后,自動(dòng)打開文件夾
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. 運(yùn)行應(yīng)用
最后,添加主函數(shù)以啟動(dòng)應(yīng)用:
if __name__ == "__main__":
app = wx.App(False)
frame = FileListFrame()
frame.Show()
app.MainLoop()
效果如下

總結(jié)
在這篇博客中,我們使用 wxPython 和 ffmpeg 創(chuàng)建了一個(gè)視頻文件管理器。它不僅能管理和播放視頻,還能生成視頻截圖,并將截圖存放到特定的文件夾中。你可以根據(jù)需要進(jìn)一步擴(kuò)展這個(gè)應(yīng)用程序,例如增加視頻剪輯、視頻合并等功能。希望這篇博客能幫助你更好地理解 wxPython 和 ffmpeg 的使用方法,并激發(fā)你在項(xiàng)目中應(yīng)用這些技術(shù)的興趣。
以上就是使用Python創(chuàng)建一個(gè)視頻管理器并實(shí)現(xiàn)視頻截圖功能的詳細(xì)內(nèi)容,更多關(guān)于Python視頻截圖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
scrapy-redis源碼分析之發(fā)送POST請(qǐng)求詳解
這篇文章主要給大家介紹了關(guān)于scrapy-redis源碼分析之發(fā)送POST請(qǐng)求的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用scrapy-redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
python 實(shí)現(xiàn)一次性在文件中寫入多行的方法
今天小編就為大家分享一篇python 實(shí)現(xiàn)一次性在文件中寫入多行的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-01-01
Python單元測(cè)試框架unittest使用方法講解
這篇文章主要介紹了Python單元測(cè)試框架unittest使用方法講解,本文講解了unittest概述、命令行接口、測(cè)試案例自動(dòng)搜索、創(chuàng)建測(cè)試代碼、構(gòu)建測(cè)試套件方法等內(nèi)容,需要的朋友可以參考下2015-04-04
Django的URLconf中使用缺省視圖參數(shù)的方法
這篇文章主要介紹了Django的URLconf中使用缺省視圖參數(shù)的方法,Django是最著名的Python的web開發(fā)框架,需要的朋友可以參考下2015-07-07
Python永久配置國(guó)內(nèi)鏡像源安裝再也不用擔(dān)心卡頓
這篇文章主要為大家介紹了Python如何永久配置國(guó)內(nèi)鏡像源,從此安裝再也不用擔(dān)心卡頓,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10
mac PyCharm添加Python解釋器及添加package路徑的方法
今天小編就為大家分享一篇mac PyCharm添加Python解釋器及添加package路徑的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10
Python使用Matplotlib實(shí)現(xiàn)雨點(diǎn)圖動(dòng)畫效果的方法
這篇文章主要介紹了Python使用Matplotlib實(shí)現(xiàn)雨點(diǎn)圖動(dòng)畫效果的方法,結(jié)合實(shí)例形式分析了win10安裝ffmpeg及animation函數(shù)的使用相關(guān)操作技巧,需要的朋友可以參考下2017-12-12

