[patch] 优化PDD的js环境补充 优化配置文件心跳环境检测 优化打包环境补充逻辑
This commit is contained in:
@@ -135,6 +135,8 @@ jobs:
|
|||||||
- name: Build production executable
|
- name: Build production executable
|
||||||
if: success()
|
if: success()
|
||||||
shell: powershell
|
shell: powershell
|
||||||
|
env:
|
||||||
|
PYTHONIOENCODING: utf-8
|
||||||
run: |
|
run: |
|
||||||
Write-Host "==========================================";
|
Write-Host "==========================================";
|
||||||
Write-Host "Step 4.5: Build production executable";
|
Write-Host "Step 4.5: Build production executable";
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ import asyncio
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, Any, Optional, Callable
|
from typing import Dict, Any, Optional, Callable
|
||||||
|
|
||||||
|
# 🔧 尝试导入PyMiniRacer(内置V8引擎,无需外部JavaScript环境)
|
||||||
|
try:
|
||||||
|
from py_mini_racer import MiniRacer
|
||||||
|
PYMINIRACER_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
PYMINIRACER_AVAILABLE = False
|
||||||
|
MiniRacer = None
|
||||||
|
|
||||||
# 新增导入,用于登录功能
|
# 新增导入,用于登录功能
|
||||||
import PIL, numpy as np, cv2
|
import PIL, numpy as np, cv2
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
@@ -2048,20 +2056,92 @@ class ChatPdd:
|
|||||||
dencode_js_path = os.path.join(js_dir, "dencode_message.js")
|
dencode_js_path = os.path.join(js_dir, "dencode_message.js")
|
||||||
encode_js_path = os.path.join(js_dir, "encode_message.js")
|
encode_js_path = os.path.join(js_dir, "encode_message.js")
|
||||||
|
|
||||||
|
# 首先设置log_callback,以便在初始化过程中使用日志
|
||||||
|
self.log_callback = log_callback
|
||||||
|
|
||||||
# 读取JS文件
|
# 读取JS文件
|
||||||
try:
|
try:
|
||||||
with open(dencode_js_path, 'r', encoding='utf-8') as f:
|
with open(dencode_js_path, 'r', encoding='utf-8') as f:
|
||||||
jscode = f.read()
|
jscode = f.read()
|
||||||
self.ctx = execjs.compile(jscode)
|
|
||||||
|
|
||||||
with open(encode_js_path, 'r', encoding='utf-8') as f:
|
with open(encode_js_path, 'r', encoding='utf-8') as f:
|
||||||
ejscode = f.read()
|
ejscode = f.read()
|
||||||
|
|
||||||
|
# 🔧 优先使用PyMiniRacer(内置V8,无需外部JavaScript环境)
|
||||||
|
if PYMINIRACER_AVAILABLE:
|
||||||
|
self._log("✅ 使用PyMiniRacer内置JavaScript引擎(无需Node.js环境)", "INFO")
|
||||||
|
self.js_engine = "PyMiniRacer"
|
||||||
|
|
||||||
|
# 🔧 关键修复:为PyMiniRacer注入浏览器API(atob/btoa)
|
||||||
|
browser_api_polyfill = """
|
||||||
|
// Base64解码函数(atob polyfill)
|
||||||
|
if (typeof atob === 'undefined') {
|
||||||
|
globalThis.atob = function(base64) {
|
||||||
|
// 简单的Base64解码实现
|
||||||
|
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||||
|
var str = String(base64).replace(/[=]+$/, '');
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
for (var bc = 0, bs, buffer, idx = 0; buffer = str.charAt(idx++); ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0) {
|
||||||
|
buffer = chars.indexOf(buffer);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64编码函数(btoa polyfill)
|
||||||
|
if (typeof btoa === 'undefined') {
|
||||||
|
globalThis.btoa = function(str) {
|
||||||
|
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
for (var block = 0, charCode, i = 0, map = chars; str.charAt(i | 0) || (map = '=', i % 1); output += map.charAt(63 & block >> 8 - i % 1 * 8)) {
|
||||||
|
charCode = str.charCodeAt(i += 3/4);
|
||||||
|
if (charCode > 0xFF) {
|
||||||
|
throw new Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
|
||||||
|
}
|
||||||
|
block = block << 8 | charCode;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 创建PyMiniRacer上下文
|
||||||
|
self.ctx_mini = MiniRacer()
|
||||||
|
# 首先注入浏览器API
|
||||||
|
self.ctx_mini.eval(browser_api_polyfill)
|
||||||
|
self._log("✅ 已注入atob/btoa浏览器API", "DEBUG")
|
||||||
|
# 然后加载实际的JS代码
|
||||||
|
self.ctx_mini.eval(jscode)
|
||||||
|
|
||||||
|
self.encode_mini = MiniRacer()
|
||||||
|
# 同样注入浏览器API
|
||||||
|
self.encode_mini.eval(browser_api_polyfill)
|
||||||
|
# 加载实际的JS代码
|
||||||
|
self.encode_mini.eval(ejscode)
|
||||||
|
|
||||||
|
# 创建包装对象,统一接口
|
||||||
|
self.ctx = self._create_miniracer_wrapper(self.ctx_mini, "dencode_data")
|
||||||
|
self.encodeex = self._create_miniracer_wrapper(self.encode_mini, "encode_data")
|
||||||
|
|
||||||
|
self._log("✅ PyMiniRacer JavaScript引擎初始化成功(含浏览器API)", "SUCCESS")
|
||||||
|
else:
|
||||||
|
# 回退到execjs(需要Node.js或其他外部JavaScript引擎)
|
||||||
|
self._log("⚠️ PyMiniRacer不可用,回退到execjs(需要Node.js环境)", "WARNING")
|
||||||
|
self.js_engine = "execjs"
|
||||||
|
self.ctx = execjs.compile(jscode)
|
||||||
self.encodeex = execjs.compile(ejscode)
|
self.encodeex = execjs.compile(ejscode)
|
||||||
|
try:
|
||||||
|
engine_name = execjs.get().name
|
||||||
|
self._log(f"📦 execjs使用的JavaScript引擎: {engine_name}", "INFO")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
self._log(f"❌ JavaScript引擎初始化失败: {str(e)}", "ERROR")
|
||||||
raise RuntimeError(f"读取JS文件失败: {str(e)}")
|
raise RuntimeError(f"读取JS文件失败: {str(e)}")
|
||||||
|
|
||||||
# 首先设置log_callback,然后才能使用_log方法
|
|
||||||
self.log_callback = log_callback
|
|
||||||
self._log("初始化ChatPdd实例", "INFO")
|
self._log("初始化ChatPdd实例", "INFO")
|
||||||
|
|
||||||
self.chat_list_stat = chat_list_stat
|
self.chat_list_stat = chat_list_stat
|
||||||
@@ -2128,6 +2208,39 @@ class ChatPdd:
|
|||||||
reset = "\033[0m"
|
reset = "\033[0m"
|
||||||
print(f"{color}[{timestamp}] [{level}] {message}{reset}")
|
print(f"{color}[{timestamp}] [{level}] {message}{reset}")
|
||||||
|
|
||||||
|
def _create_miniracer_wrapper(self, mini_racer_ctx, function_name):
|
||||||
|
"""创建PyMiniRacer的包装器,使其接口与execjs兼容"""
|
||||||
|
class MiniRacerWrapper:
|
||||||
|
def __init__(self, ctx, func_name):
|
||||||
|
self.ctx = ctx
|
||||||
|
self.func_name = func_name
|
||||||
|
|
||||||
|
def call(self, func_name, *args):
|
||||||
|
"""调用JavaScript函数"""
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
|
||||||
|
# 将参数转换为JavaScript可用的格式
|
||||||
|
js_args = ', '.join([f'"{arg}"' if isinstance(arg, str) else str(arg) for arg in args])
|
||||||
|
|
||||||
|
# 🔧 关键修复:使用JSON.stringify包装结果,确保返回可序列化的Python对象
|
||||||
|
# 这样可以避免JSObject类型转换问题
|
||||||
|
js_code = f"JSON.stringify({func_name}({js_args}))"
|
||||||
|
|
||||||
|
# 执行JavaScript并获取JSON字符串结果
|
||||||
|
json_str = self.ctx.eval(js_code)
|
||||||
|
|
||||||
|
# 将JSON字符串解析为Python对象
|
||||||
|
if json_str:
|
||||||
|
return json.loads(json_str)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"PyMiniRacer执行失败: {e}")
|
||||||
|
|
||||||
|
return MiniRacerWrapper(mini_racer_ctx, function_name)
|
||||||
|
|
||||||
# 重连机制方法
|
# 重连机制方法
|
||||||
async def calculate_reconnect_delay(self):
|
async def calculate_reconnect_delay(self):
|
||||||
"""计算指数退避的重连延迟时间"""
|
"""计算指数退避的重连延迟时间"""
|
||||||
|
|||||||
10
config.py
10
config.py
@@ -19,15 +19,15 @@ BACKEND_WS_URL = f"ws://{BACKEND_HOST}:{BACKEND_PORT}"
|
|||||||
|
|
||||||
# WebSocket配置
|
# WebSocket配置
|
||||||
WS_CONNECT_TIMEOUT = 16.0
|
WS_CONNECT_TIMEOUT = 16.0
|
||||||
WS_MESSAGE_TIMEOUT = 30.0
|
WS_MESSAGE_TIMEOUT = 60.0 # 增加消息超时(适应AI处理时间)
|
||||||
WS_PING_INTERVAL = 10 # 10秒ping间隔(提高检测频率)
|
WS_PING_INTERVAL = 20 # 20秒ping间隔(避免AI处理期间频繁心跳)
|
||||||
WS_PING_TIMEOUT = 5 # 5秒ping超时(更快检测断线)
|
WS_PING_TIMEOUT = 10 # 10秒ping超时(给予更多响应时间)
|
||||||
WS_ENABLE_PING = True # 是否启用WebSocket原生ping心跳
|
WS_ENABLE_PING = True # 是否启用WebSocket原生ping心跳
|
||||||
WS_ENABLE_APP_PING = False # 禁用应用层ping心跳(避免重复)
|
WS_ENABLE_APP_PING = False # 禁用应用层ping心跳(避免重复)
|
||||||
|
|
||||||
# AI处理超时配置
|
# AI处理超时配置
|
||||||
AI_PROCESS_TIMEOUT = 30 # AI处理超时时间(秒)
|
AI_PROCESS_TIMEOUT = 45 # AI处理超时时间(秒)- 增加以适应复杂查询
|
||||||
AI_LONG_PROCESS_THRESHOLD = 10 # AI长时间处理阈值(秒)
|
AI_LONG_PROCESS_THRESHOLD = 15 # AI长时间处理阈值(秒)
|
||||||
|
|
||||||
# 内存管理配置
|
# 内存管理配置
|
||||||
MAX_PENDING_REPLIES = 100
|
MAX_PENDING_REPLIES = 100
|
||||||
|
|||||||
272
main.py
272
main.py
@@ -62,10 +62,9 @@ class LoginWindow(QMainWindow):
|
|||||||
self.disconnect_signals = DisconnectSignals()
|
self.disconnect_signals = DisconnectSignals()
|
||||||
self.disconnect_signals.disconnected.connect(self._show_disconnect_dialog)
|
self.disconnect_signals.disconnected.connect(self._show_disconnect_dialog)
|
||||||
|
|
||||||
# 横幅动画相关
|
# 横幅相关
|
||||||
self.promo_banner = None
|
self.promo_banner = None
|
||||||
self.banner_animation = None
|
self.banner_shadow = None # 阴影效果引用
|
||||||
self.banner_color_index = 0
|
|
||||||
|
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
|
||||||
@@ -177,60 +176,94 @@ class LoginWindow(QMainWindow):
|
|||||||
print("[INFO] 窗口已自适应内容大小")
|
print("[INFO] 窗口已自适应内容大小")
|
||||||
|
|
||||||
def create_promo_banner(self, layout):
|
def create_promo_banner(self, layout):
|
||||||
"""创建宣传横幅(带动态渐变效果)"""
|
"""创建宣传横幅(使用图片,带悬停动画效果)"""
|
||||||
try:
|
try:
|
||||||
# 创建横幅容器(最小化高度)
|
# 创建横幅容器
|
||||||
banner_frame = QFrame()
|
banner_frame = QFrame()
|
||||||
banner_frame.setObjectName("promoBanner")
|
banner_frame.setObjectName("promoBanner")
|
||||||
banner_frame.setCursor(Qt.PointingHandCursor) # 鼠标变手型
|
banner_frame.setCursor(Qt.PointingHandCursor) # 鼠标变手型
|
||||||
banner_frame.setMinimumHeight(65) # 最小高度
|
|
||||||
banner_frame.setMaximumHeight(65) # 固定高度
|
|
||||||
|
|
||||||
# 保存横幅引用,用于动画
|
# 保存横幅引用,用于动画
|
||||||
self.promo_banner = banner_frame
|
self.promo_banner = banner_frame
|
||||||
|
|
||||||
# 横幅布局(最小内边距)
|
# 使用图片标签
|
||||||
|
banner_image = QLabel()
|
||||||
|
banner_image.setObjectName("bannerImage")
|
||||||
|
|
||||||
|
# 加载横幅图片
|
||||||
|
from windows_taskbar_fix import get_resource_path
|
||||||
|
image_path = get_resource_path("static/hengfu.jpg")
|
||||||
|
|
||||||
|
if os.path.exists(image_path):
|
||||||
|
from PyQt5.QtGui import QPixmap
|
||||||
|
pixmap = QPixmap(image_path)
|
||||||
|
|
||||||
|
# 设置固定的横幅尺寸
|
||||||
|
target_width = 400 # 固定宽度
|
||||||
|
target_height = 70 # 固定高度(调整为70px,更紧凑)
|
||||||
|
|
||||||
|
# 缩放图片以适应目标尺寸
|
||||||
|
scaled_pixmap = pixmap.scaled(
|
||||||
|
target_width,
|
||||||
|
target_height,
|
||||||
|
Qt.IgnoreAspectRatio, # 忽略宽高比,拉伸填充
|
||||||
|
Qt.SmoothTransformation # 平滑缩放,提高图片质量
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置图片
|
||||||
|
banner_image.setPixmap(scaled_pixmap)
|
||||||
|
banner_image.setScaledContents(False)
|
||||||
|
|
||||||
|
banner_frame.setFixedHeight(target_height)
|
||||||
|
banner_image.setFixedSize(target_width, target_height)
|
||||||
|
|
||||||
|
# 🔧 使用CSS圆角来处理边角(更简单稳定)
|
||||||
|
banner_image.setStyleSheet("""
|
||||||
|
QLabel#bannerImage {
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
print(f"[INFO] 横幅图片已加载: {image_path}, 尺寸: {target_width}x{target_height}")
|
||||||
|
else:
|
||||||
|
# 图片不存在时的备用方案
|
||||||
|
banner_image.setText("🌟 限时优惠活动 - 点击了解详情 →")
|
||||||
|
banner_image.setAlignment(Qt.AlignCenter)
|
||||||
|
banner_image.setStyleSheet("""
|
||||||
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||||
|
stop:0 #667eea, stop:1 #764ba2);
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 15px;
|
||||||
|
""")
|
||||||
|
banner_frame.setFixedHeight(60)
|
||||||
|
print(f"[WARNING] 横幅图片未找到: {image_path},使用备用样式")
|
||||||
|
|
||||||
|
# 创建布局
|
||||||
banner_layout = QHBoxLayout()
|
banner_layout = QHBoxLayout()
|
||||||
banner_layout.setContentsMargins(10, 6, 10, 6) # 最小内边距
|
banner_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
banner_layout.setSpacing(8) # 最小间距
|
banner_layout.setSpacing(0)
|
||||||
banner_frame.setLayout(banner_layout)
|
banner_frame.setLayout(banner_layout)
|
||||||
|
banner_layout.addWidget(banner_image)
|
||||||
|
|
||||||
# 左侧图标(最小化)
|
# 设置圆角和边框
|
||||||
icon_label = QLabel("🎁")
|
banner_frame.setStyleSheet("""
|
||||||
icon_label.setFont(QFont('Microsoft YaHei', 22)) # 更小的图标字体
|
QFrame#promoBanner {
|
||||||
icon_label.setAlignment(Qt.AlignCenter)
|
border-radius: 12px;
|
||||||
icon_label.setFixedSize(45, 45) # 更小的图标尺寸
|
background: transparent;
|
||||||
banner_layout.addWidget(icon_label)
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
# 右侧文字区域
|
# 初始阴影效果
|
||||||
text_layout = QVBoxLayout()
|
self.banner_shadow = QGraphicsDropShadowEffect()
|
||||||
text_layout.setSpacing(1) # 最小文字间距
|
self.banner_shadow.setBlurRadius(15)
|
||||||
|
self.banner_shadow.setXOffset(0)
|
||||||
# 标题
|
self.banner_shadow.setYOffset(3)
|
||||||
title_label = QLabel("🌟 限时优惠活动进行中")
|
self.banner_shadow.setColor(QColor(0, 0, 0, 60))
|
||||||
title_label.setFont(QFont('Microsoft YaHei', 10, QFont.Bold)) # 更小字体
|
banner_frame.setGraphicsEffect(self.banner_shadow)
|
||||||
title_label.setStyleSheet("color: white;")
|
|
||||||
text_layout.addWidget(title_label)
|
|
||||||
|
|
||||||
# 副标题
|
|
||||||
subtitle_label = QLabel("立即访问官网了解更多优惠详情 →")
|
|
||||||
subtitle_label.setFont(QFont('Microsoft YaHei', 8)) # 更小字体
|
|
||||||
subtitle_label.setStyleSheet("color: rgba(255, 255, 255, 0.9);")
|
|
||||||
text_layout.addWidget(subtitle_label)
|
|
||||||
|
|
||||||
banner_layout.addLayout(text_layout)
|
|
||||||
banner_layout.addStretch()
|
|
||||||
|
|
||||||
# 初始样式(紫色渐变背景)
|
|
||||||
self.update_banner_style(0)
|
|
||||||
|
|
||||||
# 添加阴影效果(更轻量)
|
|
||||||
shadow = QGraphicsDropShadowEffect()
|
|
||||||
shadow.setBlurRadius(10) # 减小模糊半径
|
|
||||||
shadow.setXOffset(0)
|
|
||||||
shadow.setYOffset(2) # 减小偏移
|
|
||||||
shadow.setColor(QColor(0, 0, 0, 40)) # 降低不透明度
|
|
||||||
banner_frame.setGraphicsEffect(shadow)
|
|
||||||
|
|
||||||
# 点击事件 - 跳转官网
|
# 点击事件 - 跳转官网
|
||||||
def open_official_website():
|
def open_official_website():
|
||||||
@@ -244,88 +277,82 @@ class LoginWindow(QMainWindow):
|
|||||||
|
|
||||||
banner_frame.mousePressEvent = lambda event: open_official_website()
|
banner_frame.mousePressEvent = lambda event: open_official_website()
|
||||||
|
|
||||||
|
# 🎯 添加鼠标悬停事件(动态效果)
|
||||||
|
def on_enter(event):
|
||||||
|
"""鼠标进入时的动画效果"""
|
||||||
|
try:
|
||||||
|
# 增强阴影效果
|
||||||
|
self.banner_shadow.setBlurRadius(25)
|
||||||
|
self.banner_shadow.setYOffset(5)
|
||||||
|
self.banner_shadow.setColor(QColor(0, 0, 0, 100))
|
||||||
|
|
||||||
|
# 轻微放大动画
|
||||||
|
animation = QPropertyAnimation(banner_frame, b"geometry")
|
||||||
|
original_rect = banner_frame.geometry()
|
||||||
|
|
||||||
|
expanded_rect = QRect(
|
||||||
|
original_rect.x() - 3,
|
||||||
|
original_rect.y() - 2,
|
||||||
|
original_rect.width() + 6,
|
||||||
|
original_rect.height() + 4
|
||||||
|
)
|
||||||
|
|
||||||
|
animation.setDuration(200)
|
||||||
|
animation.setStartValue(original_rect)
|
||||||
|
animation.setEndValue(expanded_rect)
|
||||||
|
animation.setEasingCurve(QEasingCurve.OutCubic)
|
||||||
|
animation.start()
|
||||||
|
|
||||||
|
# 保存动画引用避免被垃圾回收
|
||||||
|
banner_frame.hover_animation = animation
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DEBUG] 悬停动画错误: {e}")
|
||||||
|
|
||||||
|
def on_leave(event):
|
||||||
|
"""鼠标离开时的动画效果"""
|
||||||
|
try:
|
||||||
|
# 恢复阴影效果
|
||||||
|
self.banner_shadow.setBlurRadius(15)
|
||||||
|
self.banner_shadow.setYOffset(3)
|
||||||
|
self.banner_shadow.setColor(QColor(0, 0, 0, 60))
|
||||||
|
|
||||||
|
# 恢复原始大小
|
||||||
|
animation = QPropertyAnimation(banner_frame, b"geometry")
|
||||||
|
current_rect = banner_frame.geometry()
|
||||||
|
|
||||||
|
original_rect = QRect(
|
||||||
|
current_rect.x() + 3,
|
||||||
|
current_rect.y() + 2,
|
||||||
|
current_rect.width() - 6,
|
||||||
|
current_rect.height() - 4
|
||||||
|
)
|
||||||
|
|
||||||
|
animation.setDuration(200)
|
||||||
|
animation.setStartValue(current_rect)
|
||||||
|
animation.setEndValue(original_rect)
|
||||||
|
animation.setEasingCurve(QEasingCurve.InCubic)
|
||||||
|
animation.start()
|
||||||
|
|
||||||
|
# 保存动画引用避免被垃圾回收
|
||||||
|
banner_frame.leave_animation = animation
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DEBUG] 离开动画错误: {e}")
|
||||||
|
|
||||||
|
# 安装事件过滤器
|
||||||
|
banner_frame.enterEvent = on_enter
|
||||||
|
banner_frame.leaveEvent = on_leave
|
||||||
|
|
||||||
# 添加到主布局
|
# 添加到主布局
|
||||||
layout.addWidget(banner_frame)
|
layout.addWidget(banner_frame)
|
||||||
|
|
||||||
# 启动颜色渐变动画
|
print("[INFO] 宣传横幅已创建(使用图片,带悬停动画)")
|
||||||
self.start_banner_animation()
|
|
||||||
|
|
||||||
print("[INFO] 宣传横幅已创建(带动态渐变效果)")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[WARNING] 创建宣传横幅失败: {e}")
|
print(f"[WARNING] 创建宣传横幅失败: {e}")
|
||||||
|
|
||||||
def update_banner_style(self, color_index):
|
# 横幅颜色动画相关方法已移除(改用图片横幅)
|
||||||
"""更新横幅颜色样式"""
|
|
||||||
if not self.promo_banner:
|
|
||||||
return
|
|
||||||
|
|
||||||
# 定义多组渐变颜色方案(呼吸效果)
|
|
||||||
color_schemes = [
|
|
||||||
# 紫色(原色)
|
|
||||||
{
|
|
||||||
'normal': ('667eea', '764ba2'),
|
|
||||||
'hover': ('778ff5', '865cb3')
|
|
||||||
},
|
|
||||||
# 深紫色(变暗)
|
|
||||||
{
|
|
||||||
'normal': ('5a6dc8', '6a3f8f'),
|
|
||||||
'hover': ('6b7ed9', '7b50a0')
|
|
||||||
},
|
|
||||||
# 亮紫色(变亮)
|
|
||||||
{
|
|
||||||
'normal': ('7890fc', '8658b4'),
|
|
||||||
'hover': ('89a1ff', '9769c5')
|
|
||||||
},
|
|
||||||
# 紫色(原色)- 循环
|
|
||||||
{
|
|
||||||
'normal': ('667eea', '764ba2'),
|
|
||||||
'hover': ('778ff5', '865cb3')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
scheme = color_schemes[color_index % len(color_schemes)]
|
|
||||||
|
|
||||||
style = f"""
|
|
||||||
QFrame#promoBanner {{
|
|
||||||
background: qlineargradient(
|
|
||||||
x1:0, y1:0, x2:1, y2:0,
|
|
||||||
stop:0 #{scheme['normal'][0]},
|
|
||||||
stop:1 #{scheme['normal'][1]}
|
|
||||||
);
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
}}
|
|
||||||
QFrame#promoBanner:hover {{
|
|
||||||
background: qlineargradient(
|
|
||||||
x1:0, y1:0, x2:1, y2:0,
|
|
||||||
stop:0 #{scheme['hover'][0]},
|
|
||||||
stop:1 #{scheme['hover'][1]}
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.promo_banner.setStyleSheet(style)
|
|
||||||
|
|
||||||
def start_banner_animation(self):
|
|
||||||
"""启动横幅呼吸动画"""
|
|
||||||
try:
|
|
||||||
# 创建定时器,每2秒切换一次颜色
|
|
||||||
self.banner_animation = QTimer(self)
|
|
||||||
self.banner_animation.timeout.connect(self.animate_banner_color)
|
|
||||||
self.banner_animation.start(2000) # 2秒间隔
|
|
||||||
|
|
||||||
print("[INFO] 横幅呼吸动画已启动")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[WARNING] 启动横幅动画失败: {e}")
|
|
||||||
|
|
||||||
def animate_banner_color(self):
|
|
||||||
"""动画:循环改变横幅颜色"""
|
|
||||||
try:
|
|
||||||
self.banner_color_index = (self.banner_color_index + 1) % 4
|
|
||||||
self.update_banner_style(self.banner_color_index)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[WARNING] 横幅颜色动画失败: {e}")
|
|
||||||
|
|
||||||
def apply_modern_styles(self):
|
def apply_modern_styles(self):
|
||||||
"""应用现代化简约样式 - 精致美化版"""
|
"""应用现代化简约样式 - 精致美化版"""
|
||||||
@@ -779,11 +806,6 @@ class LoginWindow(QMainWindow):
|
|||||||
"""真正退出应用程序"""
|
"""真正退出应用程序"""
|
||||||
print("[INFO] 正在退出应用程序...")
|
print("[INFO] 正在退出应用程序...")
|
||||||
|
|
||||||
# 停止横幅动画
|
|
||||||
if hasattr(self, 'banner_animation') and self.banner_animation:
|
|
||||||
self.banner_animation.stop()
|
|
||||||
print("[INFO] 横幅动画已停止")
|
|
||||||
|
|
||||||
# 执行原来的关闭逻辑
|
# 执行原来的关闭逻辑
|
||||||
try:
|
try:
|
||||||
# 使用 WebSocket 管理器断开所有连接
|
# 使用 WebSocket 管理器断开所有连接
|
||||||
|
|||||||
@@ -87,11 +87,14 @@ def build_with_command():
|
|||||||
'--hidden-import=WebSocket.backend_singleton',
|
'--hidden-import=WebSocket.backend_singleton',
|
||||||
'--hidden-import=WebSocket.BackendClient',
|
'--hidden-import=WebSocket.BackendClient',
|
||||||
'--hidden-import=windows_taskbar_fix',
|
'--hidden-import=windows_taskbar_fix',
|
||||||
|
'--hidden-import=py_mini_racer', # PDD平台内置JavaScript引擎
|
||||||
|
'--hidden-import=py_mini_racer.py_mini_racer',
|
||||||
|
'--collect-all=py_mini_racer', # 🔧 收集所有PyMiniRacer文件(包括DLL)
|
||||||
'main.py'
|
'main.py'
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print(f"执行命令: {' '.join(cmd[:5])}... (共{len(cmd)}个参数)")
|
print(f"go to compile: {' '.join(cmd[:5])}... (all{len(cmd)} args)")
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
|
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
@@ -336,6 +339,18 @@ def main():
|
|||||||
return False
|
return False
|
||||||
print("Verification phase completed")
|
print("Verification phase completed")
|
||||||
|
|
||||||
|
# 🔧 修复PyMiniRacer DLL
|
||||||
|
print("\nPyMiniRacer DLL fix phase started...")
|
||||||
|
try:
|
||||||
|
import fix_pyminiracer_dll
|
||||||
|
if fix_pyminiracer_dll.auto_fix_after_build():
|
||||||
|
print("PyMiniRacer DLL fix completed")
|
||||||
|
else:
|
||||||
|
print("WARNING: PyMiniRacer DLL fix failed - PDD platform may not work without Node.js")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"WARNING: PyMiniRacer DLL fix error: {e}")
|
||||||
|
print(" PDD platform may need Node.js environment")
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print("Build completed successfully!")
|
print("Build completed successfully!")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user