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/logs/
|
||||
/pyqt5fluentdesign/install/
|
||||
|
||||
# 忽略Windows系统文件
|
||||
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,
|
||||
QPushButton, QFrame, QLabel, QGridLayout)
|
||||
QPushButton, QFrame, QLabel, QGridLayout, QMessageBox)
|
||||
from PyQt5.QtCore import Qt, QSize, pyqtSignal, QThread
|
||||
from PyQt5.QtGui import QPixmap, QFont
|
||||
from qfluentwidgets import (InfoBar, InfoBarPosition, TitleLabel, SubtitleLabel,
|
||||
@@ -14,6 +14,25 @@ import tempfile
|
||||
import subprocess
|
||||
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):
|
||||
"""下载线程,用于在后台下载文件"""
|
||||
progress = pyqtSignal(int)
|
||||
@@ -652,34 +671,44 @@ class AppDetailWindow(QMainWindow):
|
||||
|
||||
# 检查是否有应用数据
|
||||
if not hasattr(self, 'app_data'):
|
||||
InfoBar.error(
|
||||
title="错误",
|
||||
content="应用数据未加载,请刷新页面后重试。",
|
||||
orient=Qt.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP,
|
||||
parent=self
|
||||
)
|
||||
show_error_dialog("错误", "应用数据未加载,请刷新页面后重试。")
|
||||
return
|
||||
|
||||
# 创建确认对话框
|
||||
reply = QMessageBox.question(self,
|
||||
"确认安装",
|
||||
f"您确定要安装{self.app_data.get('name', '未知应用')}吗?\n\n点击'确定'开始安装。",
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.No)
|
||||
# 创建Sweet Alert风格的确认对话框
|
||||
confirm_box = QMessageBox()
|
||||
confirm_box.setWindowTitle("确认安装")
|
||||
confirm_box.setIcon(QMessageBox.Question)
|
||||
confirm_box.setText(f"您确定要安装{self.app_data.get('name', '未知应用')}吗?\n\n点击'确定'开始安装。")
|
||||
|
||||
# 设置字体大小
|
||||
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:
|
||||
# 显示开始安装的提示
|
||||
InfoBar.success(
|
||||
title="安装开始",
|
||||
content=f"{self.app_data.get('name', '未知应用')}安装已开始,请稍候...",
|
||||
orient=Qt.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP,
|
||||
duration=3000,
|
||||
parent=self
|
||||
)
|
||||
# 创建Sweet Alert风格的成功提示
|
||||
success_box = QMessageBox()
|
||||
success_box.setWindowTitle("安装开始")
|
||||
success_box.setIcon(QMessageBox.Information)
|
||||
success_box.setText(f"{self.app_data.get('name', '未知应用')}安装已开始,请稍候...")
|
||||
|
||||
# 设置字体大小
|
||||
font = QFont()
|
||||
font.setPointSize(10)
|
||||
success_box.setFont(font)
|
||||
|
||||
# 添加按钮
|
||||
success_box.setStandardButtons(QMessageBox.Ok)
|
||||
|
||||
# 显示对话框
|
||||
success_box.exec_()
|
||||
|
||||
# 1. 首先尝试从应用数据中获取直接下载链接
|
||||
download_url = self.app_data.get('direct_download_url')
|
||||
@@ -688,30 +717,75 @@ class AppDetailWindow(QMainWindow):
|
||||
if not 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:
|
||||
try:
|
||||
# 使用app_id通过API获取下载链接
|
||||
app_id = self.app_data.get('id')
|
||||
if 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']:
|
||||
download_url = download_result.get('data')
|
||||
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')
|
||||
except Exception as e:
|
||||
print(f"获取下载链接API调用失败: {str(e)}")
|
||||
|
||||
# 4. 如果仍然没有下载链接,显示错误信息
|
||||
if not download_url:
|
||||
InfoBar.error(
|
||||
title="错误",
|
||||
content="无法获取应用的下载链接,请稍后重试。\n可能原因:API返回数据中缺少下载链接字段。",
|
||||
orient=Qt.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP,
|
||||
parent=self
|
||||
)
|
||||
show_error_dialog("错误", "无法获取应用的下载链接,请稍后重试。\n可能原因:API返回数据中缺少下载链接字段。")
|
||||
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(
|
||||
f"正在下载{self.app_data.get('name', '未知应用')}...",
|
||||
@@ -723,11 +797,31 @@ class AppDetailWindow(QMainWindow):
|
||||
self.progress_dialog.setWindowTitle("正在安装")
|
||||
self.progress_dialog.setMinimumDuration(0)
|
||||
|
||||
# 创建临时保存路径
|
||||
# 创建保存路径 - 使用代码文件夹所在的install目录
|
||||
file_extension = ".exe" # 假设是Windows可执行文件
|
||||
app_name = self.app_data.get('name', '未知应用').replace(' ', '_')
|
||||
temp_dir = tempfile.gettempdir()
|
||||
save_path = os.path.join(temp_dir, f"{app_name}_installer{file_extension}")
|
||||
|
||||
# 获取当前脚本所在目录
|
||||
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()
|
||||
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)
|
||||
@@ -749,49 +843,75 @@ class AppDetailWindow(QMainWindow):
|
||||
# 在Windows上,直接运行可执行文件
|
||||
subprocess.Popen([file_path], shell=True)
|
||||
|
||||
# 显示安装成功提示
|
||||
InfoBar.success(
|
||||
title="下载完成",
|
||||
content=f"安装程序已启动,请按照向导完成安装。\n文件位置:{file_path}",
|
||||
orient=Qt.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP,
|
||||
duration=5000,
|
||||
parent=self
|
||||
)
|
||||
# 创建Sweet Alert风格的下载完成提示
|
||||
success_box = QMessageBox()
|
||||
success_box.setWindowTitle("下载完成")
|
||||
success_box.setIcon(QMessageBox.Information)
|
||||
success_box.setText(f"安装程序已启动,请按照向导完成安装。\n文件位置:{file_path}")
|
||||
|
||||
# 设置字体大小
|
||||
font = QFont()
|
||||
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:
|
||||
InfoBar.error(
|
||||
title="启动安装程序失败",
|
||||
content=f"无法启动安装程序: {str(e)}\n您可以手动运行文件: {file_path}",
|
||||
orient=Qt.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP,
|
||||
parent=self
|
||||
)
|
||||
show_error_dialog("启动安装程序失败", f"无法启动安装程序: {str(e)}\n您可以手动运行文件: {file_path}")
|
||||
|
||||
# 即使启动安装程序失败,也尝试打开下载文件夹
|
||||
try:
|
||||
subprocess.Popen(f'explorer /select,"{file_path}"')
|
||||
except Exception as ex:
|
||||
show_error_dialog("打开文件夹失败", f"无法打开下载文件夹: {str(ex)}")
|
||||
else:
|
||||
# 其他操作系统
|
||||
InfoBar.information(
|
||||
title="下载完成",
|
||||
content=f"文件已下载完成。\n\n文件位置:{file_path}\n\n请手动运行安装程序。",
|
||||
orient=Qt.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP,
|
||||
duration=5000,
|
||||
parent=self
|
||||
)
|
||||
info_box = QMessageBox()
|
||||
info_box.setWindowTitle("下载完成")
|
||||
info_box.setIcon(QMessageBox.Information)
|
||||
info_box.setText(f"文件已下载完成。\n\n文件位置:{file_path}\n\n请手动运行安装程序。")
|
||||
|
||||
# 设置字体大小
|
||||
font = QFont()
|
||||
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):
|
||||
"""下载错误处理"""
|
||||
self.progress_dialog.reject()
|
||||
|
||||
InfoBar.error(
|
||||
title="安装失败",
|
||||
content=error_msg,
|
||||
orient=Qt.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP,
|
||||
parent=self
|
||||
)
|
||||
# 使用Sweet Alert风格弹窗显示错误
|
||||
show_error_dialog("安装失败", error_msg)
|
||||
|
||||
def share_app(self):
|
||||
"""分享应用"""
|
||||
@@ -802,7 +922,7 @@ class AppDetailWindow(QMainWindow):
|
||||
|
||||
# 创建分享链接
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user