mirror of
https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT.git
synced 2025-12-17 04:20:28 +00:00
feat(core): 添加隐私协议管理和关于菜单功能
- 在 Main.py 中初始化隐私协议管理器,并在程序启动前显示隐私协议对话框 - 在 core/__init__.py 中添加 PrivacyManager 的引用 - 在 ui_manager.py 中实现关于菜单和隐私协议相关功能,包括: - 创建关于按钮和菜单 - 添加隐私协议子菜单 - 实现撤回隐私协议同意并重启软件的功能 - 优化菜单样式和字体加载
This commit is contained in:
90
PRIVACY.md
Normal file
90
PRIVACY.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# FRAISEMOE2-Installer 隐私政策
|
||||||
|
|
||||||
|
## 1. 引言
|
||||||
|
|
||||||
|
本隐私政策旨在说明 FRAISEMOE Addons Installer NEXT(以下简称"本应用")在使用过程中如何收集、使用和保护您的个人信息。我们致力于保护您的隐私,并确保您了解我们如何处理您的数据。
|
||||||
|
|
||||||
|
## 2. 收集的信息
|
||||||
|
|
||||||
|
本应用在运行过程中可能会收集或处理以下信息:
|
||||||
|
|
||||||
|
### 2.1 系统信息
|
||||||
|
- 操作系统版本信息:作为 User-Agent 的一部分,用于与服务器通信
|
||||||
|
- 程序版本号:用于检查更新和兼容性
|
||||||
|
|
||||||
|
### 2.2 网络相关信息
|
||||||
|
- IP 地址:在使用 Cloudflare 加速功能时,会进行 IP 优选
|
||||||
|
- 域名解析信息:用于优化下载速度
|
||||||
|
- 下载统计信息:用于监控下载进度和速度
|
||||||
|
|
||||||
|
### 2.3 文件信息
|
||||||
|
- 游戏安装路径:用于识别已安装的游戏和安装补丁
|
||||||
|
- 文件哈希值:用于验证文件完整性
|
||||||
|
|
||||||
|
## 3. 信息使用
|
||||||
|
|
||||||
|
我们收集的信息仅用于以下目的:
|
||||||
|
|
||||||
|
### 3.1 功能实现
|
||||||
|
- 游戏目录识别:识别已安装的游戏版本
|
||||||
|
- 文件完整性验证:确保下载文件的完整性和安全性
|
||||||
|
- 下载加速:通过 Cloudflare 优化下载速度
|
||||||
|
|
||||||
|
### 3.2 服务改进
|
||||||
|
- 提供更新:检查应用版本并推送更新
|
||||||
|
- 错误报告:收集错误信息以改进应用体验
|
||||||
|
|
||||||
|
## 4. 数据存储
|
||||||
|
|
||||||
|
### 4.1 本地存储
|
||||||
|
- 配置文件:保存在系统临时文件夹的 FRAISEMOE 子目录下
|
||||||
|
- 临时下载文件:保存在系统临时文件夹中
|
||||||
|
- 日志文件:记录程序运行日志
|
||||||
|
|
||||||
|
### 4.2 网络传输
|
||||||
|
所有网络请求均使用安全的 HTTPS 协议进行传输。
|
||||||
|
|
||||||
|
## 5. 修改系统文件
|
||||||
|
|
||||||
|
### 5.1 hosts 文件修改
|
||||||
|
- 当您选择使用 Cloudflare 加速功能时,本应用会临时修改系统 hosts 文件
|
||||||
|
- 修改前会自动创建备份(位于 %SystemRoot%\System32\drivers\etc\hosts.bak.FRAISEMOE Addons Installer NEXT)
|
||||||
|
- 程序退出时会自动恢复原始 hosts 文件
|
||||||
|
|
||||||
|
## 6. 第三方服务
|
||||||
|
|
||||||
|
本应用使用以下第三方服务:
|
||||||
|
|
||||||
|
### 6.1 Cloudflare
|
||||||
|
- 当您选择使用 Cloudflare 加速功能时,本应用会使用 CloudflareSpeedTest 工具测试并选择最优 IP 地址
|
||||||
|
- 此过程会向 Cloudflare 服务器发送请求以测试连接速度
|
||||||
|
|
||||||
|
### 6.2 云端配置服务
|
||||||
|
- 本应用从云端服务器获取配置信息(如下载链接等)
|
||||||
|
- 在此过程中,您的 IP 地址和 User-Agent 信息会被传输到云端服务器
|
||||||
|
|
||||||
|
## 7. 用户控制
|
||||||
|
|
||||||
|
您对以下功能有完全的控制权:
|
||||||
|
|
||||||
|
- 选择是否使用 Cloudflare 加速功能(需修改 hosts 文件)
|
||||||
|
- 选择安装目录和需要安装的游戏版本
|
||||||
|
- 选择是否终止可能冲突的进程
|
||||||
|
|
||||||
|
## 8. 数据安全
|
||||||
|
|
||||||
|
我们采取以下措施保护您的数据:
|
||||||
|
|
||||||
|
- 本地配置文件不包含敏感个人信息
|
||||||
|
- 网络请求使用安全的 HTTPS 协议
|
||||||
|
- hosts 文件修改会在程序退出时自动恢复
|
||||||
|
|
||||||
|
## 9. 联系我们
|
||||||
|
|
||||||
|
如果您对本隐私政策有任何疑问或建议,请通过 GitHub 项目页面联系我们。
|
||||||
|
|
||||||
|
## 10. 政策更新
|
||||||
|
|
||||||
|
本隐私政策可能会根据应用功能的变化而更新。请定期查看最新版本。
|
||||||
|
|
||||||
|
最后更新日期:2025年7月31日
|
||||||
@@ -1,9 +1,21 @@
|
|||||||
import sys
|
import sys
|
||||||
from PySide6.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
from main_window import MainWindow
|
from main_window import MainWindow
|
||||||
|
from core.privacy_manager import PrivacyManager
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
# 初始化隐私协议管理器
|
||||||
|
privacy_manager = PrivacyManager()
|
||||||
|
|
||||||
|
# 显示隐私协议对话框(仅在首次运行或用户未同意时显示)
|
||||||
|
if not privacy_manager.show_privacy_dialog():
|
||||||
|
print("用户未同意隐私协议,程序退出")
|
||||||
|
sys.exit(0) # 如果用户不同意隐私协议,退出程序
|
||||||
|
|
||||||
|
# 用户已同意隐私协议,继续启动程序
|
||||||
|
print("隐私协议已同意,启动主程序")
|
||||||
window = MainWindow()
|
window = MainWindow()
|
||||||
window.show()
|
window.show()
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
@@ -6,6 +6,7 @@ from .window_manager import WindowManager
|
|||||||
from .game_detector import GameDetector
|
from .game_detector import GameDetector
|
||||||
from .patch_manager import PatchManager
|
from .patch_manager import PatchManager
|
||||||
from .config_manager import ConfigManager
|
from .config_manager import ConfigManager
|
||||||
|
from .privacy_manager import PrivacyManager
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'MultiStageAnimations',
|
'MultiStageAnimations',
|
||||||
@@ -15,5 +16,6 @@ __all__ = [
|
|||||||
'WindowManager',
|
'WindowManager',
|
||||||
'GameDetector',
|
'GameDetector',
|
||||||
'PatchManager',
|
'PatchManager',
|
||||||
'ConfigManager'
|
'ConfigManager',
|
||||||
|
'PrivacyManager'
|
||||||
]
|
]
|
||||||
178
source/core/privacy_manager.py
Normal file
178
source/core/privacy_manager.py
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from PySide6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QCheckBox, QLabel, QMessageBox
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
|
from data.privacy_policy import PRIVACY_POLICY_BRIEF
|
||||||
|
from data.config import CACHE, APP_NAME
|
||||||
|
from utils import msgbox_frame
|
||||||
|
|
||||||
|
class PrivacyManager:
|
||||||
|
"""隐私协议管理器,负责显示隐私协议对话框并处理用户选择"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""初始化隐私协议管理器"""
|
||||||
|
# 确保缓存目录存在
|
||||||
|
os.makedirs(CACHE, exist_ok=True)
|
||||||
|
self.config_file = os.path.join(CACHE, "privacy_config.json")
|
||||||
|
self.privacy_accepted = self._load_privacy_config()
|
||||||
|
|
||||||
|
def _load_privacy_config(self):
|
||||||
|
"""加载隐私协议配置
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 用户是否已同意隐私协议
|
||||||
|
"""
|
||||||
|
if os.path.exists(self.config_file):
|
||||||
|
try:
|
||||||
|
with open(self.config_file, "r", encoding="utf-8") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
return config.get("privacy_accepted", False)
|
||||||
|
except (json.JSONDecodeError, IOError) as e:
|
||||||
|
print(f"读取隐私配置失败: {e}")
|
||||||
|
# 如果读取失败,返回False,强制显示隐私协议
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _save_privacy_config(self, accepted):
|
||||||
|
"""保存隐私协议配置
|
||||||
|
|
||||||
|
Args:
|
||||||
|
accepted: 用户是否同意隐私协议
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 配置是否保存成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 确保目录存在
|
||||||
|
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
||||||
|
|
||||||
|
# 写入配置文件
|
||||||
|
with open(self.config_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({
|
||||||
|
"privacy_accepted": accepted,
|
||||||
|
"version": "1.0" # 添加版本号,便于将来升级隐私协议时使用
|
||||||
|
}, f, indent=2)
|
||||||
|
|
||||||
|
# 更新实例变量
|
||||||
|
self.privacy_accepted = accepted
|
||||||
|
return True
|
||||||
|
except IOError as e:
|
||||||
|
print(f"保存隐私协议配置失败: {e}")
|
||||||
|
# 显示保存失败的提示
|
||||||
|
QMessageBox.warning(
|
||||||
|
None,
|
||||||
|
f"配置保存警告 - {APP_NAME}",
|
||||||
|
f"隐私设置无法保存到配置文件,下次启动时可能需要重新确认。\n\n错误信息:{e}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def show_privacy_dialog(self):
|
||||||
|
"""显示隐私协议对话框
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 用户是否同意隐私协议
|
||||||
|
"""
|
||||||
|
# 如果用户已经同意了隐私协议,直接返回True不显示对话框
|
||||||
|
if self.privacy_accepted:
|
||||||
|
print("用户已同意隐私协议,无需再次显示")
|
||||||
|
return True
|
||||||
|
|
||||||
|
print("首次运行或用户未同意隐私协议,显示隐私对话框")
|
||||||
|
|
||||||
|
# 创建隐私协议对话框
|
||||||
|
dialog = QDialog()
|
||||||
|
dialog.setWindowTitle(f"隐私政策 - {APP_NAME}")
|
||||||
|
dialog.setMinimumSize(600, 400)
|
||||||
|
dialog.setWindowFlags(dialog.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||||
|
|
||||||
|
# 创建布局
|
||||||
|
layout = QVBoxLayout(dialog)
|
||||||
|
|
||||||
|
# 添加标题
|
||||||
|
title_label = QLabel("请阅读并同意以下隐私政策")
|
||||||
|
title_label.setStyleSheet("font-size: 14px; font-weight: bold;")
|
||||||
|
layout.addWidget(title_label)
|
||||||
|
|
||||||
|
# 添加隐私协议文本框
|
||||||
|
text_browser = QTextBrowser()
|
||||||
|
text_browser.setMarkdown(PRIVACY_POLICY_BRIEF)
|
||||||
|
text_browser.setOpenExternalLinks(True)
|
||||||
|
layout.addWidget(text_browser)
|
||||||
|
|
||||||
|
# 添加同意选择框
|
||||||
|
checkbox = QCheckBox("我已阅读并同意上述隐私政策")
|
||||||
|
layout.addWidget(checkbox)
|
||||||
|
|
||||||
|
# 添加按钮
|
||||||
|
buttons_layout = QHBoxLayout()
|
||||||
|
agree_button = QPushButton("同意并继续")
|
||||||
|
agree_button.setEnabled(False) # 初始状态为禁用
|
||||||
|
decline_button = QPushButton("不同意并退出")
|
||||||
|
buttons_layout.addWidget(agree_button)
|
||||||
|
buttons_layout.addWidget(decline_button)
|
||||||
|
layout.addLayout(buttons_layout)
|
||||||
|
|
||||||
|
# 连接选择框状态变化 - 修复勾选后按钮不亮起的问题
|
||||||
|
def on_checkbox_state_changed(state):
|
||||||
|
print(f"复选框状态变更为: {state}")
|
||||||
|
agree_button.setEnabled(state == 2) # Qt.Checked 在 PySide6 中值为 2
|
||||||
|
|
||||||
|
checkbox.stateChanged.connect(on_checkbox_state_changed)
|
||||||
|
|
||||||
|
# 连接按钮点击事件
|
||||||
|
agree_button.clicked.connect(lambda: self._on_agree(dialog))
|
||||||
|
decline_button.clicked.connect(lambda: self._on_decline(dialog))
|
||||||
|
|
||||||
|
# 显示对话框
|
||||||
|
result = dialog.exec()
|
||||||
|
|
||||||
|
# 返回用户选择结果
|
||||||
|
return self.privacy_accepted
|
||||||
|
|
||||||
|
def _on_agree(self, dialog):
|
||||||
|
"""处理用户同意隐私协议
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dialog: 对话框实例
|
||||||
|
"""
|
||||||
|
# 保存配置并更新状态
|
||||||
|
self._save_privacy_config(True)
|
||||||
|
dialog.accept()
|
||||||
|
|
||||||
|
def _on_decline(self, dialog):
|
||||||
|
"""处理用户拒绝隐私协议
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dialog: 对话框实例
|
||||||
|
"""
|
||||||
|
# 显示拒绝信息
|
||||||
|
msg_box = msgbox_frame(
|
||||||
|
f"退出 - {APP_NAME}",
|
||||||
|
"\n您需要同意隐私政策才能使用本软件。\n软件将立即退出。\n",
|
||||||
|
QMessageBox.Ok,
|
||||||
|
)
|
||||||
|
msg_box.exec()
|
||||||
|
|
||||||
|
# 保存拒绝状态
|
||||||
|
self._save_privacy_config(False)
|
||||||
|
dialog.reject()
|
||||||
|
|
||||||
|
def is_privacy_accepted(self):
|
||||||
|
"""检查用户是否已同意隐私协议
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 用户是否已同意隐私协议
|
||||||
|
"""
|
||||||
|
return self.privacy_accepted
|
||||||
|
|
||||||
|
def reset_privacy_agreement(self):
|
||||||
|
"""重置隐私协议同意状态,用于测试或重新显示隐私协议
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 重置是否成功
|
||||||
|
"""
|
||||||
|
return self._save_privacy_config(False)
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
from PySide6.QtGui import QIcon, QAction, QFont
|
from PySide6.QtGui import QIcon, QAction, QFont, QCursor
|
||||||
from PySide6.QtWidgets import QMessageBox, QMainWindow, QMenu
|
from PySide6.QtWidgets import QMessageBox, QMainWindow, QMenu, QPushButton
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt, QRect
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
import os
|
||||||
|
|
||||||
from utils import load_base64_image, msgbox_frame
|
from utils import load_base64_image, msgbox_frame, resource_path
|
||||||
from data.config import APP_NAME, APP_VERSION
|
from data.config import APP_NAME, APP_VERSION
|
||||||
|
|
||||||
class UIManager:
|
class UIManager:
|
||||||
@@ -19,6 +20,9 @@ class UIManager:
|
|||||||
self.debug_action = None
|
self.debug_action = None
|
||||||
self.turbo_download_action = None
|
self.turbo_download_action = None
|
||||||
self.dev_menu = None
|
self.dev_menu = None
|
||||||
|
self.privacy_menu = None # 隐私协议菜单
|
||||||
|
self.about_menu = None # 关于菜单
|
||||||
|
self.about_btn = None # 关于按钮
|
||||||
|
|
||||||
def setup_ui(self):
|
def setup_ui(self):
|
||||||
"""设置UI元素,包括窗口图标、标题和菜单"""
|
"""设置UI元素,包括窗口图标、标题和菜单"""
|
||||||
@@ -32,76 +36,161 @@ class UIManager:
|
|||||||
# 设置窗口标题
|
# 设置窗口标题
|
||||||
self.main_window.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
|
self.main_window.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
|
||||||
|
|
||||||
|
# 创建关于按钮
|
||||||
|
self._create_about_button()
|
||||||
|
|
||||||
# 设置菜单
|
# 设置菜单
|
||||||
self._setup_help_menu()
|
self._setup_help_menu()
|
||||||
|
self._setup_about_menu() # 新增关于菜单
|
||||||
self._setup_settings_menu()
|
self._setup_settings_menu()
|
||||||
|
|
||||||
|
def _create_about_button(self):
|
||||||
|
"""创建"关于"按钮"""
|
||||||
|
if not self.ui or not hasattr(self.ui, 'menu_area'):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取菜单字体和样式
|
||||||
|
menu_font = self._get_menu_font()
|
||||||
|
|
||||||
|
# 创建关于按钮
|
||||||
|
self.about_btn = QPushButton("关于", self.ui.menu_area)
|
||||||
|
self.about_btn.setObjectName(u"about_btn")
|
||||||
|
|
||||||
|
# 获取帮助按钮的位置和样式
|
||||||
|
help_btn_x = 0
|
||||||
|
help_btn_width = 0
|
||||||
|
if hasattr(self.ui, 'help_btn'):
|
||||||
|
help_btn_x = self.ui.help_btn.x()
|
||||||
|
help_btn_width = self.ui.help_btn.width()
|
||||||
|
|
||||||
|
# 设置位置在"帮助"按钮右侧
|
||||||
|
self.about_btn.setGeometry(QRect(help_btn_x + help_btn_width + 20, 1, 80, 28))
|
||||||
|
self.about_btn.setFont(menu_font)
|
||||||
|
self.about_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
||||||
|
|
||||||
|
# 复制帮助按钮的样式
|
||||||
|
if hasattr(self.ui, 'help_btn'):
|
||||||
|
self.about_btn.setStyleSheet(self.ui.help_btn.styleSheet())
|
||||||
|
else:
|
||||||
|
# 默认样式
|
||||||
|
self.about_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;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
def _setup_help_menu(self):
|
def _setup_help_menu(self):
|
||||||
"""设置"帮助"菜单"""
|
"""设置"帮助"菜单"""
|
||||||
if not self.ui or not hasattr(self.ui, 'menu_2'):
|
if not self.ui or not hasattr(self.ui, 'menu_2'):
|
||||||
return
|
return
|
||||||
|
|
||||||
# 创建菜单项
|
# 获取菜单字体
|
||||||
project_home_action = QAction("项目主页", self.main_window)
|
menu_font = self._get_menu_font()
|
||||||
project_home_action.triggered.connect(self.open_project_home_page)
|
|
||||||
|
|
||||||
about_action = QAction("关于", self.main_window)
|
# 创建菜单项 - 移除"项目主页",添加"常见问题"和"提交错误"
|
||||||
about_action.triggered.connect(self.show_about_dialog)
|
faq_action = QAction("常见问题", self.main_window)
|
||||||
|
faq_action.triggered.connect(self.open_faq_page)
|
||||||
|
faq_action.setFont(menu_font)
|
||||||
|
|
||||||
# 添加到菜单
|
report_issue_action = QAction("提交错误", self.main_window)
|
||||||
self.ui.menu_2.addAction(project_home_action)
|
report_issue_action.triggered.connect(self.open_issues_page)
|
||||||
self.ui.menu_2.addAction(about_action)
|
report_issue_action.setFont(menu_font)
|
||||||
|
|
||||||
|
# 清除现有菜单项并添加新的菜单项
|
||||||
|
self.ui.menu_2.clear()
|
||||||
|
self.ui.menu_2.addAction(faq_action)
|
||||||
|
self.ui.menu_2.addAction(report_issue_action)
|
||||||
|
|
||||||
# 连接按钮点击事件,如果使用按钮式菜单
|
# 连接按钮点击事件,如果使用按钮式菜单
|
||||||
if hasattr(self.ui, 'help_btn'):
|
if hasattr(self.ui, 'help_btn'):
|
||||||
# 按钮已经连接到显示菜单,不需要额外处理
|
# 按钮已经连接到显示菜单,不需要额外处理
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _setup_settings_menu(self):
|
def _setup_about_menu(self):
|
||||||
"""设置"设置"菜单"""
|
"""设置"关于"菜单"""
|
||||||
if not self.ui or not hasattr(self.ui, 'menu'):
|
# 获取菜单字体
|
||||||
return
|
menu_font = self._get_menu_font()
|
||||||
|
|
||||||
# 获取自定义字体和字体族名称
|
# 创建关于菜单
|
||||||
font_family = "Arial" # 默认字体族
|
self.about_menu = QMenu("关于", self.main_window)
|
||||||
menu_font = None
|
self.about_menu.setFont(menu_font)
|
||||||
|
|
||||||
# 尝试从UI中获取字体和字体族
|
# 设置菜单样式
|
||||||
try:
|
font_family = menu_font.family()
|
||||||
# 优先从Ui_install.py中获取font_family变量
|
menu_style = self._get_menu_style(font_family)
|
||||||
import os
|
self.about_menu.setStyleSheet(menu_style)
|
||||||
from PySide6.QtGui import QFontDatabase
|
|
||||||
|
|
||||||
# 尝试直接加载字体并获取字体族
|
# 创建菜单项
|
||||||
font_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "fonts", "SmileySans-Oblique.ttf")
|
about_project_action = QAction("关于本项目", self.main_window)
|
||||||
if os.path.exists(font_path):
|
about_project_action.setFont(menu_font)
|
||||||
font_id = QFontDatabase.addApplicationFont(font_path)
|
about_project_action.triggered.connect(self.show_about_dialog)
|
||||||
if font_id != -1:
|
|
||||||
font_family = QFontDatabase.applicationFontFamilies(font_id)[0]
|
|
||||||
# 创建与UI_install.py中完全相同的菜单字体
|
|
||||||
menu_font = QFont(font_family, 14) # 字体大小为14
|
|
||||||
menu_font.setBold(True) # 设置粗体
|
|
||||||
|
|
||||||
# 如果以上方法失败,则尝试从ui获取字体
|
# 添加项目主页选项(从帮助菜单移动过来)
|
||||||
if not menu_font and hasattr(self.ui, 'menu') and self.ui.menu:
|
project_home_action = QAction("Github项目主页", self.main_window)
|
||||||
menu_font = self.ui.menu.font()
|
project_home_action.setFont(menu_font)
|
||||||
|
project_home_action.triggered.connect(self.open_project_home_page)
|
||||||
|
|
||||||
# 如果仍然没有获取到,使用默认字体
|
# 添加加入QQ群选项
|
||||||
if not menu_font:
|
qq_group_action = QAction("加入QQ群", self.main_window)
|
||||||
menu_font = QFont(font_family, 14)
|
qq_group_action.setFont(menu_font)
|
||||||
menu_font.setBold(True)
|
qq_group_action.triggered.connect(self.open_qq_group)
|
||||||
|
|
||||||
except:
|
# 创建隐私协议菜单
|
||||||
# 如果出错,使用默认字体
|
self._setup_privacy_menu()
|
||||||
menu_font = QFont(font_family, 14)
|
|
||||||
menu_font.setBold(True)
|
|
||||||
|
|
||||||
# 创建开发者选项子菜单
|
# 添加到关于菜单
|
||||||
self.dev_menu = QMenu("开发者选项", self.main_window)
|
self.about_menu.addAction(about_project_action)
|
||||||
self.dev_menu.setFont(menu_font) # 设置与UI_install.py中相同的字体
|
self.about_menu.addAction(project_home_action)
|
||||||
|
self.about_menu.addAction(qq_group_action)
|
||||||
|
self.about_menu.addSeparator()
|
||||||
|
self.about_menu.addMenu(self.privacy_menu)
|
||||||
|
|
||||||
# 使用和主菜单相同的样式,直接指定字体族、字体大小和粗细
|
# 连接按钮点击事件
|
||||||
menu_style = f"""
|
if self.about_btn:
|
||||||
|
self.about_btn.clicked.connect(lambda: self.show_menu(self.about_menu, self.about_btn))
|
||||||
|
|
||||||
|
def _setup_privacy_menu(self):
|
||||||
|
"""设置"隐私协议"菜单"""
|
||||||
|
# 获取菜单字体
|
||||||
|
menu_font = self._get_menu_font()
|
||||||
|
|
||||||
|
# 创建隐私协议子菜单
|
||||||
|
self.privacy_menu = QMenu("隐私协议", self.main_window)
|
||||||
|
self.privacy_menu.setFont(menu_font)
|
||||||
|
|
||||||
|
# 设置与其他菜单一致的样式
|
||||||
|
font_family = menu_font.family()
|
||||||
|
menu_style = self._get_menu_style(font_family)
|
||||||
|
self.privacy_menu.setStyleSheet(menu_style)
|
||||||
|
|
||||||
|
# 添加子选项
|
||||||
|
view_privacy_action = QAction("查看完整隐私协议", self.main_window)
|
||||||
|
view_privacy_action.setFont(menu_font)
|
||||||
|
view_privacy_action.triggered.connect(self.open_privacy_policy)
|
||||||
|
|
||||||
|
revoke_privacy_action = QAction("撤回隐私协议", self.main_window)
|
||||||
|
revoke_privacy_action.setFont(menu_font)
|
||||||
|
revoke_privacy_action.triggered.connect(self.revoke_privacy_agreement)
|
||||||
|
|
||||||
|
# 添加到子菜单
|
||||||
|
self.privacy_menu.addAction(view_privacy_action)
|
||||||
|
self.privacy_menu.addAction(revoke_privacy_action)
|
||||||
|
|
||||||
|
def _get_menu_style(self, font_family):
|
||||||
|
"""获取统一的菜单样式"""
|
||||||
|
return f"""
|
||||||
QMenu {{
|
QMenu {{
|
||||||
background-color: #E96948;
|
background-color: #E96948;
|
||||||
color: white;
|
color: white;
|
||||||
@@ -136,6 +225,48 @@ class UIManager:
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _get_menu_font(self):
|
||||||
|
"""获取菜单字体"""
|
||||||
|
font_family = "Arial" # 默认字体族
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PySide6.QtGui import QFontDatabase
|
||||||
|
|
||||||
|
# 尝试加载字体
|
||||||
|
font_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "fonts", "SmileySans-Oblique.ttf")
|
||||||
|
if os.path.exists(font_path):
|
||||||
|
font_id = QFontDatabase.addApplicationFont(font_path)
|
||||||
|
if font_id != -1:
|
||||||
|
font_family = QFontDatabase.applicationFontFamilies(font_id)[0]
|
||||||
|
|
||||||
|
# 创建菜单字体
|
||||||
|
menu_font = QFont(font_family, 14)
|
||||||
|
menu_font.setBold(True)
|
||||||
|
return menu_font
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"加载字体失败: {e}")
|
||||||
|
# 返回默认字体
|
||||||
|
menu_font = QFont(font_family, 14)
|
||||||
|
menu_font.setBold(True)
|
||||||
|
return menu_font
|
||||||
|
|
||||||
|
def _setup_settings_menu(self):
|
||||||
|
"""设置"设置"菜单"""
|
||||||
|
if not self.ui or not hasattr(self.ui, 'menu'):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取菜单字体
|
||||||
|
menu_font = self._get_menu_font()
|
||||||
|
font_family = menu_font.family()
|
||||||
|
|
||||||
|
# 创建开发者选项子菜单
|
||||||
|
self.dev_menu = QMenu("开发者选项", self.main_window)
|
||||||
|
self.dev_menu.setFont(menu_font) # 设置与UI_install.py中相同的字体
|
||||||
|
|
||||||
|
# 使用和主菜单相同的样式
|
||||||
|
menu_style = self._get_menu_style(font_family)
|
||||||
self.dev_menu.setStyleSheet(menu_style)
|
self.dev_menu.setStyleSheet(menu_style)
|
||||||
|
|
||||||
# 创建Debug模式选项并添加到开发者选项子菜单中
|
# 创建Debug模式选项并添加到开发者选项子菜单中
|
||||||
@@ -173,21 +304,118 @@ class UIManager:
|
|||||||
self.ui.menu.addSeparator()
|
self.ui.menu.addSeparator()
|
||||||
self.ui.menu.addMenu(self.dev_menu) # 添加开发者选项子菜单
|
self.ui.menu.addMenu(self.dev_menu) # 添加开发者选项子菜单
|
||||||
|
|
||||||
# 连接按钮点击事件,如果使用按钮式菜单
|
def show_menu(self, menu, button):
|
||||||
if hasattr(self.ui, 'settings_btn'):
|
"""显示菜单
|
||||||
# 按钮已经连接到显示菜单,不需要额外处理
|
|
||||||
pass
|
Args:
|
||||||
|
menu: 要显示的菜单
|
||||||
|
button: 触发菜单的按钮
|
||||||
|
"""
|
||||||
|
# 检查Ui_install中是否定义了show_menu方法
|
||||||
|
if hasattr(self.ui, 'show_menu'):
|
||||||
|
# 如果存在,使用UI中定义的方法
|
||||||
|
self.ui.show_menu(menu, button)
|
||||||
|
else:
|
||||||
|
# 否则,使用默认的弹出方法
|
||||||
|
global_pos = button.mapToGlobal(button.rect().bottomLeft())
|
||||||
|
menu.popup(global_pos)
|
||||||
|
|
||||||
def open_project_home_page(self):
|
def open_project_home_page(self):
|
||||||
"""打开项目主页"""
|
"""打开项目主页"""
|
||||||
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")
|
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")
|
||||||
|
|
||||||
|
def open_github_page(self):
|
||||||
|
"""打开项目GitHub页面"""
|
||||||
|
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")
|
||||||
|
|
||||||
|
def open_faq_page(self):
|
||||||
|
"""打开常见问题页面"""
|
||||||
|
import locale
|
||||||
|
# 根据系统语言选择FAQ页面
|
||||||
|
system_lang = locale.getdefaultlocale()[0]
|
||||||
|
if system_lang and system_lang.startswith('zh'):
|
||||||
|
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT/blob/master/FAQ.md")
|
||||||
|
else:
|
||||||
|
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT/blob/master/FAQ-en.md")
|
||||||
|
|
||||||
|
def open_issues_page(self):
|
||||||
|
"""打开GitHub问题页面"""
|
||||||
|
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT/issues")
|
||||||
|
|
||||||
|
def open_qq_group(self):
|
||||||
|
"""打开QQ群链接"""
|
||||||
|
webbrowser.open("https://qm.qq.com/q/g9i04i5eec")
|
||||||
|
|
||||||
|
def open_privacy_policy(self):
|
||||||
|
"""打开完整隐私协议(在GitHub上)"""
|
||||||
|
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT/blob/master/PRIVACY.md")
|
||||||
|
|
||||||
|
def revoke_privacy_agreement(self):
|
||||||
|
"""撤回隐私协议同意,并重启软件"""
|
||||||
|
# 创建确认对话框
|
||||||
|
msg_box = msgbox_frame(
|
||||||
|
f"确认操作 - {APP_NAME}",
|
||||||
|
"\n您确定要撤回隐私协议同意吗?\n\n撤回后软件将立即重启,您需要重新阅读并同意隐私协议。\n",
|
||||||
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||||
|
)
|
||||||
|
|
||||||
|
reply = msg_box.exec()
|
||||||
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
|
# 用户确认撤回
|
||||||
|
try:
|
||||||
|
# 导入隐私管理器
|
||||||
|
from core.privacy_manager import PrivacyManager
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 创建实例并重置隐私协议同意
|
||||||
|
privacy_manager = PrivacyManager()
|
||||||
|
if privacy_manager.reset_privacy_agreement():
|
||||||
|
# 显示重启提示
|
||||||
|
restart_msg = msgbox_frame(
|
||||||
|
f"操作成功 - {APP_NAME}",
|
||||||
|
"\n已成功撤回隐私协议同意。\n\n软件将立即重启。\n",
|
||||||
|
QMessageBox.StandardButton.Ok,
|
||||||
|
)
|
||||||
|
restart_msg.exec()
|
||||||
|
|
||||||
|
# 获取当前执行的Python解释器路径和脚本路径
|
||||||
|
python_executable = sys.executable
|
||||||
|
script_path = os.path.abspath(sys.argv[0])
|
||||||
|
|
||||||
|
# 构建重启命令
|
||||||
|
restart_cmd = [python_executable, script_path]
|
||||||
|
|
||||||
|
# 启动新进程
|
||||||
|
subprocess.Popen(restart_cmd)
|
||||||
|
|
||||||
|
# 退出当前进程
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
# 显示失败提示
|
||||||
|
fail_msg = msgbox_frame(
|
||||||
|
f"操作失败 - {APP_NAME}",
|
||||||
|
"\n撤回隐私协议同意失败。\n\n请检查应用权限或稍后再试。\n",
|
||||||
|
QMessageBox.StandardButton.Ok,
|
||||||
|
)
|
||||||
|
fail_msg.exec()
|
||||||
|
except Exception as e:
|
||||||
|
# 显示错误提示
|
||||||
|
error_msg = msgbox_frame(
|
||||||
|
f"错误 - {APP_NAME}",
|
||||||
|
f"\n撤回隐私协议同意时发生错误:\n\n{str(e)}\n",
|
||||||
|
QMessageBox.StandardButton.Ok,
|
||||||
|
)
|
||||||
|
error_msg.exec()
|
||||||
|
|
||||||
def show_about_dialog(self):
|
def show_about_dialog(self):
|
||||||
"""显示关于对话框"""
|
"""显示关于对话框"""
|
||||||
about_text = f"""
|
about_text = f"""
|
||||||
<p><b>{APP_NAME} v{APP_VERSION}</b></p>
|
<p><b>{APP_NAME} v{APP_VERSION}</b></p>
|
||||||
|
<p>GitHub: <a href="https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT">https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT</a></p>
|
||||||
<p>原作: <a href="https://github.com/Yanam1Anna">Yanam1Anna</a></p>
|
<p>原作: <a href="https://github.com/Yanam1Anna">Yanam1Anna</a></p>
|
||||||
<p>此应用根据 <a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE">GPL-3.0 许可证</a> 授权。</p>
|
<p>此应用根据 <a href="https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT/blob/master/LICENSE">GPL-3.0 许可证</a> 授权。</p>
|
||||||
<br>
|
<br>
|
||||||
<p><b>感谢:</b></p>
|
<p><b>感谢:</b></p>
|
||||||
<p>- <a href="https://github.com/HTony03">HTony03</a>:对原项目部分源码的重构、逻辑优化和功能实现提供了支持。</p>
|
<p>- <a href="https://github.com/HTony03">HTony03</a>:对原项目部分源码的重构、逻辑优化和功能实现提供了支持。</p>
|
||||||
|
|||||||
46
source/data/privacy_policy.py
Normal file
46
source/data/privacy_policy.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 隐私协议的缩略版内容
|
||||||
|
PRIVACY_POLICY_BRIEF = """
|
||||||
|
# FRAISEMOE2-Installer 隐私政策摘要
|
||||||
|
|
||||||
|
本应用在运行过程中会收集和处理以下信息:
|
||||||
|
|
||||||
|
## 收集的信息
|
||||||
|
- 系统信息:操作系统版本、程序版本号
|
||||||
|
- 网络信息:IP 地址(Cloudflare 加速时)、域名解析、下载统计
|
||||||
|
- 文件信息:游戏安装路径、文件哈希值
|
||||||
|
|
||||||
|
## 系统修改
|
||||||
|
- 使用 Cloudflare 加速时会临时修改系统 hosts 文件
|
||||||
|
- 修改前会自动备份,程序退出时自动恢复
|
||||||
|
|
||||||
|
## 第三方服务
|
||||||
|
- Cloudflare 服务:用于测试和优化下载速度
|
||||||
|
- 云端配置服务:获取配置信息和下载链接
|
||||||
|
|
||||||
|
完整的隐私政策可在程序中访问github仓库查看。
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 隐私协议的英文版缩略版内容
|
||||||
|
PRIVACY_POLICY_BRIEF_EN = """
|
||||||
|
# FRAISEMOE2-Installer Privacy Policy Summary
|
||||||
|
|
||||||
|
This application collects and processes the following information:
|
||||||
|
|
||||||
|
## Information Collected
|
||||||
|
- System info: OS version, application version
|
||||||
|
- Network info: IP address (for Cloudflare acceleration), DNS resolution, download statistics
|
||||||
|
- File info: Game installation paths, file hash values
|
||||||
|
|
||||||
|
## System Modifications
|
||||||
|
- Temporarily modifies system hosts file when using Cloudflare acceleration
|
||||||
|
- Automatically backs up before modification and restores upon exit
|
||||||
|
|
||||||
|
## Third-party Services
|
||||||
|
- Cloudflare services: Used for testing and optimizing download speeds
|
||||||
|
- Cloud configuration services: For obtaining configuration information and download links
|
||||||
|
|
||||||
|
The complete privacy policy can be found in the github repository of the program.
|
||||||
|
"""
|
||||||
Reference in New Issue
Block a user