2025-09-12 20:42:00 +08:00
|
|
|
|
import sys
|
|
|
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
|
|
from PyQt5.QtGui import QFont, QPalette, QColor
|
|
|
|
|
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
|
|
|
|
|
QHBoxLayout, QLabel, QPushButton, QLineEdit,
|
2025-09-17 17:48:35 +08:00
|
|
|
|
QTextEdit, QFrame, QDialog, QDialogButtonBox, QComboBox,
|
|
|
|
|
|
QSystemTrayIcon, QMenu, QAction, QMessageBox, QGraphicsDropShadowEffect)
|
|
|
|
|
|
from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QRect, QTimer, pyqtProperty
|
|
|
|
|
|
from PyQt5.QtGui import QColor
|
2025-09-12 20:42:00 +08:00
|
|
|
|
import config
|
|
|
|
|
|
from WebSocket.backend_singleton import get_websocket_manager
|
2025-09-17 17:48:35 +08:00
|
|
|
|
from windows_taskbar_fix import setup_windows_taskbar_icon
|
|
|
|
|
|
import os
|
2025-09-20 16:13:23 +08:00
|
|
|
|
# ===================== 文件日志系统 - 生产环境启用 =====================
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 重定向所有输出到文件,确保有日志记录
|
2025-09-20 16:13:23 +08:00
|
|
|
|
from exe_file_logger import setup_file_logging, log_to_file
|
2025-09-17 17:48:35 +08:00
|
|
|
|
|
2025-09-20 16:13:23 +08:00
|
|
|
|
setup_file_logging() # 生产环境启用自动日志功能
|
|
|
|
|
|
print("文件日志系统已在main.py中初始化")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 新增: 用户名密码输入对话框类
|
|
|
|
|
|
|
|
|
|
|
|
class LoginWindow(QMainWindow):
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
self.jd_worker = None
|
|
|
|
|
|
self.progress_dialog = None
|
|
|
|
|
|
self.douyin_worker = None
|
|
|
|
|
|
self.qian_niu_worker = None
|
|
|
|
|
|
self.pdd_worker = None
|
|
|
|
|
|
|
|
|
|
|
|
# 重复执行防护
|
|
|
|
|
|
self.last_login_time = 0
|
|
|
|
|
|
self.login_cooldown = 2 # 登录冷却时间(秒)
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 平台连接状态管理
|
|
|
|
|
|
self.connected_platforms = []
|
|
|
|
|
|
self.status_timer = None
|
|
|
|
|
|
self.last_platform_connect_time = 0 # 记录最后一次平台连接时间
|
|
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
# 日志管理相关变量已删除
|
|
|
|
|
|
|
|
|
|
|
|
self.initUI()
|
|
|
|
|
|
|
2025-09-29 15:38:34 +08:00
|
|
|
|
# 延迟设置版本检查器,确保WebSocket连接已建立
|
|
|
|
|
|
QTimer.singleShot(5000, self.setup_version_checker)
|
|
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
def initUI(self):
|
|
|
|
|
|
# 设置窗口基本属性
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.setWindowTitle('AI客服智能助手')
|
|
|
|
|
|
self.setGeometry(300, 300, 450, 300) # 进一步减小窗口尺寸
|
|
|
|
|
|
self.setMinimumSize(420, 280) # 设置更小的最小尺寸
|
|
|
|
|
|
self.setMaximumSize(500, 350) # 设置最大尺寸,保持紧凑
|
|
|
|
|
|
|
|
|
|
|
|
# 窗口图标将由统一的任务栏修复模块设置,这里只做备用检查
|
|
|
|
|
|
print("[INFO] 窗口图标将由统一模块设置")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
# 创建中央widget
|
|
|
|
|
|
central_widget = QWidget()
|
|
|
|
|
|
self.setCentralWidget(central_widget)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建主布局
|
|
|
|
|
|
main_layout = QVBoxLayout()
|
2025-09-17 17:48:35 +08:00
|
|
|
|
main_layout.setSpacing(15) # 减小间距
|
|
|
|
|
|
main_layout.setContentsMargins(30, 20, 30, 20) # 减小边距
|
2025-09-12 20:42:00 +08:00
|
|
|
|
central_widget.setLayout(main_layout)
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 添加标题和副标题
|
|
|
|
|
|
title_label = QLabel('AI客服智能助手')
|
|
|
|
|
|
title_label.setObjectName("title")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
title_label.setAlignment(Qt.AlignCenter)
|
2025-09-17 17:48:35 +08:00
|
|
|
|
title_label.setFont(QFont('Microsoft YaHei', 18, QFont.Bold)) # 减小字体
|
|
|
|
|
|
title_label.setStyleSheet("color: #2c3e50; margin-bottom: 3px;")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
main_layout.addWidget(title_label)
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 添加副标题说明
|
|
|
|
|
|
subtitle_label = QLabel('智能连接多平台客服,提供AI自动回复服务')
|
|
|
|
|
|
subtitle_label.setObjectName("subtitle")
|
|
|
|
|
|
subtitle_label.setAlignment(Qt.AlignCenter)
|
|
|
|
|
|
subtitle_label.setFont(QFont('Microsoft YaHei', 9)) # 减小字体
|
|
|
|
|
|
subtitle_label.setStyleSheet("color: #7f8c8d; margin-bottom: 10px;")
|
|
|
|
|
|
main_layout.addWidget(subtitle_label)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加少量垂直空间
|
|
|
|
|
|
main_layout.addStretch(1)
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
# 创建令牌输入区域
|
2025-09-17 17:48:35 +08:00
|
|
|
|
token_layout = QVBoxLayout()
|
|
|
|
|
|
token_layout.setSpacing(8) # 减小间距
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
token_label = QLabel('访问令牌')
|
|
|
|
|
|
token_label.setFont(QFont('Microsoft YaHei', 11, QFont.Bold)) # 减小字体
|
|
|
|
|
|
token_label.setStyleSheet("color: #34495e;")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
self.token_input = QLineEdit()
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.token_input.setPlaceholderText('请输入您的访问令牌以连接服务')
|
2025-09-12 20:42:00 +08:00
|
|
|
|
self.token_input.setEchoMode(QLineEdit.Password)
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.token_input.setFont(QFont('Microsoft YaHei', 10)) # 减小字体
|
2025-09-12 20:42:00 +08:00
|
|
|
|
# noinspection PyUnresolvedReferences
|
|
|
|
|
|
self.token_input.returnPressed.connect(self.login) # 表示回车提交
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.token_input.setMinimumHeight(38) # 减小输入框高度
|
2025-09-12 20:42:00 +08:00
|
|
|
|
# 预填已保存的令牌(如果存在)
|
|
|
|
|
|
try:
|
|
|
|
|
|
from config import get_saved_token
|
|
|
|
|
|
saved = get_saved_token()
|
|
|
|
|
|
if saved:
|
|
|
|
|
|
self.token_input.setText(saved)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
token_layout.addWidget(token_label)
|
|
|
|
|
|
token_layout.addWidget(self.token_input)
|
|
|
|
|
|
main_layout.addLayout(token_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建连接按钮
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.login_btn = QPushButton('连接服务')
|
|
|
|
|
|
self.login_btn.setFont(QFont('Microsoft YaHei', 11, QFont.Bold)) # 减小字体
|
|
|
|
|
|
self.login_btn.setMinimumHeight(40) # 减小按钮高度
|
2025-09-12 20:42:00 +08:00
|
|
|
|
self.login_btn.clicked.connect(self.login) # 表示点击提交
|
|
|
|
|
|
main_layout.addWidget(self.login_btn)
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 添加连接状态提示
|
|
|
|
|
|
self.status_label = QLabel('等待连接...')
|
|
|
|
|
|
self.status_label.setObjectName("status")
|
|
|
|
|
|
self.status_label.setAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.status_label.setFont(QFont('Microsoft YaHei', 9)) # 减小字体
|
|
|
|
|
|
self.status_label.setStyleSheet("color: #95a5a6; margin-top: 5px;")
|
|
|
|
|
|
main_layout.addWidget(self.status_label)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加少量底部空间
|
|
|
|
|
|
main_layout.addStretch(1)
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
# 日志框已永久删除,只使用终端输出
|
|
|
|
|
|
self.log_display = None
|
|
|
|
|
|
|
|
|
|
|
|
# 应用现代化样式
|
|
|
|
|
|
self.apply_modern_styles()
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 添加视觉效果
|
|
|
|
|
|
self.add_visual_effects()
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 初始化系统托盘
|
|
|
|
|
|
self.init_system_tray()
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 系统初始化日志输出到终端
|
|
|
|
|
|
print("[INFO] 系统初始化完成")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
def apply_modern_styles(self):
|
2025-09-17 17:48:35 +08:00
|
|
|
|
"""应用现代化简约样式 - 精致美化版"""
|
2025-09-12 20:42:00 +08:00
|
|
|
|
self.setStyleSheet("""
|
|
|
|
|
|
QMainWindow {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #f8fafb,
|
|
|
|
|
|
stop:0.5 #f1f4f6,
|
|
|
|
|
|
stop:1 #e8ecef
|
|
|
|
|
|
);
|
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QLabel {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
color: #2c3e50;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QLabel[objectName="title"] {
|
|
|
|
|
|
color: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:1, y2:0,
|
|
|
|
|
|
stop:0 #2c3e50,
|
|
|
|
|
|
stop:1 #34495e
|
|
|
|
|
|
);
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QLabel[objectName="subtitle"] {
|
|
|
|
|
|
color: #7f8c8d;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QLabel[objectName="status"] {
|
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
background: rgba(149, 165, 166, 0.1);
|
|
|
|
|
|
font-size: 9px;
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QLineEdit {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
border: 2px solid transparent;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #ffffff,
|
|
|
|
|
|
stop:1 #f8fafb
|
|
|
|
|
|
);
|
|
|
|
|
|
selection-background-color: #007bff;
|
2025-09-12 20:42:00 +08:00
|
|
|
|
selection-color: white;
|
2025-09-17 17:48:35 +08:00
|
|
|
|
color: #2c3e50;
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QLineEdit:focus {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
border: 2px solid #007bff;
|
|
|
|
|
|
background: white;
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QLineEdit:hover {
|
|
|
|
|
|
border: 2px solid #6c757d;
|
|
|
|
|
|
background: white;
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPushButton {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #4285f4,
|
|
|
|
|
|
stop:0.5 #1976d2,
|
|
|
|
|
|
stop:1 #1565c0
|
|
|
|
|
|
);
|
2025-09-12 20:42:00 +08:00
|
|
|
|
border: none;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
padding: 12px 24px;
|
2025-09-17 17:48:35 +08:00
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
min-width: 180px;
|
|
|
|
|
|
letter-spacing: 0.5px;
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPushButton:hover {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #5294f5,
|
|
|
|
|
|
stop:0.5 #2986d3,
|
|
|
|
|
|
stop:1 #1e74c1
|
|
|
|
|
|
);
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPushButton:pressed {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #3274d4,
|
|
|
|
|
|
stop:0.5 #1666c2,
|
|
|
|
|
|
stop:1 #1455b0
|
|
|
|
|
|
);
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QPushButton:disabled {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #f8f9fa,
|
|
|
|
|
|
stop:1 #e9ecef
|
|
|
|
|
|
);
|
|
|
|
|
|
color: #6c757d;
|
|
|
|
|
|
border: 1px solid #dee2e6;
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
/* 连接成功状态的按钮 */
|
|
|
|
|
|
QPushButton[objectName="connected"] {
|
|
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #28a745,
|
|
|
|
|
|
stop:0.5 #20963b,
|
|
|
|
|
|
stop:1 #1e7e34
|
|
|
|
|
|
);
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
QPushButton[objectName="connected"]:hover {
|
|
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #34ce57,
|
|
|
|
|
|
stop:0.5 #28a745,
|
|
|
|
|
|
stop:1 #20963b
|
|
|
|
|
|
);
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
# 设置全局字体,确保各Windows版本显示一致
|
|
|
|
|
|
font = QFont('Microsoft YaHei', 10) # Windows系统自带字体
|
|
|
|
|
|
QApplication.setFont(font)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置调色板确保颜色一致性
|
|
|
|
|
|
palette = QPalette()
|
2025-09-17 17:48:35 +08:00
|
|
|
|
palette.setColor(QPalette.Window, QColor(248, 250, 251))
|
|
|
|
|
|
palette.setColor(QPalette.WindowText, QColor(44, 62, 80))
|
2025-09-12 20:42:00 +08:00
|
|
|
|
palette.setColor(QPalette.Base, QColor(255, 255, 255))
|
2025-09-17 17:48:35 +08:00
|
|
|
|
palette.setColor(QPalette.AlternateBase, QColor(241, 244, 246))
|
2025-09-12 20:42:00 +08:00
|
|
|
|
palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 255))
|
2025-09-17 17:48:35 +08:00
|
|
|
|
palette.setColor(QPalette.ToolTipText, QColor(44, 62, 80))
|
|
|
|
|
|
palette.setColor(QPalette.Text, QColor(44, 62, 80))
|
|
|
|
|
|
palette.setColor(QPalette.Button, QColor(66, 133, 244))
|
2025-09-12 20:42:00 +08:00
|
|
|
|
palette.setColor(QPalette.ButtonText, QColor(255, 255, 255))
|
|
|
|
|
|
palette.setColor(QPalette.BrightText, QColor(255, 255, 255))
|
2025-09-17 17:48:35 +08:00
|
|
|
|
palette.setColor(QPalette.Highlight, QColor(66, 133, 244))
|
2025-09-12 20:42:00 +08:00
|
|
|
|
palette.setColor(QPalette.HighlightedText, QColor(255, 255, 255))
|
|
|
|
|
|
QApplication.setPalette(palette)
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
def add_visual_effects(self):
|
|
|
|
|
|
"""添加视觉效果"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 为主窗口添加阴影效果
|
|
|
|
|
|
shadow = QGraphicsDropShadowEffect()
|
|
|
|
|
|
shadow.setBlurRadius(20)
|
|
|
|
|
|
shadow.setXOffset(0)
|
|
|
|
|
|
shadow.setYOffset(5)
|
|
|
|
|
|
shadow.setColor(QColor(0, 0, 0, 40))
|
|
|
|
|
|
|
|
|
|
|
|
# 为输入框添加阴影
|
|
|
|
|
|
input_shadow = QGraphicsDropShadowEffect()
|
|
|
|
|
|
input_shadow.setBlurRadius(8)
|
|
|
|
|
|
input_shadow.setXOffset(0)
|
|
|
|
|
|
input_shadow.setYOffset(2)
|
|
|
|
|
|
input_shadow.setColor(QColor(0, 0, 0, 20))
|
|
|
|
|
|
self.token_input.setGraphicsEffect(input_shadow)
|
|
|
|
|
|
|
|
|
|
|
|
# 为按钮添加阴影
|
|
|
|
|
|
button_shadow = QGraphicsDropShadowEffect()
|
|
|
|
|
|
button_shadow.setBlurRadius(12)
|
|
|
|
|
|
button_shadow.setXOffset(0)
|
|
|
|
|
|
button_shadow.setYOffset(4)
|
|
|
|
|
|
button_shadow.setColor(QColor(66, 133, 244, 80))
|
|
|
|
|
|
self.login_btn.setGraphicsEffect(button_shadow)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置对象名称以应用特定样式
|
|
|
|
|
|
self.token_input.setObjectName("tokenInput")
|
|
|
|
|
|
self.login_btn.setObjectName("loginButton")
|
|
|
|
|
|
|
|
|
|
|
|
print("[INFO] 视觉效果已添加")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"[WARNING] 添加视觉效果失败: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
def animate_success(self):
|
|
|
|
|
|
"""连接成功的动画效果"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 按钮轻微放大动画
|
|
|
|
|
|
self.button_animation = QPropertyAnimation(self.login_btn, b"geometry")
|
|
|
|
|
|
original_rect = self.login_btn.geometry()
|
|
|
|
|
|
|
|
|
|
|
|
# 计算放大后的位置(居中放大)
|
|
|
|
|
|
expanded_rect = QRect(
|
|
|
|
|
|
original_rect.x() - 5,
|
|
|
|
|
|
original_rect.y() - 2,
|
|
|
|
|
|
original_rect.width() + 10,
|
|
|
|
|
|
original_rect.height() + 4
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
self.button_animation.setDuration(200)
|
|
|
|
|
|
self.button_animation.setStartValue(original_rect)
|
|
|
|
|
|
self.button_animation.setEndValue(expanded_rect)
|
|
|
|
|
|
self.button_animation.setEasingCurve(QEasingCurve.OutBack)
|
|
|
|
|
|
|
|
|
|
|
|
# 动画完成后恢复原始大小
|
|
|
|
|
|
def restore_size():
|
|
|
|
|
|
restore_animation = QPropertyAnimation(self.login_btn, b"geometry")
|
|
|
|
|
|
restore_animation.setDuration(150)
|
|
|
|
|
|
restore_animation.setStartValue(expanded_rect)
|
|
|
|
|
|
restore_animation.setEndValue(original_rect)
|
|
|
|
|
|
restore_animation.setEasingCurve(QEasingCurve.InBack)
|
|
|
|
|
|
restore_animation.start()
|
|
|
|
|
|
|
|
|
|
|
|
self.button_animation.finished.connect(restore_size)
|
|
|
|
|
|
self.button_animation.start()
|
|
|
|
|
|
|
|
|
|
|
|
print("[INFO] 成功动画已启动")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"[WARNING] 动画效果失败: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
def on_platform_connected(self, platform_name: str, all_platforms: list):
|
|
|
|
|
|
"""处理平台连接成功事件"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
import time
|
|
|
|
|
|
self.connected_platforms = all_platforms.copy()
|
|
|
|
|
|
self.last_platform_connect_time = time.time()
|
|
|
|
|
|
|
|
|
|
|
|
# 短暂显示平台连接成功提示
|
|
|
|
|
|
self.status_label.setText(f"🟢 {platform_name}平台连接成功!")
|
|
|
|
|
|
self.status_label.setStyleSheet(
|
|
|
|
|
|
"color: #28a745; background: rgba(40, 167, 69, 0.15); border-radius: 12px; padding: 5px 10px; font-weight: bold;")
|
|
|
|
|
|
|
|
|
|
|
|
# 停止之前的定时器
|
|
|
|
|
|
if self.status_timer:
|
|
|
|
|
|
self.status_timer.stop()
|
|
|
|
|
|
self.status_timer = None
|
|
|
|
|
|
|
|
|
|
|
|
self.add_log(f"GUI收到平台连接通知: {platform_name}, 当前已连接: {', '.join(all_platforms)}", "INFO")
|
|
|
|
|
|
|
|
|
|
|
|
# 使用线程定时器,更可靠(资源消耗极小)
|
|
|
|
|
|
import threading
|
|
|
|
|
|
|
|
|
|
|
|
def delayed_update():
|
|
|
|
|
|
import time
|
|
|
|
|
|
time.sleep(3) # 等待3秒
|
|
|
|
|
|
# 使用QTimer.singleShot确保在主线程中执行GUI更新
|
|
|
|
|
|
QTimer.singleShot(0, self.delayed_platform_summary)
|
|
|
|
|
|
|
|
|
|
|
|
# 启动轻量级守护线程(3秒后自动销毁)
|
|
|
|
|
|
timer_thread = threading.Thread(target=delayed_update, daemon=True)
|
|
|
|
|
|
timer_thread.start()
|
|
|
|
|
|
|
|
|
|
|
|
self.add_log("3秒线程定时器已启动,将自动切换到汇总显示", "INFO")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"处理平台连接事件失败: {e}", "ERROR")
|
|
|
|
|
|
|
2025-09-28 17:00:02 +08:00
|
|
|
|
def on_token_error(self, error_content: str):
|
|
|
|
|
|
"""处理token错误 - 显示红色错误信息并停止所有操作"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.add_log(f"Token验证失败: {error_content}", "ERROR")
|
2025-09-29 15:38:34 +08:00
|
|
|
|
|
2025-09-28 17:00:02 +08:00
|
|
|
|
# 在状态标签显示红色错误信息
|
|
|
|
|
|
self.status_label.setText(f"🔴 {error_content}")
|
|
|
|
|
|
self.status_label.setStyleSheet(
|
|
|
|
|
|
"color: #dc3545; background: rgba(220, 53, 69, 0.1); border-radius: 12px; padding: 5px 10px; font-weight: bold;")
|
2025-09-29 15:38:34 +08:00
|
|
|
|
|
2025-09-28 17:00:02 +08:00
|
|
|
|
# 重置按钮状态
|
|
|
|
|
|
self.login_btn.setEnabled(True)
|
|
|
|
|
|
self.login_btn.setText("重新连接")
|
|
|
|
|
|
self.login_btn.setObjectName("loginButton") # 恢复原始样式
|
2025-09-29 15:38:34 +08:00
|
|
|
|
|
2025-09-28 17:00:02 +08:00
|
|
|
|
# 清空已连接平台列表
|
|
|
|
|
|
self.connected_platforms.clear()
|
2025-09-29 15:38:34 +08:00
|
|
|
|
|
2025-09-28 17:00:02 +08:00
|
|
|
|
self.add_log("由于token无效,已停止所有连接操作", "ERROR")
|
2025-09-29 15:38:34 +08:00
|
|
|
|
|
2025-09-28 17:00:02 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"处理token错误失败: {e}", "ERROR")
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
def delayed_platform_summary(self):
|
|
|
|
|
|
"""定时器触发的汇总显示更新"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.add_log("🎯 定时器触发!开始执行汇总显示更新", "INFO")
|
|
|
|
|
|
self.update_platform_summary()
|
|
|
|
|
|
self.add_log("🎯 定时器执行完成", "INFO")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"定时器执行失败: {e}", "ERROR")
|
|
|
|
|
|
|
|
|
|
|
|
def check_and_update_summary(self):
|
|
|
|
|
|
"""检查是否应该更新汇总显示"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
import time
|
|
|
|
|
|
elapsed = time.time() - self.last_platform_connect_time
|
|
|
|
|
|
self.add_log(f"检查汇总更新: 距离最后连接 {elapsed:.1f} 秒", "INFO")
|
|
|
|
|
|
|
|
|
|
|
|
# 检查是否在最近1秒内还有新的平台连接
|
|
|
|
|
|
if elapsed >= 2.8: # 距离最后连接超过2.8秒
|
|
|
|
|
|
self.add_log("满足条件,开始更新汇总显示", "INFO")
|
|
|
|
|
|
self.update_platform_summary()
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 如果还有新连接,再延迟一会儿
|
|
|
|
|
|
self.add_log(f"还需等待 {2.8 - elapsed:.1f} 秒,延迟更新", "INFO")
|
|
|
|
|
|
QTimer.singleShot(1000, self.check_and_update_summary)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"检查汇总更新失败: {e}", "ERROR")
|
|
|
|
|
|
|
|
|
|
|
|
def update_platform_summary(self):
|
|
|
|
|
|
"""更新平台连接汇总显示"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.add_log(f"开始更新汇总显示,当前连接平台: {self.connected_platforms}", "INFO")
|
|
|
|
|
|
|
|
|
|
|
|
if self.connected_platforms:
|
|
|
|
|
|
platforms_text = "、".join(self.connected_platforms)
|
|
|
|
|
|
summary_text = f"🟢 已连接: {platforms_text}"
|
|
|
|
|
|
self.status_label.setText(summary_text)
|
|
|
|
|
|
self.status_label.setStyleSheet(
|
|
|
|
|
|
"color: #28a745; background: rgba(40, 167, 69, 0.1); border-radius: 12px; padding: 5px 10px;")
|
|
|
|
|
|
self.add_log(f"汇总显示已更新: {summary_text}", "INFO")
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.status_label.setText("🟢 连接成功!等待平台指令...")
|
|
|
|
|
|
self.status_label.setStyleSheet(
|
|
|
|
|
|
"color: #28a745; background: rgba(40, 167, 69, 0.1); border-radius: 12px; padding: 5px 10px;")
|
|
|
|
|
|
self.add_log("已更新为等待平台指令状态", "INFO")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"更新平台汇总显示失败: {e}", "ERROR")
|
|
|
|
|
|
|
|
|
|
|
|
def init_system_tray(self):
|
|
|
|
|
|
"""初始化系统托盘"""
|
|
|
|
|
|
# 检查系统是否支持托盘
|
|
|
|
|
|
if not QSystemTrayIcon.isSystemTrayAvailable():
|
|
|
|
|
|
QMessageBox.critical(None, "系统托盘",
|
|
|
|
|
|
"系统托盘不可用")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 创建托盘图标
|
|
|
|
|
|
self.tray_icon = QSystemTrayIcon(self)
|
|
|
|
|
|
|
|
|
|
|
|
# 使用自定义的美观图标
|
|
|
|
|
|
try:
|
2025-09-18 15:52:03 +08:00
|
|
|
|
# 导入路径处理函数
|
|
|
|
|
|
from windows_taskbar_fix import get_resource_path
|
|
|
|
|
|
tray_icon_path = get_resource_path("static/ai_assistant_icon_16.png")
|
2025-09-17 17:48:35 +08:00
|
|
|
|
if os.path.exists(tray_icon_path):
|
|
|
|
|
|
from PyQt5.QtGui import QIcon
|
|
|
|
|
|
self.tray_icon.setIcon(QIcon(tray_icon_path))
|
|
|
|
|
|
print(f"[INFO] 已加载托盘图标: {tray_icon_path}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"[WARNING] 托盘图标文件不存在: {tray_icon_path}")
|
|
|
|
|
|
# 备用方案:使用系统图标
|
|
|
|
|
|
self.tray_icon.setIcon(self.style().standardIcon(self.style().SP_ComputerIcon))
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"[WARNING] 加载托盘图标失败: {e}, 使用默认图标")
|
|
|
|
|
|
# 备用方案:使用系统图标
|
|
|
|
|
|
self.tray_icon.setIcon(self.style().standardIcon(self.style().SP_ComputerIcon))
|
|
|
|
|
|
|
|
|
|
|
|
# 创建托盘菜单
|
|
|
|
|
|
tray_menu = QMenu()
|
|
|
|
|
|
|
|
|
|
|
|
# 显示窗口动作
|
|
|
|
|
|
show_action = QAction("显示主窗口", self)
|
|
|
|
|
|
show_action.triggered.connect(self.show_window)
|
|
|
|
|
|
tray_menu.addAction(show_action)
|
|
|
|
|
|
|
|
|
|
|
|
# 分隔线
|
|
|
|
|
|
tray_menu.addSeparator()
|
|
|
|
|
|
|
|
|
|
|
|
# 退出动作
|
|
|
|
|
|
quit_action = QAction("退出程序", self)
|
|
|
|
|
|
quit_action.triggered.connect(self.quit_application)
|
|
|
|
|
|
tray_menu.addAction(quit_action)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置托盘菜单
|
|
|
|
|
|
self.tray_icon.setContextMenu(tray_menu)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置托盘提示
|
|
|
|
|
|
self.tray_icon.setToolTip("AI客服智能助手")
|
|
|
|
|
|
|
|
|
|
|
|
# 双击托盘图标显示窗口
|
|
|
|
|
|
self.tray_icon.activated.connect(self.tray_icon_activated)
|
|
|
|
|
|
|
|
|
|
|
|
# 显示托盘图标
|
|
|
|
|
|
self.tray_icon.show()
|
|
|
|
|
|
|
|
|
|
|
|
print("[INFO] 系统托盘已初始化")
|
|
|
|
|
|
|
|
|
|
|
|
def tray_icon_activated(self, reason):
|
|
|
|
|
|
"""托盘图标被激活时的处理"""
|
|
|
|
|
|
if reason == QSystemTrayIcon.DoubleClick:
|
|
|
|
|
|
self.show_window()
|
|
|
|
|
|
|
|
|
|
|
|
def show_window(self):
|
|
|
|
|
|
"""显示主窗口"""
|
|
|
|
|
|
self.show()
|
|
|
|
|
|
self.raise_()
|
|
|
|
|
|
self.activateWindow()
|
|
|
|
|
|
print("[INFO] 主窗口已显示")
|
|
|
|
|
|
|
|
|
|
|
|
def quit_application(self):
|
|
|
|
|
|
"""真正退出应用程序"""
|
|
|
|
|
|
print("[INFO] 正在退出应用程序...")
|
|
|
|
|
|
|
|
|
|
|
|
# 执行原来的关闭逻辑
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 使用 WebSocket 管理器断开所有连接
|
|
|
|
|
|
from WebSocket.backend_singleton import get_websocket_manager
|
|
|
|
|
|
ws_manager = get_websocket_manager()
|
|
|
|
|
|
ws_manager.disconnect_all()
|
|
|
|
|
|
|
|
|
|
|
|
# 停止所有工作线程(向后兼容)
|
|
|
|
|
|
workers = []
|
|
|
|
|
|
if hasattr(self, 'jd_worker') and self.jd_worker:
|
|
|
|
|
|
workers.append(self.jd_worker)
|
|
|
|
|
|
if hasattr(self, 'douyin_worker') and self.douyin_worker:
|
|
|
|
|
|
workers.append(self.douyin_worker)
|
|
|
|
|
|
if hasattr(self, 'qian_niu_worker') and self.qian_niu_worker:
|
|
|
|
|
|
workers.append(self.qian_niu_worker)
|
|
|
|
|
|
if hasattr(self, 'pdd_worker') and self.pdd_worker:
|
|
|
|
|
|
workers.append(self.pdd_worker)
|
|
|
|
|
|
|
|
|
|
|
|
# 停止所有线程
|
|
|
|
|
|
for worker in workers:
|
|
|
|
|
|
if worker.isRunning():
|
|
|
|
|
|
worker.stop()
|
|
|
|
|
|
worker.quit()
|
|
|
|
|
|
worker.wait(1000)
|
|
|
|
|
|
|
|
|
|
|
|
# 强制关闭事件循环
|
|
|
|
|
|
for worker in workers:
|
|
|
|
|
|
if hasattr(worker, 'loop') and worker.loop and not worker.loop.is_closed():
|
|
|
|
|
|
worker.loop.close()
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"关闭时发生错误: {str(e)}")
|
|
|
|
|
|
finally:
|
|
|
|
|
|
# 隐藏托盘图标
|
|
|
|
|
|
if hasattr(self, 'tray_icon'):
|
|
|
|
|
|
self.tray_icon.hide()
|
|
|
|
|
|
|
|
|
|
|
|
# 退出应用程序
|
|
|
|
|
|
QApplication.quit()
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
def add_log(self, message, log_type="INFO"):
|
|
|
|
|
|
"""添加日志信息 - 只输出到终端"""
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
|
|
|
|
|
|
# 根据日志类型设置颜色(ANSI颜色码)
|
|
|
|
|
|
colors = {
|
2025-09-17 17:48:35 +08:00
|
|
|
|
"ERROR": "\033[91m", # 红色
|
2025-09-12 20:42:00 +08:00
|
|
|
|
"SUCCESS": "\033[92m", # 绿色
|
|
|
|
|
|
"WARNING": "\033[93m", # 黄色
|
2025-09-17 17:48:35 +08:00
|
|
|
|
"INFO": "\033[94m", # 蓝色
|
|
|
|
|
|
"DEBUG": "\033[95m" # 紫色
|
2025-09-12 20:42:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
reset = "\033[0m" # 重置颜色
|
|
|
|
|
|
|
|
|
|
|
|
color = colors.get(log_type, colors["INFO"])
|
|
|
|
|
|
log_entry = f"{color}[{timestamp}] [{log_type}] {message}{reset}"
|
|
|
|
|
|
print(log_entry)
|
|
|
|
|
|
|
|
|
|
|
|
def login(self):
|
|
|
|
|
|
"""处理连接逻辑"""
|
|
|
|
|
|
# 防止重复快速点击
|
|
|
|
|
|
import time
|
|
|
|
|
|
current_time = time.time()
|
|
|
|
|
|
if current_time - self.last_login_time < self.login_cooldown:
|
|
|
|
|
|
self.add_log(f"请等待 {self.login_cooldown} 秒后再试", "WARNING")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
self.last_login_time = current_time
|
|
|
|
|
|
|
|
|
|
|
|
token = self.token_input.text().strip()
|
|
|
|
|
|
if not token:
|
|
|
|
|
|
self.add_log("请输入有效的连接令牌", "ERROR")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 禁用连接按钮,防止重复点击
|
|
|
|
|
|
self.login_btn.setEnabled(False)
|
|
|
|
|
|
self.login_btn.setText("连接中...")
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.status_label.setText("正在连接服务...")
|
|
|
|
|
|
self.status_label.setStyleSheet("color: #ffc107;")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
success = False
|
2025-09-12 20:42:00 +08:00
|
|
|
|
try:
|
|
|
|
|
|
# 使用 WebSocket 管理器处理连接
|
|
|
|
|
|
ws_manager = get_websocket_manager()
|
2025-09-17 17:48:35 +08:00
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
# 设置回调函数
|
|
|
|
|
|
ws_manager.set_callbacks(
|
|
|
|
|
|
log=self.add_log,
|
|
|
|
|
|
success=lambda: self.add_log("WebSocket连接管理器连接成功", "SUCCESS"),
|
2025-09-17 17:48:35 +08:00
|
|
|
|
error=lambda error: self.add_log(f"WebSocket连接管理器错误: {error}", "ERROR"),
|
2025-09-28 17:00:02 +08:00
|
|
|
|
platform_connected=self.on_platform_connected, # 新增:平台连接回调
|
|
|
|
|
|
token_error=self.on_token_error # 新增:token错误回调
|
2025-09-12 20:42:00 +08:00
|
|
|
|
)
|
2025-09-17 17:48:35 +08:00
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
# 连接后端
|
|
|
|
|
|
success = ws_manager.connect_backend(token)
|
2025-09-17 17:48:35 +08:00
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
if success:
|
|
|
|
|
|
self.add_log("已启动WebSocket连接管理器", "SUCCESS")
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.status_label.setText("🟢 连接成功!等待平台指令...")
|
|
|
|
|
|
self.status_label.setStyleSheet(
|
|
|
|
|
|
"color: #28a745; background: rgba(40, 167, 69, 0.1); border-radius: 12px; padding: 5px 10px;")
|
|
|
|
|
|
self.login_btn.setText("✓ 已连接")
|
|
|
|
|
|
self.login_btn.setObjectName("connected")
|
|
|
|
|
|
self.login_btn.setStyleSheet(self.login_btn.styleSheet()) # 刷新样式
|
|
|
|
|
|
|
|
|
|
|
|
# 添加成功动画效果
|
|
|
|
|
|
self.animate_success()
|
2025-09-12 20:42:00 +08:00
|
|
|
|
else:
|
|
|
|
|
|
self.add_log("WebSocket连接管理器启动失败", "ERROR")
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.status_label.setText("🔴 连接失败,请检查令牌")
|
|
|
|
|
|
self.status_label.setStyleSheet(
|
|
|
|
|
|
"color: #dc3545; background: rgba(220, 53, 69, 0.1); border-radius: 12px; padding: 5px 10px;")
|
|
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"连接失败: {e}", "ERROR")
|
2025-09-17 17:48:35 +08:00
|
|
|
|
self.status_label.setText(f"🔴 连接失败: {str(e)}")
|
|
|
|
|
|
self.status_label.setStyleSheet(
|
|
|
|
|
|
"color: #dc3545; background: rgba(220, 53, 69, 0.1); border-radius: 12px; padding: 5px 10px;")
|
2025-09-12 20:42:00 +08:00
|
|
|
|
finally:
|
|
|
|
|
|
self.login_btn.setEnabled(True)
|
2025-09-17 17:48:35 +08:00
|
|
|
|
if not success:
|
|
|
|
|
|
self.login_btn.setText("重新连接")
|
|
|
|
|
|
self.login_btn.setObjectName("loginButton") # 恢复原始样式
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
def verify_token(self, token):
|
|
|
|
|
|
"""简化的令牌校验:非空即通过(实际校验由后端承担)"""
|
|
|
|
|
|
return bool(token)
|
|
|
|
|
|
|
|
|
|
|
|
def closeEvent(self, event):
|
2025-09-17 17:48:35 +08:00
|
|
|
|
"""窗口关闭事件处理 - 最小化到托盘"""
|
|
|
|
|
|
if hasattr(self, 'tray_icon') and self.tray_icon.isVisible():
|
|
|
|
|
|
# 首次最小化时显示提示消息
|
|
|
|
|
|
if not hasattr(self, '_tray_message_shown'):
|
|
|
|
|
|
self.tray_icon.showMessage(
|
|
|
|
|
|
"AI客服智能助手",
|
|
|
|
|
|
"程序已最小化到系统托盘。双击托盘图标可重新显示窗口。",
|
|
|
|
|
|
QSystemTrayIcon.Information,
|
|
|
|
|
|
3000
|
|
|
|
|
|
)
|
|
|
|
|
|
self._tray_message_shown = True
|
|
|
|
|
|
|
|
|
|
|
|
# 隐藏窗口而不是关闭
|
|
|
|
|
|
self.hide()
|
|
|
|
|
|
event.ignore()
|
|
|
|
|
|
print("[INFO] 窗口已最小化到系统托盘")
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 如果托盘不可用,则正常退出
|
|
|
|
|
|
self.quit_application()
|
2025-09-12 20:42:00 +08:00
|
|
|
|
event.accept()
|
|
|
|
|
|
|
2025-09-29 15:38:34 +08:00
|
|
|
|
def setup_version_checker(self):
|
|
|
|
|
|
"""设置版本检查器的GUI回调"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
from WebSocket.backend_singleton import get_websocket_manager
|
|
|
|
|
|
ws_manager = get_websocket_manager()
|
|
|
|
|
|
if ws_manager:
|
|
|
|
|
|
ws_manager.gui_update_callback = self.show_update_notification
|
|
|
|
|
|
self.add_log("✅ 版本检查器GUI回调已设置", "SUCCESS")
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.add_log("⚠️ WebSocket管理器未初始化", "WARNING")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"❌ 设置版本检查器失败: {e}", "ERROR")
|
|
|
|
|
|
|
|
|
|
|
|
def show_update_notification(self, latest_version, download_url):
|
|
|
|
|
|
"""显示版本更新通知"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
reply = QMessageBox.question(
|
|
|
|
|
|
self,
|
|
|
|
|
|
"版本更新",
|
|
|
|
|
|
f"发现新版本 {latest_version},是否立即更新?\n\n点击确定将打开下载页面。",
|
|
|
|
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
|
|
|
|
QMessageBox.Yes
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
|
|
|
self.trigger_update(download_url)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"❌ 显示更新通知失败: {e}", "ERROR")
|
|
|
|
|
|
|
|
|
|
|
|
def trigger_update(self, download_url):
|
|
|
|
|
|
"""触发更新下载"""
|
|
|
|
|
|
import webbrowser
|
|
|
|
|
|
try:
|
|
|
|
|
|
webbrowser.open(download_url)
|
|
|
|
|
|
self.add_log("✅ 已打开更新下载页面", "SUCCESS")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.add_log(f"❌ 打开下载页面失败: {e}", "ERROR")
|
|
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
"""主函数,用于测试界面"""
|
|
|
|
|
|
app = QApplication(sys.argv)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置应用程序属性
|
|
|
|
|
|
app.setApplicationName(config.WINDOW_TITLE)
|
|
|
|
|
|
app.setApplicationVersion(config.VERSION)
|
|
|
|
|
|
|
2025-09-17 17:48:35 +08:00
|
|
|
|
# 设置应用程序唯一ID(重要:避免和Python默认图标混淆)
|
|
|
|
|
|
try:
|
|
|
|
|
|
import ctypes
|
|
|
|
|
|
# 设置应用程序用户模型ID,让Windows识别为独立应用
|
|
|
|
|
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("ShuidropAI.CustomerService.1.0")
|
|
|
|
|
|
print("[INFO] 已设置应用程序唯一ID")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"[WARNING] 设置应用程序ID失败: {e}")
|
|
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
# 创建主窗口
|
|
|
|
|
|
window = LoginWindow()
|
2025-09-17 17:48:35 +08:00
|
|
|
|
|
|
|
|
|
|
# 统一设置所有图标(使用任务栏修复模块)
|
|
|
|
|
|
try:
|
|
|
|
|
|
app_icon = setup_windows_taskbar_icon(app, window)
|
|
|
|
|
|
print("[INFO] 所有图标设置完成")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"[WARNING] 图标设置失败: {e}")
|
|
|
|
|
|
# 备用方案:使用简单的图标设置
|
|
|
|
|
|
try:
|
2025-09-18 15:52:03 +08:00
|
|
|
|
# 导入路径处理函数
|
|
|
|
|
|
from windows_taskbar_fix import get_resource_path
|
|
|
|
|
|
icon_path = get_resource_path("static/ai_assistant_icon_32.png")
|
2025-09-17 17:48:35 +08:00
|
|
|
|
if os.path.exists(icon_path):
|
|
|
|
|
|
from PyQt5.QtGui import QIcon
|
|
|
|
|
|
backup_icon = QIcon(icon_path)
|
|
|
|
|
|
app.setWindowIcon(backup_icon)
|
|
|
|
|
|
window.setWindowIcon(backup_icon)
|
|
|
|
|
|
print(f"[INFO] 已使用备用图标: {icon_path}")
|
|
|
|
|
|
except Exception as e2:
|
|
|
|
|
|
print(f"[ERROR] 备用图标也设置失败: {e2}")
|
|
|
|
|
|
|
2025-09-12 20:42:00 +08:00
|
|
|
|
window.show() # 程序启动断点
|
|
|
|
|
|
|
|
|
|
|
|
# 运行应用程序
|
|
|
|
|
|
sys.exit(app.exec_())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2025-09-17 17:48:35 +08:00
|
|
|
|
main() # sd_acF0TisgfFOtsBm4ytqb17MQbcxuX9Vp 测试令牌(token)
|
2025-09-12 20:42:00 +08:00
|
|
|
|
# username = "KLD测试"
|
|
|
|
|
|
# password = "kld168168"
|
|
|
|
|
|
# taobao nickname = "tb420723827:redboat"
|
|
|
|
|
|
|