feat(core): 集成补丁检测器以增强补丁管理功能

- 在主窗口中添加补丁检测器,支持补丁的检测和验证。
- 更新补丁管理器以使用补丁检测器进行补丁安装状态检查。
- 优化下载管理器和离线模式管理器,整合补丁检测逻辑,提升用户体验。
- 添加进度窗口以显示下载状态,增强用户反馈。
- 重构相关逻辑以支持新功能,确保代码可维护性和可读性。
This commit is contained in:
hyb-oyqq
2025-08-07 15:24:22 +08:00
parent d12739baab
commit bf80c19fe1
10 changed files with 874 additions and 554 deletions

View File

@@ -10,6 +10,7 @@ from PySide6.QtCore import QTimer, Qt, QPoint, QRect, QSize
from PySide6.QtWidgets import QMainWindow, QMessageBox, QGraphicsOpacityEffect, QGraphicsColorizeEffect
from PySide6.QtGui import QPalette, QColor, QPainterPath, QRegion, QFont
from PySide6.QtGui import QAction # Added for menu actions
from PySide6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QProgressBar, QLabel # Added for progress window
from ui.Ui_install import Ui_MainWindows
from data.config import (
@@ -26,7 +27,7 @@ from workers import (
)
from core import (
MultiStageAnimations, UIManager, DownloadManager, DebugManager,
WindowManager, GameDetector, PatchManager, ConfigManager
WindowManager, GameDetector, PatchManager, ConfigManager, PatchDetector
)
from core.ipv6_manager import IPv6Manager
from handlers import PatchToggleHandler, UninstallHandler
@@ -79,16 +80,22 @@ class MainWindow(QMainWindow):
# 5. 初始化其他管理器
self.config_manager = ConfigManager(APP_NAME, CONFIG_URL, UA, self.debug_manager)
self.game_detector = GameDetector(GAME_INFO, self.debug_manager)
self.patch_manager = PatchManager(APP_NAME, GAME_INFO, self.debug_manager)
self.patch_manager = PatchManager(APP_NAME, GAME_INFO, self.debug_manager, self)
# 6. 初始化离线模式管理器
# 6. 初始化补丁检测模块
self.patch_detector = PatchDetector(self)
# 7. 设置补丁检测器到补丁管理器
self.patch_manager.set_patch_detector(self.patch_detector)
# 8. 初始化离线模式管理器
from core.offline_mode_manager import OfflineModeManager
self.offline_mode_manager = OfflineModeManager(self)
# 7. 初始化下载管理器 - 放在最后,因为它可能依赖于其他管理器
# 9. 初始化下载管理器 - 放在最后,因为它可能依赖于其他管理器
self.download_manager = DownloadManager(self)
# 8. 初始化功能处理程序
# 10. 初始化功能处理程序
self.uninstall_handler = UninstallHandler(self)
self.patch_toggle_handler = PatchToggleHandler(self)
@@ -325,33 +332,40 @@ class MainWindow(QMainWindow):
Args:
url: 下载URL
_7z_path: 7z文件保存路径
game_version: 游戏版本名称
game_version: 游戏版本
Returns:
DownloadThread: 下载线程实例
"""
from workers import DownloadThread
return DownloadThread(url, _7z_path, game_version, parent=self)
return DownloadThread(url, _7z_path, game_version, self)
def create_progress_window(self):
"""创建下载进度窗口
"""创建进度窗口
Returns:
ProgressWindow: 进度窗口实例
QDialog: 进度窗口实例
"""
return ProgressWindow(self)
progress_window = QDialog(self)
progress_window.setWindowTitle(f"下载进度 - {APP_NAME}")
progress_window.setFixedSize(400, 150)
def create_hash_thread(self, mode, install_paths):
"""创建哈希检查线程
layout = QVBoxLayout()
Args:
mode: 检查模式,"pre""after"
install_paths: 安装路径字典
Returns:
HashThread: 哈希检查线程实例
"""
return HashThread(mode, install_paths, PLUGIN_HASH, self.installed_status, self)
# 添加进度条
progress_bar = QProgressBar()
progress_bar.setRange(0, 100)
progress_bar.setValue(0)
layout.addWidget(progress_bar)
# 添加标签
status_label = QLabel("准备下载...")
layout.addWidget(status_label)
progress_window.setLayout(layout)
progress_window.progress_bar = progress_bar
progress_window.status_label = status_label
return progress_window
def create_extraction_thread(self, _7z_path, game_folder, plugin_path, game_version):
"""创建解压线程
@@ -367,121 +381,10 @@ class MainWindow(QMainWindow):
"""
return ExtractionThread(_7z_path, game_folder, plugin_path, game_version, self)
def after_hash_compare(self):
"""进行安装后哈希比较"""
# 禁用窗口已在安装流程开始时完成
# 检查是否处于离线模式
is_offline = False
if hasattr(self, 'offline_mode_manager'):
is_offline = self.offline_mode_manager.is_in_offline_mode()
self.hash_msg_box = self.hash_manager.hash_pop_window(check_type="after", is_offline=is_offline)
install_paths = self.download_manager.get_install_paths()
self.hash_thread = self.create_hash_thread("after", install_paths)
self.hash_thread.after_finished.connect(self.on_after_hash_finished)
self.hash_thread.start()
def on_after_hash_finished(self, result):
"""哈希比较完成后的处理
Args:
result: 哈希比较结果
"""
# 确保哈希检查窗口关闭,无论是否还在显示
if self.hash_msg_box:
try:
if self.hash_msg_box.isVisible():
self.hash_msg_box.close()
else:
# 如果窗口已经不可见但没有关闭,也要尝试关闭
self.hash_msg_box.close()
except:
pass # 忽略任何关闭窗口时的错误
self.hash_msg_box = None
if not result["passed"]:
# 启用窗口以显示错误消息
self.setEnabled(True)
game = result.get("game", "未知游戏")
message = result.get("message", "发生未知错误。")
msg_box = msgbox_frame(
f"文件校验失败 - {APP_NAME}",
message,
QMessageBox.StandardButton.Ok,
)
msg_box.exec()
# 恢复窗口状态
self.setEnabled(True)
self.ui.start_install_text.setText("开始安装")
# 添加短暂延迟确保UI更新
QTimer.singleShot(100, self.show_result)
def show_result(self):
"""显示安装结果,区分不同情况"""
# 获取当前安装状态
installed_versions = [] # 成功安装的版本
skipped_versions = [] # 已有补丁跳过的版本
failed_versions = [] # 安装失败的版本
not_found_versions = [] # 未找到的版本
"""显示安装结果,调用patch_manager的show_result方法"""
self.patch_manager.show_result()
# 获取所有游戏版本路径
install_paths = self.download_manager.get_install_paths() if hasattr(self.download_manager, "get_install_paths") else {}
for game_version, is_installed in self.installed_status.items():
# 只处理install_paths中存在的游戏版本
if game_version in install_paths:
path = install_paths[game_version]
# 检查游戏是否存在但未通过本次安装补丁
if is_installed:
# 游戏已安装补丁
if hasattr(self, 'download_queue_history') and game_version not in self.download_queue_history:
# 已有补丁,被跳过下载
skipped_versions.append(game_version)
else:
# 本次成功安装
installed_versions.append(game_version)
else:
# 游戏未安装补丁
if os.path.exists(path):
# 游戏文件夹存在,但安装失败
failed_versions.append(game_version)
else:
# 游戏文件夹不存在
not_found_versions.append(game_version)
# 构建结果信息
result_text = f"\n安装结果:\n"
# 总数统计 - 不再显示已跳过的数量
total_installed = len(installed_versions)
total_failed = len(failed_versions)
result_text += f"安装成功:{total_installed} 个 安装失败:{total_failed}\n\n"
# 详细列表
if installed_versions:
result_text += f"【成功安装】:\n{chr(10).join(installed_versions)}\n\n"
if failed_versions:
result_text += f"【安装失败】:\n{chr(10).join(failed_versions)}\n\n"
if not_found_versions:
# 只有在真正检测到了游戏但未安装补丁时才显示
result_text += f"【尚未安装补丁的游戏】:\n{chr(10).join(not_found_versions)}\n"
QMessageBox.information(
self,
f"安装完成 - {APP_NAME}",
result_text
)
def closeEvent(self, event):
"""窗口关闭事件处理
@@ -651,64 +554,18 @@ class MainWindow(QMainWindow):
self.ui.start_install_text.setText("开始安装")
return
# 显示游戏选择对话框
dialog = QtWidgets.QDialog(self)
dialog.setWindowTitle("选择要安装的游戏")
dialog.resize(400, 300)
# 显示文件检验窗口
self.hash_msg_box = self.hash_manager.hash_pop_window(check_type="pre", is_offline=True)
layout = QtWidgets.QVBoxLayout(dialog)
# 获取安装路径
install_paths = self.download_manager.get_install_paths()
# 添加"选择要安装的游戏"标签
title_label = QtWidgets.QLabel("选择要安装的游戏", dialog)
title_label.setFont(QFont(title_label.font().family(), title_label.font().pointSize(), QFont.Bold))
layout.addWidget(title_label)
# 添加游戏列表控件
list_widget = QtWidgets.QListWidget(dialog)
list_widget.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) # 允许多选
for game_version in game_dirs.keys():
list_widget.addItem(game_version)
# 默认选中所有项目
list_widget.item(list_widget.count() - 1).setSelected(True)
layout.addWidget(list_widget)
# 添加全选按钮
select_all_btn = QtWidgets.QPushButton("全选", dialog)
select_all_btn.clicked.connect(lambda: list_widget.selectAll())
layout.addWidget(select_all_btn)
# 添加确定和取消按钮
buttons_layout = QtWidgets.QHBoxLayout()
ok_button = QtWidgets.QPushButton("确定", dialog)
cancel_button = QtWidgets.QPushButton("取消", dialog)
buttons_layout.addWidget(ok_button)
buttons_layout.addWidget(cancel_button)
layout.addLayout(buttons_layout)
# 连接按钮事件
ok_button.clicked.connect(dialog.accept)
cancel_button.clicked.connect(dialog.reject)
# 显示对话框
if dialog.exec() == QtWidgets.QDialog.DialogCode.Accepted:
# 获取选择的游戏
selected_games = [item.text() for item in list_widget.selectedItems()]
if selected_games:
# 使用离线模式管理器进行安装
self.offline_mode_manager.install_offline_patches(selected_games)
else:
QtWidgets.QMessageBox.information(
self,
f"通知 - {APP_NAME}",
"\n未选择任何游戏,安装已取消。\n"
)
self.setEnabled(True)
self.ui.start_install_text.setText("开始安装")
else:
# 用户取消了选择
self.setEnabled(True)
self.ui.start_install_text.setText("开始安装")
# 创建并启动哈希线程进行预检查
self.hash_thread = self.patch_detector.create_hash_thread("pre", install_paths)
self.hash_thread.pre_finished.connect(
lambda updated_status: self.patch_detector.on_offline_pre_hash_finished(updated_status, game_dirs)
)
self.hash_thread.start()
else:
# 在线模式下,检查版本是否过低
if hasattr(self, 'version_warning') and self.version_warning:
@@ -723,6 +580,8 @@ class MainWindow(QMainWindow):
# 版本正常,使用原有的下载流程
self.download_manager.file_dialog()
# 移除on_offline_pre_hash_finished方法
def check_and_set_offline_mode(self):
"""检查是否有离线补丁文件,如果有则自动启用离线模式