Files
video-subtitle-remover/gui.py
flavioy ec94f2e04b
Some checks failed
Docker Build and Push / check-secrets (push) Successful in 2s
Docker Build and Push / build-and-push (cpu, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 11.8) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.6) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.8) (push) Has been skipped
Docker Build and Push / build-and-push (directml, latest) (push) Has been skipped
Build Windows CPU / build (push) Has been cancelled
Build Windows CUDA 11.8 / build (push) Has been cancelled
Build Windows CUDA 12.6 / build (push) Has been cancelled
Build Windows CUDA 12.8 / build (push) Has been cancelled
Build Windows DirectML / build (push) Has been cancelled
添加PyInstaller macOS打包支持:资源路径处理、multiprocessing兼容、配置文件外置
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 22:08:37 +08:00

190 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
@Author : Fang Yao原作者 / 改写Jason Eric
@Time : 2023/4/1 6:07 下午(原始时间)
@FileName: gui.py
@desc: 字幕去除器图形化界面(由 PySimpleGUI 改写为 PySide6
"""
import sys
import os
# PyInstaller 打包后资源路径处理
def resource_path(relative_path):
"""获取资源文件的绝对路径(兼容 PyInstaller 打包)"""
if getattr(sys, 'frozen', False):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath(os.path.dirname(__file__)), relative_path)
import configparser
import cv2
import multiprocessing
from PySide6.QtCore import Qt, QTranslator
from PySide6 import QtCore, QtWidgets, QtGui
from PySide6.QtWidgets import QApplication, QFrame, QStackedWidget, QHBoxLayout, QLabel
from qfluentwidgets import (FluentWindow, PushButton, Slider, ProgressBar, PlainTextEdit,
setTheme, Theme, FluentIcon, CardWidget, SettingCardGroup,
ComboBoxSettingCard, SwitchSettingCard, setThemeColor, OptionsConfigItem,
OptionsValidator, SubtitleLabel, HollowHandleStyle, qconfig, ConfigItem, QConfig,
NavigationWidget, NavigationItemPosition, isDarkTheme, InfoBar)
from qframelesswindow.utils import getSystemAccentColor
from backend.config import config, tr, VERSION
from backend.tools.theme_listener import SystemThemeListener
from backend.tools.process_manager import ProcessManager
from ui.advanced_setting_interface import AdvancedSettingInterface
from ui.home_interface import HomeInterface
class SubtitleExtractorGUI(FluentWindow):
def __init__(self):
super().__init__()
# 禁用云母效果
self.setMicaEffectEnabled(False)
# 设置深色主题并跟随系统主题色
# setTheme(Theme.LIGHT)
# setThemeColor(getSystemAccentColor(), save=True)
# 初始化系统主题监听器并连接信号
# self.themeListener = SystemThemeListener(self)
# self.themeListener.start()
# 设置窗口图标
self.setWindowIcon(QtGui.QIcon(resource_path("design/vsr.ico")))
self.setWindowTitle(tr['SubtitleExtractorGUI']['Title'] + " v" + VERSION)
# 创建界面布局
self._create_layout()
self._connectSignalToSlot()
self._lazy_check_update()
def _lazy_check_update(self):
""" 延迟检查更新 """
if not config.checkUpdateOnStartup.value:
return
self.check_update_timer = QtCore.QTimer(self)
self.check_update_timer.setSingleShot(True)
self.check_update_timer.timeout.connect(lambda: self.advancedSettingInterface.check_update(ignore=True))
self.check_update_timer.start(2000)
def _connectSignalToSlot(self):
config.appRestartSig.connect(self._showRestartTooltip)
def _showRestartTooltip(self):
""" show restart tooltip """
InfoBar.success(
'Updated successfully',
'Configuration takes effect after restart',
duration=5000,
parent=self
)
def _create_layout(self):
# 创建主页面和高级设置页面
self.homeInterface = HomeInterface(self)
self.homeInterface.setObjectName("HomeInterface")
self.advancedSettingInterface = AdvancedSettingInterface(self)
self.advancedSettingInterface.setObjectName("AdvancedSettingInterface")
# 添加到主窗口作为子界面
self.addSubInterface(self.homeInterface,FluentIcon.HOME, tr['SubtitleExtractorGUI']['Title'])
self.addSubInterface(self.advancedSettingInterface, FluentIcon.SETTING, tr['Setting']['AdvancedSetting'], NavigationItemPosition.BOTTOM)
def on_navigation_item_changed(self, key):
"""导航项变更时的处理函数"""
if key == 'main':
self.stackWidget.setCurrentIndex(0)
elif key == 'advanced':
self.stackWidget.setCurrentIndex(1)
def closeEvent(self, event):
"""程序关闭时保存窗口位置并清理资源"""
self.save_window_position()
ProcessManager.instance().terminate_all()
super().closeEvent(event)
def _onThemeChangedFinished(self):
super()._onThemeChangedFinished()
def save_window_position(self):
"""保存窗口位置到配置文件"""
# 保存窗口位置和大小
config.set(config.windowX, self.x())
config.set(config.windowY, self.y())
config.set(config.windowW, self.width())
config.set(config.windowH, self.height())
def update_progress(self):
# 定时器轮询更新进度(现在更新到视频滑块上)
if self.se is not None:
try:
pos = min(self.frame_count - 1, int(self.se.progress_total / 100 * self.frame_count))
if pos != self.video_slider.value():
self.video_slider.setValue(pos)
# 检查是否完成
if self.se.isFinished:
self.processing_finished()
except Exception as e:
# 捕获任何异常,防止崩溃
print(f"更新进度时出错: {str(e)}")
def load_window_position(self):
# 尝试读取窗口位置
try:
x = config.windowX.value
y = config.windowY.value
width = config.windowW.value
height = config.windowH.value
if not x or not y:
self.center_window()
return
# 确保窗口在屏幕内
screen_rect = QtWidgets.QApplication.primaryScreen().availableGeometry()
if (x >= 0 and y >= 0 and
x + width <= screen_rect.width() and
y + height <= screen_rect.height()):
self.setGeometry(x, y, width, height)
else:
self.center_window()
except Exception as e:
print(e)
self.center_window()
def center_window(self):
"""将窗口居中显示"""
screen_rect = QtWidgets.QApplication.primaryScreen().availableGeometry()
window_rect = self.frameGeometry()
center_point = screen_rect.center()
window_rect.moveCenter(center_point)
self.move(window_rect.topLeft())
def keyPressEvent(self, event):
"""处理键盘事件"""
# 检测Ctrl+C组合键
if event.key() == QtCore.Qt.Key_C and event.modifiers() == QtCore.Qt.ControlModifier:
print("\n程序被用户中断(Ctrl+C),正在退出...")
self.close()
else:
super().keyPressEvent(event)
if __name__ == '__main__':
multiprocessing.freeze_support()
multiprocessing.set_start_method("spawn")
QApplication.setHighDpiScaleFactorRoundingPolicy(
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
app = QtWidgets.QApplication(sys.argv)
app.setAttribute(Qt.AA_DontCreateNativeWidgetSiblings)
window = SubtitleExtractorGUI()
# 先设置透明, 再显示, 否则会有闪烁的效果
window.setWindowOpacity(0.0)
window.show()
window.load_window_position()
# 使用动画效果逐渐显示窗口
animation = QtCore.QPropertyAnimation(window, b"windowOpacity")
animation.setDuration(300) # 300毫秒的动画
animation.setStartValue(0.0)
animation.setEndValue(1.0)
animation.start()
app.exec()