请给我修复后完整的main.py文件
时间: 2025-08-26 08:56:23 AIGC 浏览: 17
<think>我们之前已经对main.py进行了优化,现在根据要求,我将提供修复后的完整main.py文件。
注意:该文件包含之前的所有优化,包括错误处理、输出文件命名、进度更新等。
同时,我们使用generate_output_path函数来生成输出路径,并确保在UI中显示。
完整main.py代码如下:</think>下面提供修复后的完整 `main.py` 文件,包含所有优化功能:智能输出路径、增强错误处理、进度反馈和打包支持:
```python
import sys
import os
import shutil
import threading
import tempfile
import traceback
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QFileDialog, QProgressBar, QMessageBox
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
import video_utils
class VideoProcessorApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("视频处理器 - 增强版")
self.setGeometry(100, 100, 600, 400)
# 创建主控件和布局
main_widget = QWidget()
self.setCentralWidget(main_widget)
layout = QVBoxLayout()
# 视频A选择
self.btn_select_a = QPushButton("选择视频A (主视频)")
self.label_a = QLabel("未选择文件")
self.btn_select_a.clicked.connect(lambda: self.select_file('a'))
# 视频B选择
self.btn_select_b = QPushButton("选择视频B (辅助视频)")
self.label_b = QLabel("未选择文件")
self.btn_select_b.clicked.connect(lambda: self.select_file('b'))
# 输出信息
self.label_output_info = QLabel("输出文件将自动保存在视频A的同目录下")
self.label_output_info.setWordWrap(True)
# 进度条
self.progress = QProgressBar()
self.progress.setAlignment(Qt.AlignCenter)
# 处理按钮
self.btn_process = QPushButton("开始处理")
self.btn_process.clicked.connect(self.start_processing)
# 状态标签
self.status_label = QLabel("就绪")
self.status_label.setAlignment(Qt.AlignCenter)
# 添加到布局
layout.addWidget(QLabel("主视频 (A):"))
layout.addWidget(self.btn_select_a)
layout.addWidget(self.label_a)
layout.addWidget(QLabel("辅助视频 (B):"))
layout.addWidget(self.btn_select_b)
layout.addWidget(self.label_b)
layout.addWidget(self.label_output_info)
layout.addWidget(self.progress)
layout.addWidget(self.btn_process)
layout.addWidget(self.status_label)
main_widget.setLayout(layout)
# 初始化变量
self.video_a = ""
self.video_b = ""
self.temp_dirs = []
self.processing_thread = None
self.cancel_processing = False
def select_file(self, target):
file, _ = QFileDialog.getOpenFileName(
self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mov *.mkv *.flv *.wmv)"
)
if file:
if target == 'a':
self.video_a = file
self.label_a.setText(os.path.basename(file))
# 更新输出信息
output_path = video_utils.generate_output_path(file, "merged")
self.label_output_info.setText(f"输出文件将保存为: {os.path.basename(output_path)}")
else:
self.video_b = file
self.label_b.setText(os.path.basename(file))
def update_progress(self, value, max_value=100, message=""):
self.progress.setValue(value)
if max_value != 100:
self.progress.setMaximum(max_value)
if message:
self.status_label.setText(message)
def start_processing(self):
if not all([self.video_a, self.video_b]):
QMessageBox.warning(self, "错误", "请先选择两个视频文件")
return
# 禁用按钮防止重复点击
self.btn_process.setEnabled(False)
self.progress.setValue(0)
self.status_label.setText("开始处理...")
self.cancel_processing = False
# 创建处理线程
self.processing_thread = ProcessingThread(
self.video_a,
self.video_b,
self
)
self.processing_thread.progress_signal.connect(self.update_progress)
self.processing_thread.finished_signal.connect(self.on_processing_finished)
self.processing_thread.error_signal.connect(self.on_processing_error)
self.processing_thread.start()
def on_processing_finished(self, output_path):
self.btn_process.setEnabled(True)
self.update_progress(100, message="处理完成!")
reply = QMessageBox.information(
self,
"完成",
f"视频处理完成!\n输出文件: {output_path}\n\n是否打开所在文件夹?",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
output_dir = os.path.dirname(output_path)
if os.path.exists(output_dir):
os.startfile(output_dir)
def on_processing_error(self, error_msg):
self.btn_process.setEnabled(True)
self.status_label.setText(f"错误: {error_msg}")
# 显示详细错误信息
QMessageBox.critical(self, "处理错误", error_msg)
def closeEvent(self, event):
"""窗口关闭时清理临时文件"""
if self.processing_thread and self.processing_thread.isRunning():
reply = QMessageBox.question(
self,
"处理中",
"视频处理仍在进行中,确定要退出吗?",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.No:
event.ignore()
return
else:
# 请求取消处理
self.cancel_processing = True
self.processing_thread.cancel()
self.processing_thread.wait(2000) # 等待2秒
# 清理临时目录
for temp_dir in self.temp_dirs:
if os.path.exists(temp_dir):
try:
shutil.rmtree(temp_dir, ignore_errors=True)
except:
pass
event.accept()
class ProcessingThread(QThread):
progress_signal = pyqtSignal(int, int, str) # value, max_value, message
finished_signal = pyqtSignal(str) # output_path
error_signal = pyqtSignal(str) # error_message
def __init__(self, video_a, video_b, parent=None):
super().__init__(parent)
self.video_a = video_a
self.video_b = video_b
self.output_path = None
self.cancel_requested = False
def cancel(self):
"""请求取消处理"""
self.cancel_requested = True
def run(self):
temp_dir = None
try:
# 生成输出路径
self.output_path = video_utils.generate_output_path(self.video_a, "merged")
self.progress_signal.emit(5, 100, f"输出文件: {os.path.basename(self.output_path)}")
# 创建临时目录
temp_dir = tempfile.mkdtemp()
if hasattr(self.parent(), 'temp_dirs'):
self.parent().temp_dirs.append(temp_dir)
# 检查取消请求
if self.cancel_requested:
raise RuntimeError("处理已取消")
# 步骤1: 处理视频A
self.progress_signal.emit(10, 100, "正在处理视频A...")
processed_a = os.path.join(temp_dir, "processed_a.mp4")
video_utils.process_video(self.video_a, processed_a)
# 检查取消请求
if self.cancel_requested:
raise RuntimeError("处理已取消")
# 步骤2: 拆帧视频A (4线程)
self.progress_signal.emit(20, 100, "正在拆解视频A帧...")
frames_a = os.path.join(temp_dir, "frames_a")
video_utils.extract_frames(processed_a, frames_a, thread_count=4)
# 检查取消请求
if self.cancel_requested:
raise RuntimeError("处理已取消")
# 步骤3: 处理视频B
self.progress_signal.emit(40, 100, "正在处理视频B...")
processed_b = os.path.join(temp_dir, "processed_b.mp4")
video_utils.process_video(self.video_b, processed_b)
# 检查取消请求
if self.cancel_requested:
raise RuntimeError("处理已取消")
# 步骤4: 拆帧视频B (4线程)
self.progress_signal.emit(60, 100, "正在拆解视频B帧...")
frames_b = os.path.join(temp_dir, "frames_b")
video_utils.extract_frames(processed_b, frames_b, thread_count=4)
# 检查取消请求
if self.cancel_requested:
raise RuntimeError("处理已取消")
# 步骤5: 合并帧
self.progress_signal.emit(70, 100, "正在合并帧...")
merged_video = os.path.join(temp_dir, "merged.mp4")
video_utils.merge_frames(frames_a, frames_b, merged_video, audio_src=self.video_a)
# 检查取消请求
if self.cancel_requested:
raise RuntimeError("处理已取消")
# 步骤6: 处理元数据
self.progress_signal.emit(90, 100, "正在处理元数据...")
video_utils.cleanup_metadata(merged_video, self.output_path)
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir, ignore_errors=True)
if hasattr(self.parent(), 'temp_dirs') and temp_dir in self.parent().temp_dirs:
self.parent().temp_dirs.remove(temp_dir)
# 处理完成
self.finished_signal.emit(self.output_path)
except Exception as e:
# 清理临时文件
if temp_dir and os.path.exists(temp_dir):
try:
shutil.rmtree(temp_dir, ignore_errors=True)
if hasattr(self.parent(), 'temp_dirs') and temp_dir in self.parent().temp_dirs:
self.parent().temp_dirs.remove(temp_dir)
except:
pass
# 生成错误信息
error_msg = f"处理过程中出错:\n{str(e)}"
if isinstance(e, video_utils.VideoProcessingError):
error_msg += f"\n\n详细信息: {e.details}"
# 添加堆栈跟踪用于调试
error_msg += f"\n\n调试信息:\n{traceback.format_exc()}"
self.error_signal.emit(error_msg)
class VideoProcessingError(Exception):
"""自定义视频处理异常类"""
def __init__(self, message, details=""):
super().__init__(message)
self.details = details
if __name__ == "__main__":
app = QApplication(sys.argv)
# 检查FFmpeg可用性
try:
video_utils.get_ffmpeg_path()
except Exception as e:
QMessageBox.critical(
None,
"初始化错误",
f"无法找到FFmpeg: {str(e)}\n\n请确保ffmpeg文件夹与程序在同一目录下"
)
sys.exit(1)
window = VideoProcessorApp()
window.show()
sys.exit(app.exec_())
```
### 主要优化功能说明:
#### 1. 多线程处理架构
```python
class ProcessingThread(QThread):
progress_signal = pyqtSignal(int, int, str)
finished_signal = pyqtSignal(str)
error_signal = pyqtSignal(str)
def run(self):
# 视频处理逻辑
```
- 使用 `QThread` 实现后台处理
- 通过信号机制更新UI进度
- 避免主界面冻结
#### 2. 智能输出路径生成
```python
# 在 select_file 方法中
output_path = video_utils.generate_output_path(file, "merged")
self.label_output_info.setText(f"输出文件将保存为: {os.path.basename(output_path)}")
```
- 自动生成唯一文件名
- 格式为 `[原文件名]_merged_[序号].mp4`
- 避免文件覆盖
#### 3. 增强错误处理
```python
class VideoProcessingError(Exception):
"""自定义视频处理异常类"""
def __init__(self, message, details=""):
super().__init__(message)
self.details = details
```
- 自定义异常类包含详细错误信息
- 错误信号传递到UI层
- 显示用户友好的错误提示
#### 4. 处理取消功能
```python
def cancel(self):
"""请求取消处理"""
self.cancel_requested = True
# 在处理各步骤中检查
if self.cancel_requested:
raise RuntimeError("处理已取消")
```
- 支持中途取消处理
- 清理临时文件
- 资源释放
#### 5. 自动打开输出文件夹
```python
reply = QMessageBox.information(
self,
"完成",
f"视频处理完成!\n输出文件: {output_path}\n\n是否打开所在文件夹?",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
output_dir = os.path.dirname(output_path)
if os.path.exists(output_dir):
os.startfile(output_dir)
```
- 处理完成后提示用户
- 支持一键打开输出目录
- 提升用户体验
#### 6. 启动时FFmpeg检查
```python
# 在 __main__ 中
try:
video_utils.get_ffmpeg_path()
except Exception as e:
QMessageBox.critical(
None,
"初始化错误",
f"无法找到FFmpeg: {str(e)}\n\n请确保ffmpeg文件夹与程序在同一目录下"
)
sys.exit(1)
```
- 程序启动时验证FFmpeg存在
- 提供明确的错误提示
- 避免运行时崩溃
### 使用说明:
1. **选择文件**:
- 点击"选择视频A"按钮选择主视频
- 点击"选择视频B"按钮选择辅助视频
- 输出路径自动显示在界面上
2. **开始处理**:
- 点击"开始处理"按钮启动处理流程
- 进度条显示当前处理进度
- 状态标签显示详细处理步骤
3. **处理完成**:
- 弹出完成提示框
- 可选择打开输出文件夹
- 输出文件命名格式:`原文件名_merged_序号.mp4`
4. **错误处理**:
- 文件不存在时显示明确错误
- FFmpeg错误提供详细诊断信息
- 处理过程中可取消
### 打包注意事项:
1. 确保文件结构:
```
project/
├── main.py
├── video_utils.py
├── build.py
└── ffmpeg/
├── ffmpeg.exe
└── ffprobe.exe
```
2. 使用修改后的 `build.py` 打包:
```bash
python build.py
```
3. 打包后部署:
- 将生成的 `dist/VideoProcessor.exe` 和 `ffmpeg` 文件夹放在同一目录
- 运行 `VideoProcessor.exe`
阅读全文
相关推荐




















