feat(app_detail_window): 改进应用安装流程并添加错误处理
添加统一的错误弹窗函数,改进下载URL处理逻辑 使用本地install目录保存安装文件,添加下载完成后自动打开文件夹功能 替换InfoBar为QMessageBox实现更美观的弹窗提示
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ logs/
|
|||||||
|
|
||||||
/pyqt5fluentdesign/output/
|
/pyqt5fluentdesign/output/
|
||||||
/pyqt5fluentdesign/logs/
|
/pyqt5fluentdesign/logs/
|
||||||
|
/pyqt5fluentdesign/install/
|
||||||
|
|
||||||
# 忽略Windows系统文件
|
# 忽略Windows系统文件
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|||||||
Binary file not shown.
BIN
pyqt5fluentdesign/__pycache__/leonapp_gui.cpython-312.pyc
Normal file
BIN
pyqt5fluentdesign/__pycache__/leonapp_gui.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
# 这破代码有人能修复吗
|
# 这破代码有人能修复吗
|
||||||
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QPushButton, QFrame, QLabel, QGridLayout)
|
QPushButton, QFrame, QLabel, QGridLayout, QMessageBox)
|
||||||
from PyQt5.QtCore import Qt, QSize, pyqtSignal, QThread
|
from PyQt5.QtCore import Qt, QSize, pyqtSignal, QThread
|
||||||
from PyQt5.QtGui import QPixmap, QFont
|
from PyQt5.QtGui import QPixmap, QFont
|
||||||
from qfluentwidgets import (InfoBar, InfoBarPosition, TitleLabel, SubtitleLabel,
|
from qfluentwidgets import (InfoBar, InfoBarPosition, TitleLabel, SubtitleLabel,
|
||||||
@@ -14,6 +14,25 @@ import tempfile
|
|||||||
import subprocess
|
import subprocess
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
def show_error_dialog(title, message):
|
||||||
|
"""显示错误弹窗,模拟Sweet Alert风格"""
|
||||||
|
# 创建错误消息框
|
||||||
|
msg_box = QMessageBox()
|
||||||
|
msg_box.setIcon(QMessageBox.Critical)
|
||||||
|
msg_box.setWindowTitle(title)
|
||||||
|
|
||||||
|
# 设置字体大小
|
||||||
|
font = QFont()
|
||||||
|
font.setPointSize(10)
|
||||||
|
msg_box.setFont(font)
|
||||||
|
|
||||||
|
# 设置消息内容
|
||||||
|
msg_box.setText(message)
|
||||||
|
msg_box.setStandardButtons(QMessageBox.Ok)
|
||||||
|
|
||||||
|
# 显示弹窗
|
||||||
|
msg_box.exec_()
|
||||||
|
|
||||||
class DownloadThread(QThread):
|
class DownloadThread(QThread):
|
||||||
"""下载线程,用于在后台下载文件"""
|
"""下载线程,用于在后台下载文件"""
|
||||||
progress = pyqtSignal(int)
|
progress = pyqtSignal(int)
|
||||||
@@ -652,34 +671,44 @@ class AppDetailWindow(QMainWindow):
|
|||||||
|
|
||||||
# 检查是否有应用数据
|
# 检查是否有应用数据
|
||||||
if not hasattr(self, 'app_data'):
|
if not hasattr(self, 'app_data'):
|
||||||
InfoBar.error(
|
show_error_dialog("错误", "应用数据未加载,请刷新页面后重试。")
|
||||||
title="错误",
|
|
||||||
content="应用数据未加载,请刷新页面后重试。",
|
|
||||||
orient=Qt.Horizontal,
|
|
||||||
isClosable=True,
|
|
||||||
position=InfoBarPosition.TOP,
|
|
||||||
parent=self
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# 创建确认对话框
|
# 创建Sweet Alert风格的确认对话框
|
||||||
reply = QMessageBox.question(self,
|
confirm_box = QMessageBox()
|
||||||
"确认安装",
|
confirm_box.setWindowTitle("确认安装")
|
||||||
f"您确定要安装{self.app_data.get('name', '未知应用')}吗?\n\n点击'确定'开始安装。",
|
confirm_box.setIcon(QMessageBox.Question)
|
||||||
QMessageBox.Yes | QMessageBox.No,
|
confirm_box.setText(f"您确定要安装{self.app_data.get('name', '未知应用')}吗?\n\n点击'确定'开始安装。")
|
||||||
QMessageBox.No)
|
|
||||||
|
# 设置字体大小
|
||||||
|
font = QFont()
|
||||||
|
font.setPointSize(10)
|
||||||
|
confirm_box.setFont(font)
|
||||||
|
|
||||||
|
# 添加按钮
|
||||||
|
confirm_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
||||||
|
confirm_box.setDefaultButton(QMessageBox.No)
|
||||||
|
|
||||||
|
# 显示对话框
|
||||||
|
reply = confirm_box.exec_()
|
||||||
|
|
||||||
if reply == QMessageBox.Yes:
|
if reply == QMessageBox.Yes:
|
||||||
# 显示开始安装的提示
|
# 创建Sweet Alert风格的成功提示
|
||||||
InfoBar.success(
|
success_box = QMessageBox()
|
||||||
title="安装开始",
|
success_box.setWindowTitle("安装开始")
|
||||||
content=f"{self.app_data.get('name', '未知应用')}安装已开始,请稍候...",
|
success_box.setIcon(QMessageBox.Information)
|
||||||
orient=Qt.Horizontal,
|
success_box.setText(f"{self.app_data.get('name', '未知应用')}安装已开始,请稍候...")
|
||||||
isClosable=True,
|
|
||||||
position=InfoBarPosition.TOP,
|
# 设置字体大小
|
||||||
duration=3000,
|
font = QFont()
|
||||||
parent=self
|
font.setPointSize(10)
|
||||||
)
|
success_box.setFont(font)
|
||||||
|
|
||||||
|
# 添加按钮
|
||||||
|
success_box.setStandardButtons(QMessageBox.Ok)
|
||||||
|
|
||||||
|
# 显示对话框
|
||||||
|
success_box.exec_()
|
||||||
|
|
||||||
# 1. 首先尝试从应用数据中获取直接下载链接
|
# 1. 首先尝试从应用数据中获取直接下载链接
|
||||||
download_url = self.app_data.get('direct_download_url')
|
download_url = self.app_data.get('direct_download_url')
|
||||||
@@ -688,30 +717,75 @@ class AppDetailWindow(QMainWindow):
|
|||||||
if not download_url:
|
if not download_url:
|
||||||
download_url = self.app_data.get('download_url')
|
download_url = self.app_data.get('download_url')
|
||||||
|
|
||||||
# 3. 如果仍然没有下载链接,尝试通过API获取下载链接
|
# 3. 根据新API格式,从versions数组中获取最新版本的file_path
|
||||||
|
if not download_url and 'versions' in self.app_data and isinstance(self.app_data['versions'], list) and self.app_data['versions']:
|
||||||
|
# 假设versions数组中第一个版本是最新的
|
||||||
|
latest_version = self.app_data['versions'][0]
|
||||||
|
if isinstance(latest_version, dict) and 'file_path' in latest_version:
|
||||||
|
download_url = latest_version['file_path']
|
||||||
|
|
||||||
|
# 4. 如果仍然没有下载链接,尝试通过API获取下载链接
|
||||||
if not download_url:
|
if not download_url:
|
||||||
try:
|
try:
|
||||||
# 使用app_id通过API获取下载链接
|
# 使用app_id通过API获取下载链接
|
||||||
app_id = self.app_data.get('id')
|
app_id = self.app_data.get('id')
|
||||||
if app_id:
|
if app_id:
|
||||||
download_result = self.api_client.make_request('getdownloadlink', {'id': app_id})
|
download_result = self.api_client.make_request('getdownloadlink', {'id': app_id})
|
||||||
if isinstance(download_result, dict) and 'success' in download_result and download_result['success']:
|
if isinstance(download_result, dict):
|
||||||
|
# 检查是否是新API格式的响应
|
||||||
|
if 'data' in download_result:
|
||||||
|
if isinstance(download_result['data'], dict) and 'versions' in download_result['data'] and download_result['data']['versions']:
|
||||||
|
latest_version = download_result['data']['versions'][0]
|
||||||
|
if isinstance(latest_version, dict) and 'file_path' in latest_version:
|
||||||
|
download_url = latest_version['file_path']
|
||||||
|
# 兼容旧API格式
|
||||||
|
elif 'success' in download_result and download_result['success']:
|
||||||
download_url = download_result.get('data')
|
download_url = download_result.get('data')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"获取下载链接API调用失败: {str(e)}")
|
print(f"获取下载链接API调用失败: {str(e)}")
|
||||||
|
|
||||||
# 4. 如果仍然没有下载链接,显示错误信息
|
# 4. 如果仍然没有下载链接,显示错误信息
|
||||||
if not download_url:
|
if not download_url:
|
||||||
InfoBar.error(
|
show_error_dialog("错误", "无法获取应用的下载链接,请稍后重试。\n可能原因:API返回数据中缺少下载链接字段。")
|
||||||
title="错误",
|
|
||||||
content="无法获取应用的下载链接,请稍后重试。\n可能原因:API返回数据中缺少下载链接字段。",
|
|
||||||
orient=Qt.Horizontal,
|
|
||||||
isClosable=True,
|
|
||||||
position=InfoBarPosition.TOP,
|
|
||||||
parent=self
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 5. 更健壮的URL处理逻辑
|
||||||
|
from urllib.parse import urlparse, quote
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 检查是否看起来像一个文件名(包含扩展名)
|
||||||
|
is_filename = False
|
||||||
|
if '.' in download_url:
|
||||||
|
# 获取最后一个点后的部分作为扩展名
|
||||||
|
extension = download_url.split('.')[-1].lower()
|
||||||
|
# 常见的应用安装包扩展名
|
||||||
|
common_extensions = ['exe', 'apk', 'dmg', 'pkg', 'msi', 'appx', 'deb', 'rpm']
|
||||||
|
if extension in common_extensions and len(extension) <= 5:
|
||||||
|
is_filename = True
|
||||||
|
|
||||||
|
# 如果是文件名或不包含协议,处理为完整URL
|
||||||
|
if is_filename or not download_url.startswith(('http://', 'https://')):
|
||||||
|
# 使用基础域名
|
||||||
|
base_domain = 'leonmmcoset.jjxmm.win:8010'
|
||||||
|
|
||||||
|
# 对于所有情况,统一使用直链形式,不再使用download.php
|
||||||
|
# 对文件名/路径进行URL编码,处理空格和特殊字符
|
||||||
|
if not download_url.startswith(('http://', 'https://')):
|
||||||
|
# 使用http协议和基础域名,直接拼接文件名或路径
|
||||||
|
encoded_path = quote(download_url)
|
||||||
|
download_url = f'http://leonmmcoset.jjxmm.win:8010/{encoded_path.lstrip("/")}'
|
||||||
|
|
||||||
|
# 额外检查:确保主机名有效(包含点)
|
||||||
|
parsed_url = urlparse(download_url)
|
||||||
|
if '.' not in parsed_url.netloc:
|
||||||
|
# 如果主机名仍然无效,完全重建URL
|
||||||
|
base_domain = 'leonmmcoset.jjxmm.win:8010'
|
||||||
|
path = parsed_url.path or parsed_url.netloc
|
||||||
|
# 对路径进行URL编码
|
||||||
|
encoded_path = quote(path)
|
||||||
|
# 使用http协议
|
||||||
|
download_url = f'http://leonmmcoset.jjxmm.win:8010/{encoded_path.lstrip("/")}'
|
||||||
|
|
||||||
# 创建进度对话框
|
# 创建进度对话框
|
||||||
self.progress_dialog = QProgressDialog(
|
self.progress_dialog = QProgressDialog(
|
||||||
f"正在下载{self.app_data.get('name', '未知应用')}...",
|
f"正在下载{self.app_data.get('name', '未知应用')}...",
|
||||||
@@ -723,11 +797,31 @@ class AppDetailWindow(QMainWindow):
|
|||||||
self.progress_dialog.setWindowTitle("正在安装")
|
self.progress_dialog.setWindowTitle("正在安装")
|
||||||
self.progress_dialog.setMinimumDuration(0)
|
self.progress_dialog.setMinimumDuration(0)
|
||||||
|
|
||||||
# 创建临时保存路径
|
# 创建保存路径 - 使用代码文件夹所在的install目录
|
||||||
file_extension = ".exe" # 假设是Windows可执行文件
|
file_extension = ".exe" # 假设是Windows可执行文件
|
||||||
app_name = self.app_data.get('name', '未知应用').replace(' ', '_')
|
app_name = self.app_data.get('name', '未知应用').replace(' ', '_')
|
||||||
|
|
||||||
|
# 获取当前脚本所在目录
|
||||||
|
import os
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
# 创建install目录路径
|
||||||
|
install_dir = os.path.join(current_dir, 'install')
|
||||||
|
|
||||||
|
# 确保install目录存在
|
||||||
|
if not os.path.exists(install_dir):
|
||||||
|
try:
|
||||||
|
os.makedirs(install_dir)
|
||||||
|
except Exception as e:
|
||||||
|
# 如果创建目录失败,回退到临时目录
|
||||||
|
show_error_dialog("创建目录失败", f"无法创建安装目录: {str(e)}\n将使用临时目录")
|
||||||
temp_dir = tempfile.gettempdir()
|
temp_dir = tempfile.gettempdir()
|
||||||
save_path = os.path.join(temp_dir, f"{app_name}_installer{file_extension}")
|
save_path = os.path.join(temp_dir, f"{app_name}_installer{file_extension}")
|
||||||
|
else:
|
||||||
|
# 使用install目录
|
||||||
|
save_path = os.path.join(install_dir, f"{app_name}_installer{file_extension}")
|
||||||
|
else:
|
||||||
|
# 目录已存在,直接使用
|
||||||
|
save_path = os.path.join(install_dir, f"{app_name}_installer{file_extension}")
|
||||||
|
|
||||||
# 创建下载线程
|
# 创建下载线程
|
||||||
self.download_thread = DownloadThread(download_url, save_path)
|
self.download_thread = DownloadThread(download_url, save_path)
|
||||||
@@ -749,49 +843,75 @@ class AppDetailWindow(QMainWindow):
|
|||||||
# 在Windows上,直接运行可执行文件
|
# 在Windows上,直接运行可执行文件
|
||||||
subprocess.Popen([file_path], shell=True)
|
subprocess.Popen([file_path], shell=True)
|
||||||
|
|
||||||
# 显示安装成功提示
|
# 创建Sweet Alert风格的下载完成提示
|
||||||
InfoBar.success(
|
success_box = QMessageBox()
|
||||||
title="下载完成",
|
success_box.setWindowTitle("下载完成")
|
||||||
content=f"安装程序已启动,请按照向导完成安装。\n文件位置:{file_path}",
|
success_box.setIcon(QMessageBox.Information)
|
||||||
orient=Qt.Horizontal,
|
success_box.setText(f"安装程序已启动,请按照向导完成安装。\n文件位置:{file_path}")
|
||||||
isClosable=True,
|
|
||||||
position=InfoBarPosition.TOP,
|
# 设置字体大小
|
||||||
duration=5000,
|
font = QFont()
|
||||||
parent=self
|
font.setPointSize(10)
|
||||||
)
|
success_box.setFont(font)
|
||||||
|
|
||||||
|
# 添加按钮
|
||||||
|
success_box.setStandardButtons(QMessageBox.Ok)
|
||||||
|
|
||||||
|
# 显示对话框
|
||||||
|
success_box.exec_()
|
||||||
|
|
||||||
|
# 打开下载文件夹并选中文件
|
||||||
|
subprocess.Popen(f'explorer /select,"{file_path}"')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
InfoBar.error(
|
show_error_dialog("启动安装程序失败", f"无法启动安装程序: {str(e)}\n您可以手动运行文件: {file_path}")
|
||||||
title="启动安装程序失败",
|
|
||||||
content=f"无法启动安装程序: {str(e)}\n您可以手动运行文件: {file_path}",
|
# 即使启动安装程序失败,也尝试打开下载文件夹
|
||||||
orient=Qt.Horizontal,
|
try:
|
||||||
isClosable=True,
|
subprocess.Popen(f'explorer /select,"{file_path}"')
|
||||||
position=InfoBarPosition.TOP,
|
except Exception as ex:
|
||||||
parent=self
|
show_error_dialog("打开文件夹失败", f"无法打开下载文件夹: {str(ex)}")
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# 其他操作系统
|
# 其他操作系统
|
||||||
InfoBar.information(
|
info_box = QMessageBox()
|
||||||
title="下载完成",
|
info_box.setWindowTitle("下载完成")
|
||||||
content=f"文件已下载完成。\n\n文件位置:{file_path}\n\n请手动运行安装程序。",
|
info_box.setIcon(QMessageBox.Information)
|
||||||
orient=Qt.Horizontal,
|
info_box.setText(f"文件已下载完成。\n\n文件位置:{file_path}\n\n请手动运行安装程序。")
|
||||||
isClosable=True,
|
|
||||||
position=InfoBarPosition.TOP,
|
# 设置字体大小
|
||||||
duration=5000,
|
font = QFont()
|
||||||
parent=self
|
font.setPointSize(10)
|
||||||
)
|
info_box.setFont(font)
|
||||||
|
|
||||||
|
# 添加按钮
|
||||||
|
info_box.setStandardButtons(QMessageBox.Ok)
|
||||||
|
|
||||||
|
# 显示对话框
|
||||||
|
info_box.exec_()
|
||||||
|
|
||||||
|
# 打开下载文件夹
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
# 获取文件所在目录
|
||||||
|
folder_path = os.path.dirname(file_path)
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
# Windows下使用explorer打开文件夹并选中文件
|
||||||
|
subprocess.Popen(f'explorer /select,"{file_path}"')
|
||||||
|
elif platform.system() == 'Darwin':
|
||||||
|
# macOS下使用open命令打开文件夹
|
||||||
|
subprocess.Popen(['open', folder_path])
|
||||||
|
else:
|
||||||
|
# Linux下使用xdg-open打开文件夹
|
||||||
|
subprocess.Popen(['xdg-open', folder_path])
|
||||||
|
except Exception as e:
|
||||||
|
show_error_dialog("打开文件夹失败", f"无法打开下载文件夹: {str(e)}")
|
||||||
|
|
||||||
def on_download_error(self, error_msg):
|
def on_download_error(self, error_msg):
|
||||||
"""下载错误处理"""
|
"""下载错误处理"""
|
||||||
self.progress_dialog.reject()
|
self.progress_dialog.reject()
|
||||||
|
|
||||||
InfoBar.error(
|
# 使用Sweet Alert风格弹窗显示错误
|
||||||
title="安装失败",
|
show_error_dialog("安装失败", error_msg)
|
||||||
content=error_msg,
|
|
||||||
orient=Qt.Horizontal,
|
|
||||||
isClosable=True,
|
|
||||||
position=InfoBarPosition.TOP,
|
|
||||||
parent=self
|
|
||||||
)
|
|
||||||
|
|
||||||
def share_app(self):
|
def share_app(self):
|
||||||
"""分享应用"""
|
"""分享应用"""
|
||||||
@@ -802,7 +922,7 @@ class AppDetailWindow(QMainWindow):
|
|||||||
|
|
||||||
# 创建分享链接
|
# 创建分享链接
|
||||||
app_share_url = f"leonapp://app/{self.app_id}"
|
app_share_url = f"leonapp://app/{self.app_id}"
|
||||||
web_share_url = f"http://leonmcoset.jjxmm.win:8010/app?id={self.app_id}"
|
web_share_url = f"http://leonmmcoset.jjxmm.win:8010/app?id={self.app_id}"
|
||||||
|
|
||||||
# 创建自定义对话框
|
# 创建自定义对话框
|
||||||
dialog = QMessageBox(self)
|
dialog = QMessageBox(self)
|
||||||
|
|||||||
Reference in New Issue
Block a user