mirror of
https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT.git
synced 2026-04-05 18:36:31 +00:00
feat(core): 优化线程管理和清理机制
- 在主窗口中添加优雅的线程清理逻辑,确保在退出时安全停止所有后台线程,避免潜在的资源泄漏。 - 更新离线模式管理器和哈希线程,增强对线程引用的管理,确保在操作完成后及时清理引用。 - 改进补丁检测器,支持在离线模式下的补丁状态检查,提升用户体验和系统稳定性。 - 增强日志记录,确保在关键操作中提供详细的调试信息,便于后续排查和用户反馈。
This commit is contained in:
10
source/core/handlers/__init__.py
Normal file
10
source/core/handlers/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# Handlers package initialization
|
||||
from .extraction_handler import ExtractionHandler
|
||||
from .patch_toggle_handler import PatchToggleHandler
|
||||
from .uninstall_handler import UninstallHandler
|
||||
|
||||
__all__ = [
|
||||
'ExtractionHandler',
|
||||
'PatchToggleHandler',
|
||||
'UninstallHandler',
|
||||
]
|
||||
265
source/core/handlers/extraction_handler.py
Normal file
265
source/core/handlers/extraction_handler.py
Normal file
@@ -0,0 +1,265 @@
|
||||
import os
|
||||
import shutil
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
from PySide6.QtCore import QTimer, QCoreApplication
|
||||
|
||||
from utils.logger import setup_logger
|
||||
|
||||
# 初始化logger
|
||||
logger = setup_logger("extraction_handler")
|
||||
|
||||
class ExtractionHandler:
|
||||
"""解压处理器,负责管理解压任务和结果处理"""
|
||||
|
||||
def __init__(self, main_window):
|
||||
"""初始化解压处理器
|
||||
|
||||
Args:
|
||||
main_window: 主窗口实例,用于访问UI和状态
|
||||
"""
|
||||
self.main_window = main_window
|
||||
self.APP_NAME = main_window.APP_NAME if hasattr(main_window, 'APP_NAME') else ""
|
||||
self.extraction_progress_window = None
|
||||
|
||||
def start_extraction(self, _7z_path, game_folder, plugin_path, game_version, extracted_path=None):
|
||||
"""开始解压任务
|
||||
|
||||
Args:
|
||||
_7z_path: 7z文件路径
|
||||
game_folder: 游戏文件夹路径
|
||||
plugin_path: 插件路径
|
||||
game_version: 游戏版本名称
|
||||
extracted_path: 已解压的补丁文件路径,如果提供则直接使用它而不进行解压
|
||||
"""
|
||||
# 检查是否处于离线模式
|
||||
is_offline = False
|
||||
if hasattr(self.main_window, 'offline_mode_manager'):
|
||||
is_offline = self.main_window.offline_mode_manager.is_in_offline_mode()
|
||||
|
||||
# 创建并显示解压进度窗口,替代原来的消息框
|
||||
self.extraction_progress_window = self.main_window.create_extraction_progress_window()
|
||||
self.extraction_progress_window.show()
|
||||
|
||||
# 确保UI更新
|
||||
QCoreApplication.processEvents()
|
||||
|
||||
# 创建并启动解压线程
|
||||
self.main_window.extraction_thread = self.main_window.create_extraction_thread(
|
||||
_7z_path, game_folder, plugin_path, game_version, extracted_path
|
||||
)
|
||||
|
||||
# 连接进度信号
|
||||
self.main_window.extraction_thread.progress.connect(self.update_extraction_progress)
|
||||
|
||||
# 连接完成信号
|
||||
self.main_window.extraction_thread.finished.connect(self.on_extraction_finished_with_hash_check)
|
||||
|
||||
# 启动线程
|
||||
self.main_window.extraction_thread.start()
|
||||
|
||||
def update_extraction_progress(self, progress, status_text):
|
||||
"""更新解压进度
|
||||
|
||||
Args:
|
||||
progress: 进度百分比
|
||||
status_text: 状态文本
|
||||
"""
|
||||
if self.extraction_progress_window and hasattr(self.extraction_progress_window, 'progress_bar'):
|
||||
self.extraction_progress_window.progress_bar.setValue(progress)
|
||||
self.extraction_progress_window.status_label.setText(status_text)
|
||||
|
||||
# 确保UI更新
|
||||
QCoreApplication.processEvents()
|
||||
|
||||
def on_extraction_finished_with_hash_check(self, success, error_message, game_version):
|
||||
"""解压完成后进行哈希校验
|
||||
|
||||
Args:
|
||||
success: 是否解压成功
|
||||
error_message: 错误信息
|
||||
game_version: 游戏版本
|
||||
"""
|
||||
# 关闭解压进度窗口
|
||||
if self.extraction_progress_window:
|
||||
self.extraction_progress_window.close()
|
||||
self.extraction_progress_window = None
|
||||
|
||||
# 如果解压失败,显示错误并询问是否继续
|
||||
if not success:
|
||||
# 临时启用窗口以显示错误消息
|
||||
self.main_window.setEnabled(True)
|
||||
|
||||
QtWidgets.QMessageBox.critical(self.main_window, f"错误 - {self.APP_NAME}", error_message)
|
||||
self.main_window.installed_status[game_version] = False
|
||||
|
||||
# 询问用户是否继续其他游戏的安装
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self.main_window,
|
||||
f"继续安装? - {self.APP_NAME}",
|
||||
f"\n{game_version} 的补丁安装失败。\n\n是否继续安装其他游戏的补丁?\n",
|
||||
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
|
||||
QtWidgets.QMessageBox.StandardButton.Yes
|
||||
)
|
||||
|
||||
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
|
||||
# 继续下一个,重新禁用窗口
|
||||
self.main_window.setEnabled(False)
|
||||
# 通知DownloadManager继续下一个下载任务
|
||||
self.main_window.download_manager.on_extraction_finished(True)
|
||||
else:
|
||||
# 用户选择停止,保持窗口启用状态
|
||||
self.main_window.ui.start_install_text.setText("开始安装")
|
||||
# 通知DownloadManager停止下载队列
|
||||
self.main_window.download_manager.on_extraction_finished(False)
|
||||
return
|
||||
|
||||
# 解压成功,进行哈希校验
|
||||
self._perform_hash_check(game_version)
|
||||
|
||||
def _perform_hash_check(self, game_version):
|
||||
"""解压成功后进行哈希校验
|
||||
|
||||
Args:
|
||||
game_version: 游戏版本
|
||||
"""
|
||||
# 导入所需模块
|
||||
from data.config import GAME_INFO, PLUGIN_HASH
|
||||
from workers.hash_thread import HashThread
|
||||
|
||||
# 获取安装路径
|
||||
install_paths = {}
|
||||
if hasattr(self.main_window, 'game_detector') and hasattr(self.main_window, 'download_manager'):
|
||||
game_dirs = self.main_window.game_detector.identify_game_directories_improved(
|
||||
self.main_window.download_manager.selected_folder
|
||||
)
|
||||
|
||||
for game, info in GAME_INFO.items():
|
||||
if game in game_dirs and game == game_version:
|
||||
game_dir = game_dirs[game]
|
||||
install_path = os.path.join(game_dir, os.path.basename(info["install_path"]))
|
||||
install_paths[game] = install_path
|
||||
break
|
||||
|
||||
if not install_paths:
|
||||
# 如果找不到安装路径,直接认为安装成功
|
||||
logger.warning(f"未找到 {game_version} 的安装路径,跳过哈希校验")
|
||||
self.main_window.installed_status[game_version] = True
|
||||
self.main_window.download_manager.on_extraction_finished(True)
|
||||
return
|
||||
|
||||
# 关闭可能存在的哈希校验窗口
|
||||
self.main_window.close_hash_msg_box()
|
||||
|
||||
# 显示哈希校验窗口
|
||||
self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window(
|
||||
check_type="post",
|
||||
auto_close=True, # 添加自动关闭参数
|
||||
close_delay=1000 # 1秒后自动关闭
|
||||
)
|
||||
|
||||
# 直接创建并启动哈希线程进行校验
|
||||
hash_thread = HashThread(
|
||||
"after",
|
||||
install_paths,
|
||||
PLUGIN_HASH,
|
||||
self.main_window.installed_status,
|
||||
self.main_window
|
||||
)
|
||||
hash_thread.after_finished.connect(self.on_hash_check_finished)
|
||||
|
||||
# 保存引用以便后续使用
|
||||
self.hash_thread = hash_thread
|
||||
hash_thread.start()
|
||||
|
||||
def on_hash_check_finished(self, result):
|
||||
"""哈希校验完成后的处理
|
||||
|
||||
Args:
|
||||
result: 校验结果,包含通过状态、游戏版本和消息
|
||||
"""
|
||||
# 导入所需模块
|
||||
from data.config import GAME_INFO
|
||||
|
||||
# 关闭哈希检查窗口
|
||||
self.main_window.close_hash_msg_box()
|
||||
|
||||
if not result["passed"]:
|
||||
# 校验失败,删除已解压的文件并提示重新下载
|
||||
game_version = result["game"]
|
||||
error_message = result["message"]
|
||||
|
||||
# 临时启用窗口以显示错误消息
|
||||
self.main_window.setEnabled(True)
|
||||
|
||||
# 获取安装路径
|
||||
install_path = None
|
||||
if hasattr(self.main_window, 'game_detector') and hasattr(self.main_window, 'download_manager'):
|
||||
game_dirs = self.main_window.game_detector.identify_game_directories_improved(
|
||||
self.main_window.download_manager.selected_folder
|
||||
)
|
||||
|
||||
if game_version in game_dirs and game_version in GAME_INFO:
|
||||
game_dir = game_dirs[game_version]
|
||||
install_path = os.path.join(game_dir, os.path.basename(GAME_INFO[game_version]["install_path"]))
|
||||
|
||||
# 如果找到安装路径,尝试删除已解压的文件
|
||||
if install_path and os.path.exists(install_path):
|
||||
try:
|
||||
os.remove(install_path)
|
||||
logger.info(f"已删除校验失败的文件: {install_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"删除文件失败: {e}")
|
||||
|
||||
# 显示错误消息并询问是否重试
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self.main_window,
|
||||
f"校验失败 - {self.APP_NAME}",
|
||||
f"{error_message}\n\n是否重新下载并安装?",
|
||||
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
|
||||
QtWidgets.QMessageBox.StandardButton.Yes
|
||||
)
|
||||
|
||||
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
|
||||
# 重新下载,将游戏重新添加到下载队列
|
||||
self.main_window.setEnabled(False)
|
||||
self.main_window.installed_status[game_version] = False
|
||||
|
||||
# 获取游戏目录和下载URL
|
||||
if hasattr(self.main_window, 'download_manager') and hasattr(self.main_window, 'game_detector'):
|
||||
game_dirs = self.main_window.game_detector.identify_game_directories_improved(
|
||||
self.main_window.download_manager.selected_folder
|
||||
)
|
||||
|
||||
if game_version in game_dirs:
|
||||
# 重新将游戏添加到下载队列
|
||||
self.main_window.download_manager.download_queue.appendleft([game_version])
|
||||
# 继续下一个下载任务
|
||||
self.main_window.download_manager.next_download_task()
|
||||
else:
|
||||
# 如果找不到游戏目录,继续下一个
|
||||
self.main_window.download_manager.on_extraction_finished(True)
|
||||
else:
|
||||
# 如果无法重新下载,继续下一个
|
||||
self.main_window.download_manager.on_extraction_finished(True)
|
||||
else:
|
||||
# 用户选择不重试,继续下一个
|
||||
self.main_window.installed_status[game_version] = False
|
||||
self.main_window.download_manager.on_extraction_finished(True)
|
||||
else:
|
||||
# 校验通过,更新安装状态
|
||||
game_version = result["game"]
|
||||
self.main_window.installed_status[game_version] = True
|
||||
# 通知DownloadManager继续下一个下载任务
|
||||
self.main_window.download_manager.on_extraction_finished(True)
|
||||
|
||||
def on_extraction_finished(self, success, error_message, game_version):
|
||||
"""兼容旧版本的回调函数
|
||||
|
||||
Args:
|
||||
success: 是否解压成功
|
||||
error_message: 错误信息
|
||||
game_version: 游戏版本
|
||||
"""
|
||||
# 调用新的带哈希校验的回调函数
|
||||
self.on_extraction_finished_with_hash_check(success, error_message, game_version)
|
||||
438
source/core/handlers/patch_toggle_handler.py
Normal file
438
source/core/handlers/patch_toggle_handler.py
Normal file
@@ -0,0 +1,438 @@
|
||||
import os
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QLabel, QListWidget, QPushButton, QHBoxLayout,
|
||||
QAbstractItemView, QRadioButton, QButtonGroup, QFileDialog, QMessageBox
|
||||
)
|
||||
from PySide6.QtCore import QObject, Signal, QThread
|
||||
from PySide6.QtGui import QFont
|
||||
from utils import msgbox_frame
|
||||
from utils.logger import setup_logger
|
||||
|
||||
# 初始化logger
|
||||
logger = setup_logger("patch_toggle_handler")
|
||||
|
||||
class PatchToggleThread(QThread):
|
||||
"""在后台线程中处理补丁切换逻辑"""
|
||||
finished = Signal(object)
|
||||
|
||||
def __init__(self, handler, selected_folder):
|
||||
super().__init__()
|
||||
self.handler = handler
|
||||
self.selected_folder = selected_folder
|
||||
|
||||
def run(self):
|
||||
# 在后台线程中执行耗时操作
|
||||
game_dirs = self.handler.game_detector.identify_game_directories_improved(self.selected_folder)
|
||||
self.finished.emit(game_dirs)
|
||||
|
||||
class PatchToggleHandler(QObject):
|
||||
"""
|
||||
处理补丁启用/禁用功能的类
|
||||
"""
|
||||
def __init__(self, main_window):
|
||||
"""
|
||||
初始化补丁切换处理程序
|
||||
|
||||
Args:
|
||||
main_window: 主窗口实例,用于访问其他组件
|
||||
"""
|
||||
super().__init__()
|
||||
self.main_window = main_window
|
||||
self.debug_manager = main_window.debug_manager
|
||||
self.game_detector = main_window.game_detector
|
||||
self.patch_manager = main_window.patch_manager
|
||||
self.app_name = main_window.patch_manager.app_name
|
||||
self.toggle_thread = None
|
||||
|
||||
def handle_toggle_patch_button_click(self):
|
||||
"""
|
||||
处理禁/启用补丁按钮点击事件
|
||||
打开文件选择对话框选择游戏目录,然后禁用或启用对应游戏的补丁
|
||||
"""
|
||||
selected_folder = QFileDialog.getExistingDirectory(self.main_window, "选择游戏上级目录", "")
|
||||
|
||||
if not selected_folder:
|
||||
return
|
||||
|
||||
self.main_window.show_loading_dialog("正在识别游戏目录并检查补丁状态...")
|
||||
|
||||
self.toggle_thread = PatchToggleThread(self, selected_folder)
|
||||
self.toggle_thread.finished.connect(self.on_game_detection_finished)
|
||||
self.toggle_thread.start()
|
||||
|
||||
def on_game_detection_finished(self, game_dirs):
|
||||
"""游戏识别完成后的回调"""
|
||||
self.main_window.hide_loading_dialog()
|
||||
|
||||
if not game_dirs:
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
"\n未在选择的目录中找到任何支持的游戏。\n",
|
||||
)
|
||||
return
|
||||
|
||||
games_with_patch = {}
|
||||
for game_version, game_dir in game_dirs.items():
|
||||
if self.patch_manager.check_patch_installed(game_dir, game_version):
|
||||
is_disabled, _ = self.patch_manager.check_patch_disabled(game_dir, game_version)
|
||||
status = "已禁用" if is_disabled else "已启用"
|
||||
games_with_patch[game_version] = {"dir": game_dir, "status": status}
|
||||
|
||||
if not games_with_patch:
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
"\n目录中未找到已安装补丁的游戏。\n",
|
||||
)
|
||||
return
|
||||
|
||||
selected_games, operation = self._show_multi_game_dialog(games_with_patch)
|
||||
|
||||
if not selected_games:
|
||||
return
|
||||
|
||||
selected_game_dirs = {game: games_with_patch[game]["dir"] for game in selected_games if game in games_with_patch}
|
||||
|
||||
self._execute_batch_toggle(selected_game_dirs, operation)
|
||||
|
||||
def _handle_multiple_games(self, game_dirs, debug_mode):
|
||||
"""
|
||||
处理多个游戏的补丁切换
|
||||
|
||||
Args:
|
||||
game_dirs: 游戏目录字典
|
||||
debug_mode: 是否为调试模式
|
||||
"""
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 禁/启用功能 - 在上级目录中找到以下游戏: {list(game_dirs.keys())}")
|
||||
|
||||
# 查找已安装补丁的游戏,只处理那些已安装补丁的游戏
|
||||
games_with_patch = {}
|
||||
for game_version, game_dir in game_dirs.items():
|
||||
if self.patch_manager.check_patch_installed(game_dir, game_version):
|
||||
# 检查补丁当前状态(是否禁用)
|
||||
is_disabled, disabled_path = self.patch_manager.check_patch_disabled(game_dir, game_version)
|
||||
status = "已禁用" if is_disabled else "已启用"
|
||||
games_with_patch[game_version] = {
|
||||
"dir": game_dir,
|
||||
"disabled": is_disabled,
|
||||
"status": status
|
||||
}
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 禁/启用功能 - {game_version} 已安装补丁,当前状态: {status}")
|
||||
|
||||
# 检查是否有已安装补丁的游戏
|
||||
if not games_with_patch:
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
"\n未在选择的目录中找到已安装补丁的游戏。\n请确认您选择了正确的游戏目录,并且该目录中的游戏已安装过补丁。\n",
|
||||
QMessageBox.StandardButton.Ok
|
||||
)
|
||||
return
|
||||
|
||||
# 显示选择对话框
|
||||
selected_games, operation = self._show_multi_game_dialog(games_with_patch)
|
||||
|
||||
if not selected_games:
|
||||
return # 用户取消了操作
|
||||
|
||||
# 过滤games_with_patch,只保留选中的游戏
|
||||
selected_game_dirs = {}
|
||||
for game in selected_games:
|
||||
if game in games_with_patch:
|
||||
selected_game_dirs[game] = games_with_patch[game]["dir"]
|
||||
|
||||
# 确认操作
|
||||
operation_text = "禁用" if operation == "disable" else "启用" if operation == "enable" else "切换"
|
||||
game_list = '\n'.join([f"{game} ({games_with_patch[game]['status']})" for game in selected_games])
|
||||
reply = QMessageBox.question(
|
||||
self.main_window,
|
||||
f"确认{operation_text}操作 - {self.app_name}",
|
||||
f"\n确定要{operation_text}以下游戏补丁吗?\n\n{game_list}\n",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.No:
|
||||
return
|
||||
|
||||
# 执行批量操作
|
||||
self._execute_batch_toggle(selected_game_dirs, operation, debug_mode)
|
||||
|
||||
def _handle_single_game(self, selected_folder, debug_mode):
|
||||
"""
|
||||
处理单个游戏的补丁切换
|
||||
|
||||
Args:
|
||||
selected_folder: 选择的游戏目录
|
||||
debug_mode: 是否为调试模式
|
||||
"""
|
||||
# 未找到游戏目录,尝试将选择的目录作为游戏目录
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 禁/启用功能 - 未在上级目录找到游戏,尝试将选择的目录视为游戏目录")
|
||||
|
||||
game_version = self.game_detector.identify_game_version(selected_folder)
|
||||
|
||||
if game_version:
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 禁/启用功能 - 识别为游戏: {game_version}")
|
||||
|
||||
# 检查是否已安装补丁
|
||||
if self.patch_manager.check_patch_installed(selected_folder, game_version):
|
||||
# 检查补丁当前状态
|
||||
is_disabled, disabled_path = self.patch_manager.check_patch_disabled(selected_folder, game_version)
|
||||
current_status = "已禁用" if is_disabled else "已启用"
|
||||
|
||||
# 显示单游戏操作对话框
|
||||
operation = self._show_single_game_dialog(game_version, current_status, is_disabled)
|
||||
|
||||
if not operation:
|
||||
return # 用户取消了操作
|
||||
|
||||
# 执行操作
|
||||
result = self.patch_manager.toggle_patch(selected_folder, game_version, operation=operation)
|
||||
if not result["success"]:
|
||||
# 操作失败的消息已在toggle_patch中显示
|
||||
pass
|
||||
else:
|
||||
# 没有安装补丁
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
f"\n未在 {game_version} 中找到已安装的补丁。\n请确认该游戏已经安装过补丁。\n",
|
||||
QMessageBox.StandardButton.Ok
|
||||
)
|
||||
else:
|
||||
# 两种方式都未识别到游戏
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 禁/启用功能 - 无法识别游戏")
|
||||
|
||||
msg_box = msgbox_frame(
|
||||
f"错误 - {self.app_name}",
|
||||
"\n所选目录不是有效的NEKOPARA游戏目录。\n请选择包含游戏可执行文件的目录或其上级目录。\n",
|
||||
QMessageBox.StandardButton.Ok,
|
||||
)
|
||||
msg_box.exec()
|
||||
|
||||
def _show_multi_game_dialog(self, games_with_patch):
|
||||
"""
|
||||
显示多游戏选择对话框
|
||||
|
||||
Args:
|
||||
games_with_patch: 已安装补丁的游戏信息
|
||||
|
||||
Returns:
|
||||
tuple: (选择的游戏列表, 操作类型)
|
||||
"""
|
||||
dialog = QDialog(self.main_window)
|
||||
dialog.setWindowTitle("选择要操作的游戏补丁")
|
||||
dialog.resize(400, 400) # 增加高度以适应新增的单选按钮
|
||||
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
# 添加"已安装补丁的游戏"标签
|
||||
already_installed_label = QLabel("已安装补丁的游戏:", dialog)
|
||||
already_installed_label.setFont(QFont(already_installed_label.font().family(), already_installed_label.font().pointSize(), QFont.Bold))
|
||||
layout.addWidget(already_installed_label)
|
||||
|
||||
# 添加游戏列表和状态
|
||||
games_status_text = ""
|
||||
for game, info in games_with_patch.items():
|
||||
games_status_text += f"{game} ({info['status']})\n"
|
||||
games_status_label = QLabel(games_status_text.strip(), dialog)
|
||||
layout.addWidget(games_status_label)
|
||||
|
||||
# 添加一些间距
|
||||
layout.addSpacing(10)
|
||||
|
||||
# 添加"请选择要操作的游戏"标签
|
||||
info_label = QLabel("请选择要操作的游戏:", dialog)
|
||||
info_label.setFont(QFont(info_label.font().family(), info_label.font().pointSize(), QFont.Bold))
|
||||
layout.addWidget(info_label)
|
||||
|
||||
# 添加列表控件,只显示已安装补丁的游戏
|
||||
list_widget = QListWidget(dialog)
|
||||
list_widget.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) # 允许多选
|
||||
for game, info in games_with_patch.items():
|
||||
list_widget.addItem(f"{game} ({info['status']})")
|
||||
layout.addWidget(list_widget)
|
||||
|
||||
# 添加全选按钮
|
||||
select_all_btn = QPushButton("全选", dialog)
|
||||
select_all_btn.clicked.connect(lambda: list_widget.selectAll())
|
||||
layout.addWidget(select_all_btn)
|
||||
|
||||
# 添加操作选择单选按钮
|
||||
operation_label = QLabel("请选择要执行的操作:", dialog)
|
||||
operation_label.setFont(QFont(operation_label.font().family(), operation_label.font().pointSize(), QFont.Bold))
|
||||
layout.addWidget(operation_label)
|
||||
|
||||
# 创建单选按钮组
|
||||
radio_button_group = QButtonGroup(dialog)
|
||||
|
||||
# 添加"自动切换状态"单选按钮(默认选中)
|
||||
auto_toggle_radio = QRadioButton("自动切换状态(禁用<->启用)", dialog)
|
||||
auto_toggle_radio.setChecked(True)
|
||||
radio_button_group.addButton(auto_toggle_radio, 0)
|
||||
layout.addWidget(auto_toggle_radio)
|
||||
|
||||
# 添加"全部禁用"单选按钮
|
||||
disable_all_radio = QRadioButton("禁用选中的补丁", dialog)
|
||||
radio_button_group.addButton(disable_all_radio, 1)
|
||||
layout.addWidget(disable_all_radio)
|
||||
|
||||
# 添加"全部启用"单选按钮
|
||||
enable_all_radio = QRadioButton("启用选中的补丁", dialog)
|
||||
radio_button_group.addButton(enable_all_radio, 2)
|
||||
layout.addWidget(enable_all_radio)
|
||||
|
||||
# 添加确定和取消按钮
|
||||
buttons_layout = QHBoxLayout()
|
||||
ok_button = QPushButton("确定", dialog)
|
||||
cancel_button = 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)
|
||||
|
||||
# 显示对话框并等待用户选择
|
||||
result = dialog.exec()
|
||||
|
||||
if result != QDialog.DialogCode.Accepted or list_widget.selectedItems() == []:
|
||||
# 用户取消或未选择任何游戏
|
||||
return [], None
|
||||
|
||||
# 获取用户选择的游戏
|
||||
selected_items = [item.text() for item in list_widget.selectedItems()]
|
||||
selected_games = []
|
||||
|
||||
# 从选中项文本中提取游戏名称
|
||||
for item in selected_items:
|
||||
# 去除状态后缀 " (已启用)" 或 " (已禁用)"
|
||||
game_name = item.split(" (")[0]
|
||||
selected_games.append(game_name)
|
||||
|
||||
# 获取选中的操作类型
|
||||
operation = None
|
||||
if radio_button_group.checkedId() == 1: # 禁用选中的补丁
|
||||
operation = "disable"
|
||||
elif radio_button_group.checkedId() == 2: # 启用选中的补丁
|
||||
operation = "enable"
|
||||
# 否则为None,表示自动切换状态
|
||||
|
||||
return selected_games, operation
|
||||
|
||||
def _show_single_game_dialog(self, game_version, current_status, is_disabled):
|
||||
"""
|
||||
显示单游戏操作对话框
|
||||
|
||||
Args:
|
||||
game_version: 游戏版本
|
||||
current_status: 当前补丁状态
|
||||
is_disabled: 是否已禁用
|
||||
|
||||
Returns:
|
||||
str: 操作类型,"enable"或"disable",或None表示取消
|
||||
"""
|
||||
dialog = QDialog(self.main_window)
|
||||
dialog.setWindowTitle(f"{game_version} 补丁操作")
|
||||
dialog.resize(300, 200)
|
||||
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
# 添加当前状态标签
|
||||
status_label = QLabel(f"当前补丁状态: {current_status}", dialog)
|
||||
status_label.setFont(QFont(status_label.font().family(), status_label.font().pointSize(), QFont.Bold))
|
||||
layout.addWidget(status_label)
|
||||
|
||||
# 添加操作选择单选按钮
|
||||
operation_label = QLabel("请选择要执行的操作:", dialog)
|
||||
layout.addWidget(operation_label)
|
||||
|
||||
# 创建单选按钮组
|
||||
radio_button_group = QButtonGroup(dialog)
|
||||
|
||||
# 添加可选操作
|
||||
if is_disabled:
|
||||
# 当前是禁用状态,显示启用选项
|
||||
enable_radio = QRadioButton("启用补丁", dialog)
|
||||
enable_radio.setChecked(True)
|
||||
radio_button_group.addButton(enable_radio, 0)
|
||||
layout.addWidget(enable_radio)
|
||||
else:
|
||||
# 当前是启用状态,显示禁用选项
|
||||
disable_radio = QRadioButton("禁用补丁", dialog)
|
||||
disable_radio.setChecked(True)
|
||||
radio_button_group.addButton(disable_radio, 0)
|
||||
layout.addWidget(disable_radio)
|
||||
|
||||
# 添加确定和取消按钮
|
||||
buttons_layout = QHBoxLayout()
|
||||
ok_button = QPushButton("确定", dialog)
|
||||
cancel_button = 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)
|
||||
|
||||
# 显示对话框并等待用户选择
|
||||
result = dialog.exec()
|
||||
|
||||
if result != QDialog.DialogCode.Accepted:
|
||||
# 用户取消
|
||||
return None
|
||||
|
||||
# 根据当前状态确定操作
|
||||
return "enable" if is_disabled else "disable"
|
||||
|
||||
def _execute_batch_toggle(self, selected_game_dirs, operation, debug_mode):
|
||||
"""
|
||||
执行批量补丁切换操作
|
||||
|
||||
Args:
|
||||
selected_game_dirs: 选择的游戏目录
|
||||
operation: 操作类型
|
||||
debug_mode: 是否为调试模式
|
||||
"""
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
results = []
|
||||
|
||||
for game_version, game_dir in selected_game_dirs.items():
|
||||
try:
|
||||
# 使用静默模式进行操作
|
||||
result = self.patch_manager.toggle_patch(game_dir, game_version, operation=operation, silent=True)
|
||||
|
||||
if result["success"]:
|
||||
success_count += 1
|
||||
else:
|
||||
fail_count += 1
|
||||
|
||||
results.append({
|
||||
"version": game_version,
|
||||
"success": result["success"],
|
||||
"message": result["message"],
|
||||
"action": result["action"]
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
if debug_mode:
|
||||
logger.error(f"DEBUG: 切换 {game_version} 补丁状态时出错: {str(e)}")
|
||||
fail_count += 1
|
||||
results.append({
|
||||
"version": game_version,
|
||||
"success": False,
|
||||
"message": f"操作出错: {str(e)}",
|
||||
"action": "none"
|
||||
})
|
||||
|
||||
# 显示操作结果
|
||||
self.patch_manager.show_toggle_result(success_count, fail_count, results)
|
||||
388
source/core/handlers/uninstall_handler.py
Normal file
388
source/core/handlers/uninstall_handler.py
Normal file
@@ -0,0 +1,388 @@
|
||||
import os
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QLabel, QListWidget, QPushButton, QHBoxLayout,
|
||||
QAbstractItemView, QFileDialog, QMessageBox
|
||||
)
|
||||
from PySide6.QtCore import QObject, Signal, QThread
|
||||
from PySide6.QtGui import QFont
|
||||
from utils import msgbox_frame
|
||||
from utils.logger import setup_logger
|
||||
|
||||
# 初始化logger
|
||||
logger = setup_logger("uninstall_handler")
|
||||
|
||||
class UninstallThread(QThread):
|
||||
"""在后台线程中处理卸载逻辑"""
|
||||
finished = Signal(object)
|
||||
|
||||
def __init__(self, handler, selected_folder):
|
||||
super().__init__()
|
||||
self.handler = handler
|
||||
self.selected_folder = selected_folder
|
||||
|
||||
def run(self):
|
||||
# 在后台线程中执行耗时操作
|
||||
game_dirs = self.handler.game_detector.identify_game_directories_improved(self.selected_folder)
|
||||
self.finished.emit(game_dirs)
|
||||
|
||||
class UninstallHandler(QObject):
|
||||
"""
|
||||
处理补丁卸载功能的类
|
||||
"""
|
||||
def __init__(self, main_window):
|
||||
"""
|
||||
初始化卸载处理程序
|
||||
|
||||
Args:
|
||||
main_window: 主窗口实例,用于访问其他组件
|
||||
"""
|
||||
super().__init__()
|
||||
self.main_window = main_window
|
||||
self.debug_manager = main_window.debug_manager
|
||||
self.game_detector = main_window.game_detector
|
||||
self.patch_manager = main_window.patch_manager
|
||||
self.app_name = main_window.patch_manager.app_name
|
||||
self.uninstall_thread = None
|
||||
|
||||
# 记录初始化日志
|
||||
debug_mode = self.debug_manager._is_debug_mode() if hasattr(self.debug_manager, '_is_debug_mode') else False
|
||||
if debug_mode:
|
||||
logger.debug("DEBUG: 卸载处理程序已初始化")
|
||||
|
||||
def handle_uninstall_button_click(self):
|
||||
"""
|
||||
处理卸载补丁按钮点击事件
|
||||
打开文件选择对话框选择游戏目录,然后卸载对应游戏的补丁
|
||||
"""
|
||||
# 获取游戏目录
|
||||
debug_mode = self.debug_manager._is_debug_mode()
|
||||
|
||||
logger.info("用户点击了卸载补丁按钮")
|
||||
if debug_mode:
|
||||
logger.debug("DEBUG: 处理卸载补丁按钮点击事件")
|
||||
|
||||
# 提示用户选择目录
|
||||
file_dialog_info = "选择游戏上级目录" if debug_mode else "选择游戏目录"
|
||||
selected_folder = QFileDialog.getExistingDirectory(self.main_window, file_dialog_info, "")
|
||||
|
||||
if not selected_folder or selected_folder == "":
|
||||
logger.info("用户取消了目录选择")
|
||||
if debug_mode:
|
||||
logger.debug("DEBUG: 用户取消了目录选择,退出卸载流程")
|
||||
return # 用户取消了选择
|
||||
|
||||
logger.info(f"用户选择了目录: {selected_folder}")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 用户选择了目录: {selected_folder}")
|
||||
|
||||
self.main_window.show_loading_dialog("正在识别游戏目录...")
|
||||
|
||||
self.uninstall_thread = UninstallThread(self, selected_folder)
|
||||
self.uninstall_thread.finished.connect(self.on_game_detection_finished)
|
||||
self.uninstall_thread.start()
|
||||
|
||||
def on_game_detection_finished(self, game_dirs):
|
||||
"""游戏识别完成后的回调"""
|
||||
self.main_window.hide_loading_dialog()
|
||||
|
||||
if not game_dirs:
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
"\n未在选择的目录中找到任何支持的游戏。\n",
|
||||
)
|
||||
return
|
||||
|
||||
games_with_patch = {}
|
||||
for game_version, game_dir in game_dirs.items():
|
||||
if self.patch_manager.check_patch_installed(game_dir, game_version):
|
||||
games_with_patch[game_version] = game_dir
|
||||
|
||||
if not games_with_patch:
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
"\n目录中未找到已安装补丁的游戏。\n",
|
||||
)
|
||||
return
|
||||
|
||||
selected_games = self._show_game_selection_dialog(games_with_patch)
|
||||
|
||||
if not selected_games:
|
||||
return
|
||||
|
||||
selected_game_dirs = {game: games_with_patch[game] for game in selected_games if game in games_with_patch}
|
||||
|
||||
game_list = '\n'.join(selected_games)
|
||||
reply = QMessageBox.question(
|
||||
self.main_window,
|
||||
f"确认卸载 - {self.app_name}",
|
||||
f"\n确定要卸载以下游戏的补丁吗?\n\n{game_list}\n",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.No:
|
||||
return
|
||||
|
||||
success_count, fail_count, results = self.patch_manager.batch_uninstall_patches(selected_game_dirs)
|
||||
self.patch_manager.show_uninstall_result(success_count, fail_count, results)
|
||||
|
||||
def _handle_multiple_games(self, game_dirs, debug_mode):
|
||||
"""
|
||||
处理多个游戏的补丁卸载
|
||||
|
||||
Args:
|
||||
game_dirs: 游戏目录字典
|
||||
debug_mode: 是否为调试模式
|
||||
"""
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 在上级目录中找到以下游戏: {list(game_dirs.keys())}")
|
||||
|
||||
# 查找已安装补丁的游戏,只处理那些已安装补丁的游戏
|
||||
logger.info("检查哪些游戏已安装补丁")
|
||||
games_with_patch = {}
|
||||
for game_version, game_dir in game_dirs.items():
|
||||
is_installed = self.patch_manager.check_patch_installed(game_dir, game_version)
|
||||
if is_installed:
|
||||
games_with_patch[game_version] = game_dir
|
||||
logger.info(f"游戏 {game_version} 已安装补丁")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - {game_version} 已安装补丁,目录: {game_dir}")
|
||||
else:
|
||||
logger.info(f"游戏 {game_version} 未安装补丁")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - {game_version} 未安装补丁,跳过")
|
||||
|
||||
# 检查是否有已安装补丁的游戏
|
||||
if not games_with_patch:
|
||||
logger.info("未找到已安装补丁的游戏")
|
||||
if debug_mode:
|
||||
logger.debug("DEBUG: 卸载功能 - 未找到已安装补丁的游戏,显示提示消息")
|
||||
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
"\n未在选择的目录中找到已安装补丁的游戏。\n请确认您选择了正确的游戏目录,并且该目录中的游戏已安装过补丁。\n",
|
||||
QMessageBox.StandardButton.Ok
|
||||
)
|
||||
return
|
||||
|
||||
# 显示选择对话框
|
||||
logger.info("显示游戏选择对话框")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 显示游戏选择对话框,可选游戏: {list(games_with_patch.keys())}")
|
||||
|
||||
selected_games = self._show_game_selection_dialog(games_with_patch)
|
||||
|
||||
if not selected_games:
|
||||
logger.info("用户未选择任何游戏或取消了选择")
|
||||
if debug_mode:
|
||||
logger.debug("DEBUG: 卸载功能 - 用户未选择任何游戏或取消了选择,退出卸载流程")
|
||||
return # 用户取消了选择
|
||||
|
||||
logger.info(f"用户选择了以下游戏: {selected_games}")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 用户选择了以下游戏: {selected_games}")
|
||||
|
||||
# 过滤game_dirs,只保留选中的游戏
|
||||
selected_game_dirs = {game: games_with_patch[game] for game in selected_games if game in games_with_patch}
|
||||
|
||||
# 确认卸载
|
||||
game_list = '\n'.join(selected_games)
|
||||
logger.info("显示卸载确认对话框")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 显示卸载确认对话框,选择的游戏: {selected_games}")
|
||||
|
||||
reply = QMessageBox.question(
|
||||
self.main_window,
|
||||
f"确认卸载 - {self.app_name}",
|
||||
f"\n确定要卸载以下游戏的补丁吗?\n\n{game_list}\n",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.No:
|
||||
logger.info("用户取消了卸载操作")
|
||||
if debug_mode:
|
||||
logger.debug("DEBUG: 卸载功能 - 用户取消了卸载操作,退出卸载流程")
|
||||
return
|
||||
|
||||
logger.info("开始批量卸载补丁")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 开始批量卸载补丁,游戏: {list(selected_game_dirs.keys())}")
|
||||
|
||||
# 使用批量卸载方法
|
||||
success_count, fail_count, results = self.patch_manager.batch_uninstall_patches(selected_game_dirs)
|
||||
|
||||
logger.info(f"批量卸载完成,成功: {success_count},失败: {fail_count}")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 批量卸载完成,成功: {success_count},失败: {fail_count}")
|
||||
if results:
|
||||
for result in results:
|
||||
status = "成功" if result["success"] else "失败"
|
||||
logger.debug(f"DEBUG: 卸载结果 - {result['version']}: {status}, 消息: {result['message']}, 删除文件数: {result['files_removed']}")
|
||||
|
||||
self.patch_manager.show_uninstall_result(success_count, fail_count, results)
|
||||
|
||||
def _handle_single_game(self, selected_folder, debug_mode):
|
||||
"""
|
||||
处理单个游戏的补丁卸载
|
||||
|
||||
Args:
|
||||
selected_folder: 选择的游戏目录
|
||||
debug_mode: 是否为调试模式
|
||||
"""
|
||||
# 未找到游戏目录,尝试将选择的目录作为游戏目录
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 未在上级目录找到游戏,尝试将选择的目录视为游戏目录")
|
||||
|
||||
logger.info("尝试识别单个游戏版本")
|
||||
game_version = self.game_detector.identify_game_version(selected_folder)
|
||||
|
||||
if game_version:
|
||||
logger.info(f"识别为游戏: {game_version}")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 识别为游戏: {game_version}")
|
||||
|
||||
# 检查是否已安装补丁
|
||||
logger.info(f"检查 {game_version} 是否已安装补丁")
|
||||
is_installed = self.patch_manager.check_patch_installed(selected_folder, game_version)
|
||||
|
||||
if is_installed:
|
||||
logger.info(f"{game_version} 已安装补丁")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - {game_version} 已安装补丁")
|
||||
|
||||
# 确认卸载
|
||||
logger.info("显示卸载确认对话框")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 显示卸载确认对话框,游戏: {game_version}")
|
||||
|
||||
reply = QMessageBox.question(
|
||||
self.main_window,
|
||||
f"确认卸载 - {self.app_name}",
|
||||
f"\n确定要卸载 {game_version} 的补丁吗?\n游戏目录: {selected_folder}\n",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
logger.info(f"开始卸载 {game_version} 的补丁")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 用户确认卸载 {game_version} 的补丁")
|
||||
|
||||
# 创建单个游戏的目录字典,使用批量卸载流程
|
||||
single_game_dir = {game_version: selected_folder}
|
||||
|
||||
logger.info("执行批量卸载方法(单游戏)")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 执行批量卸载方法(单游戏): {game_version}")
|
||||
|
||||
success_count, fail_count, results = self.patch_manager.batch_uninstall_patches(single_game_dir)
|
||||
|
||||
logger.info(f"卸载完成,成功: {success_count},失败: {fail_count}")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 卸载完成,成功: {success_count},失败: {fail_count}")
|
||||
if results:
|
||||
for result in results:
|
||||
status = "成功" if result["success"] else "失败"
|
||||
logger.debug(f"DEBUG: 卸载结果 - {result['version']}: {status}, 消息: {result['message']}, 删除文件数: {result['files_removed']}")
|
||||
|
||||
self.patch_manager.show_uninstall_result(success_count, fail_count, results)
|
||||
else:
|
||||
logger.info("用户取消了卸载操作")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 用户取消了卸载 {game_version} 的补丁")
|
||||
else:
|
||||
logger.info(f"{game_version} 未安装补丁")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - {game_version} 未安装补丁,显示提示消息")
|
||||
|
||||
# 没有安装补丁
|
||||
QMessageBox.information(
|
||||
self.main_window,
|
||||
f"提示 - {self.app_name}",
|
||||
f"\n未在 {game_version} 中找到已安装的补丁。\n请确认该游戏已经安装过补丁。\n",
|
||||
QMessageBox.StandardButton.Ok
|
||||
)
|
||||
else:
|
||||
# 两种方式都未识别到游戏
|
||||
logger.info("无法识别游戏")
|
||||
if debug_mode:
|
||||
logger.debug(f"DEBUG: 卸载功能 - 无法识别游戏,显示错误消息")
|
||||
|
||||
msg_box = msgbox_frame(
|
||||
f"错误 - {self.app_name}",
|
||||
"\n所选目录不是有效的NEKOPARA游戏目录。\n请选择包含游戏可执行文件的目录或其上级目录。\n",
|
||||
QMessageBox.StandardButton.Ok,
|
||||
)
|
||||
msg_box.exec()
|
||||
|
||||
def _show_game_selection_dialog(self, games_with_patch):
|
||||
"""
|
||||
显示游戏选择对话框
|
||||
|
||||
Args:
|
||||
games_with_patch: 已安装补丁的游戏目录字典
|
||||
|
||||
Returns:
|
||||
list: 选择的游戏列表
|
||||
"""
|
||||
dialog = QDialog(self.main_window)
|
||||
dialog.setWindowTitle("选择要卸载的游戏补丁")
|
||||
dialog.resize(400, 300)
|
||||
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
# 添加"已安装补丁的游戏"标签
|
||||
already_installed_label = QLabel("已安装补丁的游戏:", dialog)
|
||||
already_installed_label.setFont(QFont(already_installed_label.font().family(), already_installed_label.font().pointSize(), QFont.Weight.Bold))
|
||||
layout.addWidget(already_installed_label)
|
||||
|
||||
# 添加已安装游戏列表(可选,这里使用静态标签替代,保持一致性)
|
||||
installed_games_text = ", ".join(games_with_patch.keys())
|
||||
installed_games_label = QLabel(installed_games_text, dialog)
|
||||
layout.addWidget(installed_games_label)
|
||||
|
||||
# 添加一些间距
|
||||
layout.addSpacing(10)
|
||||
|
||||
# 添加"请选择要卸载补丁的游戏"标签
|
||||
info_label = QLabel("请选择要卸载补丁的游戏:", dialog)
|
||||
info_label.setFont(QFont(info_label.font().family(), info_label.font().pointSize(), QFont.Weight.Bold))
|
||||
layout.addWidget(info_label)
|
||||
|
||||
# 添加列表控件,只显示已安装补丁的游戏
|
||||
list_widget = QListWidget(dialog)
|
||||
list_widget.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) # 允许多选
|
||||
for game in games_with_patch.keys():
|
||||
list_widget.addItem(game)
|
||||
layout.addWidget(list_widget)
|
||||
|
||||
# 添加全选按钮
|
||||
select_all_btn = QPushButton("全选", dialog)
|
||||
select_all_btn.clicked.connect(lambda: list_widget.selectAll())
|
||||
layout.addWidget(select_all_btn)
|
||||
|
||||
# 添加确定和取消按钮
|
||||
buttons_layout = QHBoxLayout()
|
||||
ok_button = QPushButton("确定", dialog)
|
||||
cancel_button = 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)
|
||||
|
||||
# 显示对话框并等待用户选择
|
||||
result = dialog.exec()
|
||||
|
||||
if result != QDialog.DialogCode.Accepted or list_widget.selectedItems() == []:
|
||||
# 用户取消或未选择任何游戏
|
||||
return []
|
||||
|
||||
# 获取用户选择的游戏
|
||||
return [item.text() for item in list_widget.selectedItems()]
|
||||
Reference in New Issue
Block a user