- 将配置对话框改为标签页布局,分为API配置、爬虫配置和界面设置 - 优化主窗口UI,包括指示灯样式、分数显示和截图区域 - 添加窗口圆角和半透明效果 - 改进右键菜单功能,增加刷新操作 - 优化状态显示和分数颜色标识
618 lines
22 KiB
Python
618 lines
22 KiB
Python
"""
|
||
PySide6 GUI界面模块
|
||
"""
|
||
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||
QPushButton, QSlider, QDialog, QFormLayout,
|
||
QLineEdit, QSpinBox, QMessageBox, QSystemTrayIcon,
|
||
QMenu, QTextEdit, QGroupBox, QDialogButtonBox, QCheckBox, QScrollArea, QFileDialog,
|
||
QTabWidget)
|
||
from PySide6.QtCore import Qt, QTimer, Signal, QPoint
|
||
from PySide6.QtGui import QFont, QColor, QPainter, QBrush, QPen, QIcon, QAction, QPixmap
|
||
from typing import Callable, Optional
|
||
from loguru import logger
|
||
|
||
|
||
class SentimentIndicator(QWidget):
|
||
"""情感指示灯组件"""
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.score = 50
|
||
self.label_text = "中性"
|
||
self.setMinimumSize(100, 100)
|
||
|
||
def set_value(self, score: int, label: str = None):
|
||
"""设置数值和标签"""
|
||
self.score = max(0, min(100, score))
|
||
if label:
|
||
self.label_text = label
|
||
self.update()
|
||
|
||
def paintEvent(self, event):
|
||
"""绘制指示灯"""
|
||
painter = QPainter(self)
|
||
painter.setRenderHint(QPainter.Antialiasing)
|
||
|
||
center = self.rect().center()
|
||
radius = min(self.width(), self.height()) // 2 - 10
|
||
|
||
# 根据分数确定颜色
|
||
color = self._get_color(self.score)
|
||
|
||
# 绘制外圈
|
||
painter.setPen(QPen(QColor(100, 100, 100), 2))
|
||
painter.setBrush(QBrush(QColor(30, 30, 30)))
|
||
painter.drawEllipse(center, radius, radius)
|
||
|
||
# 绘制内圈(渐变效果)
|
||
gradient_color = QColor(color)
|
||
painter.setPen(Qt.NoPen)
|
||
painter.setBrush(QBrush(gradient_color))
|
||
painter.drawEllipse(center, radius - 4, radius - 4)
|
||
|
||
# 绘制发光效果
|
||
for i in range(3, 0, -1):
|
||
glow_color = QColor(color)
|
||
glow_color.setAlpha(50 // i)
|
||
painter.setBrush(QBrush(glow_color))
|
||
painter.drawEllipse(center, radius - i * 5, radius - i * 5)
|
||
|
||
def _get_color(self, score: int) -> QColor:
|
||
"""根据分数获取颜色"""
|
||
# 45-55分区间:每1分一个颜色(从绿色到黄色渐变)
|
||
if 45 <= score <= 55:
|
||
# 在45-55分之间,每1分一个颜色
|
||
ratio = (score - 45) / 10 # 0到1之间的比例
|
||
# 从绿色(0, 200, 0)渐变到黄色(255, 255, 0)
|
||
r = int(0 + 255 * ratio)
|
||
g = 200 if ratio < 0.5 else int(200 + 55 * (ratio - 0.5) * 2)
|
||
b = int(0 + 0 * ratio)
|
||
return QColor(r, g, b)
|
||
|
||
# 45分以下:每5分一个颜色(从深蓝到绿色渐变)
|
||
elif score < 45:
|
||
# 将分数映射到0-8的区间(0-40分,每5分一个级别)
|
||
level = score // 5
|
||
# 从深蓝色(0, 100, 255)渐变到绿色(0, 200, 0)
|
||
ratio = level / 8 # 0-8对应0-40分
|
||
r = int(0 + 0 * ratio)
|
||
g = int(100 + 100 * ratio)
|
||
b = int(255 - 255 * ratio)
|
||
return QColor(r, g, b)
|
||
|
||
# 55分以上:每5分一个颜色(从黄色到红色渐变)
|
||
else:
|
||
# 将分数映射到0-8的区间(60-100分,每5分一个级别)
|
||
level = (score - 60) // 5
|
||
level = max(0, min(8, level)) # 限制在0-8范围内
|
||
# 从黄色(255, 255, 0)渐变到红色(255, 0, 0)
|
||
ratio = level / 8
|
||
r = 255
|
||
g = int(255 - 255 * ratio)
|
||
b = 0
|
||
return QColor(r, g, b)
|
||
|
||
def get_description(self, score: int) -> str:
|
||
"""获取描述文本"""
|
||
if score < 30:
|
||
return "极度悲观"
|
||
elif score < 39:
|
||
return "悲观"
|
||
elif score < 45:
|
||
return "偏悲观"
|
||
elif score < 55:
|
||
return "中立"
|
||
elif score < 65:
|
||
return "偏乐观"
|
||
elif score < 70:
|
||
return "乐观"
|
||
else:
|
||
return "极度乐观"
|
||
|
||
|
||
class ConfigDialog(QDialog):
|
||
"""配置对话框"""
|
||
|
||
def __init__(self, config_manager, parent=None):
|
||
super().__init__(parent)
|
||
self.config_manager = config_manager
|
||
self.setWindowTitle("配置")
|
||
self.setMinimumWidth(400)
|
||
self._init_ui()
|
||
|
||
def _init_ui(self):
|
||
layout = QVBoxLayout(self)
|
||
layout.setContentsMargins(0, 0, 0, 0)
|
||
|
||
# 创建标签页
|
||
tab_widget = QTabWidget()
|
||
tab_widget.setStyleSheet(
|
||
"QTabWidget::pane { border: none; }"
|
||
"QTabBar::tab { padding: 12px 20px; font-size: 13px; }"
|
||
"QTabBar::tab:selected { background-color: #2196F3; color: white; }"
|
||
)
|
||
|
||
# API配置标签页
|
||
api_tab = QWidget()
|
||
api_layout = QFormLayout(api_tab)
|
||
api_layout.setContentsMargins(20, 20, 20, 20)
|
||
api_layout.setSpacing(12)
|
||
|
||
llm_config = self.config_manager.llm_api_config
|
||
|
||
self.base_url_edit = QLineEdit(llm_config.get('base_url', ''))
|
||
self.base_url_edit.setPlaceholderText("https://api.openai.com/v1")
|
||
self.api_key_edit = QLineEdit(llm_config.get('api_key', ''))
|
||
self.api_key_edit.setEchoMode(QLineEdit.Password)
|
||
self.model_edit = QLineEdit(llm_config.get('model', ''))
|
||
self.model_edit.setPlaceholderText("gpt-3.5-turbo")
|
||
self.timeout_spin = QSpinBox()
|
||
self.timeout_spin.setRange(10, 300)
|
||
self.timeout_spin.setValue(llm_config.get('timeout', 30))
|
||
self.timeout_spin.setSuffix(" 秒")
|
||
|
||
api_layout.addRow("API Base URL:", self.base_url_edit)
|
||
api_layout.addRow("API Key:", self.api_key_edit)
|
||
api_layout.addRow("Model:", self.model_edit)
|
||
api_layout.addRow("超时时间:", self.timeout_spin)
|
||
|
||
# 爬虫配置标签页
|
||
spider_tab = QWidget()
|
||
spider_layout = QFormLayout(spider_tab)
|
||
spider_layout.setContentsMargins(20, 20, 20, 20)
|
||
spider_layout.setSpacing(12)
|
||
|
||
spider_config = self.config_manager.spider_config
|
||
|
||
self.url_edit = QLineEdit(spider_config.get('target_url', ''))
|
||
self.url_edit.setPlaceholderText("https://example.com")
|
||
self.xpath_edit = QLineEdit(spider_config.get('xpath', ''))
|
||
self.xpath_edit.setPlaceholderText("//div[@class='content']")
|
||
self.user_agent_edit = QLineEdit(spider_config.get('user_agent', ''))
|
||
self.user_agent_edit.setPlaceholderText("Mozilla/5.0...")
|
||
self.interval_spin = QSpinBox()
|
||
self.interval_spin.setRange(10, 3600)
|
||
self.interval_spin.setValue(spider_config.get('fetch_interval', 15))
|
||
self.interval_spin.setSuffix(" 秒")
|
||
|
||
spider_layout.addRow("目标 URL:", self.url_edit)
|
||
spider_layout.addRow("XPath 表达式:", self.xpath_edit)
|
||
spider_layout.addRow("User Agent:", self.user_agent_edit)
|
||
spider_layout.addRow("刷新间隔:", self.interval_spin)
|
||
|
||
# Chrome浏览器路径
|
||
chrome_path_layout = QHBoxLayout()
|
||
chrome_path_layout.setSpacing(8)
|
||
self.chrome_path_edit = QLineEdit(spider_config.get('chrome_path', ''))
|
||
self.chrome_path_edit.setPlaceholderText("留空则自动查找Chrome浏览器")
|
||
self.chrome_browse_btn = QPushButton("浏览...")
|
||
self.chrome_browse_btn.setFixedWidth(60)
|
||
self.chrome_browse_btn.clicked.connect(self._browse_chrome_path)
|
||
chrome_path_layout.addWidget(self.chrome_path_edit)
|
||
chrome_path_layout.addWidget(self.chrome_browse_btn)
|
||
spider_layout.addRow("Chrome 路径:", chrome_path_layout)
|
||
|
||
# 界面配置标签页
|
||
ui_tab = QWidget()
|
||
ui_layout = QFormLayout(ui_tab)
|
||
ui_layout.setContentsMargins(20, 20, 20, 20)
|
||
ui_layout.setSpacing(12)
|
||
|
||
ui_config = self.config_manager.ui_config
|
||
|
||
# 透明度
|
||
opacity_layout = QHBoxLayout()
|
||
opacity_layout.setSpacing(8)
|
||
self.opacity_slider = QSlider(Qt.Horizontal)
|
||
self.opacity_slider.setRange(30, 100)
|
||
self.opacity_slider.setValue(int(ui_config.get('opacity', 0.9) * 100))
|
||
self.opacity_slider.setFixedWidth(120)
|
||
self.opacity_label = QLabel(f"{int(ui_config.get('opacity', 0.9) * 100)}%")
|
||
self.opacity_label.setFixedWidth(40)
|
||
self.opacity_slider.valueChanged.connect(
|
||
lambda v: self.opacity_label.setText(f"{v}%")
|
||
)
|
||
opacity_layout.addWidget(self.opacity_slider)
|
||
opacity_layout.addWidget(self.opacity_label)
|
||
opacity_layout.addStretch()
|
||
|
||
# 窗口置顶
|
||
self.ontop_btn = QPushButton("窗口置顶")
|
||
self.ontop_btn.setCheckable(True)
|
||
self.ontop_btn.setFixedWidth(100)
|
||
self.ontop_btn.setChecked(ui_config.get('is_on_top', True))
|
||
self.ontop_btn.setStyleSheet(
|
||
"QPushButton { background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; padding: 6px; }"
|
||
"QPushButton:checked { background-color: #2196F3; color: white; }"
|
||
)
|
||
|
||
ui_layout.addRow("透明度:", opacity_layout)
|
||
ui_layout.addRow("窗口行为:", self.ontop_btn)
|
||
|
||
# 阈值配置
|
||
thresholds = ui_config.get('thresholds', {})
|
||
|
||
threshold_layout = QHBoxLayout()
|
||
threshold_layout.setSpacing(12)
|
||
|
||
self.cold_spin = QSpinBox()
|
||
self.cold_spin.setRange(0, 50)
|
||
self.cold_spin.setValue(thresholds.get('cold', 30))
|
||
self.cold_spin.setSuffix(" 分")
|
||
self.cold_spin.setFixedWidth(80)
|
||
|
||
self.warm_spin = QSpinBox()
|
||
self.warm_spin.setRange(50, 100)
|
||
self.warm_spin.setValue(thresholds.get('warm', 70))
|
||
self.warm_spin.setSuffix(" 分")
|
||
self.warm_spin.setFixedWidth(80)
|
||
|
||
threshold_layout.addWidget(QLabel("寒冷:"))
|
||
threshold_layout.addWidget(self.cold_spin)
|
||
threshold_layout.addWidget(QLabel("温暖:"))
|
||
threshold_layout.addWidget(self.warm_spin)
|
||
threshold_layout.addStretch()
|
||
|
||
ui_layout.addRow("阈值设置:", threshold_layout)
|
||
|
||
# 添加标签页
|
||
tab_widget.addTab(api_tab, "API 配置")
|
||
tab_widget.addTab(spider_tab, "爬虫配置")
|
||
tab_widget.addTab(ui_tab, "界面设置")
|
||
|
||
layout.addWidget(tab_widget)
|
||
|
||
# 底部按钮
|
||
button_layout = QHBoxLayout()
|
||
button_layout.setContentsMargins(20, 0, 20, 20)
|
||
button_layout.setSpacing(8)
|
||
|
||
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||
button_box.accepted.connect(self._save_config)
|
||
button_box.rejected.connect(self.reject)
|
||
|
||
# 设置按钮样式
|
||
ok_btn = button_box.button(QDialogButtonBox.Ok)
|
||
cancel_btn = button_box.button(QDialogButtonBox.Cancel)
|
||
ok_btn.setStyleSheet(
|
||
"QPushButton { background-color: #2196F3; color: white; border: none; border-radius: 4px; padding: 8px 16px; }"
|
||
"QPushButton:hover { background-color: #1976D2; }"
|
||
)
|
||
cancel_btn.setStyleSheet(
|
||
"QPushButton { background-color: #f5f5f5; color: #333; border: 1px solid #ddd; border-radius: 4px; padding: 8px 16px; }"
|
||
"QPushButton:hover { background-color: #e0e0e0; }"
|
||
)
|
||
|
||
button_layout.addStretch()
|
||
button_layout.addWidget(button_box)
|
||
layout.addLayout(button_layout)
|
||
|
||
def _browse_chrome_path(self):
|
||
"""浏览Chrome路径"""
|
||
file_path, _ = QFileDialog.getOpenFileName(
|
||
self,
|
||
"选择Chrome浏览器可执行文件",
|
||
"",
|
||
"Chrome浏览器 (*.exe);;所有文件 (*.*)"
|
||
)
|
||
if file_path:
|
||
self.chrome_path_edit.setText(file_path)
|
||
|
||
def _save_config(self):
|
||
"""保存配置"""
|
||
# LLM API
|
||
self.config_manager.update_llm_api(
|
||
base_url=self.base_url_edit.text(),
|
||
api_key=self.api_key_edit.text(),
|
||
model=self.model_edit.text(),
|
||
timeout=self.timeout_spin.value()
|
||
)
|
||
|
||
# 爬虫
|
||
self.config_manager.update_spider(
|
||
target_url=self.url_edit.text(),
|
||
xpath=self.xpath_edit.text(),
|
||
user_agent=self.user_agent_edit.text(),
|
||
fetch_interval=self.interval_spin.value(),
|
||
chrome_path=self.chrome_path_edit.text()
|
||
)
|
||
|
||
# UI
|
||
self.config_manager.update_ui(
|
||
opacity=self.opacity_slider.value() / 100.0,
|
||
is_on_top=self.ontop_btn.isChecked(),
|
||
cold_threshold=self.cold_spin.value(),
|
||
warm_threshold=self.warm_spin.value()
|
||
)
|
||
|
||
self.accept()
|
||
|
||
|
||
class MainWindow(QWidget):
|
||
"""主窗口"""
|
||
|
||
def __init__(self, config_manager, spider_manager=None, parent=None):
|
||
super().__init__(parent)
|
||
self.config_manager = config_manager
|
||
self.spider_manager = spider_manager
|
||
|
||
# 获取页面标题并设置窗口标题
|
||
self._set_window_title()
|
||
|
||
self._init_ui()
|
||
self._apply_config()
|
||
|
||
# 拖拽相关
|
||
self.dragging = False
|
||
self.drag_position = QPoint()
|
||
|
||
# 系统托盘
|
||
self._init_tray_icon()
|
||
|
||
def _init_ui(self):
|
||
"""初始化UI"""
|
||
layout = QVBoxLayout(self)
|
||
layout.setContentsMargins(16, 16, 16, 16)
|
||
layout.setSpacing(12)
|
||
|
||
# 标题
|
||
self.title_label = QLabel("上证指数 sh000001")
|
||
self.title_label.setAlignment(Qt.AlignCenter)
|
||
title_font = QFont()
|
||
title_font.setPointSize(16)
|
||
title_font.setBold(True)
|
||
self.title_label.setFont(title_font)
|
||
self.title_label.setStyleSheet("color: #333;")
|
||
|
||
# 指示灯
|
||
self.indicator = SentimentIndicator()
|
||
self.indicator.setMinimumSize(120, 120)
|
||
|
||
# 分数和标签
|
||
self.score_label = QLabel("50")
|
||
self.score_label.setAlignment(Qt.AlignCenter)
|
||
score_font = QFont()
|
||
score_font.setPointSize(24)
|
||
score_font.setBold(True)
|
||
self.score_label.setFont(score_font)
|
||
self.score_label.setStyleSheet("color: #2196F3;")
|
||
|
||
self.sentiment_label = QLabel("中性")
|
||
self.sentiment_label.setAlignment(Qt.AlignCenter)
|
||
sentiment_font = QFont()
|
||
sentiment_font.setPointSize(12)
|
||
self.sentiment_label.setFont(sentiment_font)
|
||
self.sentiment_label.setStyleSheet("color: #666;")
|
||
|
||
# 状态信息
|
||
self.status_label = QLabel("等待数据...")
|
||
self.status_label.setAlignment(Qt.AlignCenter)
|
||
self.status_label.setStyleSheet("color: #999; font-size: 11px;")
|
||
|
||
# 上证所截图显示
|
||
self.screenshot_label = QLabel("等待截图...")
|
||
self.screenshot_label.setAlignment(Qt.AlignCenter)
|
||
self.screenshot_label.setMinimumSize(380, 180)
|
||
self.screenshot_label.setMaximumHeight(200)
|
||
self.screenshot_label.setStyleSheet(
|
||
"QLabel {"
|
||
" border: 1px solid #e0e0e0;"
|
||
" border-radius: 8px;"
|
||
" background-color: #f5f5f5;"
|
||
"}"
|
||
)
|
||
|
||
|
||
|
||
# 添加到主布局
|
||
layout.addWidget(self.title_label)
|
||
layout.addWidget(self.indicator, alignment=Qt.AlignCenter)
|
||
layout.addWidget(self.score_label)
|
||
layout.addWidget(self.sentiment_label)
|
||
layout.addWidget(self.screenshot_label)
|
||
layout.addWidget(self.status_label)
|
||
|
||
# 设置窗口标志(无边框、可拖拽)
|
||
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||
self.setStyleSheet(
|
||
"QWidget {"
|
||
" background-color: rgba(255, 255, 255, 0.95);"
|
||
" border-radius: 12px;"
|
||
"}"
|
||
)
|
||
|
||
def _set_window_title(self):
|
||
"""设置窗口标题"""
|
||
logger.debug("设置窗口标题")
|
||
|
||
# 尝试从爬虫获取页面标题
|
||
if hasattr(self, 'spider_manager') and self.spider_manager:
|
||
try:
|
||
page_title = self.spider_manager.get_page_title()
|
||
if page_title:
|
||
# 从页面标题中提取股票名称
|
||
import re
|
||
match = re.search(r'(上证指数sh\d+)', page_title)
|
||
if match:
|
||
stock_name = match.group(1)
|
||
window_title = f"冷暖值 - {stock_name}"
|
||
self.setWindowTitle(window_title)
|
||
|
||
# 同时更新主标题标签(如果已初始化)
|
||
if hasattr(self, 'title_label'):
|
||
self.title_label.setText("上证指数sh000001")
|
||
logger.info(f"设置窗口标题: {window_title}")
|
||
return
|
||
except Exception as e:
|
||
logger.error(f"获取页面标题失败: {e}")
|
||
|
||
# 如果获取失败,使用默认标题
|
||
self.setWindowTitle("冷暖值 - 股吧人气")
|
||
logger.info("使用默认窗口标题")
|
||
|
||
def _init_tray_icon(self):
|
||
"""初始化系统托盘"""
|
||
logger.debug("初始化系统托盘")
|
||
self.tray_icon = QSystemTrayIcon(self)
|
||
self.tray_icon.setToolTip("股吧人气指示器")
|
||
|
||
# 创建托盘菜单
|
||
tray_menu = QMenu()
|
||
show_action = QAction("显示", self)
|
||
hide_action = QAction("隐藏", self)
|
||
quit_action = QAction("退出", self)
|
||
|
||
show_action.triggered.connect(self.show)
|
||
hide_action.triggered.connect(self.hide)
|
||
quit_action.triggered.connect(self.quit_app)
|
||
|
||
tray_menu.addAction(show_action)
|
||
tray_menu.addAction(hide_action)
|
||
tray_menu.addAction(quit_action)
|
||
|
||
self.tray_icon.setContextMenu(tray_menu)
|
||
self.tray_icon.show()
|
||
logger.info("系统托盘初始化完成")
|
||
|
||
def quit_app(self):
|
||
"""退出应用"""
|
||
logger.info("退出应用")
|
||
self.close()
|
||
import sys
|
||
sys.exit(0)
|
||
|
||
def _apply_config(self):
|
||
"""应用配置"""
|
||
ui_config = self.config_manager.ui_config
|
||
self.setWindowOpacity(ui_config.get('opacity', 0.9))
|
||
|
||
if ui_config.get('is_on_top', True):
|
||
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
|
||
else:
|
||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
|
||
|
||
thresholds = ui_config.get('thresholds', {})
|
||
|
||
def mousePressEvent(self, event):
|
||
"""鼠标按下事件"""
|
||
if event.button() == Qt.LeftButton:
|
||
self.dragging = True
|
||
self.drag_position = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
||
|
||
def mouseMoveEvent(self, event):
|
||
"""鼠标移动事件"""
|
||
if self.dragging:
|
||
self.move(event.globalPosition().toPoint() - self.drag_position)
|
||
|
||
def mouseReleaseEvent(self, event):
|
||
"""鼠标释放事件"""
|
||
if event.button() == Qt.LeftButton:
|
||
self.dragging = False
|
||
|
||
def contextMenuEvent(self, event):
|
||
"""右键菜单"""
|
||
context_menu = QMenu(self)
|
||
refresh_action = QAction("刷新", self)
|
||
config_action = QAction("配置", self)
|
||
quit_action = QAction("退出", self)
|
||
|
||
refresh_action.triggered.connect(self._on_refresh)
|
||
config_action.triggered.connect(self.show_config)
|
||
quit_action.triggered.connect(self.quit_app)
|
||
|
||
context_menu.addAction(refresh_action)
|
||
context_menu.addAction(config_action)
|
||
context_menu.addSeparator()
|
||
context_menu.addAction(quit_action)
|
||
context_menu.exec(event.globalPos())
|
||
|
||
def show_config(self):
|
||
"""显示配置对话框"""
|
||
dialog = ConfigDialog(self.config_manager, self)
|
||
if dialog.exec() == QDialog.Accepted:
|
||
self._apply_config()
|
||
|
||
def _on_refresh(self):
|
||
"""刷新回调"""
|
||
if hasattr(self, '_refresh_callback') and self._refresh_callback:
|
||
self._refresh_callback()
|
||
logger.info("执行刷新操作")
|
||
|
||
def update_indicator(self, score: int, label: str = None):
|
||
"""更新指示灯"""
|
||
if label is None:
|
||
label = self.indicator.get_description(score)
|
||
self.indicator.set_value(score, label)
|
||
self.score_label.setText(str(score))
|
||
self.sentiment_label.setText(label)
|
||
|
||
color = self._get_score_color(score)
|
||
self.score_label.setStyleSheet(f"color: {color}; font-size: 24px; font-weight: bold;")
|
||
|
||
logger.debug(f"更新指示灯: {score}分 - {label}")
|
||
|
||
def _get_score_color(self, score: int) -> str:
|
||
"""根据分数获取颜色值"""
|
||
if score < 30:
|
||
return "#1565C0"
|
||
elif score < 39:
|
||
return "#1976D2"
|
||
elif score < 45:
|
||
return "#42A5F5"
|
||
elif score < 55:
|
||
return "#66BB6A"
|
||
elif score < 65:
|
||
return "#FFA726"
|
||
elif score < 70:
|
||
return "#FB8C00"
|
||
else:
|
||
return "#E53935"
|
||
|
||
def update_status(self, text: str):
|
||
"""更新状态"""
|
||
self.status_label.setText(text)
|
||
logger.debug(f"更新状态: {text}")
|
||
|
||
def set_refresh_callback(self, callback: Callable):
|
||
"""设置刷新回调"""
|
||
self._refresh_callback = callback
|
||
logger.debug("设置刷新回调")
|
||
|
||
def set_config_callback(self, callback: Callable):
|
||
"""设置配置回调(已废弃,配置直接通过右键菜单调用)"""
|
||
logger.debug("配置回调已废弃,配置直接通过右键菜单调用")
|
||
|
||
def show_message(self, title: str, message: str):
|
||
"""显示消息"""
|
||
logger.info(f"显示消息: {title} - {message}")
|
||
QMessageBox.information(self, title, message)
|
||
|
||
def update_sse_screenshot(self, screenshot_path: str):
|
||
"""更新上证所截图显示"""
|
||
logger.info(f"更新截图显示: {screenshot_path}")
|
||
pixmap = QPixmap(screenshot_path)
|
||
if not pixmap.isNull():
|
||
self.screenshot_label.setPixmap(pixmap.scaled(
|
||
self.screenshot_label.size(),
|
||
Qt.KeepAspectRatio,
|
||
Qt.SmoothTransformation
|
||
))
|
||
self.screenshot_label.setText("")
|
||
logger.info("截图显示更新成功")
|
||
else:
|
||
self.screenshot_label.setText("截图加载失败")
|
||
logger.warning("截图加载失败")
|
||
|
||
def add_waveform_data(self, time_str: str, value: float):
|
||
"""添加波形图数据点(暂未实现波形图功能)"""
|
||
logger.debug(f"波形图数据: 时间={time_str}, 值={value}")
|
||
|
||
|
||
class QCheckBox(QPushButton):
|
||
"""自定义复选框"""
|
||
def __init__(self, text=""):
|
||
super().__init__(text)
|
||
self.setCheckable(True)
|
||
self.setChecked(False)
|