80a4cdfc by 柴进

feat(ui): 迁移 StyleDesignerTab + TaskQueueWidget 样式

- StyleDesignerTab 删 8 处 inline setStyleSheet:
  - 主按钮"生成珠宝图片" variant=primary(统一两个 tab 的视觉权重)
  - 随机/恢复按钮去硬编码颜色,恢复按钮 variant=ghost
  - 各 caption label 用 role=caption(统一字号)
  - prompt_group 的 font-size:16px inline 删除
  - result_label 用 #previewImage objectName + has_image property 切换
  - 类内补 _set_status helper(之前批量替换时漏了)

- task_queue.py:
  - TaskQueueWidget 用 #taskQueueSidebar objectName,
    顶部新增 #sidebarHeader 40px header bar,
    与主区 TabBar 视觉同高 — 修复"任务队列"标题飘到 tab 旁边的视觉断层
  - 删除 list/widget 内嵌 stylesheet,全部走全局主题
  - QListWidgetItem 状态色(橙/蓝/绿/红/灰)改用 theme.get_color() 读当前
    主题 token,浅深模式都对

- theme.py 新增 get_color(token, fallback) 公共函数:
  用于 widget 内嵌颜色调用(如 setForeground 等 QSS 命中不到的渲染层 API)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent de306ffe
