Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
331f7a25d2 | ||
|
|
642b2ec17f | ||
|
|
41aab89669 | ||
|
|
f6a57215c2 | ||
|
|
38549e098e | ||
|
|
286270a819 | ||
|
|
0f9c91b59a | ||
|
|
3753375bed | ||
|
|
dab2ba2dc5 | ||
|
|
f86cb7aa7e | ||
|
|
0cf9f5e6c2 | ||
|
|
f9715f91f7 | ||
|
|
98e51d443e |
@@ -1,7 +1,7 @@
|
||||
# 🍓FRAISEMOE-Addons-Installer-NEXT🍓
|
||||
|
||||
```
|
||||
🔊 Note: This repository is still under active development, and most of the documentation is not yet available. We appreciate your understanding.
|
||||
🔊 Note: This repository's documentation updates have stabilized. If there are any missing parts, please promptly raise an issue. Thank you all for your support!
|
||||
The English version is not updated in real-time! Please check the Simplified Chinese version for more updates! Thank you for your support!
|
||||
```
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🍓FRAISEMOE-Addons-Installer-NEXT🍓
|
||||
|
||||
```
|
||||
🔊 注意:本库仍然努力更新中,大部分文档不可用,敬请谅解。
|
||||
🔊 注意:本库文档更新已趋于稳定,如有遗漏部分请及时提出issue,感谢各位支持!
|
||||
```
|
||||
|
||||
<!-- PROJECT SHIELDS -->
|
||||
|
||||
BIN
source/IMG/BG/title_bg1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
source/IMG/BG/title_bg2.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
source/IMG/BTN/Button.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
source/IMG/ICO/cloudflare_logo_icon.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
@@ -1,15 +1,24 @@
|
||||
import sys
|
||||
from PySide6.QtCore import (QObject, QPropertyAnimation, QParallelAnimationGroup,
|
||||
QPoint, QEasingCurve, QTimer, Signal)
|
||||
from PySide6.QtWidgets import QGraphicsOpacityEffect
|
||||
QPoint, QEasingCurve, QTimer, Signal, QRect)
|
||||
from PySide6.QtWidgets import QGraphicsOpacityEffect, QPushButton
|
||||
from PySide6.QtGui import QColor
|
||||
|
||||
class MultiStageAnimations(QObject):
|
||||
animation_finished = Signal()
|
||||
def __init__(self, ui, parent=None):
|
||||
super().__init__(parent)
|
||||
self.ui = ui
|
||||
# 获取画布尺寸
|
||||
self.canvas_width = ui.centralwidget.width()
|
||||
self.canvas_height = ui.centralwidget.height()
|
||||
self.parent = parent # 保存父窗口引用以获取当前尺寸
|
||||
|
||||
# 获取画布尺寸 - 动态从父窗口获取
|
||||
if parent:
|
||||
self.canvas_width = parent.width()
|
||||
self.canvas_height = parent.height()
|
||||
else:
|
||||
# 默认尺寸
|
||||
self.canvas_width = 1280
|
||||
self.canvas_height = 720
|
||||
|
||||
# 动画时序配置
|
||||
self.animation_config = {
|
||||
@@ -18,29 +27,141 @@ class MultiStageAnimations(QObject):
|
||||
},
|
||||
"mainbg": {
|
||||
"delay_after": 500
|
||||
},
|
||||
"button_click": {
|
||||
"scale_duration": 100,
|
||||
"scale_min": 0.95,
|
||||
"scale_max": 1.0
|
||||
}
|
||||
}
|
||||
|
||||
# 第一阶段:Logo动画配置
|
||||
# 第一阶段:Logo动画配置,根据新布局调整Y坐标
|
||||
self.logo_widgets = [
|
||||
{"widget": ui.vol1bg, "delay": 0, "duration": 500, "end_pos": QPoint(0, 120)},
|
||||
{"widget": ui.vol2bg, "delay": 80, "duration": 500, "end_pos": QPoint(0, 180)},
|
||||
{"widget": ui.vol3bg, "delay": 160, "duration": 500, "end_pos": QPoint(0, 240)},
|
||||
{"widget": ui.vol4bg, "delay": 240, "duration": 500, "end_pos": QPoint(0, 300)},
|
||||
{"widget": ui.afterbg, "delay": 320, "duration": 500, "end_pos": QPoint(0, 360)}
|
||||
{"widget": ui.vol1bg, "delay": 0, "duration": 500, "end_pos": QPoint(0, 150)},
|
||||
{"widget": ui.vol2bg, "delay": 80, "duration": 500, "end_pos": QPoint(0, 210)},
|
||||
{"widget": ui.vol3bg, "delay": 160, "duration": 500, "end_pos": QPoint(0, 270)},
|
||||
{"widget": ui.vol4bg, "delay": 240, "duration": 500, "end_pos": QPoint(0, 330)},
|
||||
{"widget": ui.afterbg, "delay": 320, "duration": 500, "end_pos": QPoint(0, 390)}
|
||||
]
|
||||
|
||||
# 第二阶段:菜单元素
|
||||
# 第二阶段:菜单元素,位置会在开始动画时动态计算
|
||||
self.menu_widgets = [
|
||||
{"widget": ui.menubg, "end_pos": QPoint(710, 0), "duration": 600},
|
||||
{"widget": ui.start_install_btn, "end_pos": QPoint(780, 250), "duration": 600},
|
||||
{"widget": ui.exit_btn, "end_pos": QPoint(780, 340), "duration": 600}
|
||||
# 移除菜单背景动画
|
||||
# {"widget": ui.menubg, "end_pos": QPoint(720, 55), "duration": 600},
|
||||
{"widget": ui.button_container, "end_pos": None, "duration": 600},
|
||||
{"widget": ui.uninstall_container, "end_pos": None, "duration": 600}, # 添加卸载补丁按钮
|
||||
{"widget": ui.exit_container, "end_pos": None, "duration": 600}
|
||||
]
|
||||
|
||||
self.animations = []
|
||||
self.timers = []
|
||||
|
||||
# 设置按钮点击动画
|
||||
self.setup_button_click_animations()
|
||||
|
||||
def setup_button_click_animations(self):
|
||||
"""设置按钮点击动画"""
|
||||
# 为开始安装按钮添加点击动画
|
||||
self.ui.start_install_btn.pressed.connect(
|
||||
lambda: self.start_button_click_animation(self.ui.button_container)
|
||||
)
|
||||
self.ui.start_install_btn.released.connect(
|
||||
lambda: self.end_button_click_animation(self.ui.button_container)
|
||||
)
|
||||
|
||||
# 为卸载补丁按钮添加点击动画
|
||||
self.ui.uninstall_btn.pressed.connect(
|
||||
lambda: self.start_button_click_animation(self.ui.uninstall_container)
|
||||
)
|
||||
self.ui.uninstall_btn.released.connect(
|
||||
lambda: self.end_button_click_animation(self.ui.uninstall_container)
|
||||
)
|
||||
|
||||
# 为退出按钮添加点击动画
|
||||
self.ui.exit_btn.pressed.connect(
|
||||
lambda: self.start_button_click_animation(self.ui.exit_container)
|
||||
)
|
||||
self.ui.exit_btn.released.connect(
|
||||
lambda: self.end_button_click_animation(self.ui.exit_container)
|
||||
)
|
||||
|
||||
def start_button_click_animation(self, button_container):
|
||||
"""开始按钮点击动画"""
|
||||
# 创建缩放动画
|
||||
scale_anim = QPropertyAnimation(button_container.children()[0], b"geometry") # 只对按钮背景应用动画
|
||||
scale_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
||||
|
||||
# 获取当前几何形状
|
||||
current_geometry = button_container.children()[0].geometry()
|
||||
|
||||
# 计算缩放后的几何形状(保持中心点不变)
|
||||
scale_factor = self.animation_config["button_click"]["scale_min"]
|
||||
width_diff = current_geometry.width() * (1 - scale_factor) / 2
|
||||
height_diff = current_geometry.height() * (1 - scale_factor) / 2
|
||||
|
||||
new_geometry = QRect(
|
||||
current_geometry.x() + width_diff,
|
||||
current_geometry.y() + height_diff,
|
||||
current_geometry.width() * scale_factor,
|
||||
current_geometry.height() * scale_factor
|
||||
)
|
||||
|
||||
scale_anim.setEndValue(new_geometry)
|
||||
scale_anim.setEasingCurve(QEasingCurve.Type.OutQuad)
|
||||
|
||||
# 启动动画
|
||||
scale_anim.start()
|
||||
self.animations.append(scale_anim)
|
||||
|
||||
# 对文本标签也应用同样的动画
|
||||
text_anim = QPropertyAnimation(button_container.children()[1], b"geometry")
|
||||
text_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
||||
text_geometry = button_container.children()[1].geometry()
|
||||
|
||||
new_text_geometry = QRect(
|
||||
text_geometry.x() + width_diff,
|
||||
text_geometry.y() + height_diff,
|
||||
text_geometry.width() * scale_factor,
|
||||
text_geometry.height() * scale_factor
|
||||
)
|
||||
|
||||
text_anim.setEndValue(new_text_geometry)
|
||||
text_anim.setEasingCurve(QEasingCurve.Type.OutQuad)
|
||||
text_anim.start()
|
||||
self.animations.append(text_anim)
|
||||
|
||||
def end_button_click_animation(self, button_container):
|
||||
"""结束按钮点击动画,恢复正常外观"""
|
||||
# 创建恢复动画 - 对背景
|
||||
scale_anim = QPropertyAnimation(button_container.children()[0], b"geometry")
|
||||
scale_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
||||
|
||||
# 恢复到原始大小 (10,10,191,91)
|
||||
original_geometry = QRect(10, 10, 191, 91)
|
||||
scale_anim.setEndValue(original_geometry)
|
||||
scale_anim.setEasingCurve(QEasingCurve.Type.OutElastic)
|
||||
|
||||
# 启动动画
|
||||
scale_anim.start()
|
||||
self.animations.append(scale_anim)
|
||||
|
||||
# 恢复文本标签
|
||||
text_anim = QPropertyAnimation(button_container.children()[1], b"geometry")
|
||||
text_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
||||
|
||||
# 恢复文本到原始大小 (10,7,191,91)
|
||||
text_anim.setEndValue(QRect(10, 7, 191, 91))
|
||||
text_anim.setEasingCurve(QEasingCurve.Type.OutElastic)
|
||||
text_anim.start()
|
||||
self.animations.append(text_anim)
|
||||
|
||||
def initialize(self):
|
||||
"""初始化所有组件状态"""
|
||||
# 更新画布尺寸
|
||||
if self.parent:
|
||||
self.canvas_width = self.parent.width()
|
||||
self.canvas_height = self.parent.height()
|
||||
|
||||
# 设置Mainbg初始状态
|
||||
effect = QGraphicsOpacityEffect(self.ui.Mainbg)
|
||||
effect.setOpacity(0)
|
||||
@@ -125,6 +246,12 @@ class MultiStageAnimations(QObject):
|
||||
self.animations.append(main_anim)
|
||||
def start_menu_animations(self):
|
||||
"""启动菜单动画(从下往上)"""
|
||||
# 更新按钮最终位置
|
||||
self._update_button_positions()
|
||||
|
||||
# 跟踪最后一个动画,用于连接finished信号
|
||||
last_anim = None
|
||||
|
||||
for item in self.menu_widgets:
|
||||
anim_group = QParallelAnimationGroup()
|
||||
|
||||
@@ -144,11 +271,69 @@ class MultiStageAnimations(QObject):
|
||||
anim_group.addAnimation(pos_anim)
|
||||
anim_group.addAnimation(opacity_anim)
|
||||
|
||||
if item["widget"] == self.ui.exit_btn:
|
||||
anim_group.finished.connect(self.animation_finished.emit)
|
||||
# 记录最后一个按钮的动画
|
||||
if item["widget"] == self.ui.exit_container:
|
||||
last_anim = anim_group
|
||||
|
||||
anim_group.start()
|
||||
self.animations.append(anim_group)
|
||||
|
||||
# 在最后一个动画完成时发出信号
|
||||
if last_anim:
|
||||
last_anim.finished.connect(self.animation_finished.emit)
|
||||
|
||||
def _update_button_positions(self):
|
||||
"""更新按钮最终位置"""
|
||||
# 根据当前窗口大小动态计算按钮位置
|
||||
if self.parent:
|
||||
width = self.parent.width()
|
||||
height = self.parent.height()
|
||||
|
||||
# 计算按钮位置
|
||||
right_margin = 20 # 减小右边距,使按钮更靠右
|
||||
|
||||
# 开始安装按钮
|
||||
if hasattr(self.ui, 'button_container'):
|
||||
btn_width = self.ui.button_container.width()
|
||||
x_pos = width - btn_width - right_margin
|
||||
y_pos = int((height - 65) * 0.28) - 10 # 与resizeEvent中保持一致
|
||||
|
||||
# 更新动画目标位置
|
||||
for item in self.menu_widgets:
|
||||
if item["widget"] == self.ui.button_container:
|
||||
item["end_pos"] = QPoint(x_pos, y_pos)
|
||||
|
||||
# 卸载补丁按钮
|
||||
if hasattr(self.ui, 'uninstall_container'):
|
||||
btn_width = self.ui.uninstall_container.width()
|
||||
x_pos = width - btn_width - right_margin
|
||||
y_pos = int((height - 65) * 0.46) - 10 # 与resizeEvent中保持一致
|
||||
|
||||
# 更新动画目标位置
|
||||
for item in self.menu_widgets:
|
||||
if item["widget"] == self.ui.uninstall_container:
|
||||
item["end_pos"] = QPoint(x_pos, y_pos)
|
||||
|
||||
# 退出按钮
|
||||
if hasattr(self.ui, 'exit_container'):
|
||||
btn_width = self.ui.exit_container.width()
|
||||
x_pos = width - btn_width - right_margin
|
||||
y_pos = int((height - 65) * 0.64) - 10 # 与resizeEvent中保持一致
|
||||
|
||||
# 更新动画目标位置
|
||||
for item in self.menu_widgets:
|
||||
if item["widget"] == self.ui.exit_container:
|
||||
item["end_pos"] = QPoint(x_pos, y_pos)
|
||||
else:
|
||||
# 默认位置
|
||||
for item in self.menu_widgets:
|
||||
if item["widget"] == self.ui.button_container:
|
||||
item["end_pos"] = QPoint(1050, 200)
|
||||
elif item["widget"] == self.ui.uninstall_container:
|
||||
item["end_pos"] = QPoint(1050, 310)
|
||||
elif item["widget"] == self.ui.exit_container:
|
||||
item["end_pos"] = QPoint(1050, 420)
|
||||
|
||||
def start_animations(self):
|
||||
"""启动完整动画序列"""
|
||||
self.clear_animations()
|
||||
|
||||
@@ -3,11 +3,13 @@ import requests
|
||||
import json
|
||||
from collections import deque
|
||||
from urllib.parse import urlparse
|
||||
import re # Added for recursive search
|
||||
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QIcon, QPixmap
|
||||
|
||||
from utils import msgbox_frame, HostsManager
|
||||
from utils import msgbox_frame, HostsManager, resource_path
|
||||
from data.config import APP_NAME, PLUGIN, GAME_INFO, UA, CONFIG_URL
|
||||
from workers import IpOptimizerThread
|
||||
|
||||
@@ -41,10 +43,29 @@ class DownloadManager:
|
||||
|
||||
def get_install_paths(self):
|
||||
"""获取所有游戏版本的安装路径"""
|
||||
return {
|
||||
game: os.path.join(self.selected_folder, info["install_path"])
|
||||
for game, info in GAME_INFO.items()
|
||||
}
|
||||
# 使用改进的目录识别功能
|
||||
game_dirs = self.main_window.identify_game_directories_improved(self.selected_folder)
|
||||
install_paths = {}
|
||||
|
||||
debug_mode = self.is_debug_mode()
|
||||
|
||||
for game, info in GAME_INFO.items():
|
||||
if game in game_dirs:
|
||||
# 如果找到了游戏目录,使用它
|
||||
game_dir = game_dirs[game]
|
||||
install_path = os.path.join(game_dir, os.path.basename(info["install_path"]))
|
||||
install_paths[game] = install_path
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 使用识别到的游戏目录 {game}: {game_dir}")
|
||||
print(f"DEBUG: 安装路径设置为: {install_path}")
|
||||
else:
|
||||
# 回退到原始路径计算方式
|
||||
install_path = os.path.join(self.selected_folder, info["install_path"])
|
||||
install_paths[game] = install_path
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 未识别到游戏目录 {game}, 使用默认路径: {install_path}")
|
||||
|
||||
return install_paths
|
||||
|
||||
def is_debug_mode(self):
|
||||
"""检查是否处于调试模式"""
|
||||
@@ -139,10 +160,33 @@ class DownloadManager:
|
||||
def download_action(self):
|
||||
"""开始下载流程"""
|
||||
# 禁用开始安装按钮
|
||||
self.main_window.ui.start_install_btn.setEnabled(False)
|
||||
self.main_window.set_start_button_enabled(False)
|
||||
|
||||
# 清空下载历史记录
|
||||
self.main_window.download_queue_history = []
|
||||
|
||||
# 使用改进的目录识别功能
|
||||
game_dirs = self.main_window.identify_game_directories_improved(self.selected_folder)
|
||||
|
||||
debug_mode = self.is_debug_mode()
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 开始下载流程, 识别到 {len(game_dirs)} 个游戏目录")
|
||||
|
||||
# 检查是否找到任何游戏目录
|
||||
if not game_dirs:
|
||||
if debug_mode:
|
||||
print("DEBUG: 未识别到任何游戏目录,设置目录未找到错误")
|
||||
# 设置特定的错误类型,以便在按钮点击处理中区分处理
|
||||
self.main_window.last_error_message = "directory_not_found"
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self.main_window,
|
||||
f"目录错误 - {APP_NAME}",
|
||||
"\n未能识别到任何游戏目录。\n\n请确认您选择的是游戏的上级目录,并且该目录中包含NEKOPARA系列游戏文件夹。\n"
|
||||
)
|
||||
return
|
||||
|
||||
# 显示哈希检查窗口
|
||||
self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window()
|
||||
self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window(check_type="pre")
|
||||
|
||||
# 执行预检查
|
||||
install_paths = self.get_install_paths()
|
||||
@@ -169,7 +213,7 @@ class DownloadManager:
|
||||
self.main_window, f"错误 - {APP_NAME}", "\n网络状态异常或服务器故障,请重试\n"
|
||||
)
|
||||
# 重新启用开始安装按钮
|
||||
self.main_window.ui.start_install_btn.setEnabled(True)
|
||||
self.main_window.set_start_button_enabled(True)
|
||||
return
|
||||
|
||||
# 填充下载队列
|
||||
@@ -184,8 +228,18 @@ class DownloadManager:
|
||||
# 询问用户是否使用Cloudflare加速
|
||||
msg_box = QtWidgets.QMessageBox(self.main_window)
|
||||
msg_box.setWindowTitle(f"下载优化 - {APP_NAME}")
|
||||
msg_box.setText("是否愿意通过Cloudflare加速来优化下载速度?\n\n这将临时修改系统的hosts文件,并需要管理员权限。")
|
||||
msg_box.setIcon(QtWidgets.QMessageBox.Icon.Question)
|
||||
msg_box.setText("是否愿意通过Cloudflare加速来优化下载速度?\n\n这将临时修改系统的hosts文件,并需要管理员权限。\n如您的杀毒软件提醒有软件正在修改hosts文件,请注意放行。")
|
||||
|
||||
# 设置Cloudflare图标
|
||||
cf_icon_path = resource_path("IMG/ICO/cloudflare_logo_icon.ico")
|
||||
if os.path.exists(cf_icon_path):
|
||||
cf_pixmap = QPixmap(cf_icon_path)
|
||||
if not cf_pixmap.isNull():
|
||||
msg_box.setWindowIcon(QIcon(cf_pixmap))
|
||||
msg_box.setIconPixmap(cf_pixmap.scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation))
|
||||
else:
|
||||
msg_box.setIcon(QtWidgets.QMessageBox.Icon.Question)
|
||||
|
||||
yes_button = msg_box.addButton("是,开启加速", QtWidgets.QMessageBox.ButtonRole.YesRole)
|
||||
no_button = msg_box.addButton("否,直接下载", QtWidgets.QMessageBox.ButtonRole.NoRole)
|
||||
@@ -210,26 +264,61 @@ class DownloadManager:
|
||||
# 清空现有队列
|
||||
self.download_queue.clear()
|
||||
|
||||
# 创建下载历史记录列表,用于跟踪本次安装的游戏
|
||||
if not hasattr(self.main_window, 'download_queue_history'):
|
||||
self.main_window.download_queue_history = []
|
||||
|
||||
# 获取所有识别到的游戏目录
|
||||
game_dirs = self.main_window.identify_game_directories_improved(self.selected_folder)
|
||||
|
||||
debug_mode = self.is_debug_mode()
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 填充下载队列, 识别到的游戏目录: {game_dirs}")
|
||||
|
||||
# 添加nekopara 1-4
|
||||
for i in range(1, 5):
|
||||
game_version = f"NEKOPARA Vol.{i}"
|
||||
if not self.main_window.installed_status.get(game_version, False):
|
||||
url = config.get(f"vol{i}")
|
||||
if not url: continue
|
||||
game_folder = os.path.join(self.selected_folder, f"NEKOPARA Vol. {i}")
|
||||
|
||||
# 确定游戏文件夹路径
|
||||
if game_version in game_dirs:
|
||||
game_folder = game_dirs[game_version]
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 使用识别到的游戏目录 {game_version}: {game_folder}")
|
||||
else:
|
||||
# 回退到传统方式
|
||||
game_folder = os.path.join(self.selected_folder, f"NEKOPARA Vol. {i}")
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 使用默认游戏目录 {game_version}: {game_folder}")
|
||||
|
||||
_7z_path = os.path.join(PLUGIN, f"vol.{i}.7z")
|
||||
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
|
||||
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
|
||||
# 记录到下载历史
|
||||
self.main_window.download_queue_history.append(game_version)
|
||||
|
||||
# 添加nekopara after
|
||||
game_version = "NEKOPARA After"
|
||||
if not self.main_window.installed_status.get(game_version, False):
|
||||
url = config.get("after")
|
||||
if url:
|
||||
game_folder = os.path.join(self.selected_folder, "NEKOPARA After")
|
||||
# 确定After的游戏文件夹路径
|
||||
if game_version in game_dirs:
|
||||
game_folder = game_dirs[game_version]
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 使用识别到的游戏目录 {game_version}: {game_folder}")
|
||||
else:
|
||||
game_folder = os.path.join(self.selected_folder, "NEKOPARA After")
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 使用默认游戏目录 {game_version}: {game_folder}")
|
||||
|
||||
_7z_path = os.path.join(PLUGIN, "after.7z")
|
||||
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
|
||||
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
|
||||
# 记录到下载历史
|
||||
self.main_window.download_queue_history.append(game_version)
|
||||
|
||||
def _start_ip_optimization(self, url):
|
||||
"""开始IP优化过程
|
||||
@@ -240,10 +329,21 @@ class DownloadManager:
|
||||
# 禁用退出按钮
|
||||
self.main_window.ui.exit_btn.setEnabled(False)
|
||||
|
||||
# 使用Cloudflare图标创建消息框
|
||||
|
||||
self.optimizing_msg_box = msgbox_frame(
|
||||
f"通知 - {APP_NAME}",
|
||||
"\n正在优选Cloudflare IP,请稍候...\n\n这可能需要5-10分钟,请耐心等待喵~"
|
||||
)
|
||||
# 设置Cloudflare图标
|
||||
cf_icon_path = resource_path("IMG/ICO/cloudflare_logo_icon.ico")
|
||||
if os.path.exists(cf_icon_path):
|
||||
cf_pixmap = QPixmap(cf_icon_path)
|
||||
if not cf_pixmap.isNull():
|
||||
self.optimizing_msg_box.setWindowIcon(QIcon(cf_pixmap))
|
||||
self.optimizing_msg_box.setIconPixmap(cf_pixmap.scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation))
|
||||
|
||||
# 我们不再提供"跳过"按钮
|
||||
self.optimizing_msg_box.setStandardButtons(QtWidgets.QMessageBox.StandardButton.NoButton)
|
||||
self.optimizing_msg_box.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
@@ -271,11 +371,33 @@ class DownloadManager:
|
||||
|
||||
# 显示优选结果
|
||||
if not ip:
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self.main_window,
|
||||
f"优选失败 - {APP_NAME}",
|
||||
"\n未能找到合适的Cloudflare IP,将使用默认网络进行下载。\n"
|
||||
)
|
||||
msg_box = QtWidgets.QMessageBox(self.main_window)
|
||||
msg_box.setWindowTitle(f"优选失败 - {APP_NAME}")
|
||||
msg_box.setText("\n未能找到合适的Cloudflare IP,将使用默认网络进行下载。\n\n10秒后自动继续...")
|
||||
msg_box.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
ok_button = msg_box.addButton("确定 (10)", QtWidgets.QMessageBox.ButtonRole.AcceptRole)
|
||||
|
||||
# 创建计时器实现倒计时
|
||||
countdown = 10
|
||||
timer = QtCore.QTimer(self.main_window)
|
||||
|
||||
def update_countdown():
|
||||
nonlocal countdown
|
||||
countdown -= 1
|
||||
ok_button.setText(f"确定 ({countdown})")
|
||||
if countdown <= 0:
|
||||
timer.stop()
|
||||
if msg_box.isVisible():
|
||||
msg_box.accept()
|
||||
|
||||
timer.timeout.connect(update_countdown)
|
||||
timer.start(1000) # 每秒更新一次
|
||||
|
||||
# 显示对话框,但不阻塞主线程
|
||||
msg_box.open()
|
||||
|
||||
# 连接关闭信号以停止计时器
|
||||
msg_box.finished.connect(timer.stop)
|
||||
else:
|
||||
# 应用优选IP到hosts文件
|
||||
if self.download_queue:
|
||||
@@ -286,11 +408,33 @@ class DownloadManager:
|
||||
self.hosts_manager.clean_hostname_entries(hostname)
|
||||
|
||||
if self.hosts_manager.apply_ip(hostname, ip):
|
||||
QtWidgets.QMessageBox.information(
|
||||
self.main_window,
|
||||
f"成功 - {APP_NAME}",
|
||||
f"\n已将优选IP ({ip}) 应用到hosts文件。\n"
|
||||
)
|
||||
msg_box = QtWidgets.QMessageBox(self.main_window)
|
||||
msg_box.setWindowTitle(f"成功 - {APP_NAME}")
|
||||
msg_box.setText(f"\n已将优选IP ({ip}) 应用到hosts文件。\n\n10秒后自动继续...")
|
||||
msg_box.setIcon(QtWidgets.QMessageBox.Icon.Information)
|
||||
ok_button = msg_box.addButton("确定 (10)", QtWidgets.QMessageBox.ButtonRole.AcceptRole)
|
||||
|
||||
# 创建计时器实现倒计时
|
||||
countdown = 10
|
||||
timer = QtCore.QTimer(self.main_window)
|
||||
|
||||
def update_countdown():
|
||||
nonlocal countdown
|
||||
countdown -= 1
|
||||
ok_button.setText(f"确定 ({countdown})")
|
||||
if countdown <= 0:
|
||||
timer.stop()
|
||||
if msg_box.isVisible():
|
||||
msg_box.accept()
|
||||
|
||||
timer.timeout.connect(update_countdown)
|
||||
timer.start(1000) # 每秒更新一次
|
||||
|
||||
# 显示对话框,但不阻塞主线程
|
||||
msg_box.open()
|
||||
|
||||
# 连接关闭信号以停止计时器
|
||||
msg_box.finished.connect(timer.stop)
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self.main_window,
|
||||
@@ -298,8 +442,8 @@ class DownloadManager:
|
||||
"\n修改hosts文件失败,请检查程序是否以管理员权限运行。\n"
|
||||
)
|
||||
|
||||
# 开始下载
|
||||
self.next_download_task()
|
||||
# 计时器结束或用户点击确定时,继续下载
|
||||
QtCore.QTimer.singleShot(10000, self.next_download_task)
|
||||
|
||||
def next_download_task(self):
|
||||
"""处理下载队列中的下一个任务"""
|
||||
@@ -325,21 +469,109 @@ class DownloadManager:
|
||||
_7z_path: 7z文件保存路径
|
||||
plugin_path: 插件路径
|
||||
"""
|
||||
game_exe = {
|
||||
game: os.path.join(
|
||||
self.selected_folder, info["install_path"].split("/")[0], info["exe"]
|
||||
# 使用改进的目录识别获取安装路径
|
||||
install_paths = self.get_install_paths()
|
||||
|
||||
debug_mode = self.is_debug_mode()
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 准备下载游戏 {game_version}")
|
||||
print(f"DEBUG: 游戏文件夹: {game_folder}")
|
||||
|
||||
# 获取游戏可执行文件路径
|
||||
game_dirs = self.main_window.identify_game_directories_improved(self.selected_folder)
|
||||
game_exe_exists = False
|
||||
|
||||
if game_version in game_dirs:
|
||||
game_dir = game_dirs[game_version]
|
||||
# 游戏目录已经通过可执行文件验证了,可以直接认为存在
|
||||
game_exe_exists = True
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 游戏目录已验证: {game_dir}")
|
||||
print(f"DEBUG: 游戏可执行文件存在: {game_exe_exists}")
|
||||
else:
|
||||
# 回退到传统方法检查游戏是否存在
|
||||
# 尝试多种可能的文件名格式
|
||||
expected_exe = GAME_INFO[game_version]["exe"]
|
||||
traditional_folder = os.path.join(
|
||||
self.selected_folder,
|
||||
GAME_INFO[game_version]["install_path"].split("/")[0]
|
||||
)
|
||||
for game, info in GAME_INFO.items()
|
||||
}
|
||||
|
||||
# 定义多种可能的可执行文件变体
|
||||
exe_variants = [
|
||||
expected_exe, # 标准文件名
|
||||
expected_exe + ".nocrack", # Steam加密版本
|
||||
expected_exe.replace(".exe", ""), # 无扩展名版本
|
||||
expected_exe.replace("NEKOPARA", "nekopara").lower(), # 全小写变体
|
||||
expected_exe.lower(), # 小写变体
|
||||
expected_exe.lower() + ".nocrack", # 小写变体的Steam加密版本
|
||||
]
|
||||
|
||||
# 对于Vol.3可能有特殊名称
|
||||
if "Vol.3" in game_version:
|
||||
# 增加可能的卷3特定的变体
|
||||
exe_variants.extend([
|
||||
"NEKOPARAVol3.exe",
|
||||
"NEKOPARAVol3.exe.nocrack",
|
||||
"nekoparavol3.exe",
|
||||
"nekoparavol3.exe.nocrack",
|
||||
"nekopara_vol3.exe",
|
||||
"nekopara_vol3.exe.nocrack",
|
||||
"vol3.exe",
|
||||
"vol3.exe.nocrack"
|
||||
])
|
||||
|
||||
# 检查所有可能的文件名
|
||||
for exe_variant in exe_variants:
|
||||
exe_path = os.path.join(traditional_folder, exe_variant)
|
||||
if os.path.exists(exe_path):
|
||||
game_exe_exists = True
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 找到游戏可执行文件: {exe_path}")
|
||||
break
|
||||
|
||||
# 如果仍未找到,尝试递归搜索
|
||||
if not game_exe_exists and os.path.exists(traditional_folder):
|
||||
# 提取卷号或检查是否是After
|
||||
vol_match = re.search(r"Vol\.(\d+)", game_version)
|
||||
vol_num = None
|
||||
if vol_match:
|
||||
vol_num = vol_match.group(1)
|
||||
|
||||
is_after = "After" in game_version
|
||||
|
||||
# 遍历游戏目录及其子目录
|
||||
for root, dirs, files in os.walk(traditional_folder):
|
||||
for file in files:
|
||||
file_lower = file.lower()
|
||||
if file.endswith('.exe') or file.endswith('.exe.nocrack'):
|
||||
# 检查文件名中是否包含卷号或关键词
|
||||
if ((vol_num and (f"vol{vol_num}" in file_lower or
|
||||
f"vol.{vol_num}" in file_lower or
|
||||
f"vol {vol_num}" in file_lower)) or
|
||||
(is_after and "after" in file_lower)):
|
||||
game_exe_exists = True
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 通过递归搜索找到游戏可执行文件: {os.path.join(root, file)}")
|
||||
break
|
||||
if game_exe_exists:
|
||||
break
|
||||
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 使用传统方法检查游戏目录: {traditional_folder}")
|
||||
print(f"DEBUG: 游戏可执行文件存在: {game_exe_exists}")
|
||||
|
||||
# 检查游戏是否已安装
|
||||
if (
|
||||
game_version not in game_exe
|
||||
or not os.path.exists(game_exe[game_version])
|
||||
not game_exe_exists
|
||||
or self.main_window.installed_status[game_version]
|
||||
):
|
||||
self.main_window.installed_status[game_version] = False
|
||||
self.main_window.show_result()
|
||||
if debug_mode:
|
||||
print(f"DEBUG: 跳过下载游戏 {game_version}")
|
||||
print(f"DEBUG: 游戏存在: {game_exe_exists}")
|
||||
print(f"DEBUG: 已安装补丁: {self.main_window.installed_status[game_version]}")
|
||||
self.main_window.installed_status[game_version] = False if not game_exe_exists else True
|
||||
self.next_download_task()
|
||||
return
|
||||
|
||||
# 创建进度窗口并开始下载
|
||||
@@ -428,7 +660,7 @@ class DownloadManager:
|
||||
return
|
||||
|
||||
# 下载成功,开始解压缩
|
||||
self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window()
|
||||
self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window(check_type="extraction")
|
||||
|
||||
# 创建并启动解压线程
|
||||
self.main_window.extraction_thread = self.main_window.create_extraction_thread(
|
||||
@@ -488,6 +720,6 @@ class DownloadManager:
|
||||
|
||||
# 重新启用退出按钮和开始安装按钮
|
||||
self.main_window.ui.exit_btn.setEnabled(True)
|
||||
self.main_window.ui.start_install_btn.setEnabled(True)
|
||||
self.main_window.set_start_button_enabled(True)
|
||||
|
||||
self.main_window.show_result()
|
||||
@@ -1,11 +1,10 @@
|
||||
from PySide6.QtGui import QIcon, QAction
|
||||
from PySide6.QtGui import QIcon, QAction, QFont
|
||||
from PySide6.QtWidgets import QMessageBox, QMainWindow
|
||||
from PySide6.QtCore import Qt
|
||||
import webbrowser
|
||||
|
||||
from utils import load_base64_image, msgbox_frame
|
||||
from data.config import APP_NAME, APP_VERSION
|
||||
from data.pic_data import img_data
|
||||
|
||||
class UIManager:
|
||||
def __init__(self, main_window):
|
||||
@@ -22,10 +21,11 @@ class UIManager:
|
||||
def setup_ui(self):
|
||||
"""设置UI元素,包括窗口图标、标题和菜单"""
|
||||
# 设置窗口图标
|
||||
icon_data = img_data.get("icon")
|
||||
if icon_data:
|
||||
pixmap = load_base64_image(icon_data)
|
||||
self.main_window.setWindowIcon(QIcon(pixmap))
|
||||
import os
|
||||
from utils import resource_path
|
||||
icon_path = resource_path(os.path.join("IMG", "ICO", "icon.png"))
|
||||
if os.path.exists(icon_path):
|
||||
self.main_window.setWindowIcon(QIcon(icon_path))
|
||||
|
||||
# 设置窗口标题
|
||||
self.main_window.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
|
||||
@@ -39,20 +39,28 @@ class UIManager:
|
||||
if not self.ui or not hasattr(self.ui, 'menu_2'):
|
||||
return
|
||||
|
||||
# 创建菜单项
|
||||
project_home_action = QAction("项目主页", self.main_window)
|
||||
project_home_action.triggered.connect(self.open_project_home_page)
|
||||
|
||||
about_action = QAction("关于", self.main_window)
|
||||
about_action.triggered.connect(self.show_about_dialog)
|
||||
|
||||
# 添加到菜单
|
||||
self.ui.menu_2.addAction(project_home_action)
|
||||
self.ui.menu_2.addAction(about_action)
|
||||
|
||||
# 连接按钮点击事件,如果使用按钮式菜单
|
||||
if hasattr(self.ui, 'help_btn'):
|
||||
# 按钮已经连接到显示菜单,不需要额外处理
|
||||
pass
|
||||
|
||||
def _setup_settings_menu(self):
|
||||
"""设置"设置"菜单"""
|
||||
if not self.ui or not hasattr(self.ui, 'menu'):
|
||||
return
|
||||
|
||||
# 创建菜单项
|
||||
self.debug_action = QAction("Debug模式", self.main_window, checkable=True)
|
||||
|
||||
# 安全地获取config属性
|
||||
@@ -67,13 +75,22 @@ class UIManager:
|
||||
if hasattr(self.main_window, 'toggle_debug_mode'):
|
||||
self.debug_action.triggered.connect(self.main_window.toggle_debug_mode)
|
||||
|
||||
# 添加到菜单
|
||||
self.ui.menu.addAction(self.debug_action)
|
||||
|
||||
# 为未来功能预留的"切换下载源"按钮
|
||||
self.switch_source_action = QAction("切换下载源", self.main_window)
|
||||
self.switch_source_action.setEnabled(False) # 暂时禁用
|
||||
self.ui.menu.addAction(self.switch_source_action)
|
||||
|
||||
|
||||
# 添加分隔符
|
||||
self.ui.menu.addSeparator()
|
||||
|
||||
# 连接按钮点击事件,如果使用按钮式菜单
|
||||
if hasattr(self.ui, 'settings_btn'):
|
||||
# 按钮已经连接到显示菜单,不需要额外处理
|
||||
pass
|
||||
|
||||
def open_project_home_page(self):
|
||||
"""打开项目主页"""
|
||||
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")
|
||||
|
||||
@@ -3,7 +3,7 @@ import base64
|
||||
|
||||
# 配置信息
|
||||
app_data = {
|
||||
"APP_VERSION": "1.1.2",
|
||||
"APP_VERSION": "1.1.3",
|
||||
"APP_NAME": "FRAISEMOE Addons Installer NEXT",
|
||||
"TEMP": "TEMP",
|
||||
"CACHE": "FRAISEMOE",
|
||||
|
||||
BIN
source/fonts/SmileySans-Oblique.ttf
Normal file
|
Before Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 179 KiB |
|
Before Width: | Height: | Size: 571 KiB |
|
Before Width: | Height: | Size: 250 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 229 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 166 KiB |
@@ -1,4 +1,3 @@
|
||||
from data.pic_data import img_data
|
||||
from PySide6.QtGui import QPixmap
|
||||
import base64
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
@@ -8,105 +7,459 @@ from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
|
||||
QCursor, QFont, QFontDatabase, QGradient,
|
||||
QIcon, QImage, QKeySequence, QLinearGradient,
|
||||
QPainter, QPalette, QPixmap, QRadialGradient,
|
||||
QTransform)
|
||||
QTransform, QPainterPath, QRegion)
|
||||
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QMenu,
|
||||
QMenuBar, QPushButton, QSizePolicy, QWidget)
|
||||
def load_base64_image(base64_str):
|
||||
pixmap = QPixmap()
|
||||
pixmap.loadFromData(base64.b64decode(base64_str))
|
||||
return pixmap
|
||||
QMenuBar, QPushButton, QSizePolicy, QWidget, QHBoxLayout)
|
||||
import os
|
||||
|
||||
# 导入配置常量
|
||||
from data.config import APP_NAME, APP_VERSION
|
||||
from utils import load_image_from_file
|
||||
|
||||
class Ui_MainWindows(object):
|
||||
def setupUi(self, MainWindows):
|
||||
if not MainWindows.objectName():
|
||||
MainWindows.setObjectName(u"MainWindows")
|
||||
MainWindows.setEnabled(True)
|
||||
MainWindows.resize(1024, 576)
|
||||
MainWindows.setMinimumSize(QSize(1024, 576))
|
||||
MainWindows.setMaximumSize(QSize(1024, 576))
|
||||
# 调整窗口默认大小为1280x720以匹配背景图片
|
||||
MainWindows.resize(1280, 720)
|
||||
# 锁定窗口比例为16:9,确保不会变形,同时限制最大尺寸不超过背景图片
|
||||
MainWindows.setMinimumSize(QSize(1024, 576)) # 16:9最小尺寸
|
||||
MainWindows.setMaximumSize(QSize(1280, 720)) # 将最大尺寸限制为1280x720
|
||||
# 设置固定纵横比
|
||||
self.aspect_ratio = 16/9
|
||||
MainWindows.setMouseTracking(False)
|
||||
MainWindows.setTabletTracking(False)
|
||||
MainWindows.setAcceptDrops(True)
|
||||
MainWindows.setAutoFillBackground(True)
|
||||
MainWindows.setAutoFillBackground(False)
|
||||
MainWindows.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
|
||||
MainWindows.setAnimated(True)
|
||||
MainWindows.setDocumentMode(False)
|
||||
MainWindows.setDockNestingEnabled(False)
|
||||
|
||||
# 加载自定义字体
|
||||
font_id = QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(os.path.dirname(__file__)), "fonts", "SmileySans-Oblique.ttf"))
|
||||
font_family = QFontDatabase.applicationFontFamilies(font_id)[0] if font_id != -1 else "Arial"
|
||||
self.custom_font = QFont(font_family, 16) # 创建字体对象,大小为16
|
||||
self.custom_font.setWeight(QFont.Weight.Medium) # 设置为中等粗细,不要太粗
|
||||
|
||||
self.centralwidget = QWidget(MainWindows)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
self.centralwidget.setAutoFillBackground(True)
|
||||
self.loadbg = QLabel(self.centralwidget)
|
||||
self.centralwidget.setAutoFillBackground(False) # 修改为False以支持透明背景
|
||||
self.centralwidget.setStyleSheet("""
|
||||
QWidget#centralwidget {
|
||||
background-color: transparent;
|
||||
}
|
||||
""")
|
||||
|
||||
# 圆角背景容器
|
||||
self.main_container = QWidget(self.centralwidget)
|
||||
self.main_container.setObjectName(u"main_container")
|
||||
self.main_container.setGeometry(QRect(0, 0, 1280, 720))
|
||||
self.main_container.setStyleSheet("""
|
||||
QWidget#main_container {
|
||||
background-color: #E96948;
|
||||
border-radius: 20px;
|
||||
border: 1px solid #E96948;
|
||||
}
|
||||
""")
|
||||
|
||||
# 内容容器 - 用于限制内容在圆角范围内
|
||||
self.content_container = QWidget(self.main_container)
|
||||
self.content_container.setObjectName(u"content_container")
|
||||
self.content_container.setGeometry(QRect(0, 0, 1280, 720))
|
||||
self.content_container.setStyleSheet("""
|
||||
QWidget#content_container {
|
||||
background-color: transparent;
|
||||
border-radius: 20px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 添加圆角裁剪,确保内容在圆角范围内
|
||||
rect = QRect(0, 0, 1280, 720)
|
||||
path = QPainterPath()
|
||||
path.addRoundedRect(rect, 20, 20)
|
||||
region = QRegion(path.toFillPolygon().toPolygon())
|
||||
self.content_container.setMask(region)
|
||||
|
||||
# 标题栏
|
||||
self.title_bar = QWidget(self.content_container)
|
||||
self.title_bar.setObjectName(u"title_bar")
|
||||
self.title_bar.setGeometry(QRect(0, 0, 1280, 35)) # 减小高度从40到35
|
||||
self.title_bar.setStyleSheet("""
|
||||
QWidget#title_bar {
|
||||
background-color: #E96948;
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
border-bottom: 1px solid #F47A5B;
|
||||
}
|
||||
""")
|
||||
|
||||
# 标题栏布局
|
||||
self.title_layout = QHBoxLayout(self.title_bar)
|
||||
self.title_layout.setSpacing(10)
|
||||
self.title_layout.setContentsMargins(10, 0, 10, 0)
|
||||
|
||||
# 添加最小化和关闭按钮到标题栏
|
||||
self.minimize_btn = QPushButton(self.title_bar)
|
||||
self.minimize_btn.setObjectName(u"minimize_btn")
|
||||
self.minimize_btn.setMinimumSize(QSize(24, 24))
|
||||
self.minimize_btn.setMaximumSize(QSize(24, 24))
|
||||
self.minimize_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
||||
self.minimize_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #FFC107;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #FFD54F;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #FFA000;
|
||||
}
|
||||
""")
|
||||
self.minimize_btn.setText("—")
|
||||
self.minimize_btn.setFont(QFont(font_family, 10))
|
||||
|
||||
self.close_btn = QPushButton(self.title_bar)
|
||||
self.close_btn.setObjectName(u"close_btn")
|
||||
self.close_btn.setMinimumSize(QSize(24, 24))
|
||||
self.close_btn.setMaximumSize(QSize(24, 24))
|
||||
self.close_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
||||
self.close_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #F44336;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #EF5350;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #D32F2F;
|
||||
}
|
||||
""")
|
||||
self.close_btn.setText("×")
|
||||
self.close_btn.setFont(QFont(font_family, 14))
|
||||
|
||||
# 标题文本
|
||||
self.title_label = QLabel(self.title_bar)
|
||||
self.title_label.setObjectName(u"title_label")
|
||||
# 直接使用APP_NAME并添加版本号
|
||||
self.title_label.setText(f"{APP_NAME} v{APP_VERSION}")
|
||||
title_font = QFont(font_family, 14) # 减小字体从16到14
|
||||
title_font.setBold(True)
|
||||
self.title_label.setFont(title_font)
|
||||
self.title_label.setStyleSheet("color: #333333; padding-left: 10px;")
|
||||
self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
# 添加按钮到标题栏布局
|
||||
self.title_layout.addWidget(self.title_label)
|
||||
self.title_layout.addStretch(1)
|
||||
self.title_layout.addWidget(self.minimize_btn)
|
||||
self.title_layout.addSpacing(5)
|
||||
self.title_layout.addWidget(self.close_btn)
|
||||
|
||||
# 修改菜单区域 - 确保足够宽以容纳更多菜单项
|
||||
self.menu_area = QWidget(self.content_container)
|
||||
self.menu_area.setObjectName(u"menu_area")
|
||||
self.menu_area.setGeometry(QRect(0, 35, 1280, 30)) # 调整位置从40到35,高度从35到30
|
||||
self.menu_area.setStyleSheet("""
|
||||
QWidget#menu_area {
|
||||
background-color: #E96948;
|
||||
}
|
||||
""")
|
||||
|
||||
# 不再使用菜单栏,改用普通按钮
|
||||
# 创建菜单按钮字体
|
||||
menu_font = QFont(font_family, 14) # 进一步减小字体大小到14
|
||||
menu_font.setBold(True)
|
||||
|
||||
# 设置按钮
|
||||
self.settings_btn = QPushButton("设置", self.menu_area)
|
||||
self.settings_btn.setObjectName(u"settings_btn")
|
||||
self.settings_btn.setGeometry(QRect(20, 1, 80, 28)) # 调整高度和Y位置
|
||||
self.settings_btn.setFont(menu_font)
|
||||
self.settings_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
||||
self.settings_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: none;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #F47A5B;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #D25A3C;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 帮助按钮
|
||||
self.help_btn = QPushButton("帮助", self.menu_area)
|
||||
self.help_btn.setObjectName(u"help_btn")
|
||||
self.help_btn.setGeometry(QRect(120, 1, 80, 28)) # 调整高度和Y位置
|
||||
self.help_btn.setFont(menu_font)
|
||||
self.help_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
||||
self.help_btn.setStyleSheet(self.settings_btn.styleSheet())
|
||||
|
||||
# 将原来的菜单项移到全局,方便访问
|
||||
self.menu = QMenu(self.content_container)
|
||||
self.menu.setObjectName(u"menu")
|
||||
self.menu.setTitle("设置")
|
||||
self.menu.setFont(menu_font)
|
||||
self.menu.setStyleSheet("""
|
||||
QMenu {
|
||||
background-color: #E96948;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #F47A5B;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
QMenu::item {
|
||||
padding: 6px 20px 6px 15px;
|
||||
background-color: transparent;
|
||||
min-width: 120px;
|
||||
color: white;
|
||||
}
|
||||
QMenu::item:selected {
|
||||
background-color: #F47A5B;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QMenu::separator {
|
||||
height: 1px;
|
||||
background-color: #F47A5B;
|
||||
margin: 5px 15px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.menu_2 = QMenu(self.content_container)
|
||||
self.menu_2.setObjectName(u"menu_2")
|
||||
self.menu_2.setTitle("帮助")
|
||||
self.menu_2.setFont(menu_font)
|
||||
self.menu_2.setStyleSheet(self.menu.styleSheet())
|
||||
|
||||
# 连接按钮点击事件到显示对应菜单
|
||||
self.settings_btn.clicked.connect(lambda: self.show_menu(self.menu, self.settings_btn))
|
||||
self.help_btn.clicked.connect(lambda: self.show_menu(self.menu_2, self.help_btn))
|
||||
|
||||
# 预留位置给未来可能的第三个按钮
|
||||
# 第三个按钮可以这样添加:
|
||||
# self.third_btn = QPushButton("第三项", self.menu_area)
|
||||
# self.third_btn.setObjectName(u"third_btn")
|
||||
# self.third_btn.setGeometry(QRect(320, 0, 120, 35))
|
||||
# self.third_btn.setFont(menu_font)
|
||||
# self.third_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
||||
# self.third_btn.setStyleSheet(self.settings_btn.styleSheet())
|
||||
# self.third_btn.clicked.connect(lambda: self.show_menu(self.menu_3, self.third_btn))
|
||||
|
||||
# 内容子容器
|
||||
self.inner_content = QWidget(self.content_container)
|
||||
self.inner_content.setObjectName(u"inner_content")
|
||||
# 确保宽度足够大,保证右侧元素完全显示
|
||||
self.inner_content.setGeometry(QRect(0, 65, 1280, 655)) # 调整Y位置从75到65,高度从645到655
|
||||
self.inner_content.setStyleSheet("""
|
||||
QWidget#inner_content {
|
||||
background-color: transparent;
|
||||
border-bottom-left-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 添加底部圆角裁剪
|
||||
inner_rect = QRect(0, 0, 1280, 665)
|
||||
inner_path = QPainterPath()
|
||||
inner_path.addRoundedRect(inner_rect, 20, 20)
|
||||
inner_region = QRegion(inner_path.toFillPolygon().toPolygon())
|
||||
self.inner_content.setMask(inner_region)
|
||||
|
||||
# 在主容器中添加背景和内容元素
|
||||
# 修改loadbg使用title_bg1.png作为整个背景
|
||||
# 原来的loadbg保持不变
|
||||
self.loadbg = QLabel(self.inner_content)
|
||||
self.loadbg.setObjectName(u"loadbg")
|
||||
self.loadbg.setGeometry(QRect(0, 0, 1031, 561))
|
||||
self.loadbg.setPixmap(load_base64_image(img_data["loadbg"]))
|
||||
self.loadbg.setGeometry(QRect(0, 0, 1280, 655))
|
||||
# 加载背景图并允许拉伸
|
||||
bg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "BG", "bg1.jpg")
|
||||
bg_pixmap = QPixmap(bg_path)
|
||||
self.loadbg.setPixmap(bg_pixmap)
|
||||
self.loadbg.setScaledContents(True)
|
||||
self.vol1bg = QLabel(self.centralwidget)
|
||||
|
||||
self.vol1bg = QLabel(self.inner_content)
|
||||
self.vol1bg.setObjectName(u"vol1bg")
|
||||
self.vol1bg.setGeometry(QRect(0, 120, 93, 64))
|
||||
self.vol1bg.setPixmap(load_base64_image(img_data["vol1"]))
|
||||
self.vol1bg.setGeometry(QRect(0, 150, 93, 64))
|
||||
# 直接加载图片文件
|
||||
vol1_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "LOGO", "vo01_logo.png")
|
||||
self.vol1bg.setPixmap(QPixmap(vol1_path))
|
||||
self.vol1bg.setScaledContents(True)
|
||||
self.vol2bg = QLabel(self.centralwidget)
|
||||
|
||||
self.vol2bg = QLabel(self.inner_content)
|
||||
self.vol2bg.setObjectName(u"vol2bg")
|
||||
self.vol2bg.setGeometry(QRect(0, 180, 93, 64))
|
||||
self.vol2bg.setPixmap(load_base64_image(img_data["vol2"]))
|
||||
self.vol2bg.setGeometry(QRect(0, 210, 93, 64))
|
||||
# 直接加载图片文件
|
||||
vol2_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "LOGO", "vo02_logo.png")
|
||||
self.vol2bg.setPixmap(QPixmap(vol2_path))
|
||||
self.vol2bg.setScaledContents(True)
|
||||
self.vol3bg = QLabel(self.centralwidget)
|
||||
|
||||
self.vol3bg = QLabel(self.inner_content)
|
||||
self.vol3bg.setObjectName(u"vol3bg")
|
||||
self.vol3bg.setGeometry(QRect(0, 240, 93, 64))
|
||||
self.vol3bg.setPixmap(load_base64_image(img_data["vol3"]))
|
||||
self.vol3bg.setGeometry(QRect(0, 270, 93, 64))
|
||||
# 直接加载图片文件
|
||||
vol3_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "LOGO", "vo03_logo.png")
|
||||
self.vol3bg.setPixmap(QPixmap(vol3_path))
|
||||
self.vol3bg.setScaledContents(True)
|
||||
self.vol4bg = QLabel(self.centralwidget)
|
||||
|
||||
self.vol4bg = QLabel(self.inner_content)
|
||||
self.vol4bg.setObjectName(u"vol4bg")
|
||||
self.vol4bg.setGeometry(QRect(0, 300, 93, 64))
|
||||
self.vol4bg.setPixmap(load_base64_image(img_data["vol4"]))
|
||||
self.vol4bg.setGeometry(QRect(0, 330, 93, 64))
|
||||
# 直接加载图片文件
|
||||
vol4_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "LOGO", "vo04_logo.png")
|
||||
self.vol4bg.setPixmap(QPixmap(vol4_path))
|
||||
self.vol4bg.setScaledContents(True)
|
||||
self.afterbg = QLabel(self.centralwidget)
|
||||
|
||||
self.afterbg = QLabel(self.inner_content)
|
||||
self.afterbg.setObjectName(u"afterbg")
|
||||
self.afterbg.setGeometry(QRect(0, 360, 93, 64))
|
||||
self.afterbg.setPixmap(load_base64_image(img_data["after"]))
|
||||
self.afterbg.setGeometry(QRect(0, 390, 93, 64))
|
||||
# 直接加载图片文件
|
||||
after_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "LOGO", "voaf_logo.png")
|
||||
self.afterbg.setPixmap(QPixmap(after_path))
|
||||
self.afterbg.setScaledContents(True)
|
||||
self.Mainbg = QLabel(self.centralwidget)
|
||||
|
||||
# 修复Mainbg位置并使用title_bg1.png作为背景图片
|
||||
self.Mainbg = QLabel(self.inner_content)
|
||||
self.Mainbg.setObjectName(u"Mainbg")
|
||||
self.Mainbg.setGeometry(QRect(0, 0, 1031, 561))
|
||||
self.Mainbg.setPixmap(load_base64_image(img_data["Mainbg"]))
|
||||
self.Mainbg.setScaledContents(True)
|
||||
self.start_install_btn = QPushButton(self.centralwidget)
|
||||
self.Mainbg.setGeometry(QRect(0, 0, 1280, 655))
|
||||
# 允许拉伸以填满整个区域
|
||||
main_bg_pixmap = load_image_from_file(os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "BG", "title_bg1.png"))
|
||||
|
||||
# 如果加载的图片不是空的,则设置,并允许拉伸填满
|
||||
if not main_bg_pixmap.isNull():
|
||||
self.Mainbg.setPixmap(main_bg_pixmap)
|
||||
self.Mainbg.setScaledContents(True)
|
||||
self.Mainbg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
# 使用新的按钮图片
|
||||
button_pixmap = load_image_from_file(os.path.join(os.path.dirname(os.path.dirname(__file__)), "IMG", "BTN", "Button.png"))
|
||||
|
||||
# 创建文本标签布局的按钮
|
||||
# 开始安装按钮 - 基于背景图片和标签组合
|
||||
# 调整开始安装按钮的位置
|
||||
self.button_container = QWidget(self.inner_content)
|
||||
self.button_container.setObjectName(u"start_install_container")
|
||||
self.button_container.setGeometry(QRect(1050, 200, 211, 111)) # 调整Y坐标,上移至200
|
||||
# 不要隐藏容器,让动画系统来控制它的可见性和位置
|
||||
|
||||
# 使用原来的按钮背景图片
|
||||
self.start_install_bg = QLabel(self.button_container)
|
||||
self.start_install_bg.setObjectName(u"start_install_bg")
|
||||
self.start_install_bg.setGeometry(QRect(10, 10, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.start_install_bg.setPixmap(button_pixmap)
|
||||
self.start_install_bg.setScaledContents(True)
|
||||
|
||||
self.start_install_text = QLabel(self.button_container)
|
||||
self.start_install_text.setObjectName(u"start_install_text")
|
||||
self.start_install_text.setGeometry(QRect(10, 7, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.start_install_text.setText("开始安装")
|
||||
self.start_install_text.setFont(self.custom_font)
|
||||
self.start_install_text.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.start_install_text.setStyleSheet("letter-spacing: 1px;")
|
||||
|
||||
# 点击区域透明按钮
|
||||
self.start_install_btn = QPushButton(self.button_container)
|
||||
self.start_install_btn.setObjectName(u"start_install_btn")
|
||||
self.start_install_btn.setEnabled(True)
|
||||
self.start_install_btn.setGeometry(QRect(780, 250, 191, 91))
|
||||
self.start_install_btn.setAutoFillBackground(False)
|
||||
start_install_icon = QIcon()
|
||||
start_install_pixmap = load_base64_image(img_data["start_install_btn"])
|
||||
if not start_install_pixmap.isNull():
|
||||
start_install_icon.addPixmap(start_install_pixmap)
|
||||
self.start_install_btn.setIcon(start_install_icon)
|
||||
self.start_install_btn.setIcon(start_install_icon)
|
||||
self.start_install_btn.setIconSize(QSize(189, 110))
|
||||
self.start_install_btn.setCheckable(False)
|
||||
self.start_install_btn.setAutoRepeat(False)
|
||||
self.start_install_btn.setAutoDefault(False)
|
||||
self.start_install_btn.setGeometry(QRect(10, 10, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.start_install_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) # 设置鼠标悬停时为手形光标
|
||||
self.start_install_btn.setFlat(True)
|
||||
self.exit_btn = QPushButton(self.centralwidget)
|
||||
self.start_install_btn.raise_() # 确保按钮在最上层
|
||||
self.start_install_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
""")
|
||||
|
||||
# 添加卸载补丁按钮 - 新增
|
||||
self.uninstall_container = QWidget(self.inner_content)
|
||||
self.uninstall_container.setObjectName(u"uninstall_container")
|
||||
self.uninstall_container.setGeometry(QRect(1050, 310, 211, 111)) # 调整Y坐标,位于310位置
|
||||
|
||||
# 使用相同的按钮背景图片
|
||||
self.uninstall_bg = QLabel(self.uninstall_container)
|
||||
self.uninstall_bg.setObjectName(u"uninstall_bg")
|
||||
self.uninstall_bg.setGeometry(QRect(10, 10, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.uninstall_bg.setPixmap(button_pixmap)
|
||||
self.uninstall_bg.setScaledContents(True)
|
||||
|
||||
self.uninstall_text = QLabel(self.uninstall_container)
|
||||
self.uninstall_text.setObjectName(u"uninstall_text")
|
||||
self.uninstall_text.setGeometry(QRect(10, 7, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.uninstall_text.setText("卸载补丁")
|
||||
self.uninstall_text.setFont(self.custom_font)
|
||||
self.uninstall_text.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.uninstall_text.setStyleSheet("letter-spacing: 1px;")
|
||||
|
||||
# 点击区域透明按钮
|
||||
self.uninstall_btn = QPushButton(self.uninstall_container)
|
||||
self.uninstall_btn.setObjectName(u"uninstall_btn")
|
||||
self.uninstall_btn.setGeometry(QRect(10, 10, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.uninstall_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) # 设置鼠标悬停时为手形光标
|
||||
self.uninstall_btn.setFlat(True)
|
||||
self.uninstall_btn.raise_() # 确保按钮在最上层
|
||||
self.uninstall_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
""")
|
||||
|
||||
# 退出按钮 - 基于背景图片和标签组合,调整位置
|
||||
self.exit_container = QWidget(self.inner_content)
|
||||
self.exit_container.setObjectName(u"exit_container")
|
||||
self.exit_container.setGeometry(QRect(1050, 420, 211, 111)) # 调整Y坐标,下移至420
|
||||
# 不要隐藏容器,让动画系统来控制它的可见性和位置
|
||||
|
||||
# 使用原来的按钮背景图片
|
||||
self.exit_bg = QLabel(self.exit_container)
|
||||
self.exit_bg.setObjectName(u"exit_bg")
|
||||
self.exit_bg.setGeometry(QRect(10, 10, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.exit_bg.setPixmap(button_pixmap)
|
||||
self.exit_bg.setScaledContents(True)
|
||||
|
||||
self.exit_text = QLabel(self.exit_container)
|
||||
self.exit_text.setObjectName(u"exit_text")
|
||||
self.exit_text.setGeometry(QRect(10, 7, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.exit_text.setText("退出程序")
|
||||
self.exit_text.setFont(self.custom_font)
|
||||
self.exit_text.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.exit_text.setStyleSheet("letter-spacing: 1px;")
|
||||
|
||||
# 点击区域透明按钮
|
||||
self.exit_btn = QPushButton(self.exit_container)
|
||||
self.exit_btn.setObjectName(u"exit_btn")
|
||||
self.exit_btn.setEnabled(True)
|
||||
self.exit_btn.setGeometry(QRect(780, 340, 191, 91))
|
||||
self.exit_btn.setAutoFillBackground(False)
|
||||
exit_icon = QIcon()
|
||||
exit_pixmap = load_base64_image(img_data["exit_btn"])
|
||||
if not exit_pixmap.isNull():
|
||||
exit_icon.addPixmap(exit_pixmap)
|
||||
self.exit_btn.setIcon(exit_icon)
|
||||
self.exit_btn.setIcon(exit_icon)
|
||||
self.exit_btn.setIconSize(QSize(189, 110))
|
||||
self.exit_btn.setCheckable(False)
|
||||
self.exit_btn.setGeometry(QRect(10, 10, 191, 91)) # 居中放置在扩大的容器中
|
||||
self.exit_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) # 设置鼠标悬停时为手形光标
|
||||
self.exit_btn.setFlat(True)
|
||||
self.menubg = QLabel(self.centralwidget)
|
||||
self.menubg.setObjectName(u"menubg")
|
||||
self.menubg.setGeometry(QRect(710, 0, 321, 561))
|
||||
self.menubg.setPixmap(load_base64_image(img_data["menubg"]))
|
||||
self.menubg.setScaledContents(True)
|
||||
self.exit_btn.raise_() # 确保按钮在最上层
|
||||
self.exit_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
""")
|
||||
|
||||
MainWindows.setCentralWidget(self.centralwidget)
|
||||
|
||||
# 调整层级顺序
|
||||
self.loadbg.raise_()
|
||||
self.vol1bg.raise_()
|
||||
self.vol2bg.raise_()
|
||||
@@ -114,28 +467,22 @@ class Ui_MainWindows(object):
|
||||
self.vol4bg.raise_()
|
||||
self.afterbg.raise_()
|
||||
self.Mainbg.raise_()
|
||||
self.menubg.raise_()
|
||||
self.start_install_btn.raise_()
|
||||
self.exit_btn.raise_()
|
||||
self.menubar = QMenuBar(MainWindows)
|
||||
self.menubar.setObjectName(u"menubar")
|
||||
self.menubar.setGeometry(QRect(0, 0, 1024, 21))
|
||||
self.menu = QMenu(self.menubar)
|
||||
self.menu.setObjectName(u"menu")
|
||||
self.menu_2 = QMenu(self.menubar)
|
||||
self.menu_2.setObjectName(u"menu_2")
|
||||
MainWindows.setMenuBar(self.menubar)
|
||||
|
||||
self.menubar.addAction(self.menu.menuAction())
|
||||
self.menubar.addAction(self.menu_2.menuAction())
|
||||
self.menu.addSeparator()
|
||||
self.button_container.raise_()
|
||||
self.uninstall_container.raise_() # 添加新按钮到层级顺序
|
||||
self.exit_container.raise_()
|
||||
self.menu_area.raise_() # 确保菜单区域在背景之上
|
||||
# self.menubar.raise_() # 不再需要菜单栏
|
||||
self.settings_btn.raise_() # 确保设置按钮在上层
|
||||
self.help_btn.raise_() # 确保帮助按钮在上层
|
||||
self.title_bar.raise_() # 确保标题栏在最上层
|
||||
|
||||
self.retranslateUi(MainWindows)
|
||||
|
||||
QMetaObject.connectSlotsByName(MainWindows)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, MainWindows):
|
||||
MainWindows.setWindowTitle(QCoreApplication.translate("MainWindows", u" UI Test", None))
|
||||
MainWindows.setWindowTitle(QCoreApplication.translate("MainWindows", f"{APP_NAME} v{APP_VERSION}", None))
|
||||
self.loadbg.setText("")
|
||||
self.vol1bg.setText("")
|
||||
self.vol2bg.setText("")
|
||||
@@ -146,10 +493,18 @@ class Ui_MainWindows(object):
|
||||
#if QT_CONFIG(accessibility)
|
||||
self.start_install_btn.setAccessibleDescription("")
|
||||
#endif // QT_CONFIG(accessibility)
|
||||
self.start_install_btn.setText("")
|
||||
self.exit_btn.setText("")
|
||||
self.menubg.setText("")
|
||||
self.menu.setTitle(QCoreApplication.translate("MainWindows", u"\u8bbe\u7f6e", None))
|
||||
self.menu_2.setTitle(QCoreApplication.translate("MainWindows", u"\u5e2e\u52a9", None))
|
||||
self.menu.setTitle(QCoreApplication.translate("MainWindows", u"设置", None))
|
||||
self.menu_2.setTitle(QCoreApplication.translate("MainWindows", u"帮助", None))
|
||||
# retranslateUi
|
||||
|
||||
def show_menu(self, menu, button):
|
||||
"""显示菜单
|
||||
|
||||
Args:
|
||||
menu: 要显示的菜单
|
||||
button: 触发菜单的按钮
|
||||
"""
|
||||
# 计算菜单显示位置
|
||||
pos = button.mapToGlobal(button.rect().bottomLeft())
|
||||
menu.exec(pos)
|
||||
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindows</class>
|
||||
<widget class="QMainWindow" name="MainWindows">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1024</width>
|
||||
<height>576</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>576</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>576</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="mouseTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabletTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string> UI Test</string>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonStyle::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
<property name="animated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dockNestingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QLabel" name="loadbg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1031</width>
|
||||
<height>561</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/BG/bg2.jpg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="vol1bg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>120</y>
|
||||
<width>93</width>
|
||||
<height>64</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/LOGO/vo01_logo.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="vol2bg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>180</y>
|
||||
<width>93</width>
|
||||
<height>64</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/LOGO/vo02_logo.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="vol3bg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>240</y>
|
||||
<width>93</width>
|
||||
<height>64</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/LOGO/vo03_logo.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="vol4bg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>300</y>
|
||||
<width>93</width>
|
||||
<height>64</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/LOGO/vo04_logo.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="afterbg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>360</y>
|
||||
<width>93</width>
|
||||
<height>64</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/LOGO/voaf_logo.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="Mainbg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1031</width>
|
||||
<height>561</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/BG/bg3.jpg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="start_install_btn">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>780</x>
|
||||
<y>250</y>
|
||||
<width>191</width>
|
||||
<height>91</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="accessibleDescription">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>IMG/BTN/start_install.bmp</normaloff>IMG/BTN/start_install.bmp</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>189</width>
|
||||
<height>110</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoRepeat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="exit_btn">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>780</x>
|
||||
<y>340</y>
|
||||
<width>191</width>
|
||||
<height>91</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>IMG/BTN/exit.bmp</normaloff>IMG/BTN/exit.bmp</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>189</width>
|
||||
<height>110</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="menubg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>710</x>
|
||||
<y>0</y>
|
||||
<width>321</width>
|
||||
<height>561</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>IMG/BG/menubg.jpg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<zorder>loadbg</zorder>
|
||||
<zorder>vol1bg</zorder>
|
||||
<zorder>vol2bg</zorder>
|
||||
<zorder>vol3bg</zorder>
|
||||
<zorder>vol4bg</zorder>
|
||||
<zorder>afterbg</zorder>
|
||||
<zorder>Mainbg</zorder>
|
||||
<zorder>menubg</zorder>
|
||||
<zorder>start_install_btn</zorder>
|
||||
<zorder>exit_btn</zorder>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1024</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu">
|
||||
<property name="title">
|
||||
<string>设置</string>
|
||||
</property>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_2"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_2">
|
||||
<property name="title">
|
||||
<string>关于</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menu"/>
|
||||
<addaction name="menu_2"/>
|
||||
</widget>
|
||||
<action name="action_2">
|
||||
<property name="text">
|
||||
<string>update - sd</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>480</width>
|
||||
<height>270</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,12 +1,14 @@
|
||||
from .logger import Logger
|
||||
from .helpers import (
|
||||
load_base64_image, HashManager, AdminPrivileges, msgbox_frame,
|
||||
load_config, save_config, HostsManager, censor_url, resource_path
|
||||
load_config, save_config, HostsManager, censor_url, resource_path,
|
||||
load_image_from_file
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'Logger',
|
||||
'load_base64_image',
|
||||
'load_image_from_file',
|
||||
'HashManager',
|
||||
'AdminPrivileges',
|
||||
'msgbox_frame',
|
||||
|
||||
@@ -9,7 +9,6 @@ import psutil
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
import re
|
||||
from PySide6.QtGui import QIcon, QPixmap
|
||||
from data.pic_data import img_data
|
||||
from data.config import APP_NAME, CONFIG_FILE
|
||||
|
||||
def resource_path(relative_path):
|
||||
@@ -28,7 +27,7 @@ def resource_path(relative_path):
|
||||
if relative_path in ("aria2c.exe", "cfst.exe"):
|
||||
return os.path.join(base_path, 'bin', relative_path)
|
||||
elif relative_path in ("ip.txt", "ipv6.txt"):
|
||||
return os.path.join(base_path, 'resources', 'data', relative_path)
|
||||
return os.path.join(base_path, 'data', relative_path)
|
||||
|
||||
return os.path.join(base_path, relative_path)
|
||||
|
||||
@@ -37,14 +36,28 @@ def load_base64_image(base64_str):
|
||||
pixmap.loadFromData(base64.b64decode(base64_str))
|
||||
return pixmap
|
||||
|
||||
def load_image_from_file(file_path):
|
||||
"""加载图像文件到QPixmap
|
||||
|
||||
Args:
|
||||
file_path: 图像文件路径
|
||||
|
||||
Returns:
|
||||
QPixmap: 加载的图像
|
||||
"""
|
||||
if os.path.exists(file_path):
|
||||
return QPixmap(file_path)
|
||||
return QPixmap()
|
||||
|
||||
def msgbox_frame(title, text, buttons=QtWidgets.QMessageBox.StandardButton.NoButton):
|
||||
msg_box = QtWidgets.QMessageBox()
|
||||
msg_box.setWindowTitle(title)
|
||||
msg_box.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
|
||||
|
||||
icon_data = img_data.get("icon")
|
||||
if icon_data:
|
||||
pixmap = load_base64_image(icon_data)
|
||||
# 直接加载图标文件
|
||||
icon_path = resource_path(os.path.join("IMG", "ICO", "icon.png"))
|
||||
if os.path.exists(icon_path):
|
||||
pixmap = QPixmap(icon_path)
|
||||
if not pixmap.isNull():
|
||||
msg_box.setWindowIcon(QIcon(pixmap))
|
||||
msg_box.setIconPixmap(pixmap.scaled(64, 64, QtCore.Qt.AspectRatioMode.KeepAspectRatio, QtCore.Qt.TransformationMode.SmoothTransformation))
|
||||
@@ -99,8 +112,25 @@ class HashManager:
|
||||
print(f"Error calculating hash for {file_path}: {e}")
|
||||
return results
|
||||
|
||||
def hash_pop_window(self):
|
||||
msg_box = msgbox_frame(f"通知 - {APP_NAME}", "\n正在检验文件状态...\n")
|
||||
def hash_pop_window(self, check_type="default"):
|
||||
"""显示文件检验窗口
|
||||
|
||||
Args:
|
||||
check_type: 检查类型,可以是 'pre'(预检查), 'after'(后检查), 'extraction'(解压后检查)
|
||||
|
||||
Returns:
|
||||
QMessageBox: 消息框实例
|
||||
"""
|
||||
message = "\n正在检验文件状态...\n"
|
||||
|
||||
if check_type == "pre":
|
||||
message = "\n正在检查游戏文件以确定需要安装的补丁...\n"
|
||||
elif check_type == "after":
|
||||
message = "\n正在检验本地文件完整性...\n"
|
||||
elif check_type == "extraction":
|
||||
message = "\n正在验证下载的解压文件完整性...\n"
|
||||
|
||||
msg_box = msgbox_frame(f"通知 - {APP_NAME}", message)
|
||||
msg_box.open()
|
||||
QtWidgets.QApplication.processEvents()
|
||||
return msg_box
|
||||
@@ -158,6 +188,7 @@ class AdminPrivileges:
|
||||
"nekopara_vol1.exe",
|
||||
"nekopara_vol2.exe",
|
||||
"NEKOPARAvol3.exe",
|
||||
"NEKOPARAvol3.exe.nocrack",
|
||||
"nekopara_vol4.exe",
|
||||
"nekopara_after.exe",
|
||||
]
|
||||
@@ -200,33 +231,40 @@ class AdminPrivileges:
|
||||
|
||||
def check_and_terminate_processes(self):
|
||||
for proc in psutil.process_iter(["pid", "name"]):
|
||||
if proc.info["name"] in self.required_exes:
|
||||
msg_box = msgbox_frame(
|
||||
f"进程检测 - {APP_NAME}",
|
||||
f"\n检测到游戏正在运行: {proc.info['name']} \n\n是否终止?\n",
|
||||
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
|
||||
)
|
||||
reply = msg_box.exec()
|
||||
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
|
||||
try:
|
||||
proc.terminate()
|
||||
proc.wait(timeout=3)
|
||||
except psutil.AccessDenied:
|
||||
proc_name = proc.info["name"].lower() if proc.info["name"] else ""
|
||||
|
||||
# 检查进程名是否匹配任何需要终止的游戏进程
|
||||
for exe in self.required_exes:
|
||||
if exe.lower() == proc_name:
|
||||
# 获取不带.nocrack的游戏名称用于显示
|
||||
display_name = exe.replace(".nocrack", "")
|
||||
|
||||
msg_box = msgbox_frame(
|
||||
f"进程检测 - {APP_NAME}",
|
||||
f"\n检测到游戏正在运行: {display_name} \n\n是否终止?\n",
|
||||
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
|
||||
)
|
||||
reply = msg_box.exec()
|
||||
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
|
||||
try:
|
||||
proc.terminate()
|
||||
proc.wait(timeout=3)
|
||||
except psutil.AccessDenied:
|
||||
msg_box = msgbox_frame(
|
||||
f"错误 - {APP_NAME}",
|
||||
f"\n无法关闭游戏: {display_name} \n\n请手动关闭后重启应用\n",
|
||||
QtWidgets.QMessageBox.StandardButton.Ok,
|
||||
)
|
||||
msg_box.exec()
|
||||
sys.exit(1)
|
||||
else:
|
||||
msg_box = msgbox_frame(
|
||||
f"错误 - {APP_NAME}",
|
||||
f"\n无法关闭游戏: {proc.info['name']} \n\n请手动关闭后重启应用\n",
|
||||
f"进程检测 - {APP_NAME}",
|
||||
f"\n未关闭的游戏: {display_name} \n\n请手动关闭后重启应用\n",
|
||||
QtWidgets.QMessageBox.StandardButton.Ok,
|
||||
)
|
||||
msg_box.exec()
|
||||
sys.exit(1)
|
||||
else:
|
||||
msg_box = msgbox_frame(
|
||||
f"进程检测 - {APP_NAME}",
|
||||
f"\n未关闭的游戏: {proc.info['name']} \n\n请手动关闭后重启应用\n",
|
||||
QtWidgets.QMessageBox.StandardButton.Ok,
|
||||
)
|
||||
msg_box.exec()
|
||||
sys.exit(1)
|
||||
|
||||
class HostsManager:
|
||||
def __init__(self):
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import json
|
||||
import requests
|
||||
import webbrowser
|
||||
from PySide6.QtCore import QThread, Signal
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
import sys
|
||||
|
||||
class ConfigFetchThread(QThread):
|
||||
finished = Signal(object, str) # data, error_message
|
||||
@@ -30,8 +33,12 @@ class ConfigFetchThread(QThread):
|
||||
# 首先,总是尝试解析JSON
|
||||
config_data = response.json()
|
||||
|
||||
# 检查是否是要求更新的错误信息
|
||||
if config_data.get("message") == "请使用最新版本的FRAISEMOE Addons Installer NEXT进行下载安装":
|
||||
# 检查是否是要求更新的错误信息 - 使用Unicode编码的更新提示文本
|
||||
update_required_msg = "\u8bf7\u4f7f\u7528\u6700\u65b0\u7248\u672c\u7684FraiseMoe2-Next\u8fdb\u884c\u4e0b\u8f7d"
|
||||
if isinstance(config_data, str) and config_data == update_required_msg:
|
||||
self.finished.emit(None, "update_required")
|
||||
return
|
||||
elif isinstance(config_data, dict) and config_data.get("message") == update_required_msg:
|
||||
self.finished.emit(None, "update_required")
|
||||
return
|
||||
|
||||
@@ -44,9 +51,15 @@ class ConfigFetchThread(QThread):
|
||||
|
||||
self.finished.emit(config_data, "")
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.finished.emit(None, f"网络请求失败: {e}")
|
||||
error_msg = "访问云端配置失败,请检查网络状况或稍后再试。"
|
||||
if self.debug_mode:
|
||||
error_msg += f"\n详细错误: {e}"
|
||||
self.finished.emit(None, error_msg)
|
||||
except (ValueError, json.JSONDecodeError) as e:
|
||||
self.finished.emit(None, f"JSON解析失败: {e}")
|
||||
error_msg = "访问云端配置失败,请检查网络状况或稍后再试。"
|
||||
if self.debug_mode:
|
||||
error_msg += f"\nJSON解析失败: {e}"
|
||||
self.finished.emit(None, error_msg)
|
||||
finally:
|
||||
if self.debug_mode:
|
||||
print("--- Finished fetching cloud config ---")
|
||||
@@ -38,7 +38,7 @@ class IpOptimizer:
|
||||
"-url", url, # 指定测速地址
|
||||
"-f", ip_txt_path, # IP文件
|
||||
"-dd", # 禁用下载测速,按延迟排序
|
||||
"-o",
|
||||
"-o","" # 不写入结果文件
|
||||
]
|
||||
|
||||
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
|
||||
@@ -119,10 +119,8 @@ class IpOptimizer:
|
||||
if match and not optimal_ip: # 只保存第一个匹配的IP(最优IP)
|
||||
optimal_ip = match.group(1)
|
||||
print(f"找到最优 IP: {optimal_ip}")
|
||||
|
||||
# 如果已经看到完成标记,可以退出了
|
||||
if found_completion:
|
||||
break
|
||||
# 找到最优IP后立即退出循环,不等待完成标记
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取输出时发生错误: {e}")
|
||||
|
||||