feat: apply gui semantic risk colors
This commit is contained in:
+93
-3
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user