...@@ -4007,6 +4007,7 @@ class StyleDesignerTab(QWidget): ...@@ -4007,6 +4007,7 @@ class StyleDesignerTab(QWidget):
4007 self.logger = logging.getLogger(__name__) 4007 self.logger = logging.getLogger(__name__)
4008 self.library_manager = library_manager 4008 self.library_manager = library_manager
4009 self.parent_window = parent 4009 self.parent_window = parent
4010 self.setObjectName("styleDesignerTab")
4010 4011
4011 # 字段顺序 4012 # 字段顺序
4012 self.categories = ["主石形状", "主石材质", "金属", "花头形式", "戒臂结构", "戒臂处理", "特殊元素", "辅石镶嵌"] 4013 self.categories = ["主石形状", "主石材质", "金属", "花头形式", "戒臂结构", "戒臂处理", "特殊元素", "辅石镶嵌"]
...@@ -4051,38 +4052,15 @@ class StyleDesignerTab(QWidget): ...@@ -4051,38 +4052,15 @@ class StyleDesignerTab(QWidget):
4051 # 全局按钮区域 4052 # 全局按钮区域
4052 buttons_layout = QHBoxLayout() 4053 buttons_layout = QHBoxLayout()
4053 4054
4054 # 随机生成按钮 4055 # 随机生成按钮(次要操作,无 variant 走默认)
4055 random_btn = QPushButton("🎲 随机生成参数") 4056 random_btn = QPushButton("🎲 随机生成参数")
4056 random_btn.clicked.connect(self.randomize_parameters) 4057 random_btn.clicked.connect(self.randomize_parameters)
4057 random_btn.setStyleSheet("""
4058 QPushButton {
4059 background-color: #4CAF50;
4060 color: white;
4061 padding: 8px;
4062 border-radius: 4px;
4063 font-weight: bold;
4064 }
4065 QPushButton:hover {
4066 background-color: #45a049;
4067 }
4068 """)
4069 buttons_layout.addWidget(random_btn) 4058 buttons_layout.addWidget(random_btn)
4070 4059
4071 # 全局恢复按钮 4060 # 全局恢复按钮(轻量警告 - 用 ghost)
4072 reset_all_btn = QPushButton("🔄 恢复所有默认词库") 4061 reset_all_btn = QPushButton("🔄 恢复所有默认词库")
4062 reset_all_btn.setProperty("variant", "ghost")
4073 reset_all_btn.clicked.connect(self.reset_all_library) 4063 reset_all_btn.clicked.connect(self.reset_all_library)
4074 reset_all_btn.setStyleSheet("""
4075 QPushButton {
4076 background-color: #ff9800;
4077 color: white;
4078 padding: 8px;
4079 border-radius: 4px;
4080 font-weight: bold;
4081 }
4082 QPushButton:hover {
4083 background-color: #f57c00;
4084 }
4085 """)
4086 buttons_layout.addWidget(reset_all_btn) 4064 buttons_layout.addWidget(reset_all_btn)
4087 4065
4088 buttons_layout.addStretch() 4066 buttons_layout.addStretch()
...@@ -4101,7 +4079,6 @@ class StyleDesignerTab(QWidget): ...@@ -4101,7 +4079,6 @@ class StyleDesignerTab(QWidget):
4101 self.prompt_preview.setReadOnly(True) 4079 self.prompt_preview.setReadOnly(True)
4102 prompt_layout.addWidget(self.prompt_preview) 4080 prompt_layout.addWidget(self.prompt_preview)
4103 prompt_group.setLayout(prompt_layout) 4081 prompt_group.setLayout(prompt_layout)
4104 prompt_group.setStyleSheet("font-size: 16px;")
4105 4082
4106 content_row.addWidget(prompt_group, 2) 4083 content_row.addWidget(prompt_group, 2)
4107 4084
...@@ -4111,7 +4088,7 @@ class StyleDesignerTab(QWidget): ...@@ -4111,7 +4088,7 @@ class StyleDesignerTab(QWidget):
4111 4088
4112 # 生成模式(放在最前面) 4089 # 生成模式(放在最前面)
4113 mode_label = QLabel("生成模式") 4090 mode_label = QLabel("生成模式")
4114 mode_label.setStyleSheet("QLabel { font-size: 14px; line-height: 18px; }") 4091 mode_label.setProperty("role", "caption")
4115 settings_layout.addWidget(mode_label) 4092 settings_layout.addWidget(mode_label)
4116 self.generation_mode = QComboBox() 4093 self.generation_mode = QComboBox()
4117 self.generation_mode.addItems(["极速模式", "慢速模式"]) 4094 self.generation_mode.addItems(["极速模式", "慢速模式"])
...@@ -4123,7 +4100,7 @@ class StyleDesignerTab(QWidget): ...@@ -4123,7 +4100,7 @@ class StyleDesignerTab(QWidget):
4123 4100
4124 # 宽高比 4101 # 宽高比
4125 aspect_label = QLabel("宽高比") 4102 aspect_label = QLabel("宽高比")
4126 aspect_label.setStyleSheet("QLabel { font-size: 14px; line-height: 18px; }") 4103 aspect_label.setProperty("role", "caption")
4127 settings_layout.addWidget(aspect_label) 4104 settings_layout.addWidget(aspect_label)
4128 self.aspect_ratio = QComboBox() 4105 self.aspect_ratio = QComboBox()
4129 self.aspect_ratio.addItems([ 4106 self.aspect_ratio.addItems([
...@@ -4140,7 +4117,7 @@ class StyleDesignerTab(QWidget): ...@@ -4140,7 +4117,7 @@ class StyleDesignerTab(QWidget):
4140 4117
4141 # 图片尺寸 4118 # 图片尺寸
4142 size_label = QLabel("图片尺寸") 4119 size_label = QLabel("图片尺寸")
4143 size_label.setStyleSheet("QLabel { font-size: 14px; line-height: 18px; }") 4120 size_label.setProperty("role", "caption")
4144 settings_layout.addWidget(size_label) 4121 settings_layout.addWidget(size_label)
4145 self.image_size = QComboBox() 4122 self.image_size = QComboBox()
4146 self.image_size.addItems(["1K", "2K", "4K"]) 4123 self.image_size.addItems(["1K", "2K", "4K"])
...@@ -4159,20 +4136,8 @@ class StyleDesignerTab(QWidget): ...@@ -4159,20 +4136,8 @@ class StyleDesignerTab(QWidget):
4159 # Action buttons - 与图片生成页面保持一致 4136 # Action buttons - 与图片生成页面保持一致
4160 action_layout = QHBoxLayout() 4137 action_layout = QHBoxLayout()
4161 self.generate_btn = QPushButton("🎨 生成珠宝图片") 4138 self.generate_btn = QPushButton("🎨 生成珠宝图片")
4139 self.generate_btn.setProperty("variant", "primary")
4162 self.generate_btn.clicked.connect(self.generate_image) 4140 self.generate_btn.clicked.connect(self.generate_image)
4163 self.generate_btn.setStyleSheet("""
4164 QPushButton {
4165 background-color: #007AFF;
4166 color: white;
4167 padding: 12px;
4168 border-radius: 4px;
4169 font-size: 14px;
4170 font-weight: bold;
4171 }
4172 QPushButton:hover {
4173 background-color: #0051D5;
4174 }
4175 """)
4176 action_layout.addWidget(self.generate_btn) 4141 action_layout.addWidget(self.generate_btn)
4177 4142
4178 self.download_btn = QPushButton("💾 下载图片") 4143 self.download_btn = QPushButton("💾 下载图片")
...@@ -4181,7 +4146,7 @@ class StyleDesignerTab(QWidget): ...@@ -4181,7 +4146,7 @@ class StyleDesignerTab(QWidget):
4181 action_layout.addWidget(self.download_btn) 4146 action_layout.addWidget(self.download_btn)
4182 4147
4183 self.status_label = QLabel("● 就绪") 4148 self.status_label = QLabel("● 就绪")
4184 self.status_label.setStyleSheet("QLabel { font-size: 14px; line-height: 18px; }") 4149 self.status_label.setProperty("status", "muted")
4185 action_layout.addWidget(self.status_label) 4150 action_layout.addWidget(self.status_label)
4186 action_layout.addStretch() 4151 action_layout.addStretch()
4187 4152
...@@ -4192,19 +4157,11 @@ class StyleDesignerTab(QWidget): ...@@ -4192,19 +4157,11 @@ class StyleDesignerTab(QWidget):
4192 preview_layout = QVBoxLayout() 4157 preview_layout = QVBoxLayout()
4193 4158
4194 self.result_label = QLabel("生成的图片将在这里显示\n双击用系统查看器打开") 4159 self.result_label = QLabel("生成的图片将在这里显示\n双击用系统查看器打开")
4160 self.result_label.setObjectName("previewImage")
4161 self.result_label.setProperty("has_image", "false")
4195 self.result_label.setAlignment(Qt.AlignCenter) 4162 self.result_label.setAlignment(Qt.AlignCenter)
4196 self.result_label.setMinimumHeight(300) 4163 self.result_label.setMinimumHeight(300)
4197 self.result_label.setMinimumWidth(400) 4164 self.result_label.setMinimumWidth(400)
4198 self.result_label.setStyleSheet("""
4199 QLabel {
4200 color: #999999;
4201 font-size: 14px;
4202 line-height: 18px;
4203 border: 1px solid #ddd;
4204 border-radius: 4px;
4205 background-color: #f9f9f9;
4206 }
4207 """)
4208 self.result_label.setScaledContents(False) 4165 self.result_label.setScaledContents(False)
4209 # 保存原始的双击处理方法引用 4166 # 保存原始的双击处理方法引用
4210 self.original_mouseDoubleClickEvent = self.result_label.mouseDoubleClickEvent 4167 self.original_mouseDoubleClickEvent = self.result_label.mouseDoubleClickEvent
...@@ -4221,6 +4178,14 @@ class StyleDesignerTab(QWidget): ...@@ -4221,6 +4178,14 @@ class StyleDesignerTab(QWidget):
4221 # 初始化 prompt 预览 4178 # 初始化 prompt 预览
4222 self.update_prompt_preview() 4179 self.update_prompt_preview()
4223 4180
4181 def _set_status(self, status: str, text: str | None = None) -> None:
4182 """统一状态标签视觉。status: success|warning|danger|info|muted"""
4183 if text is not None:
4184 self.status_label.setText(text)
4185 self.status_label.setProperty("status", status)
4186 self.status_label.style().unpolish(self.status_label)
4187 self.status_label.style().polish(self.status_label)
4188
4224 def randomize_parameters(self): 4189 def randomize_parameters(self):
4225 """随机生成一套参数(跳过锁定的字段)""" 4190 """随机生成一套参数(跳过锁定的字段)"""
4226 for category, combo in self.combo_boxes.items(): 4191 for category, combo in self.combo_boxes.items():
...@@ -4239,7 +4204,7 @@ class StyleDesignerTab(QWidget): ...@@ -4239,7 +4204,7 @@ class StyleDesignerTab(QWidget):
4239 # 标签 4204 # 标签
4240 label = QLabel(f"{category}:") 4205 label = QLabel(f"{category}:")
4241 label.setMinimumWidth(70) # 减小标签宽度以适应两列布局 4206 label.setMinimumWidth(70) # 减小标签宽度以适应两列布局
4242 label.setStyleSheet("QLabel { font-size: 14px; line-height: 18px; }") 4207 label.setProperty("role", "caption")
4243 layout.addWidget(label) 4208 layout.addWidget(label)
4244 4209
4245 # 下拉框 4210 # 下拉框
...@@ -4671,12 +4636,9 @@ class StyleDesignerTab(QWidget): ...@@ -4671,12 +4636,9 @@ class StyleDesignerTab(QWidget):
4671 Qt.SmoothTransformation 4636 Qt.SmoothTransformation
4672 ) 4637 )
4673 self.result_label.setPixmap(scaled_pixmap) 4638 self.result_label.setPixmap(scaled_pixmap)
4674 self.result_label.setStyleSheet(""" 4639 self.result_label.setProperty("has_image", "true")
4675 QLabel { 4640 self.result_label.style().unpolish(self.result_label)
4676 border: 1px solid #ddd; 4641 self.result_label.style().polish(self.result_label)
4677 background-color: white;
4678 }
4679 """)
4680 # 启用下载按钮 4642 # 启用下载按钮
4681 self.download_btn.setEnabled(True) 4643 self.download_btn.setEnabled(True)
4682 except Exception as e: 4644 except Exception as e:
......
...@@ -447,66 +447,49 @@ class TaskQueueWidget(QWidget): ...@@ -447,66 +447,49 @@ class TaskQueueWidget(QWidget):
447 self._update_summary() 447 self._update_summary()
448 448
449 def _setup_ui(self): 449 def _setup_ui(self):
450 """构建右侧任务列表 UI""" 450 """构建右侧任务列表 UI(视觉走全局主题)"""
451 self.setObjectName("taskQueueSidebar")
452
451 layout = QVBoxLayout() 453 layout = QVBoxLayout()
452 layout.setContentsMargins(8, 8, 8, 8) 454 layout.setContentsMargins(0, 0, 0, 0)
453 layout.setSpacing(2) 455 layout.setSpacing(0)
456
457 # Header bar — 与主区 TabBar 同高(40px),消除"任务队列"飘上去的视觉
458 header = QWidget()
459 header.setObjectName("sidebarHeader")
460 header_layout = QHBoxLayout()
461 header_layout.setContentsMargins(0, 0, 0, 0)
462 header_layout.setSpacing(0)
454 463
455 # 标题
456 title = QLabel("任务队列") 464 title = QLabel("任务队列")
457 title.setStyleSheet("QLabel { font-weight: bold; font-size: 10px; color: #666; }") 465 title.setAlignment(Qt.AlignVCenter)
458 title.setAlignment(Qt.AlignCenter)
459 title.setToolTip("鼠标悬停查看详情\n右键等待中或运行中的任务可取消") 466 title.setToolTip("鼠标悬停查看详情\n右键等待中或运行中的任务可取消")
460 layout.addWidget(title) 467 header_layout.addWidget(title)
461 468 header_layout.addStretch()
462 # 分隔线 469 header.setLayout(header_layout)
463 line = QLabel() 470 layout.addWidget(header)
464 line.setFrameStyle(QFrame.HLine | QFrame.Sunken)
465 layout.addWidget(line)
466 471
467 # 任务状态列表 - 可点击的列表项 472 # 任务状态列表 - 可点击的列表项(样式由全局 QSS 提供)
468 self.task_list = QListWidget() 473 self.task_list = QListWidget()
469 self.task_list.setStyleSheet(""" 474 self.task_list.setObjectName("taskList")
470 QListWidget { 475 self.task_list.setFrameShape(QFrame.NoFrame)
471 border: none;
472 font-size: 11px;
473 padding: 2px;
474 }
475 QListWidget::item {
476 padding: 4px 2px;
477 border-bottom: 1px solid #eee;
478 min-height: 20px;
479 }
480 QListWidget::item:hover {
481 background-color: #e3f2fd;
482 cursor: pointer;
483 }
484 QListWidget::item:selected {
485 background-color: #bbdefb;
486 }
487 """)
488 self.task_list.itemClicked.connect(self._on_task_item_clicked) 476 self.task_list.itemClicked.connect(self._on_task_item_clicked)
489 477
490 # 启用右键菜单 478 # 启用右键菜单
491 self.task_list.setContextMenuPolicy(Qt.CustomContextMenu) 479 self.task_list.setContextMenuPolicy(Qt.CustomContextMenu)
492 self.task_list.customContextMenuRequested.connect(self._show_context_menu) 480 self.task_list.customContextMenuRequested.connect(self._show_context_menu)
493 481
494 layout.addWidget(self.task_list) 482 # 加 padding 让列表脱离边缘
483 list_wrap = QVBoxLayout()
484 list_wrap.setContentsMargins(8, 8, 8, 8)
485 list_wrap.addWidget(self.task_list)
486 layout.addLayout(list_wrap)
495 487
496 layout.addStretch()
497 self.setLayout(layout) 488 self.setLayout(layout)
498 489
499 # 设置极窄宽度 490 # 设置极窄宽度
500 self.setMaximumWidth(120) 491 self.setMaximumWidth(140)
501 self.setMinimumWidth(80) 492 self.setMinimumWidth(100)
502
503 # 样式
504 self.setStyleSheet("""
505 TaskQueueWidget {
506 background-color: #f5f5f5;
507 border-left: 1px solid #ddd;
508 }
509 """)
510 493
511 def _connect_signals(self): 494 def _connect_signals(self):
512 """绑定信号""" 495 """绑定信号"""
...@@ -519,30 +502,30 @@ class TaskQueueWidget(QWidget): ...@@ -519,30 +502,30 @@ class TaskQueueWidget(QWidget):
519 502
520 def _update_summary(self): 503 def _update_summary(self):
521 """更新任务列表 - 显示可点击的状态项""" 504 """更新任务列表 - 显示可点击的状态项"""
505 from theme import get_color
522 self.task_list.clear() 506 self.task_list.clear()
523 tasks = self.manager.get_all_tasks() 507 tasks = self.manager.get_all_tasks()
524 508
525 # 按状态分类并转换为文字 509 # 按状态分类并转换为文字(颜色读自当前主题,浅深都对)
526 for task in tasks: 510 for task in tasks:
527 # 状态文字和颜色
528 if task.status == TaskStatus.RUNNING: 511 if task.status == TaskStatus.RUNNING:
529 status_text = "执行中" 512 status_text = "执行中"
530 color = "#FF9500" # 橙色 513 color = get_color("warning", "#FF9500")
531 elif task.status == TaskStatus.PENDING: 514 elif task.status == TaskStatus.PENDING:
532 status_text = "等待中" 515 status_text = "等待中"
533 color = "#007AFF" # 蓝色 516 color = get_color("accent", "#007AFF")
534 elif task.status == TaskStatus.COMPLETED: 517 elif task.status == TaskStatus.COMPLETED:
535 status_text = "已完成" 518 status_text = "已完成"
536 color = "#34C759" # 绿色 519 color = get_color("success", "#34C759")
537 elif task.status == TaskStatus.FAILED: 520 elif task.status == TaskStatus.FAILED:
538 status_text = "失败" 521 status_text = "失败"
539 color = "#FF3B30" # 红色 522 color = get_color("danger", "#FF3B30")
540 elif task.status == TaskStatus.CANCELLED: 523 elif task.status == TaskStatus.CANCELLED:
541 status_text = "已取消" 524 status_text = "已取消"
542 color = "#8E8E93" # 中性灰 525 color = get_color("text_tertiary", "#8E8E93")
543 else: 526 else:
544 status_text = "未知" 527 status_text = "未知"
545 color = "#666666" # 灰色 528 color = get_color("text_secondary", "#666666")
546 529
547 # 创建列表项 530 # 创建列表项
548 item = QListWidgetItem(status_text) 531 item = QListWidgetItem(status_text)
...@@ -717,7 +700,7 @@ class TaskQueueWidget(QWidget): ...@@ -717,7 +700,7 @@ class TaskQueueWidget(QWidget):
717 700
718 # 标题 701 # 标题
719 title = QLabel("📋 任务队列") 702 title = QLabel("📋 任务队列")
720 title.setStyleSheet("QLabel { font-weight: bold; font-size: 14px; }") 703 title.setProperty("role", "title")
721 layout.addWidget(title) 704 layout.addWidget(title)
722 705
723 # 任务列表 706 # 任务列表
...@@ -729,13 +712,14 @@ class TaskQueueWidget(QWidget): ...@@ -729,13 +712,14 @@ class TaskQueueWidget(QWidget):
729 item = QListWidgetItem() 712 item = QListWidgetItem()
730 item.setData(Qt.UserRole, task.id) 713 item.setData(Qt.UserRole, task.id)
731 714
715 from theme import get_color
732 status_map = { 716 status_map = {
733 TaskStatus.RUNNING: ("●", "#FF9500"), 717 TaskStatus.RUNNING: ("●", get_color("warning", "#FF9500")),
734 TaskStatus.PENDING: ("○", "#8E8E93"), 718 TaskStatus.PENDING: ("○", get_color("text_tertiary", "#8E8E93")),
735 TaskStatus.COMPLETED: ("✓", "#34C759"), 719 TaskStatus.COMPLETED: ("✓", get_color("success", "#34C759")),
736 TaskStatus.FAILED: ("✗", "#FF3B30"), 720 TaskStatus.FAILED: ("✗", get_color("danger", "#FF3B30")),
737 } 721 }
738 icon, color = status_map.get(task.status, ("?", "#000")) 722 icon, color = status_map.get(task.status, ("?", get_color("text_primary", "#000")))
739 723
740 prompt_preview = task.prompt[:30] + "..." if len(task.prompt) > 30 else task.prompt 724 prompt_preview = task.prompt[:30] + "..." if len(task.prompt) > 30 else task.prompt
741 display_text = f"{icon} {prompt_preview}" 725 display_text = f"{icon} {prompt_preview}"
......
...@@ -748,4 +748,24 @@ def apply_theme(app: QApplication) -> ThemeManager: ...@@ -748,4 +748,24 @@ def apply_theme(app: QApplication) -> ThemeManager:
748 返回 ThemeManager 实例,调用方应持有引用以保持信号连接(否则会被 748 返回 ThemeManager 实例,调用方应持有引用以保持信号连接(否则会被
749 Python GC 当作未引用的 QObject 回收,导致系统切换失效)。 749 Python GC 当作未引用的 QObject 回收,导致系统切换失效)。
750 """ 750 """
751 return ThemeManager(app) 751 mgr = ThemeManager(app)
752 # 全局可访问点,方便 get_color() 等无 widget 引用的场景
753 app.theme_manager = mgr
754 return mgr
755
756
757 def get_color(token: str, fallback: str = "#000000") -> str:
758 """读取当前主题下的颜色 token。
759
760 用于 widget 内嵌颜色调用(如 QListWidgetItem.setForeground 等),
761 QSS 命中不到的渲染层 API。注意:返回的是当前快照,主题切换后需要
762 在下一次重绘时重新读取。
763 """
764 try:
765 app = QApplication.instance()
766 mgr = getattr(app, "theme_manager", None)
767 mode = mgr.current_mode() if mgr else "light"
768 except Exception:
769 mode = "light"
770 table = TOKENS_DARK if mode == "dark" else TOKENS_LIGHT
771 return table.get(token, fallback)
......