From be4d1b3a6477df0ba51846858640665d8ffe8578 Mon Sep 17 00:00:00 2001 From: Minidoracat Date: Tue, 3 Jun 2025 16:43:36 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20gui=20=E6=96=B0=E5=A2=9E=E9=87=8D?= =?UTF-8?q?=E7=BD=AE=E8=A8=AD=E5=AE=9A=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=85=81?= =?UTF-8?q?=E8=A8=B1=E7=94=A8=E6=88=B6=E6=B8=85=E9=99=A4=E6=89=80=E6=9C=89?= =?UTF-8?q?=E5=B7=B2=E4=BF=9D=E5=AD=98=E7=9A=84=E8=A8=AD=E5=AE=9A=E4=B8=A6?= =?UTF-8?q?=E6=81=A2=E5=BE=A9=E5=88=B0=E9=A0=90=E8=A8=AD=E7=8B=80=E6=85=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mcp_feedback_enhanced/gui/main.py | 12 + .../gui/tabs/settings_tab.py | 669 ++++++++++++------ .../gui/window/config_manager.py | 19 +- .../gui/window/feedback_window.py | 71 ++ .../gui/window/tab_manager.py | 7 +- .../locales/en/translations.json | 12 + .../locales/zh-CN/translations.json | 12 + .../locales/zh-TW/translations.json | 12 + 8 files changed, 599 insertions(+), 215 deletions(-) diff --git a/src/mcp_feedback_enhanced/gui/main.py b/src/mcp_feedback_enhanced/gui/main.py index e03a0e0..5e4467d 100644 --- a/src/mcp_feedback_enhanced/gui/main.py +++ b/src/mcp_feedback_enhanced/gui/main.py @@ -9,6 +9,7 @@ GUI 主要入口點 from typing import Optional from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtGui import QFont import sys from .models import FeedbackResult @@ -31,6 +32,17 @@ def feedback_ui(project_directory: str, summary: str) -> Optional[FeedbackResult if app is None: app = QApplication(sys.argv) + # 設定全域微軟正黑體字體 + font = QFont("Microsoft JhengHei", 11) # 微軟正黑體,11pt + app.setFont(font) + + # 設定字體回退順序,確保中文字體正確顯示 + app.setStyleSheet(""" + * { + font-family: "Microsoft JhengHei", "微軟正黑體", "Microsoft YaHei", "微软雅黑", "SimHei", "黑体", sans-serif; + } + """) + # 創建主窗口 window = FeedbackWindow(project_directory, summary) window.show() diff --git a/src/mcp_feedback_enhanced/gui/tabs/settings_tab.py b/src/mcp_feedback_enhanced/gui/tabs/settings_tab.py index 2fd3ad7..865a13c 100644 --- a/src/mcp_feedback_enhanced/gui/tabs/settings_tab.py +++ b/src/mcp_feedback_enhanced/gui/tabs/settings_tab.py @@ -9,18 +9,21 @@ from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, - QGroupBox, QComboBox, QRadioButton, QButtonGroup, QMessageBox, - QCheckBox + QComboBox, QRadioButton, QButtonGroup, QMessageBox, + QCheckBox, QPushButton, QFrame ) -from PySide6.QtCore import Signal +from PySide6.QtCore import Signal, Qt +from PySide6.QtGui import QFont from ...i18n import t, get_i18n_manager +from ...debug import gui_debug_log as debug_log class SettingsTab(QWidget): """設置分頁組件""" language_changed = Signal() layout_change_requested = Signal(bool, str) # 佈局變更請求信號 (combined_mode, orientation) + reset_requested = Signal() # 重置設定請求信號 def __init__(self, combined_mode: bool, config_manager, parent=None): super().__init__(parent) @@ -28,315 +31,557 @@ class SettingsTab(QWidget): self.config_manager = config_manager self.layout_orientation = self.config_manager.get_layout_orientation() self.i18n = get_i18n_manager() + + # 保存需要更新的UI元素引用 + self.ui_elements = {} + + # 設置全域字體為微軟正黑體 + self._setup_font() self._setup_ui() # 在UI設置完成後,確保正確設置初始狀態 self._set_initial_layout_state() + def _setup_font(self) -> None: + """設置全域字體""" + font = QFont("Microsoft JhengHei", 9) # 微軟正黑體,調整為 9pt + self.setFont(font) + + # 設置整個控件的樣式表,確保中文字體正確 + self.setStyleSheet(""" + QWidget { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + } + """) + def _setup_ui(self) -> None: """設置用戶介面""" - layout = QVBoxLayout(self) - layout.setSpacing(16) - layout.setContentsMargins(16, 16, 16, 16) + # 主容器 + main_layout = QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) - # === 語言設置區域 === - self.language_group = QGroupBox(t('settings.language.title')) - self.language_group.setObjectName('language_group') - language_layout = QVBoxLayout(self.language_group) - language_layout.setSpacing(12) - language_layout.setContentsMargins(16, 16, 16, 16) + # 左側內容區域 + content_widget = QWidget() + content_widget.setMaximumWidth(600) + content_layout = QVBoxLayout(content_widget) + content_layout.setContentsMargins(20, 20, 20, 20) + content_layout.setSpacing(16) - # 語言選擇器 - language_row = QHBoxLayout() + # === 語言設置 === + self._create_language_section(content_layout) - self.language_label = QLabel(t('settings.language.selector')) - self.language_label.setStyleSheet("font-weight: bold; color: #e0e0e0; font-size: 14px;") - language_row.addWidget(self.language_label) + # 添加分隔線 + self._add_separator(content_layout) + + # === 界面佈局 === + self._create_layout_section(content_layout) + + # 添加分隔線 + self._add_separator(content_layout) + + # === 視窗設置 === + self._create_window_section(content_layout) + + # 添加分隔線 + self._add_separator(content_layout) + + # === 重置設定 === + self._create_reset_section(content_layout) + + # 添加彈性空間 + content_layout.addStretch() + + # 添加到主布局 + main_layout.addWidget(content_widget) + main_layout.addStretch() # 右側彈性空間 + + # 設定初始狀態 + self._set_initial_layout_state() + + def _add_separator(self, layout: QVBoxLayout) -> None: + """添加分隔線""" + separator = QFrame() + separator.setFrameShape(QFrame.HLine) + separator.setStyleSheet(""" + QFrame { + color: #444444; + background-color: #444444; + border: none; + height: 1px; + margin: 6px 0px; + } + """) + layout.addWidget(separator) + + def _create_section_header(self, title: str, emoji: str = "") -> QLabel: + """創建區塊標題""" + text = f"{emoji} {title}" if emoji else title + label = QLabel(text) + label.setStyleSheet(""" + QLabel { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + font-size: 16px; + font-weight: bold; + color: #ffffff; + margin-bottom: 6px; + margin-top: 2px; + } + """) + return label + + def _create_description(self, text: str) -> QLabel: + """創建說明文字""" + label = QLabel(text) + label.setStyleSheet(""" + QLabel { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + color: #aaaaaa; + font-size: 12px; + margin-bottom: 12px; + line-height: 1.3; + } + """) + label.setWordWrap(True) + return label + + def _create_language_section(self, layout: QVBoxLayout) -> None: + """創建語言設置區域""" + header = self._create_section_header(t('settings.language.title'), "🌐") + layout.addWidget(header) + # 保存引用以便更新 + self.ui_elements['language_header'] = header + + desc = self._create_description(t('settings.language.description')) + layout.addWidget(desc) + # 保存引用以便更新 + self.ui_elements['language_desc'] = desc + + # 語言選擇器容器 + lang_container = QHBoxLayout() + lang_container.setContentsMargins(0, 0, 0, 0) self.language_selector = QComboBox() - self.language_selector.setMinimumWidth(180) - self.language_selector.setMinimumHeight(35) + self.language_selector.setMinimumHeight(28) + self.language_selector.setMaximumWidth(140) self.language_selector.setStyleSheet(""" QComboBox { - background-color: #404040; - border: 1px solid #606060; + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + background-color: #3a3a3a; + border: 1px solid #555555; border-radius: 4px; - padding: 8px 12px; - color: #e0e0e0; - font-size: 14px; + padding: 4px 8px; + color: #ffffff; + font-size: 12px; } QComboBox:hover { border-color: #0078d4; } QComboBox::drop-down { border: none; - width: 25px; + width: 20px; } QComboBox::down-arrow { - image: none; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 7px solid #e0e0e0; - margin-right: 6px; + image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMgNEw2IDdMOSA0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPg==); + width: 12px; + height: 12px; } QComboBox QAbstractItemView { - background-color: #404040; - border: 1px solid #606060; + background-color: #3a3a3a; + border: 1px solid #555555; selection-background-color: #0078d4; - color: #e0e0e0; - font-size: 14px; + color: #ffffff; + font-size: 12px; } """) - # 填充語言選項和連接信號 + # 填充語言選項 self._populate_language_selector() self.language_selector.currentIndexChanged.connect(self._on_language_changed) - language_row.addWidget(self.language_selector) - language_row.addStretch() - language_layout.addLayout(language_row) + lang_container.addWidget(self.language_selector) + lang_container.addStretch() + layout.addLayout(lang_container) + + def _create_layout_section(self, layout: QVBoxLayout) -> None: + """創建界面佈局區域""" + header = self._create_section_header(t('settings.layout.title'), "📐") + layout.addWidget(header) + # 保存引用以便更新 + self.ui_elements['layout_header'] = header - # 語言說明 - self.language_description_label = QLabel(t('settings.language.description')) - self.language_description_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-top: 8px;") - self.language_description_label.setWordWrap(True) - language_layout.addWidget(self.language_description_label) + desc = self._create_description(t('settings.layout.description')) + layout.addWidget(desc) + # 保存引用以便更新 + self.ui_elements['layout_desc'] = desc - layout.addWidget(self.language_group) + # 選項容器 + options_layout = QVBoxLayout() + options_layout.setSpacing(2) - # === 界面佈局設置區域 === - self.layout_group = QGroupBox(t('settings.layout.title')) - self.layout_group.setObjectName('layout_group') - layout_layout = QVBoxLayout(self.layout_group) - layout_layout.setSpacing(12) - layout_layout.setContentsMargins(16, 16, 16, 16) - - # 佈局模式選擇 - 使用三個獨立的單選按鈕 + # 創建按鈕組 self.layout_button_group = QButtonGroup() # 分離模式 self.separate_mode_radio = QRadioButton(t('settings.layout.separateMode')) - self.separate_mode_radio.setChecked(not self.combined_mode) - self.separate_mode_radio.setStyleSheet("font-size: 14px; font-weight: bold; color: #e0e0e0;") + self.separate_mode_radio.setStyleSheet(""" + QRadioButton { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + font-size: 13px; + color: #ffffff; + spacing: 8px; + padding: 2px 0px; + } + QRadioButton::indicator { + width: 16px; + height: 16px; + } + QRadioButton::indicator:unchecked { + border: 2px solid #666666; + border-radius: 9px; + background-color: transparent; + } + QRadioButton::indicator:checked { + border: 2px solid #0078d4; + border-radius: 9px; + background-color: #0078d4; + image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiB2aWV3Qm94PSIwIDAgOCA4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8Y2lyY2xlIGN4PSI0IiBjeT0iNCIgcj0iMiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+); + } + QRadioButton::indicator:hover { + border-color: #0078d4; + } + """) self.layout_button_group.addButton(self.separate_mode_radio, 0) - layout_layout.addWidget(self.separate_mode_radio) + options_layout.addWidget(self.separate_mode_radio) - self.separate_desc_label = QLabel(t('settings.layout.separateModeDescription')) - self.separate_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 12px;") - self.separate_desc_label.setWordWrap(True) - layout_layout.addWidget(self.separate_desc_label) + separate_hint = QLabel(f" {t('settings.layout.separateModeDescription')}") + separate_hint.setStyleSheet(""" + QLabel { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + color: #888888; + font-size: 11px; + margin-left: 20px; + margin-bottom: 4px; + } + """) + options_layout.addWidget(separate_hint) + # 保存引用以便更新 + self.ui_elements['separate_hint'] = separate_hint - # 合併模式(垂直布局) + # 合併模式(垂直) self.combined_vertical_radio = QRadioButton(t('settings.layout.combinedVertical')) - self.combined_vertical_radio.setChecked(self.combined_mode and self.layout_orientation == 'vertical') - self.combined_vertical_radio.setStyleSheet("font-size: 14px; font-weight: bold; color: #e0e0e0;") + self.combined_vertical_radio.setStyleSheet(self.separate_mode_radio.styleSheet()) self.layout_button_group.addButton(self.combined_vertical_radio, 1) - layout_layout.addWidget(self.combined_vertical_radio) + options_layout.addWidget(self.combined_vertical_radio) - self.combined_vertical_desc_label = QLabel(t('settings.layout.combinedVerticalDescription')) - self.combined_vertical_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 12px;") - self.combined_vertical_desc_label.setWordWrap(True) - layout_layout.addWidget(self.combined_vertical_desc_label) + vertical_hint = QLabel(f" {t('settings.layout.combinedVerticalDescription')}") + vertical_hint.setStyleSheet(separate_hint.styleSheet()) + options_layout.addWidget(vertical_hint) + # 保存引用以便更新 + self.ui_elements['vertical_hint'] = vertical_hint - # 合併模式(水平布局) + # 合併模式(水平) self.combined_horizontal_radio = QRadioButton(t('settings.layout.combinedHorizontal')) - self.combined_horizontal_radio.setChecked(self.combined_mode and self.layout_orientation == 'horizontal') - self.combined_horizontal_radio.setStyleSheet("font-size: 14px; font-weight: bold; color: #e0e0e0;") + self.combined_horizontal_radio.setStyleSheet(self.separate_mode_radio.styleSheet()) self.layout_button_group.addButton(self.combined_horizontal_radio, 2) - layout_layout.addWidget(self.combined_horizontal_radio) + options_layout.addWidget(self.combined_horizontal_radio) - self.combined_horizontal_desc_label = QLabel(t('settings.layout.combinedHorizontalDescription')) - self.combined_horizontal_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 12px;") - self.combined_horizontal_desc_label.setWordWrap(True) - layout_layout.addWidget(self.combined_horizontal_desc_label) + horizontal_hint = QLabel(f" {t('settings.layout.combinedHorizontalDescription')}") + horizontal_hint.setStyleSheet(separate_hint.styleSheet()) + options_layout.addWidget(horizontal_hint) + # 保存引用以便更新 + self.ui_elements['horizontal_hint'] = horizontal_hint + + layout.addLayout(options_layout) # 連接佈局變更信號 self.layout_button_group.buttonToggled.connect(self._on_layout_changed) + + def _create_window_section(self, layout: QVBoxLayout) -> None: + """創建視窗設置區域""" + header = self._create_section_header(t('settings.window.title'), "🖥️") + layout.addWidget(header) + # 保存引用以便更新 + self.ui_elements['window_header'] = header - layout.addWidget(self.layout_group) + desc = self._create_description(t('settings.window.alwaysCenterDescription')) + layout.addWidget(desc) + # 保存引用以便更新 + self.ui_elements['window_desc'] = desc - # === 視窗定位設置區域 === - self.window_group = QGroupBox(t('settings.window.title')) - self.window_group.setObjectName('window_group') - window_layout = QVBoxLayout(self.window_group) - window_layout.setSpacing(12) - window_layout.setContentsMargins(16, 16, 16, 16) + # 選項容器 + options_layout = QVBoxLayout() + options_layout.setSpacing(2) - # 總是在主螢幕中心顯示視窗選項 self.always_center_checkbox = QCheckBox(t('settings.window.alwaysCenter')) self.always_center_checkbox.setChecked(self.config_manager.get_always_center_window()) - self.always_center_checkbox.setStyleSheet("font-size: 14px; font-weight: bold; color: #e0e0e0;") + self.always_center_checkbox.setStyleSheet(""" + QCheckBox { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + font-size: 13px; + color: #ffffff; + spacing: 8px; + padding: 2px 0px; + } + QCheckBox::indicator { + width: 16px; + height: 16px; + } + QCheckBox::indicator:unchecked { + border: 2px solid #666666; + border-radius: 3px; + background-color: transparent; + } + QCheckBox::indicator:checked { + border: 2px solid #0078d4; + border-radius: 3px; + background-color: #0078d4; + image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEwIDNMNC41IDguNUwyIDYiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+); + } + QCheckBox::indicator:hover { + border-color: #0078d4; + } + """) self.always_center_checkbox.stateChanged.connect(self._on_always_center_changed) - window_layout.addWidget(self.always_center_checkbox) + options_layout.addWidget(self.always_center_checkbox) - self.center_desc_label = QLabel(t('settings.window.alwaysCenterDescription')) - self.center_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 8px;") - self.center_desc_label.setWordWrap(True) - window_layout.addWidget(self.center_desc_label) + center_hint = QLabel(f" {t('settings.window.alwaysCenterDescription')}") + center_hint.setStyleSheet(""" + QLabel { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + color: #888888; + font-size: 11px; + margin-left: 20px; + margin-bottom: 4px; + } + """) + options_layout.addWidget(center_hint) + # 保存引用以便更新 + self.ui_elements['center_hint'] = center_hint - layout.addWidget(self.window_group) - layout.addStretch() + layout.addLayout(options_layout) + def _create_reset_section(self, layout: QVBoxLayout) -> None: + """創建重置設定區域""" + header = self._create_section_header(t('settings.reset.title'), "🔄") + layout.addWidget(header) + # 保存引用以便更新 + self.ui_elements['reset_header'] = header + + desc = self._create_description(t('settings.reset.description')) + layout.addWidget(desc) + # 保存引用以便更新 + self.ui_elements['reset_desc'] = desc + + reset_container = QHBoxLayout() + reset_container.setContentsMargins(0, 0, 0, 0) + + self.reset_button = QPushButton(t('settings.reset.button')) + self.reset_button.setMinimumHeight(32) + self.reset_button.setMaximumWidth(110) + self.reset_button.setStyleSheet(""" + QPushButton { + font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif; + background-color: #dc3545; + color: white; + border: none; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + padding: 6px 12px; + } + QPushButton:hover { + background-color: #e55565; + } + QPushButton:pressed { + background-color: #c82333; + } + """) + self.reset_button.clicked.connect(self._on_reset_settings) + + reset_container.addWidget(self.reset_button) + reset_container.addStretch() + layout.addLayout(reset_container) + def _populate_language_selector(self) -> None: """填充語言選擇器""" - # 保存當前選擇 - current_lang = self.i18n.get_current_language() + languages = [ + ('zh-TW', '繁體中文'), + ('zh-CN', '简体中文'), + ('en', 'English') + ] - # 暫時斷開信號連接,避免觸發語言變更事件 - try: - self.language_selector.currentIndexChanged.disconnect() - except RuntimeError: - pass # 如果沒有連接則忽略 + current_language = self.i18n.get_current_language() - # 清空並重新填充 + # 暫時斷開信號連接以避免觸發變更事件 + self.language_selector.blockSignals(True) + + # 先清空現有選項 self.language_selector.clear() - for lang_code in self.i18n.get_supported_languages(): - display_name = self.i18n.get_language_display_name(lang_code) - self.language_selector.addItem(display_name, lang_code) - # 設置當前選中的語言 - for i in range(self.language_selector.count()): - if self.language_selector.itemData(i) == current_lang: + for i, (code, name) in enumerate(languages): + self.language_selector.addItem(name, code) + if code == current_language: self.language_selector.setCurrentIndex(i) - break # 重新連接信號 - self.language_selector.currentIndexChanged.connect(self._on_language_changed) + self.language_selector.blockSignals(False) def _on_language_changed(self, index: int) -> None: - """處理語言變更""" - lang_code = self.language_selector.itemData(index) - if lang_code and self.i18n.set_language(lang_code): - # 發送語言變更信號 + """語言變更事件處理""" + if index < 0: + return + + language_code = self.language_selector.itemData(index) + if language_code and language_code != self.i18n.get_current_language(): + # 先保存語言設定 + self.config_manager.set_language(language_code) + # 再設定語言 + self.i18n.set_language(language_code) + # 發出信號 self.language_changed.emit() def _on_layout_changed(self, button, checked: bool) -> None: - """處理佈局變更""" - if not checked: # 只處理選中的按鈕 + """佈局變更事件處理""" + if not checked: return - - # 確定新的模式和方向 - new_combined_mode = button == self.combined_vertical_radio or button == self.combined_horizontal_radio - new_orientation = 'vertical' if button == self.combined_vertical_radio else 'horizontal' + button_id = self.layout_button_group.id(button) + + if button_id == 0: # 分離模式 + new_combined_mode = False + new_orientation = 'vertical' + elif button_id == 1: # 合併模式(垂直) + new_combined_mode = True + new_orientation = 'vertical' + elif button_id == 2: # 合併模式(水平) + new_combined_mode = True + new_orientation = 'horizontal' + else: + return + + # 檢查是否真的有變更 if new_combined_mode != self.combined_mode or new_orientation != self.layout_orientation: - # 提示用戶需要重新創建界面 - reply = QMessageBox.question( - self, - t('app.layoutChangeTitle'), - t('app.layoutChangeMessage'), - QMessageBox.Yes | QMessageBox.No, - QMessageBox.Yes - ) + # 先保存配置 + self.config_manager.set_layout_mode(new_combined_mode) + self.config_manager.set_layout_orientation(new_orientation) - if reply == QMessageBox.Yes: - # 用戶確認變更,發送佈局變更請求 - self.combined_mode = new_combined_mode - self.layout_orientation = new_orientation - self.layout_change_requested.emit(self.combined_mode, self.layout_orientation) - else: - # 用戶選擇不重新載入,恢復原來的選項 - if self.combined_mode: - if self.layout_orientation == 'vertical': - self.combined_vertical_radio.setChecked(True) - else: - self.combined_horizontal_radio.setChecked(True) - else: - self.separate_mode_radio.setChecked(True) + # 更新內部狀態 + self.combined_mode = new_combined_mode + self.layout_orientation = new_orientation + + # 發出佈局變更請求信號 + self.layout_change_requested.emit(new_combined_mode, new_orientation) def _on_always_center_changed(self, state: int) -> None: - """處理視窗定位設置變更""" - always_center = state == 2 # Qt.Checked = 2 + """視窗定位選項變更事件處理""" + always_center = state == Qt.CheckState.Checked.value + # 立即保存設定 self.config_manager.set_always_center_window(always_center) + debug_log(f"視窗定位設置已保存: {always_center}") # 調試輸出 + + def _on_reset_settings(self) -> None: + """重置設定事件處理""" + reply = QMessageBox.question( + self, + t('settings.reset.confirmTitle'), + t('settings.reset.confirmMessage'), + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + + if reply == QMessageBox.Yes: + self.reset_requested.emit() def update_texts(self) -> None: - """更新界面文字(用於語言切換)""" - # 更新GroupBox標題 - self.language_group.setTitle(t('settings.language.title')) - self.layout_group.setTitle(t('settings.layout.title')) - self.window_group.setTitle(t('settings.window.title')) + """更新界面文字(不重新創建界面)""" + # 更新區塊標題 + if 'language_header' in self.ui_elements: + self.ui_elements['language_header'].setText(f"🌐 {t('settings.language.title')}") + if 'layout_header' in self.ui_elements: + self.ui_elements['layout_header'].setText(f"📐 {t('settings.layout.title')}") + if 'window_header' in self.ui_elements: + self.ui_elements['window_header'].setText(f"🖥️ {t('settings.window.title')}") + if 'reset_header' in self.ui_elements: + self.ui_elements['reset_header'].setText(f"🔄 {t('settings.reset.title')}") - # 更新標籤文字 - self.language_label.setText(t('settings.language.selector')) - self.language_description_label.setText(t('settings.language.description')) + # 更新描述文字 + if 'language_desc' in self.ui_elements: + self.ui_elements['language_desc'].setText(t('settings.language.description')) + if 'layout_desc' in self.ui_elements: + self.ui_elements['layout_desc'].setText(t('settings.layout.description')) + if 'window_desc' in self.ui_elements: + self.ui_elements['window_desc'].setText(t('settings.window.alwaysCenterDescription')) + if 'reset_desc' in self.ui_elements: + self.ui_elements['reset_desc'].setText(t('settings.reset.description')) - # 更新佈局設置文字 - self.separate_mode_radio.setText(t('settings.layout.separateMode')) - self.combined_vertical_radio.setText(t('settings.layout.combinedVertical')) - self.combined_horizontal_radio.setText(t('settings.layout.combinedHorizontal')) + # 更新提示文字 + if 'separate_hint' in self.ui_elements: + self.ui_elements['separate_hint'].setText(f" {t('settings.layout.separateModeDescription')}") + if 'vertical_hint' in self.ui_elements: + self.ui_elements['vertical_hint'].setText(f" {t('settings.layout.combinedVerticalDescription')}") + if 'horizontal_hint' in self.ui_elements: + self.ui_elements['horizontal_hint'].setText(f" {t('settings.layout.combinedHorizontalDescription')}") + if 'center_hint' in self.ui_elements: + self.ui_elements['center_hint'].setText(f" {t('settings.window.alwaysCenterDescription')}") - # 更新佈局描述文字 - self.separate_desc_label.setText(t('settings.layout.separateModeDescription')) - self.combined_vertical_desc_label.setText(t('settings.layout.combinedVerticalDescription')) - self.combined_horizontal_desc_label.setText(t('settings.layout.combinedHorizontalDescription')) + # 更新按鈕文字 + if hasattr(self, 'reset_button'): + self.reset_button.setText(t('settings.reset.button')) - # 更新視窗設置文字 - self.always_center_checkbox.setText(t('settings.window.alwaysCenter')) - self.center_desc_label.setText(t('settings.window.alwaysCenterDescription')) + # 更新複選框文字 + if hasattr(self, 'always_center_checkbox'): + self.always_center_checkbox.setText(t('settings.window.alwaysCenter')) - # 重新填充語言選擇器 - self._populate_language_selector() + # 更新單選按鈕文字 + if hasattr(self, 'separate_mode_radio'): + self.separate_mode_radio.setText(t('settings.layout.separateMode')) + if hasattr(self, 'combined_vertical_radio'): + self.combined_vertical_radio.setText(t('settings.layout.combinedVertical')) + if hasattr(self, 'combined_horizontal_radio'): + self.combined_horizontal_radio.setText(t('settings.layout.combinedHorizontal')) - # 重新設置勾選狀態,確保設置被正確保持 - self.always_center_checkbox.setChecked(self.config_manager.get_always_center_window()) + # 注意:不要重新填充語言選擇器,避免重複選項問題 + + def reload_settings_from_config(self) -> None: + """從配置重新載入設定狀態""" + # 重新載入語言設定 + if hasattr(self, 'language_selector'): + self._populate_language_selector() + + # 重新載入佈局設定 + self.combined_mode = self.config_manager.get_layout_mode() + self.layout_orientation = self.config_manager.get_layout_orientation() + self._set_initial_layout_state() + + # 重新載入視窗設定 + if hasattr(self, 'always_center_checkbox'): + always_center = self.config_manager.get_always_center_window() + self.always_center_checkbox.setChecked(always_center) + debug_log(f"重新載入視窗定位設置: {always_center}") # 調試輸出 def set_layout_mode(self, combined_mode: bool) -> None: """設置佈局模式""" self.combined_mode = combined_mode - # 暫時斷開信號連接,避免觸發變更事件 - try: - self.layout_button_group.buttonToggled.disconnect() - except RuntimeError: - pass - - # 根據當前模式和方向設置正確的選項 - if combined_mode: - if self.layout_orientation == 'vertical': - self.combined_vertical_radio.setChecked(True) - else: # horizontal - self.combined_horizontal_radio.setChecked(True) - else: - self.separate_mode_radio.setChecked(True) - - # 重新連接信號 - self.layout_button_group.buttonToggled.connect(self._on_layout_changed) + self._set_initial_layout_state() def set_layout_orientation(self, orientation: str) -> None: """設置佈局方向""" self.layout_orientation = orientation - - # 暫時斷開信號連接,避免觸發變更事件 - try: - self.layout_button_group.buttonToggled.disconnect() - except RuntimeError: - pass - - # 如果是合併模式,根據方向設置正確的選項 - if self.combined_mode: - if orientation == 'vertical': - self.combined_vertical_radio.setChecked(True) - else: # horizontal - self.combined_horizontal_radio.setChecked(True) - - # 重新連接信號 - self.layout_button_group.buttonToggled.connect(self._on_layout_changed) + self._set_initial_layout_state() def _set_initial_layout_state(self) -> None: """設置初始佈局狀態""" - # 暫時斷開信號連接,避免觸發變更事件 - try: - self.layout_button_group.buttonToggled.disconnect() - except RuntimeError: - pass - - # 根據當前配置設置正確的選項 - if self.combined_mode: - if self.layout_orientation == 'vertical': + if hasattr(self, 'separate_mode_radio'): + # 暫時斷開信號連接以避免觸發變更事件 + self.layout_button_group.blockSignals(True) + + if not self.combined_mode: + self.separate_mode_radio.setChecked(True) + elif self.layout_orientation == 'vertical': self.combined_vertical_radio.setChecked(True) - else: # horizontal + else: self.combined_horizontal_radio.setChecked(True) - else: - self.separate_mode_radio.setChecked(True) - - # 重新連接信號 - self.layout_button_group.buttonToggled.connect(self._on_layout_changed) \ No newline at end of file + + # 重新連接信號 + self.layout_button_group.blockSignals(False) \ No newline at end of file diff --git a/src/mcp_feedback_enhanced/gui/window/config_manager.py b/src/mcp_feedback_enhanced/gui/window/config_manager.py index dff9d37..ff2479b 100644 --- a/src/mcp_feedback_enhanced/gui/window/config_manager.py +++ b/src/mcp_feedback_enhanced/gui/window/config_manager.py @@ -120,4 +120,21 @@ class ConfigManager: def set_always_center_window(self, always_center: bool) -> None: """設置總是在主螢幕中心顯示視窗""" self.set('always_center_window', always_center) - debug_log(f"視窗定位設置: {'總是中心顯示' if always_center else '智能定位'}") \ No newline at end of file + debug_log(f"視窗定位設置: {'總是中心顯示' if always_center else '智能定位'}") + + def reset_settings(self) -> None: + """重置所有設定到預設值""" + try: + # 清空配置緩存 + self._config_cache = {} + + # 刪除配置文件 + if self._config_file.exists(): + self._config_file.unlink() + debug_log("配置文件已刪除") + + debug_log("所有設定已重置到預設值") + + except Exception as e: + debug_log(f"重置設定失敗: {e}") + raise \ No newline at end of file diff --git a/src/mcp_feedback_enhanced/gui/window/feedback_window.py b/src/mcp_feedback_enhanced/gui/window/feedback_window.py index 1b42f69..a3cec55 100644 --- a/src/mcp_feedback_enhanced/gui/window/feedback_window.py +++ b/src/mcp_feedback_enhanced/gui/window/feedback_window.py @@ -34,6 +34,12 @@ class FeedbackWindow(QMainWindow): # 初始化組件 self.config_manager = ConfigManager() + + # 載入保存的語言設定 + saved_language = self.config_manager.get_language() + if saved_language: + self.i18n.set_language(saved_language) + self.combined_mode = self.config_manager.get_layout_mode() self.layout_orientation = self.config_manager.get_layout_orientation() @@ -348,6 +354,71 @@ class FeedbackWindow(QMainWindow): if self.tab_manager.feedback_tab: self.tab_manager.feedback_tab.handle_image_paste_from_textarea() + def _on_reset_settings_requested(self) -> None: + """處理重置設定請求""" + try: + # 重置配置管理器的所有設定 + self.config_manager.reset_settings() + + # 重置應用程式狀態 + self.combined_mode = False # 重置為分離模式 + self.layout_orientation = 'vertical' # 重置為垂直布局 + + # 重新設置語言為預設 + self.i18n.set_language('zh-TW') + + # 保存當前內容 + current_data = self.tab_manager.get_feedback_data() + + # 重新創建分頁 + self.tab_manager.set_layout_mode(self.combined_mode) + self.tab_manager.set_layout_orientation(self.layout_orientation) + self.tab_manager.create_tabs() + + # 恢復內容 + self.tab_manager.restore_content( + current_data["interactive_feedback"], + current_data["command_logs"], + current_data["images"] + ) + + # 重新連接信號 + self.tab_manager.connect_signals(self) + + # 重新載入設定分頁的狀態 + if self.tab_manager.settings_tab: + self.tab_manager.settings_tab.reload_settings_from_config() + + # 刷新UI文字 + self._refresh_ui_texts() + + # 重新應用視窗定位(使用重置後的設定) + self._apply_window_positioning() + + # 切換到設定分頁顯示重置結果 + settings_tab_index = 3 # 分離模式下設定分頁是第4個(索引3) + if settings_tab_index < self.tab_widget.count(): + self.tab_widget.setCurrentIndex(settings_tab_index) + + # 顯示成功訊息 + QMessageBox.information( + self, + t('settings.reset.successTitle'), + t('settings.reset.successMessage'), + QMessageBox.Ok + ) + + debug_log("設定重置成功") + + except Exception as e: + debug_log(f"重置設定失敗: {e}") + QMessageBox.critical( + self, + t('errors.title'), + t('settings.reset.error', error=str(e)), + QMessageBox.Ok + ) + def _submit_feedback(self) -> None: """提交回饋""" # 獲取所有回饋數據 diff --git a/src/mcp_feedback_enhanced/gui/window/tab_manager.py b/src/mcp_feedback_enhanced/gui/window/tab_manager.py index 2ba7ff6..017b6ca 100644 --- a/src/mcp_feedback_enhanced/gui/window/tab_manager.py +++ b/src/mcp_feedback_enhanced/gui/window/tab_manager.py @@ -312,10 +312,13 @@ class TabManager: """連接信號""" # 連接設置分頁的信號 if self.settings_tab: - if hasattr(parent, 'language_changed'): - self.settings_tab.language_changed.connect(parent.language_changed) + # 語言變更信號直接連接到父窗口的刷新方法 + if hasattr(parent, '_refresh_ui_texts'): + self.settings_tab.language_changed.connect(parent._refresh_ui_texts) if hasattr(parent, '_on_layout_change_requested'): self.settings_tab.layout_change_requested.connect(parent._on_layout_change_requested) + if hasattr(parent, '_on_reset_settings_requested'): + self.settings_tab.reset_requested.connect(parent._on_reset_settings_requested) # 連接回饋分頁的圖片貼上信號 if self.feedback_tab: diff --git a/src/mcp_feedback_enhanced/locales/en/translations.json b/src/mcp_feedback_enhanced/locales/en/translations.json index 0911d17..3f4b1c3 100644 --- a/src/mcp_feedback_enhanced/locales/en/translations.json +++ b/src/mcp_feedback_enhanced/locales/en/translations.json @@ -92,6 +92,7 @@ }, "layout": { "title": "Interface Layout", + "description": "Choose the display method for AI summary and feedback input areas", "separateMode": "Separate Mode", "separateModeDescription": "AI summary and feedback are in separate tabs", "combinedVertical": "Combined Mode (Vertical Layout)", @@ -103,6 +104,17 @@ "title": "Window Positioning", "alwaysCenter": "Always show window at primary screen center", "alwaysCenterDescription": "Recommended for multi-monitor setups or when experiencing window positioning issues" + }, + "reset": { + "title": "Reset Settings", + "description": "Clear all saved settings and restore to default state", + "button": "Reset Settings", + "confirmTitle": "Confirm Reset Settings", + "confirmMessage": "Are you sure you want to reset all settings? This will clear all saved preferences and restore to default state.", + "successTitle": "Reset Successful", + "successMessage": "All settings have been successfully reset to default values.", + "errorTitle": "Reset Failed", + "errorMessage": "Error occurred while resetting settings: {error}" } }, "buttons": { diff --git a/src/mcp_feedback_enhanced/locales/zh-CN/translations.json b/src/mcp_feedback_enhanced/locales/zh-CN/translations.json index 1a74bac..cb4ed66 100644 --- a/src/mcp_feedback_enhanced/locales/zh-CN/translations.json +++ b/src/mcp_feedback_enhanced/locales/zh-CN/translations.json @@ -72,6 +72,7 @@ }, "layout": { "title": "界面布局", + "description": "选择 AI 摘要和反馈输入区域的显示方式", "separateMode": "分离模式", "separateModeDescription": "AI 摘要和反馈分别在不同页签", "combinedVertical": "合并模式(垂直布局)", @@ -83,6 +84,17 @@ "title": "窗口定位", "alwaysCenter": "总是在主屏幕中心显示窗口", "alwaysCenterDescription": "建议在多屏幕环境或遇到窗口定位问题时开启此选项" + }, + "reset": { + "title": "重置设置", + "description": "清除所有已保存的设置,恢复到默认状态", + "button": "重置设置", + "confirmTitle": "确认重置设置", + "confirmMessage": "确定要重置所有设置吗?这将清除所有已保存的偏好设置并恢复到默认状态。", + "successTitle": "重置成功", + "successMessage": "所有设置已成功重置为默认值。", + "errorTitle": "重置失败", + "errorMessage": "重置设置时发生错误:{error}" } }, "buttons": { diff --git a/src/mcp_feedback_enhanced/locales/zh-TW/translations.json b/src/mcp_feedback_enhanced/locales/zh-TW/translations.json index 37c2ff2..b2c65c7 100644 --- a/src/mcp_feedback_enhanced/locales/zh-TW/translations.json +++ b/src/mcp_feedback_enhanced/locales/zh-TW/translations.json @@ -88,6 +88,7 @@ }, "layout": { "title": "界面佈局", + "description": "選擇 AI 摘要和回饋輸入區域的顯示方式", "separateMode": "分離模式", "separateModeDescription": "AI 摘要和回饋分別在不同頁籤", "combinedVertical": "合併模式(垂直布局)", @@ -99,6 +100,17 @@ "title": "視窗定位", "alwaysCenter": "總是在主螢幕中心顯示視窗", "alwaysCenterDescription": "建議在多螢幕環境或遇到視窗定位問題時開啟此選項" + }, + "reset": { + "title": "重置設定", + "description": "清除所有已保存的設定,恢復到預設狀態", + "button": "重置設定", + "confirmTitle": "確認重置設定", + "confirmMessage": "確定要重置所有設定嗎?這將清除所有已保存的偏好設定並恢復到預設狀態。", + "successTitle": "重置成功", + "successMessage": "所有設定已成功重置為預設值。", + "errorTitle": "重置失敗", + "errorMessage": "重置設定時發生錯誤:{error}" } }, "buttons": {