gui 新增重置設定功能,允許用戶清除所有已保存的設定並恢復到預設狀態

This commit is contained in:
Minidoracat 2025-06-03 16:43:36 +08:00
parent ac05fd5b9a
commit be4d1b3a64
8 changed files with 599 additions and 215 deletions

View File

@ -9,6 +9,7 @@ GUI 主要入口點
from typing import Optional from typing import Optional
from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtGui import QFont
import sys import sys
from .models import FeedbackResult from .models import FeedbackResult
@ -31,6 +32,17 @@ def feedback_ui(project_directory: str, summary: str) -> Optional[FeedbackResult
if app is None: if app is None:
app = QApplication(sys.argv) 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 = FeedbackWindow(project_directory, summary)
window.show() window.show()

View File

@ -9,18 +9,21 @@
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QGroupBox, QComboBox, QRadioButton, QButtonGroup, QMessageBox, QComboBox, QRadioButton, QButtonGroup, QMessageBox,
QCheckBox 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 ...i18n import t, get_i18n_manager
from ...debug import gui_debug_log as debug_log
class SettingsTab(QWidget): class SettingsTab(QWidget):
"""設置分頁組件""" """設置分頁組件"""
language_changed = Signal() language_changed = Signal()
layout_change_requested = Signal(bool, str) # 佈局變更請求信號 (combined_mode, orientation) layout_change_requested = Signal(bool, str) # 佈局變更請求信號 (combined_mode, orientation)
reset_requested = Signal() # 重置設定請求信號
def __init__(self, combined_mode: bool, config_manager, parent=None): def __init__(self, combined_mode: bool, config_manager, parent=None):
super().__init__(parent) super().__init__(parent)
@ -28,315 +31,557 @@ class SettingsTab(QWidget):
self.config_manager = config_manager self.config_manager = config_manager
self.layout_orientation = self.config_manager.get_layout_orientation() self.layout_orientation = self.config_manager.get_layout_orientation()
self.i18n = get_i18n_manager() self.i18n = get_i18n_manager()
# 保存需要更新的UI元素引用
self.ui_elements = {}
# 設置全域字體為微軟正黑體
self._setup_font()
self._setup_ui() self._setup_ui()
# 在UI設置完成後確保正確設置初始狀態 # 在UI設置完成後確保正確設置初始狀態
self._set_initial_layout_state() 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: def _setup_ui(self) -> None:
"""設置用戶介面""" """設置用戶介面"""
layout = QVBoxLayout(self) # 主容器
layout.setSpacing(16) main_layout = QHBoxLayout(self)
layout.setContentsMargins(16, 16, 16, 16) main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.setSpacing(0)
# === 語言設置區域 === # 左側內容區域
self.language_group = QGroupBox(t('settings.language.title')) content_widget = QWidget()
self.language_group.setObjectName('language_group') content_widget.setMaximumWidth(600)
language_layout = QVBoxLayout(self.language_group) content_layout = QVBoxLayout(content_widget)
language_layout.setSpacing(12) content_layout.setContentsMargins(20, 20, 20, 20)
language_layout.setContentsMargins(16, 16, 16, 16) 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;") self._add_separator(content_layout)
language_row.addWidget(self.language_label)
# === 界面佈局 ===
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 = QComboBox()
self.language_selector.setMinimumWidth(180) self.language_selector.setMinimumHeight(28)
self.language_selector.setMinimumHeight(35) self.language_selector.setMaximumWidth(140)
self.language_selector.setStyleSheet(""" self.language_selector.setStyleSheet("""
QComboBox { QComboBox {
background-color: #404040; font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
border: 1px solid #606060; background-color: #3a3a3a;
border: 1px solid #555555;
border-radius: 4px; border-radius: 4px;
padding: 8px 12px; padding: 4px 8px;
color: #e0e0e0; color: #ffffff;
font-size: 14px; font-size: 12px;
} }
QComboBox:hover { QComboBox:hover {
border-color: #0078d4; border-color: #0078d4;
} }
QComboBox::drop-down { QComboBox::drop-down {
border: none; border: none;
width: 25px; width: 20px;
} }
QComboBox::down-arrow { QComboBox::down-arrow {
image: none; image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMgNEw2IDdMOSA0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPg==);
border-left: 5px solid transparent; width: 12px;
border-right: 5px solid transparent; height: 12px;
border-top: 7px solid #e0e0e0;
margin-right: 6px;
} }
QComboBox QAbstractItemView { QComboBox QAbstractItemView {
background-color: #404040; background-color: #3a3a3a;
border: 1px solid #606060; border: 1px solid #555555;
selection-background-color: #0078d4; selection-background-color: #0078d4;
color: #e0e0e0; color: #ffffff;
font-size: 14px; font-size: 12px;
} }
""") """)
# 填充語言選項和連接信號 # 填充語言選項
self._populate_language_selector() self._populate_language_selector()
self.language_selector.currentIndexChanged.connect(self._on_language_changed) self.language_selector.currentIndexChanged.connect(self._on_language_changed)
language_row.addWidget(self.language_selector) lang_container.addWidget(self.language_selector)
language_row.addStretch() lang_container.addStretch()
language_layout.addLayout(language_row) 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
# 語言說明 desc = self._create_description(t('settings.layout.description'))
self.language_description_label = QLabel(t('settings.language.description')) layout.addWidget(desc)
self.language_description_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-top: 8px;") # 保存引用以便更新
self.language_description_label.setWordWrap(True) self.ui_elements['layout_desc'] = desc
language_layout.addWidget(self.language_description_label)
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.layout_button_group = QButtonGroup()
# 分離模式 # 分離模式
self.separate_mode_radio = QRadioButton(t('settings.layout.separateMode')) self.separate_mode_radio = QRadioButton(t('settings.layout.separateMode'))
self.separate_mode_radio.setChecked(not self.combined_mode) self.separate_mode_radio.setStyleSheet("""
self.separate_mode_radio.setStyleSheet("font-size: 14px; font-weight: bold; color: #e0e0e0;") 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) 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')) separate_hint = QLabel(f" {t('settings.layout.separateModeDescription')}")
self.separate_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 12px;") separate_hint.setStyleSheet("""
self.separate_desc_label.setWordWrap(True) QLabel {
layout_layout.addWidget(self.separate_desc_label) 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 = QRadioButton(t('settings.layout.combinedVertical'))
self.combined_vertical_radio.setChecked(self.combined_mode and self.layout_orientation == 'vertical') self.combined_vertical_radio.setStyleSheet(self.separate_mode_radio.styleSheet())
self.combined_vertical_radio.setStyleSheet("font-size: 14px; font-weight: bold; color: #e0e0e0;")
self.layout_button_group.addButton(self.combined_vertical_radio, 1) 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')) vertical_hint = QLabel(f" {t('settings.layout.combinedVerticalDescription')}")
self.combined_vertical_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 12px;") vertical_hint.setStyleSheet(separate_hint.styleSheet())
self.combined_vertical_desc_label.setWordWrap(True) options_layout.addWidget(vertical_hint)
layout_layout.addWidget(self.combined_vertical_desc_label) # 保存引用以便更新
self.ui_elements['vertical_hint'] = vertical_hint
# 合併模式(水平布局) # 合併模式(水平
self.combined_horizontal_radio = QRadioButton(t('settings.layout.combinedHorizontal')) 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(self.separate_mode_radio.styleSheet())
self.combined_horizontal_radio.setStyleSheet("font-size: 14px; font-weight: bold; color: #e0e0e0;")
self.layout_button_group.addButton(self.combined_horizontal_radio, 2) 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')) horizontal_hint = QLabel(f" {t('settings.layout.combinedHorizontalDescription')}")
self.combined_horizontal_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 12px;") horizontal_hint.setStyleSheet(separate_hint.styleSheet())
self.combined_horizontal_desc_label.setWordWrap(True) options_layout.addWidget(horizontal_hint)
layout_layout.addWidget(self.combined_horizontal_desc_label) # 保存引用以便更新
self.ui_elements['horizontal_hint'] = horizontal_hint
layout.addLayout(options_layout)
# 連接佈局變更信號 # 連接佈局變更信號
self.layout_button_group.buttonToggled.connect(self._on_layout_changed) 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')) options_layout = QVBoxLayout()
self.window_group.setObjectName('window_group') options_layout.setSpacing(2)
window_layout = QVBoxLayout(self.window_group)
window_layout.setSpacing(12)
window_layout.setContentsMargins(16, 16, 16, 16)
# 總是在主螢幕中心顯示視窗選項
self.always_center_checkbox = QCheckBox(t('settings.window.alwaysCenter')) 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.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) 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')) center_hint = QLabel(f" {t('settings.window.alwaysCenterDescription')}")
self.center_desc_label.setStyleSheet("color: #9e9e9e; font-size: 12px; margin-left: 20px; margin-bottom: 8px;") center_hint.setStyleSheet("""
self.center_desc_label.setWordWrap(True) QLabel {
window_layout.addWidget(self.center_desc_label) 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.addLayout(options_layout)
layout.addStretch()
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: def _populate_language_selector(self) -> None:
"""填充語言選擇器""" """填充語言選擇器"""
# 保存當前選擇 languages = [
current_lang = self.i18n.get_current_language() ('zh-TW', '繁體中文'),
('zh-CN', '简体中文'),
('en', 'English')
]
# 暫時斷開信號連接,避免觸發語言變更事件 current_language = self.i18n.get_current_language()
try:
self.language_selector.currentIndexChanged.disconnect()
except RuntimeError:
pass # 如果沒有連接則忽略
# 清空並重新填充 # 暫時斷開信號連接以避免觸發變更事件
self.language_selector.blockSignals(True)
# 先清空現有選項
self.language_selector.clear() 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, (code, name) in enumerate(languages):
for i in range(self.language_selector.count()): self.language_selector.addItem(name, code)
if self.language_selector.itemData(i) == current_lang: if code == current_language:
self.language_selector.setCurrentIndex(i) 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: def _on_language_changed(self, index: int) -> None:
"""處理語言變更""" """語言變更事件處理"""
lang_code = self.language_selector.itemData(index) if index < 0:
if lang_code and self.i18n.set_language(lang_code): 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() self.language_changed.emit()
def _on_layout_changed(self, button, checked: bool) -> None: def _on_layout_changed(self, button, checked: bool) -> None:
"""處理佈局變更""" """佈局變更事件處理"""
if not checked: # 只處理選中的按鈕 if not checked:
return 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: if new_combined_mode != self.combined_mode or new_orientation != self.layout_orientation:
# 提示用戶需要重新創建界面 # 先保存配置
reply = QMessageBox.question( self.config_manager.set_layout_mode(new_combined_mode)
self, self.config_manager.set_layout_orientation(new_orientation)
t('app.layoutChangeTitle'),
t('app.layoutChangeMessage'),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes
)
if reply == QMessageBox.Yes: # 更新內部狀態
# 用戶確認變更,發送佈局變更請求 self.combined_mode = new_combined_mode
self.combined_mode = new_combined_mode self.layout_orientation = new_orientation
self.layout_orientation = new_orientation
self.layout_change_requested.emit(self.combined_mode, self.layout_orientation) # 發出佈局變更請求信號
else: self.layout_change_requested.emit(new_combined_mode, new_orientation)
# 用戶選擇不重新載入,恢復原來的選項
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)
def _on_always_center_changed(self, state: int) -> None: 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) 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: def update_texts(self) -> None:
"""更新界面文字(用於語言切換)""" """更新界面文字(不重新創建界面)"""
# 更新GroupBox標題 # 更新區塊標題
self.language_group.setTitle(t('settings.language.title')) if 'language_header' in self.ui_elements:
self.layout_group.setTitle(t('settings.layout.title')) self.ui_elements['language_header'].setText(f"🌐 {t('settings.language.title')}")
self.window_group.setTitle(t('settings.window.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')) if 'language_desc' in self.ui_elements:
self.language_description_label.setText(t('settings.language.description')) 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')) if 'separate_hint' in self.ui_elements:
self.combined_vertical_radio.setText(t('settings.layout.combinedVertical')) self.ui_elements['separate_hint'].setText(f" {t('settings.layout.separateModeDescription')}")
self.combined_horizontal_radio.setText(t('settings.layout.combinedHorizontal')) 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')) if hasattr(self, 'reset_button'):
self.combined_vertical_desc_label.setText(t('settings.layout.combinedVerticalDescription')) self.reset_button.setText(t('settings.reset.button'))
self.combined_horizontal_desc_label.setText(t('settings.layout.combinedHorizontalDescription'))
# 更新視窗設置文字 # 更新複選框文字
self.always_center_checkbox.setText(t('settings.window.alwaysCenter')) if hasattr(self, 'always_center_checkbox'):
self.center_desc_label.setText(t('settings.window.alwaysCenterDescription')) 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: def set_layout_mode(self, combined_mode: bool) -> None:
"""設置佈局模式""" """設置佈局模式"""
self.combined_mode = combined_mode self.combined_mode = combined_mode
# 暫時斷開信號連接,避免觸發變更事件 self._set_initial_layout_state()
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)
def set_layout_orientation(self, orientation: str) -> None: def set_layout_orientation(self, orientation: str) -> None:
"""設置佈局方向""" """設置佈局方向"""
self.layout_orientation = orientation self.layout_orientation = orientation
self._set_initial_layout_state()
# 暫時斷開信號連接,避免觸發變更事件
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)
def _set_initial_layout_state(self) -> None: def _set_initial_layout_state(self) -> None:
"""設置初始佈局狀態""" """設置初始佈局狀態"""
# 暫時斷開信號連接,避免觸發變更事件 if hasattr(self, 'separate_mode_radio'):
try: # 暫時斷開信號連接以避免觸發變更事件
self.layout_button_group.buttonToggled.disconnect() self.layout_button_group.blockSignals(True)
except RuntimeError:
pass if not self.combined_mode:
self.separate_mode_radio.setChecked(True)
# 根據當前配置設置正確的選項 elif self.layout_orientation == 'vertical':
if self.combined_mode:
if self.layout_orientation == 'vertical':
self.combined_vertical_radio.setChecked(True) self.combined_vertical_radio.setChecked(True)
else: # horizontal else:
self.combined_horizontal_radio.setChecked(True) self.combined_horizontal_radio.setChecked(True)
else:
self.separate_mode_radio.setChecked(True) # 重新連接信號
self.layout_button_group.blockSignals(False)
# 重新連接信號
self.layout_button_group.buttonToggled.connect(self._on_layout_changed)

View File

@ -120,4 +120,21 @@ class ConfigManager:
def set_always_center_window(self, always_center: bool) -> None: def set_always_center_window(self, always_center: bool) -> None:
"""設置總是在主螢幕中心顯示視窗""" """設置總是在主螢幕中心顯示視窗"""
self.set('always_center_window', always_center) self.set('always_center_window', always_center)
debug_log(f"視窗定位設置: {'總是中心顯示' if always_center else '智能定位'}") 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

View File

@ -34,6 +34,12 @@ class FeedbackWindow(QMainWindow):
# 初始化組件 # 初始化組件
self.config_manager = ConfigManager() 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.combined_mode = self.config_manager.get_layout_mode()
self.layout_orientation = self.config_manager.get_layout_orientation() self.layout_orientation = self.config_manager.get_layout_orientation()
@ -348,6 +354,71 @@ class FeedbackWindow(QMainWindow):
if self.tab_manager.feedback_tab: if self.tab_manager.feedback_tab:
self.tab_manager.feedback_tab.handle_image_paste_from_textarea() 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: def _submit_feedback(self) -> None:
"""提交回饋""" """提交回饋"""
# 獲取所有回饋數據 # 獲取所有回饋數據

View File

@ -312,10 +312,13 @@ class TabManager:
"""連接信號""" """連接信號"""
# 連接設置分頁的信號 # 連接設置分頁的信號
if self.settings_tab: 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'): if hasattr(parent, '_on_layout_change_requested'):
self.settings_tab.layout_change_requested.connect(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: if self.feedback_tab:

View File

@ -92,6 +92,7 @@
}, },
"layout": { "layout": {
"title": "Interface Layout", "title": "Interface Layout",
"description": "Choose the display method for AI summary and feedback input areas",
"separateMode": "Separate Mode", "separateMode": "Separate Mode",
"separateModeDescription": "AI summary and feedback are in separate tabs", "separateModeDescription": "AI summary and feedback are in separate tabs",
"combinedVertical": "Combined Mode (Vertical Layout)", "combinedVertical": "Combined Mode (Vertical Layout)",
@ -103,6 +104,17 @@
"title": "Window Positioning", "title": "Window Positioning",
"alwaysCenter": "Always show window at primary screen center", "alwaysCenter": "Always show window at primary screen center",
"alwaysCenterDescription": "Recommended for multi-monitor setups or when experiencing window positioning issues" "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": { "buttons": {

View File

@ -72,6 +72,7 @@
}, },
"layout": { "layout": {
"title": "界面布局", "title": "界面布局",
"description": "选择 AI 摘要和反馈输入区域的显示方式",
"separateMode": "分离模式", "separateMode": "分离模式",
"separateModeDescription": "AI 摘要和反馈分别在不同页签", "separateModeDescription": "AI 摘要和反馈分别在不同页签",
"combinedVertical": "合并模式(垂直布局)", "combinedVertical": "合并模式(垂直布局)",
@ -83,6 +84,17 @@
"title": "窗口定位", "title": "窗口定位",
"alwaysCenter": "总是在主屏幕中心显示窗口", "alwaysCenter": "总是在主屏幕中心显示窗口",
"alwaysCenterDescription": "建议在多屏幕环境或遇到窗口定位问题时开启此选项" "alwaysCenterDescription": "建议在多屏幕环境或遇到窗口定位问题时开启此选项"
},
"reset": {
"title": "重置设置",
"description": "清除所有已保存的设置,恢复到默认状态",
"button": "重置设置",
"confirmTitle": "确认重置设置",
"confirmMessage": "确定要重置所有设置吗?这将清除所有已保存的偏好设置并恢复到默认状态。",
"successTitle": "重置成功",
"successMessage": "所有设置已成功重置为默认值。",
"errorTitle": "重置失败",
"errorMessage": "重置设置时发生错误:{error}"
} }
}, },
"buttons": { "buttons": {

View File

@ -88,6 +88,7 @@
}, },
"layout": { "layout": {
"title": "界面佈局", "title": "界面佈局",
"description": "選擇 AI 摘要和回饋輸入區域的顯示方式",
"separateMode": "分離模式", "separateMode": "分離模式",
"separateModeDescription": "AI 摘要和回饋分別在不同頁籤", "separateModeDescription": "AI 摘要和回饋分別在不同頁籤",
"combinedVertical": "合併模式(垂直布局)", "combinedVertical": "合併模式(垂直布局)",
@ -99,6 +100,17 @@
"title": "視窗定位", "title": "視窗定位",
"alwaysCenter": "總是在主螢幕中心顯示視窗", "alwaysCenter": "總是在主螢幕中心顯示視窗",
"alwaysCenterDescription": "建議在多螢幕環境或遇到視窗定位問題時開啟此選項" "alwaysCenterDescription": "建議在多螢幕環境或遇到視窗定位問題時開啟此選項"
},
"reset": {
"title": "重置設定",
"description": "清除所有已保存的設定,恢復到預設狀態",
"button": "重置設定",
"confirmTitle": "確認重置設定",
"confirmMessage": "確定要重置所有設定嗎?這將清除所有已保存的偏好設定並恢復到預設狀態。",
"successTitle": "重置成功",
"successMessage": "所有設定已成功重置為預設值。",
"errorTitle": "重置失敗",
"errorMessage": "重置設定時發生錯誤:{error}"
} }
}, },
"buttons": { "buttons": {