feat: apply gui semantic risk colors

This commit is contained in:
chengma
2026-07-01 17:56:27 +08:00
parent ca41ed2874
commit ba6f3e4af4
5 changed files with 142 additions and 13 deletions
+93 -3
View File
@@ -10,7 +10,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
try:
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PySide6.QtGui import QPixmap
from PySide6.QtGui import QColor, QPixmap
from PySide6.QtWidgets import (
QAbstractItemView,
QApplication,
@@ -83,6 +83,13 @@ QTabBar::tab:hover:!selected {
background: #eaf2ff;
}
"""
COLOR_SUCCESS = "#1a7f37"
COLOR_DANGER = "#cf222e"
COLOR_INFO = "#0969da"
COLOR_PENDING = "#9a6700"
COLOR_MUTED = "#6e7781"
COLOR_WARNING = "#bc4c00"
COLOR_DANGER_BG = "#ffebe9"
if QT_IMPORT_ERROR is None:
@@ -101,6 +108,35 @@ if QT_IMPORT_ERROR is None:
)
def _qcolor(color):
return QColor(color)
def _status_base_color(status):
if status == "running":
return COLOR_INFO
if status == "failed":
return COLOR_DANGER
if status in {"skipped", "cancelled"}:
return COLOR_MUTED
return None
def _danger_metric_text(text, active):
if not active:
return text
return f'<span style="color:{COLOR_DANGER}; font-weight:600;">{text}</span>'
def _danger_outline_button_style(object_name):
return (
f"QPushButton#{object_name} {{ "
f"color: {COLOR_DANGER}; border: 1px solid {COLOR_DANGER}; "
"font-weight: 600; padding: 3px 10px; border-radius: 4px; "
"}"
)
def _database_path(db_path=None, config=None) -> str:
return db_path or appconfig.db_path(config)
@@ -298,6 +334,8 @@ if QT_IMPORT_ERROR is None:
task = self.tasks[index.row()]
if role == Qt.DisplayRole:
return self._display_value(task, index.column())
if role == Qt.ForegroundRole and index.column() == 3:
return self._stage_color(task)
if role == Qt.ToolTipRole and self.is_unmatched(task):
return "别名未匹配账号,采集时将略过"
return None
@@ -331,6 +369,16 @@ if QT_IMPORT_ERROR is None:
return self.STATUS_TEXT[task.status]
return self.STAGE_TEXT.get(task.stage, task.stage)
def _stage_color(self, task):
if self.is_unmatched(task):
return _qcolor(COLOR_MUTED)
base_color = _status_base_color(getattr(task, "status", None))
if base_color is not None:
return _qcolor(base_color)
if getattr(task, "stage", None) in {"collected", "generated", "applied"}:
return _qcolor(COLOR_SUCCESS)
return _qcolor(COLOR_PENDING)
def _display_value(self, task, column):
values = [
self._account_name(task),
@@ -397,6 +445,8 @@ if QT_IMPORT_ERROR is None:
task = self.tasks[index.row()]
if role in (Qt.DisplayRole, Qt.EditRole):
return self._display_value(task, index.column())
if role == Qt.ForegroundRole and index.column() == 4:
return self._status_color(task)
if role == Qt.ToolTipRole:
if index.column() == 3 and self._can_edit_title(task):
return "双击可微调新标题,只修改本地待更新内容"
@@ -466,6 +516,14 @@ if QT_IMPORT_ERROR is None:
return self.STATUS_TEXT[task.status]
return self.STAGE_TEXT.get(task.stage, task.stage)
def _status_color(self, task):
base_color = _status_base_color(getattr(task, "status", None))
if base_color is not None:
return _qcolor(base_color)
if getattr(task, "stage", None) in {"generated", "applied"}:
return _qcolor(COLOR_SUCCESS)
return _qcolor(COLOR_PENDING)
def _display_value(self, task, column):
values = [
self._account_name(task),
@@ -525,6 +583,11 @@ if QT_IMPORT_ERROR is None:
task = self.tasks[index.row()]
if role == Qt.DisplayRole:
return self._display_value(task, index.column())
if role == Qt.ForegroundRole:
if index.column() == 4:
return self._stage_color(task)
if index.column() == 5:
return self._result_color(task)
if role == Qt.ToolTipRole and task.last_error:
return task.last_error
return None
@@ -568,6 +631,23 @@ if QT_IMPORT_ERROR is None:
return "待更新"
return self.STATUS_TEXT.get(task.status, task.status)
def _stage_color(self, task):
if getattr(task, "stage", None) == "applied":
return _qcolor(COLOR_SUCCESS)
return _qcolor(COLOR_PENDING)
def _result_color(self, task):
base_color = _status_base_color(getattr(task, "status", None))
if base_color is not None:
return _qcolor(base_color)
if getattr(task, "status", None) == "pending":
return _qcolor(COLOR_PENDING)
if getattr(task, "status", None) == "success" and getattr(task, "stage", None) == "generated":
return _qcolor(COLOR_PENDING)
if getattr(task, "stage", None) == "applied" or getattr(task, "status", None) == "success":
return _qcolor(COLOR_SUCCESS)
return _qcolor(COLOR_PENDING)
class GenerateTab(QWidget):
"""Tab 2: prompt area plus generation task filters/list."""
@@ -1316,7 +1396,10 @@ if QT_IMPORT_ERROR is None:
self.start_update_button.setObjectName("startUpdateButton")
self.start_update_button.setMinimumWidth(118)
self.start_update_button.setStyleSheet(
"QPushButton#startUpdateButton { font-weight: 600; padding: 6px 16px; }"
"QPushButton#startUpdateButton { "
"font-weight: 600; padding: 6px 16px; "
f"color: {COLOR_WARNING}; border: 1px solid {COLOR_WARNING}; "
"border-radius: 4px; }"
)
self.stop_update_button = QPushButton("停止")
self.reset_update_button = QPushButton("重置更新状态")
@@ -2044,9 +2127,11 @@ if QT_IMPORT_ERROR is None:
toolbar.addStretch(1)
self.summary_label = QLabel("未导入任务")
self.summary_label.setTextFormat(Qt.RichText)
self.match_detail_label = QLabel("")
self.show_all_button = QPushButton("全部")
self.show_unmatched_button = QPushButton("未匹配(0)")
self.show_unmatched_button.setObjectName("showUnmatchedButton")
summary_layout = QHBoxLayout()
summary_layout.addWidget(self.summary_label)
@@ -2620,12 +2705,17 @@ if QT_IMPORT_ERROR is None:
total = stats.get("total", len(task_rows))
valid = stats.get("valid", len(task_rows))
invalid = stats.get("invalid", 0)
invalid_text = _danger_metric_text(f"无效{invalid}", invalid > 0)
unmatched_text = _danger_metric_text(f"未匹配{unmatched}", unmatched > 0)
self.summary_label.setText(
f"{files} 文件 · {total} 行 · 有效{valid}/无效{invalid} · 匹配{matched} · 未匹配{unmatched}"
f"{files} 文件 · {total} 行 · 有效{valid}/{invalid_text} · 匹配{matched} · {unmatched_text}"
)
self.match_detail_label.setText(self._match_detail(task_rows, account_rows))
self.show_unmatched_button.setText(f"未匹配({unmatched})")
self.show_unmatched_button.setEnabled(unmatched > 0)
self.show_unmatched_button.setStyleSheet(
_danger_outline_button_style("showUnmatchedButton") if unmatched > 0 else ""
)
if unmatched == 0 and self.model.filter_mode == "unmatched":
self.model.set_filter_mode("all")