mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
Merge branch 'feature/auto-focus-input-box' of github.com:penn201500/mcp-feedback-enhanced into penn201500-feature/auto-focus-input-box
This commit is contained in:
commit
04798e4aa4
@ -117,7 +117,8 @@
|
|||||||
},
|
},
|
||||||
"window": {
|
"window": {
|
||||||
"title": "Window Positioning",
|
"title": "Window Positioning",
|
||||||
"alwaysCenter": "Always show window at primary screen center"
|
"alwaysCenter": "Always show window at primary screen center",
|
||||||
|
"autoFocus": "Auto-focus input box when window opens"
|
||||||
},
|
},
|
||||||
"reset": {
|
"reset": {
|
||||||
"title": "Reset Settings",
|
"title": "Reset Settings",
|
||||||
@ -357,4 +358,4 @@
|
|||||||
"findPortFailed": "❌ Failed to find available port: {error}"
|
"findPortFailed": "❌ Failed to find available port: {error}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -97,7 +97,8 @@
|
|||||||
},
|
},
|
||||||
"window": {
|
"window": {
|
||||||
"title": "窗口定位",
|
"title": "窗口定位",
|
||||||
"alwaysCenter": "总是在主屏幕中心显示窗口"
|
"alwaysCenter": "总是在主屏幕中心显示窗口",
|
||||||
|
"autoFocus": "窗口打开时自动聚焦到输入框"
|
||||||
},
|
},
|
||||||
"reset": {
|
"reset": {
|
||||||
"title": "重置设置",
|
"title": "重置设置",
|
||||||
@ -352,4 +353,4 @@
|
|||||||
"thanks": "致谢与贡献",
|
"thanks": "致谢与贡献",
|
||||||
"thanksText": "感谢原作者 Fábio Ferreira (@fabiomlferreira) 创建了原始的 interactive-feedback-mcp 项目。\n\n本增强版本由 Minidoracat 开发和维护,大幅扩展了项目功能,新增了 GUI 界面、图片支持、多语言能力以及许多其他改进功能。\n\n同时感谢 sanshao85 的 mcp-feedback-collector 项目提供的 UI 设计灵感。\n\n开源协作让技术变得更美好!"
|
"thanksText": "感谢原作者 Fábio Ferreira (@fabiomlferreira) 创建了原始的 interactive-feedback-mcp 项目。\n\n本增强版本由 Minidoracat 开发和维护,大幅扩展了项目功能,新增了 GUI 界面、图片支持、多语言能力以及许多其他改进功能。\n\n同时感谢 sanshao85 的 mcp-feedback-collector 项目提供的 UI 设计灵感。\n\n开源协作让技术变得更美好!"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -129,7 +129,8 @@
|
|||||||
},
|
},
|
||||||
"window": {
|
"window": {
|
||||||
"title": "視窗定位",
|
"title": "視窗定位",
|
||||||
"alwaysCenter": "總是在主螢幕中心顯示視窗"
|
"alwaysCenter": "總是在主螢幕中心顯示視窗",
|
||||||
|
"autoFocus": "窗口打開時自動聚焦到輸入框"
|
||||||
},
|
},
|
||||||
"reset": {
|
"reset": {
|
||||||
"title": "重置設定",
|
"title": "重置設定",
|
||||||
@ -352,4 +353,4 @@
|
|||||||
"findPortFailed": "❌ 尋找可用端口失敗: {error}"
|
"findPortFailed": "❌ 尋找可用端口失敗: {error}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,65 +27,65 @@ class SettingsTab(QWidget):
|
|||||||
layout_change_requested = Signal(bool, str) # 佈局變更請求信號 (combined_mode, orientation)
|
layout_change_requested = Signal(bool, str) # 佈局變更請求信號 (combined_mode, orientation)
|
||||||
reset_requested = Signal() # 重置設定請求信號
|
reset_requested = Signal() # 重置設定請求信號
|
||||||
timeout_settings_changed = Signal(bool, int) # 超時設置變更信號 (enabled, duration)
|
timeout_settings_changed = Signal(bool, int) # 超時設置變更信號 (enabled, duration)
|
||||||
|
|
||||||
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)
|
||||||
self.combined_mode = combined_mode
|
self.combined_mode = combined_mode
|
||||||
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元素引用
|
# 保存需要更新的UI元素引用
|
||||||
self.ui_elements = {}
|
self.ui_elements = {}
|
||||||
|
|
||||||
# 設置全域字體為微軟正黑體
|
# 設置全域字體為微軟正黑體
|
||||||
self._setup_font()
|
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:
|
def _setup_font(self) -> None:
|
||||||
"""設置全域字體"""
|
"""設置全域字體"""
|
||||||
font = QFont("Microsoft JhengHei", 9) # 微軟正黑體,調整為 9pt
|
font = QFont("Microsoft JhengHei", 9) # 微軟正黑體,調整為 9pt
|
||||||
self.setFont(font)
|
self.setFont(font)
|
||||||
|
|
||||||
# 設置整個控件的樣式表,確保中文字體正確
|
# 設置整個控件的樣式表,確保中文字體正確
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QWidget {
|
QWidget {
|
||||||
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
def _setup_ui(self) -> None:
|
def _setup_ui(self) -> None:
|
||||||
"""設置用戶介面"""
|
"""設置用戶介面"""
|
||||||
# 主容器
|
# 主容器
|
||||||
main_layout = QHBoxLayout(self)
|
main_layout = QHBoxLayout(self)
|
||||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
main_layout.setSpacing(0)
|
main_layout.setSpacing(0)
|
||||||
|
|
||||||
# 左側內容區域
|
# 左側內容區域
|
||||||
content_widget = QWidget()
|
content_widget = QWidget()
|
||||||
content_widget.setMaximumWidth(600)
|
content_widget.setMaximumWidth(600)
|
||||||
content_layout = QVBoxLayout(content_widget)
|
content_layout = QVBoxLayout(content_widget)
|
||||||
content_layout.setContentsMargins(20, 20, 20, 20)
|
content_layout.setContentsMargins(20, 20, 20, 20)
|
||||||
content_layout.setSpacing(16)
|
content_layout.setSpacing(16)
|
||||||
|
|
||||||
# === 語言設置 ===
|
# === 語言設置 ===
|
||||||
self._create_language_section(content_layout)
|
self._create_language_section(content_layout)
|
||||||
|
|
||||||
# 添加分隔線
|
# 添加分隔線
|
||||||
self._add_separator(content_layout)
|
self._add_separator(content_layout)
|
||||||
|
|
||||||
# === 界面佈局 ===
|
# === 界面佈局 ===
|
||||||
self._create_layout_section(content_layout)
|
self._create_layout_section(content_layout)
|
||||||
|
|
||||||
# 添加分隔線
|
# 添加分隔線
|
||||||
self._add_separator(content_layout)
|
self._add_separator(content_layout)
|
||||||
|
|
||||||
# === 視窗設置 ===
|
# === 視窗設置 ===
|
||||||
self._create_window_section(content_layout)
|
self._create_window_section(content_layout)
|
||||||
|
|
||||||
# 添加分隔線
|
# 添加分隔線
|
||||||
self._add_separator(content_layout)
|
self._add_separator(content_layout)
|
||||||
|
|
||||||
@ -97,17 +97,17 @@ class SettingsTab(QWidget):
|
|||||||
|
|
||||||
# === 重置設定 ===
|
# === 重置設定 ===
|
||||||
self._create_reset_section(content_layout)
|
self._create_reset_section(content_layout)
|
||||||
|
|
||||||
# 添加彈性空間
|
# 添加彈性空間
|
||||||
content_layout.addStretch()
|
content_layout.addStretch()
|
||||||
|
|
||||||
# 添加到主布局
|
# 添加到主布局
|
||||||
main_layout.addWidget(content_widget)
|
main_layout.addWidget(content_widget)
|
||||||
main_layout.addStretch() # 右側彈性空間
|
main_layout.addStretch() # 右側彈性空間
|
||||||
|
|
||||||
# 設定初始狀態
|
# 設定初始狀態
|
||||||
self._set_initial_layout_state()
|
self._set_initial_layout_state()
|
||||||
|
|
||||||
def _add_separator(self, layout: QVBoxLayout) -> None:
|
def _add_separator(self, layout: QVBoxLayout) -> None:
|
||||||
"""添加分隔線"""
|
"""添加分隔線"""
|
||||||
separator = QFrame()
|
separator = QFrame()
|
||||||
@ -122,7 +122,7 @@ class SettingsTab(QWidget):
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
layout.addWidget(separator)
|
layout.addWidget(separator)
|
||||||
|
|
||||||
def _create_section_header(self, title: str, emoji: str = "") -> QLabel:
|
def _create_section_header(self, title: str, emoji: str = "") -> QLabel:
|
||||||
"""創建區塊標題"""
|
"""創建區塊標題"""
|
||||||
text = f"{emoji} {title}" if emoji else title
|
text = f"{emoji} {title}" if emoji else title
|
||||||
@ -138,7 +138,7 @@ class SettingsTab(QWidget):
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
return label
|
return label
|
||||||
|
|
||||||
def _create_description(self, text: str) -> QLabel:
|
def _create_description(self, text: str) -> QLabel:
|
||||||
"""創建說明文字"""
|
"""創建說明文字"""
|
||||||
label = QLabel(text)
|
label = QLabel(text)
|
||||||
@ -153,18 +153,18 @@ class SettingsTab(QWidget):
|
|||||||
""")
|
""")
|
||||||
label.setWordWrap(True)
|
label.setWordWrap(True)
|
||||||
return label
|
return label
|
||||||
|
|
||||||
def _create_language_section(self, layout: QVBoxLayout) -> None:
|
def _create_language_section(self, layout: QVBoxLayout) -> None:
|
||||||
"""創建語言設置區域"""
|
"""創建語言設置區域"""
|
||||||
header = self._create_section_header(t('settings.language.title'), "🌐")
|
header = self._create_section_header(t('settings.language.title'), "🌐")
|
||||||
layout.addWidget(header)
|
layout.addWidget(header)
|
||||||
# 保存引用以便更新
|
# 保存引用以便更新
|
||||||
self.ui_elements['language_header'] = header
|
self.ui_elements['language_header'] = header
|
||||||
|
|
||||||
# 語言選擇器容器
|
# 語言選擇器容器
|
||||||
lang_container = QHBoxLayout()
|
lang_container = QHBoxLayout()
|
||||||
lang_container.setContentsMargins(0, 0, 0, 0)
|
lang_container.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
self.language_selector = QComboBox()
|
self.language_selector = QComboBox()
|
||||||
self.language_selector.setMinimumHeight(28)
|
self.language_selector.setMinimumHeight(28)
|
||||||
self.language_selector.setMaximumWidth(140)
|
self.language_selector.setMaximumWidth(140)
|
||||||
@ -197,29 +197,29 @@ class SettingsTab(QWidget):
|
|||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# 填充語言選項
|
# 填充語言選項
|
||||||
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)
|
||||||
|
|
||||||
lang_container.addWidget(self.language_selector)
|
lang_container.addWidget(self.language_selector)
|
||||||
lang_container.addStretch()
|
lang_container.addStretch()
|
||||||
layout.addLayout(lang_container)
|
layout.addLayout(lang_container)
|
||||||
|
|
||||||
def _create_layout_section(self, layout: QVBoxLayout) -> None:
|
def _create_layout_section(self, layout: QVBoxLayout) -> None:
|
||||||
"""創建界面佈局區域"""
|
"""創建界面佈局區域"""
|
||||||
header = self._create_section_header(t('settings.layout.title'), "📐")
|
header = self._create_section_header(t('settings.layout.title'), "📐")
|
||||||
layout.addWidget(header)
|
layout.addWidget(header)
|
||||||
# 保存引用以便更新
|
# 保存引用以便更新
|
||||||
self.ui_elements['layout_header'] = header
|
self.ui_elements['layout_header'] = header
|
||||||
|
|
||||||
# 選項容器
|
# 選項容器
|
||||||
options_layout = QVBoxLayout()
|
options_layout = QVBoxLayout()
|
||||||
options_layout.setSpacing(2)
|
options_layout.setSpacing(2)
|
||||||
|
|
||||||
# 創建按鈕組
|
# 創建按鈕組
|
||||||
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.setStyleSheet("""
|
self.separate_mode_radio.setStyleSheet("""
|
||||||
@ -251,7 +251,7 @@ class SettingsTab(QWidget):
|
|||||||
""")
|
""")
|
||||||
self.layout_button_group.addButton(self.separate_mode_radio, 0)
|
self.layout_button_group.addButton(self.separate_mode_radio, 0)
|
||||||
options_layout.addWidget(self.separate_mode_radio)
|
options_layout.addWidget(self.separate_mode_radio)
|
||||||
|
|
||||||
separate_hint = QLabel(f" {t('settings.layout.separateModeDescription')}")
|
separate_hint = QLabel(f" {t('settings.layout.separateModeDescription')}")
|
||||||
separate_hint.setStyleSheet("""
|
separate_hint.setStyleSheet("""
|
||||||
QLabel {
|
QLabel {
|
||||||
@ -265,53 +265,59 @@ class SettingsTab(QWidget):
|
|||||||
options_layout.addWidget(separate_hint)
|
options_layout.addWidget(separate_hint)
|
||||||
# 保存引用以便更新
|
# 保存引用以便更新
|
||||||
self.ui_elements['separate_hint'] = 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.setStyleSheet(self.separate_mode_radio.styleSheet())
|
self.combined_vertical_radio.setStyleSheet(self.separate_mode_radio.styleSheet())
|
||||||
self.layout_button_group.addButton(self.combined_vertical_radio, 1)
|
self.layout_button_group.addButton(self.combined_vertical_radio, 1)
|
||||||
options_layout.addWidget(self.combined_vertical_radio)
|
options_layout.addWidget(self.combined_vertical_radio)
|
||||||
|
|
||||||
vertical_hint = QLabel(f" {t('settings.layout.combinedVerticalDescription')}")
|
vertical_hint = QLabel(f" {t('settings.layout.combinedVerticalDescription')}")
|
||||||
vertical_hint.setStyleSheet(separate_hint.styleSheet())
|
vertical_hint.setStyleSheet(separate_hint.styleSheet())
|
||||||
options_layout.addWidget(vertical_hint)
|
options_layout.addWidget(vertical_hint)
|
||||||
# 保存引用以便更新
|
# 保存引用以便更新
|
||||||
self.ui_elements['vertical_hint'] = vertical_hint
|
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.setStyleSheet(self.separate_mode_radio.styleSheet())
|
self.combined_horizontal_radio.setStyleSheet(self.separate_mode_radio.styleSheet())
|
||||||
self.layout_button_group.addButton(self.combined_horizontal_radio, 2)
|
self.layout_button_group.addButton(self.combined_horizontal_radio, 2)
|
||||||
options_layout.addWidget(self.combined_horizontal_radio)
|
options_layout.addWidget(self.combined_horizontal_radio)
|
||||||
|
|
||||||
horizontal_hint = QLabel(f" {t('settings.layout.combinedHorizontalDescription')}")
|
horizontal_hint = QLabel(f" {t('settings.layout.combinedHorizontalDescription')}")
|
||||||
horizontal_hint.setStyleSheet(separate_hint.styleSheet())
|
horizontal_hint.setStyleSheet(separate_hint.styleSheet())
|
||||||
options_layout.addWidget(horizontal_hint)
|
options_layout.addWidget(horizontal_hint)
|
||||||
# 保存引用以便更新
|
# 保存引用以便更新
|
||||||
self.ui_elements['horizontal_hint'] = horizontal_hint
|
self.ui_elements['horizontal_hint'] = horizontal_hint
|
||||||
|
|
||||||
layout.addLayout(options_layout)
|
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:
|
def _create_window_section(self, layout: QVBoxLayout) -> None:
|
||||||
"""創建視窗設置區域"""
|
"""創建視窗設置區域"""
|
||||||
header = self._create_section_header(t('settings.window.title'), "🖥️")
|
header = self._create_section_header(t('settings.window.title'), "🖥️")
|
||||||
layout.addWidget(header)
|
layout.addWidget(header)
|
||||||
# 保存引用以便更新
|
# 保存引用以便更新
|
||||||
self.ui_elements['window_header'] = header
|
self.ui_elements['window_header'] = header
|
||||||
|
|
||||||
# 選項容器
|
# 選項容器
|
||||||
options_layout = QVBoxLayout()
|
options_layout = QVBoxLayout()
|
||||||
options_layout.setSpacing(8)
|
options_layout.setSpacing(8)
|
||||||
|
|
||||||
# 使用現代化的 Switch 組件
|
# 使用現代化的 Switch 組件
|
||||||
self.always_center_switch = SwitchWithLabel(t('settings.window.alwaysCenter'))
|
self.always_center_switch = SwitchWithLabel(t('settings.window.alwaysCenter'))
|
||||||
self.always_center_switch.setChecked(self.config_manager.get_always_center_window())
|
self.always_center_switch.setChecked(self.config_manager.get_always_center_window())
|
||||||
self.always_center_switch.toggled.connect(self._on_always_center_changed)
|
self.always_center_switch.toggled.connect(self._on_always_center_changed)
|
||||||
options_layout.addWidget(self.always_center_switch)
|
options_layout.addWidget(self.always_center_switch)
|
||||||
|
|
||||||
|
# 自動聚焦開關
|
||||||
|
self.auto_focus_switch = SwitchWithLabel(t('settings.window.autoFocus'))
|
||||||
|
self.auto_focus_switch.setChecked(self.config_manager.get_auto_focus_enabled())
|
||||||
|
self.auto_focus_switch.toggled.connect(self._on_auto_focus_changed)
|
||||||
|
options_layout.addWidget(self.auto_focus_switch)
|
||||||
|
|
||||||
layout.addLayout(options_layout)
|
layout.addLayout(options_layout)
|
||||||
|
|
||||||
def _create_timeout_section(self, layout: QVBoxLayout) -> None:
|
def _create_timeout_section(self, layout: QVBoxLayout) -> None:
|
||||||
@ -376,10 +382,10 @@ class SettingsTab(QWidget):
|
|||||||
layout.addWidget(header)
|
layout.addWidget(header)
|
||||||
# 保存引用以便更新
|
# 保存引用以便更新
|
||||||
self.ui_elements['reset_header'] = header
|
self.ui_elements['reset_header'] = header
|
||||||
|
|
||||||
reset_container = QHBoxLayout()
|
reset_container = QHBoxLayout()
|
||||||
reset_container.setContentsMargins(0, 0, 0, 0)
|
reset_container.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
self.reset_button = QPushButton(t('settings.reset.button'))
|
self.reset_button = QPushButton(t('settings.reset.button'))
|
||||||
self.reset_button.setMinimumHeight(32)
|
self.reset_button.setMinimumHeight(32)
|
||||||
self.reset_button.setMaximumWidth(110)
|
self.reset_button.setMaximumWidth(110)
|
||||||
@ -402,7 +408,7 @@ class SettingsTab(QWidget):
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
self.reset_button.clicked.connect(self._on_reset_settings)
|
self.reset_button.clicked.connect(self._on_reset_settings)
|
||||||
|
|
||||||
reset_container.addWidget(self.reset_button)
|
reset_container.addWidget(self.reset_button)
|
||||||
reset_container.addStretch()
|
reset_container.addStretch()
|
||||||
layout.addLayout(reset_container)
|
layout.addLayout(reset_container)
|
||||||
@ -414,28 +420,28 @@ class SettingsTab(QWidget):
|
|||||||
('zh-CN', '简体中文'),
|
('zh-CN', '简体中文'),
|
||||||
('en', 'English')
|
('en', 'English')
|
||||||
]
|
]
|
||||||
|
|
||||||
current_language = self.i18n.get_current_language()
|
current_language = self.i18n.get_current_language()
|
||||||
|
|
||||||
# 暫時斷開信號連接以避免觸發變更事件
|
# 暫時斷開信號連接以避免觸發變更事件
|
||||||
self.language_selector.blockSignals(True)
|
self.language_selector.blockSignals(True)
|
||||||
|
|
||||||
# 先清空現有選項
|
# 先清空現有選項
|
||||||
self.language_selector.clear()
|
self.language_selector.clear()
|
||||||
|
|
||||||
for i, (code, name) in enumerate(languages):
|
for i, (code, name) in enumerate(languages):
|
||||||
self.language_selector.addItem(name, code)
|
self.language_selector.addItem(name, code)
|
||||||
if code == current_language:
|
if code == current_language:
|
||||||
self.language_selector.setCurrentIndex(i)
|
self.language_selector.setCurrentIndex(i)
|
||||||
|
|
||||||
# 重新連接信號
|
# 重新連接信號
|
||||||
self.language_selector.blockSignals(False)
|
self.language_selector.blockSignals(False)
|
||||||
|
|
||||||
def _on_language_changed(self, index: int) -> None:
|
def _on_language_changed(self, index: int) -> None:
|
||||||
"""語言變更事件處理"""
|
"""語言變更事件處理"""
|
||||||
if index < 0:
|
if index < 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
language_code = self.language_selector.itemData(index)
|
language_code = self.language_selector.itemData(index)
|
||||||
if language_code and language_code != self.i18n.get_current_language():
|
if language_code and language_code != self.i18n.get_current_language():
|
||||||
# 先保存語言設定
|
# 先保存語言設定
|
||||||
@ -444,14 +450,14 @@ class SettingsTab(QWidget):
|
|||||||
self.i18n.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
|
||||||
|
|
||||||
button_id = self.layout_button_group.id(button)
|
button_id = self.layout_button_group.id(button)
|
||||||
|
|
||||||
if button_id == 0: # 分離模式
|
if button_id == 0: # 分離模式
|
||||||
new_combined_mode = False
|
new_combined_mode = False
|
||||||
new_orientation = 'vertical'
|
new_orientation = 'vertical'
|
||||||
@ -463,7 +469,7 @@ class SettingsTab(QWidget):
|
|||||||
new_orientation = 'horizontal'
|
new_orientation = 'horizontal'
|
||||||
else:
|
else:
|
||||||
return
|
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:
|
||||||
# 批量保存配置(避免多次寫入文件)
|
# 批量保存配置(避免多次寫入文件)
|
||||||
@ -471,20 +477,26 @@ class SettingsTab(QWidget):
|
|||||||
'combined_mode': new_combined_mode,
|
'combined_mode': new_combined_mode,
|
||||||
'layout_orientation': new_orientation
|
'layout_orientation': new_orientation
|
||||||
})
|
})
|
||||||
|
|
||||||
# 更新內部狀態
|
# 更新內部狀態
|
||||||
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(new_combined_mode, new_orientation)
|
self.layout_change_requested.emit(new_combined_mode, new_orientation)
|
||||||
|
|
||||||
def _on_always_center_changed(self, checked: bool) -> None:
|
def _on_always_center_changed(self, checked: bool) -> None:
|
||||||
"""視窗定位選項變更事件處理"""
|
"""視窗定位選項變更事件處理"""
|
||||||
# 立即保存設定
|
# 立即保存設定
|
||||||
self.config_manager.set_always_center_window(checked)
|
self.config_manager.set_always_center_window(checked)
|
||||||
debug_log(f"視窗定位設置已保存: {checked}") # 調試輸出
|
debug_log(f"視窗定位設置已保存: {checked}") # 調試輸出
|
||||||
|
|
||||||
|
def _on_auto_focus_changed(self, checked: bool) -> None:
|
||||||
|
"""自動聚焦選項變更事件處理"""
|
||||||
|
# 立即保存設定
|
||||||
|
self.config_manager.set_auto_focus_enabled(checked)
|
||||||
|
debug_log(f"自動聚焦設置已保存: {checked}") # 調試輸出
|
||||||
|
|
||||||
def _on_timeout_enabled_changed(self, enabled: bool) -> None:
|
def _on_timeout_enabled_changed(self, enabled: bool) -> None:
|
||||||
"""超時啟用狀態變更事件處理"""
|
"""超時啟用狀態變更事件處理"""
|
||||||
# 立即保存設定
|
# 立即保存設定
|
||||||
@ -514,10 +526,10 @@ class SettingsTab(QWidget):
|
|||||||
QMessageBox.Yes | QMessageBox.No,
|
QMessageBox.Yes | QMessageBox.No,
|
||||||
QMessageBox.No
|
QMessageBox.No
|
||||||
)
|
)
|
||||||
|
|
||||||
if reply == QMessageBox.Yes:
|
if reply == QMessageBox.Yes:
|
||||||
self.reset_requested.emit()
|
self.reset_requested.emit()
|
||||||
|
|
||||||
def update_texts(self) -> None:
|
def update_texts(self) -> None:
|
||||||
"""更新界面文字(不重新創建界面)"""
|
"""更新界面文字(不重新創建界面)"""
|
||||||
# 更新區塊標題
|
# 更新區塊標題
|
||||||
@ -546,10 +558,12 @@ class SettingsTab(QWidget):
|
|||||||
# 更新按鈕文字
|
# 更新按鈕文字
|
||||||
if hasattr(self, 'reset_button'):
|
if hasattr(self, 'reset_button'):
|
||||||
self.reset_button.setText(t('settings.reset.button'))
|
self.reset_button.setText(t('settings.reset.button'))
|
||||||
|
|
||||||
# 更新切換開關文字
|
# 更新切換開關文字
|
||||||
if hasattr(self, 'always_center_switch'):
|
if hasattr(self, 'always_center_switch'):
|
||||||
self.always_center_switch.setText(t('settings.window.alwaysCenter'))
|
self.always_center_switch.setText(t('settings.window.alwaysCenter'))
|
||||||
|
if hasattr(self, 'auto_focus_switch'):
|
||||||
|
self.auto_focus_switch.setText(t('settings.window.autoFocus'))
|
||||||
if hasattr(self, 'timeout_enabled_switch'):
|
if hasattr(self, 'timeout_enabled_switch'):
|
||||||
self.timeout_enabled_switch.setText(t('timeout.enable'))
|
self.timeout_enabled_switch.setText(t('timeout.enable'))
|
||||||
|
|
||||||
@ -558,7 +572,7 @@ class SettingsTab(QWidget):
|
|||||||
self.ui_elements['timeout_duration_label'].setText(t('timeout.duration.label'))
|
self.ui_elements['timeout_duration_label'].setText(t('timeout.duration.label'))
|
||||||
if hasattr(self, 'timeout_duration_spinbox'):
|
if hasattr(self, 'timeout_duration_spinbox'):
|
||||||
self.timeout_duration_spinbox.setSuffix(" " + t('timeout.seconds'))
|
self.timeout_duration_spinbox.setSuffix(" " + t('timeout.seconds'))
|
||||||
|
|
||||||
# 更新單選按鈕文字
|
# 更新單選按鈕文字
|
||||||
if hasattr(self, 'separate_mode_radio'):
|
if hasattr(self, 'separate_mode_radio'):
|
||||||
self.separate_mode_radio.setText(t('settings.layout.separateMode'))
|
self.separate_mode_radio.setText(t('settings.layout.separateMode'))
|
||||||
@ -566,25 +580,29 @@ class SettingsTab(QWidget):
|
|||||||
self.combined_vertical_radio.setText(t('settings.layout.combinedVertical'))
|
self.combined_vertical_radio.setText(t('settings.layout.combinedVertical'))
|
||||||
if hasattr(self, 'combined_horizontal_radio'):
|
if hasattr(self, 'combined_horizontal_radio'):
|
||||||
self.combined_horizontal_radio.setText(t('settings.layout.combinedHorizontal'))
|
self.combined_horizontal_radio.setText(t('settings.layout.combinedHorizontal'))
|
||||||
|
|
||||||
# 注意:不要重新填充語言選擇器,避免重複選項問題
|
# 注意:不要重新填充語言選擇器,避免重複選項問題
|
||||||
|
|
||||||
def reload_settings_from_config(self) -> None:
|
def reload_settings_from_config(self) -> None:
|
||||||
"""從配置重新載入設定狀態"""
|
"""從配置重新載入設定狀態"""
|
||||||
# 重新載入語言設定
|
# 重新載入語言設定
|
||||||
if hasattr(self, 'language_selector'):
|
if hasattr(self, 'language_selector'):
|
||||||
self._populate_language_selector()
|
self._populate_language_selector()
|
||||||
|
|
||||||
# 重新載入佈局設定
|
# 重新載入佈局設定
|
||||||
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()
|
||||||
self._set_initial_layout_state()
|
self._set_initial_layout_state()
|
||||||
|
|
||||||
# 重新載入視窗設定
|
# 重新載入視窗設定
|
||||||
if hasattr(self, 'always_center_switch'):
|
if hasattr(self, 'always_center_switch'):
|
||||||
always_center = self.config_manager.get_always_center_window()
|
always_center = self.config_manager.get_always_center_window()
|
||||||
self.always_center_switch.setChecked(always_center)
|
self.always_center_switch.setChecked(always_center)
|
||||||
debug_log(f"重新載入視窗定位設置: {always_center}") # 調試輸出
|
debug_log(f"重新載入視窗定位設置: {always_center}") # 調試輸出
|
||||||
|
if hasattr(self, 'auto_focus_switch'):
|
||||||
|
auto_focus = self.config_manager.get_auto_focus_enabled()
|
||||||
|
self.auto_focus_switch.setChecked(auto_focus)
|
||||||
|
debug_log(f"重新載入自動聚焦設置: {auto_focus}") # 調試輸出
|
||||||
|
|
||||||
# 重新載入超時設定
|
# 重新載入超時設定
|
||||||
if hasattr(self, 'timeout_enabled_switch'):
|
if hasattr(self, 'timeout_enabled_switch'):
|
||||||
@ -595,29 +613,29 @@ class SettingsTab(QWidget):
|
|||||||
timeout_duration = self.config_manager.get_timeout_duration()
|
timeout_duration = self.config_manager.get_timeout_duration()
|
||||||
self.timeout_duration_spinbox.setValue(timeout_duration)
|
self.timeout_duration_spinbox.setValue(timeout_duration)
|
||||||
debug_log(f"重新載入超時時間設置: {timeout_duration}") # 調試輸出
|
debug_log(f"重新載入超時時間設置: {timeout_duration}") # 調試輸出
|
||||||
|
|
||||||
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()
|
self._set_initial_layout_state()
|
||||||
|
|
||||||
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()
|
self._set_initial_layout_state()
|
||||||
|
|
||||||
def _set_initial_layout_state(self) -> None:
|
def _set_initial_layout_state(self) -> None:
|
||||||
"""設置初始佈局狀態"""
|
"""設置初始佈局狀態"""
|
||||||
if hasattr(self, 'separate_mode_radio'):
|
if hasattr(self, 'separate_mode_radio'):
|
||||||
# 暫時斷開信號連接以避免觸發變更事件
|
# 暫時斷開信號連接以避免觸發變更事件
|
||||||
self.layout_button_group.blockSignals(True)
|
self.layout_button_group.blockSignals(True)
|
||||||
|
|
||||||
if not self.combined_mode:
|
if not self.combined_mode:
|
||||||
self.separate_mode_radio.setChecked(True)
|
self.separate_mode_radio.setChecked(True)
|
||||||
elif self.layout_orientation == 'vertical':
|
elif self.layout_orientation == 'vertical':
|
||||||
self.combined_vertical_radio.setChecked(True)
|
self.combined_vertical_radio.setChecked(True)
|
||||||
else:
|
else:
|
||||||
self.combined_horizontal_radio.setChecked(True)
|
self.combined_horizontal_radio.setChecked(True)
|
||||||
|
|
||||||
# 重新連接信號
|
# 重新連接信號
|
||||||
self.layout_button_group.blockSignals(False)
|
self.layout_button_group.blockSignals(False)
|
||||||
|
@ -16,18 +16,18 @@ from ...debug import gui_debug_log as debug_log
|
|||||||
|
|
||||||
class ConfigManager:
|
class ConfigManager:
|
||||||
"""配置管理器"""
|
"""配置管理器"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._config_file = self._get_config_file_path()
|
self._config_file = self._get_config_file_path()
|
||||||
self._config_cache = {}
|
self._config_cache = {}
|
||||||
self._load_config()
|
self._load_config()
|
||||||
|
|
||||||
def _get_config_file_path(self) -> Path:
|
def _get_config_file_path(self) -> Path:
|
||||||
"""獲取配置文件路徑"""
|
"""獲取配置文件路徑"""
|
||||||
config_dir = Path.home() / ".config" / "mcp-feedback-enhanced"
|
config_dir = Path.home() / ".config" / "mcp-feedback-enhanced"
|
||||||
config_dir.mkdir(parents=True, exist_ok=True)
|
config_dir.mkdir(parents=True, exist_ok=True)
|
||||||
return config_dir / "ui_settings.json"
|
return config_dir / "ui_settings.json"
|
||||||
|
|
||||||
def _load_config(self) -> None:
|
def _load_config(self) -> None:
|
||||||
"""載入配置"""
|
"""載入配置"""
|
||||||
try:
|
try:
|
||||||
@ -41,7 +41,7 @@ class ConfigManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"載入配置失敗: {e}")
|
debug_log(f"載入配置失敗: {e}")
|
||||||
self._config_cache = {}
|
self._config_cache = {}
|
||||||
|
|
||||||
def _save_config(self) -> None:
|
def _save_config(self) -> None:
|
||||||
"""保存配置"""
|
"""保存配置"""
|
||||||
try:
|
try:
|
||||||
@ -50,16 +50,16 @@ class ConfigManager:
|
|||||||
debug_log("配置文件保存成功")
|
debug_log("配置文件保存成功")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"保存配置失敗: {e}")
|
debug_log(f"保存配置失敗: {e}")
|
||||||
|
|
||||||
def get(self, key: str, default: Any = None) -> Any:
|
def get(self, key: str, default: Any = None) -> Any:
|
||||||
"""獲取配置值"""
|
"""獲取配置值"""
|
||||||
return self._config_cache.get(key, default)
|
return self._config_cache.get(key, default)
|
||||||
|
|
||||||
def set(self, key: str, value: Any) -> None:
|
def set(self, key: str, value: Any) -> None:
|
||||||
"""設置配置值"""
|
"""設置配置值"""
|
||||||
self._config_cache[key] = value
|
self._config_cache[key] = value
|
||||||
self._save_config()
|
self._save_config()
|
||||||
|
|
||||||
def update_partial_config(self, updates: Dict[str, Any]) -> None:
|
def update_partial_config(self, updates: Dict[str, Any]) -> None:
|
||||||
"""批量更新配置項目,只保存指定的設定而不影響其他參數"""
|
"""批量更新配置項目,只保存指定的設定而不影響其他參數"""
|
||||||
try:
|
try:
|
||||||
@ -68,84 +68,84 @@ class ConfigManager:
|
|||||||
if self._config_file.exists():
|
if self._config_file.exists():
|
||||||
with open(self._config_file, 'r', encoding='utf-8') as f:
|
with open(self._config_file, 'r', encoding='utf-8') as f:
|
||||||
current_config = json.load(f)
|
current_config = json.load(f)
|
||||||
|
|
||||||
# 只更新指定的項目
|
# 只更新指定的項目
|
||||||
for key, value in updates.items():
|
for key, value in updates.items():
|
||||||
current_config[key] = value
|
current_config[key] = value
|
||||||
# 同時更新內存緩存
|
# 同時更新內存緩存
|
||||||
self._config_cache[key] = value
|
self._config_cache[key] = value
|
||||||
|
|
||||||
# 保存到文件
|
# 保存到文件
|
||||||
with open(self._config_file, 'w', encoding='utf-8') as f:
|
with open(self._config_file, 'w', encoding='utf-8') as f:
|
||||||
json.dump(current_config, f, ensure_ascii=False, indent=2)
|
json.dump(current_config, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
debug_log(f"部分配置已更新: {list(updates.keys())}")
|
debug_log(f"部分配置已更新: {list(updates.keys())}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"更新部分配置失敗: {e}")
|
debug_log(f"更新部分配置失敗: {e}")
|
||||||
|
|
||||||
def get_layout_mode(self) -> bool:
|
def get_layout_mode(self) -> bool:
|
||||||
"""獲取佈局模式(False=分離模式,True=合併模式)"""
|
"""獲取佈局模式(False=分離模式,True=合併模式)"""
|
||||||
return self.get('combined_mode', False)
|
return self.get('combined_mode', False)
|
||||||
|
|
||||||
def set_layout_mode(self, combined_mode: bool) -> None:
|
def set_layout_mode(self, combined_mode: bool) -> None:
|
||||||
"""設置佈局模式"""
|
"""設置佈局模式"""
|
||||||
self.update_partial_config({'combined_mode': combined_mode})
|
self.update_partial_config({'combined_mode': combined_mode})
|
||||||
debug_log(f"佈局模式設置: {'合併模式' if combined_mode else '分離模式'}")
|
debug_log(f"佈局模式設置: {'合併模式' if combined_mode else '分離模式'}")
|
||||||
|
|
||||||
def get_layout_orientation(self) -> str:
|
def get_layout_orientation(self) -> str:
|
||||||
"""獲取佈局方向(vertical=垂直(上下),horizontal=水平(左右))"""
|
"""獲取佈局方向(vertical=垂直(上下),horizontal=水平(左右))"""
|
||||||
return self.get('layout_orientation', 'vertical')
|
return self.get('layout_orientation', 'vertical')
|
||||||
|
|
||||||
def set_layout_orientation(self, orientation: str) -> None:
|
def set_layout_orientation(self, orientation: str) -> None:
|
||||||
"""設置佈局方向"""
|
"""設置佈局方向"""
|
||||||
if orientation not in ['vertical', 'horizontal']:
|
if orientation not in ['vertical', 'horizontal']:
|
||||||
orientation = 'vertical'
|
orientation = 'vertical'
|
||||||
self.update_partial_config({'layout_orientation': orientation})
|
self.update_partial_config({'layout_orientation': orientation})
|
||||||
debug_log(f"佈局方向設置: {'垂直(上下)' if orientation == 'vertical' else '水平(左右)'}")
|
debug_log(f"佈局方向設置: {'垂直(上下)' if orientation == 'vertical' else '水平(左右)'}")
|
||||||
|
|
||||||
def get_language(self) -> str:
|
def get_language(self) -> str:
|
||||||
"""獲取語言設置"""
|
"""獲取語言設置"""
|
||||||
return self.get('language', 'zh-TW')
|
return self.get('language', 'zh-TW')
|
||||||
|
|
||||||
def set_language(self, language: str) -> None:
|
def set_language(self, language: str) -> None:
|
||||||
"""設置語言"""
|
"""設置語言"""
|
||||||
self.update_partial_config({'language': language})
|
self.update_partial_config({'language': language})
|
||||||
debug_log(f"語言設置: {language}")
|
debug_log(f"語言設置: {language}")
|
||||||
|
|
||||||
def get_splitter_sizes(self, splitter_name: str) -> list:
|
def get_splitter_sizes(self, splitter_name: str) -> list:
|
||||||
"""獲取分割器尺寸"""
|
"""獲取分割器尺寸"""
|
||||||
sizes = self.get(f'splitter_sizes.{splitter_name}', [])
|
sizes = self.get(f'splitter_sizes.{splitter_name}', [])
|
||||||
if sizes:
|
if sizes:
|
||||||
debug_log(f"載入分割器 {splitter_name} 尺寸: {sizes}")
|
debug_log(f"載入分割器 {splitter_name} 尺寸: {sizes}")
|
||||||
return sizes
|
return sizes
|
||||||
|
|
||||||
def set_splitter_sizes(self, splitter_name: str, sizes: list) -> None:
|
def set_splitter_sizes(self, splitter_name: str, sizes: list) -> None:
|
||||||
"""設置分割器尺寸"""
|
"""設置分割器尺寸"""
|
||||||
self.update_partial_config({f'splitter_sizes.{splitter_name}': sizes})
|
self.update_partial_config({f'splitter_sizes.{splitter_name}': sizes})
|
||||||
debug_log(f"保存分割器 {splitter_name} 尺寸: {sizes}")
|
debug_log(f"保存分割器 {splitter_name} 尺寸: {sizes}")
|
||||||
|
|
||||||
def get_window_geometry(self) -> dict:
|
def get_window_geometry(self) -> dict:
|
||||||
"""獲取窗口幾何信息"""
|
"""獲取窗口幾何信息"""
|
||||||
geometry = self.get('window_geometry', {})
|
geometry = self.get('window_geometry', {})
|
||||||
if geometry:
|
if geometry:
|
||||||
debug_log(f"載入窗口幾何信息: {geometry}")
|
debug_log(f"載入窗口幾何信息: {geometry}")
|
||||||
return geometry
|
return geometry
|
||||||
|
|
||||||
def set_window_geometry(self, geometry: dict) -> None:
|
def set_window_geometry(self, geometry: dict) -> None:
|
||||||
"""設置窗口幾何信息(使用部分更新避免覆蓋其他設定)"""
|
"""設置窗口幾何信息(使用部分更新避免覆蓋其他設定)"""
|
||||||
self.update_partial_config({'window_geometry': geometry})
|
self.update_partial_config({'window_geometry': geometry})
|
||||||
debug_log(f"保存窗口幾何信息: {geometry}")
|
debug_log(f"保存窗口幾何信息: {geometry}")
|
||||||
|
|
||||||
def get_always_center_window(self) -> bool:
|
def get_always_center_window(self) -> bool:
|
||||||
"""獲取總是在主螢幕中心顯示視窗的設置"""
|
"""獲取總是在主螢幕中心顯示視窗的設置"""
|
||||||
return self.get('always_center_window', False)
|
return self.get('always_center_window', False)
|
||||||
|
|
||||||
def set_always_center_window(self, always_center: bool) -> None:
|
def set_always_center_window(self, always_center: bool) -> None:
|
||||||
"""設置總是在主螢幕中心顯示視窗"""
|
"""設置總是在主螢幕中心顯示視窗"""
|
||||||
self.update_partial_config({'always_center_window': always_center})
|
self.update_partial_config({'always_center_window': always_center})
|
||||||
debug_log(f"視窗定位設置: {'總是中心顯示' if always_center else '智能定位'}")
|
debug_log(f"視窗定位設置: {'總是中心顯示' if always_center else '智能定位'}")
|
||||||
|
|
||||||
def get_image_size_limit(self) -> int:
|
def get_image_size_limit(self) -> int:
|
||||||
"""獲取圖片大小限制(bytes),0 表示無限制"""
|
"""獲取圖片大小限制(bytes),0 表示無限制"""
|
||||||
return self.get('image_size_limit', 0)
|
return self.get('image_size_limit', 0)
|
||||||
@ -199,6 +199,15 @@ class ConfigManager:
|
|||||||
})
|
})
|
||||||
debug_log(f"超時設置: {'啟用' if enabled else '停用'}, {seconds} 秒")
|
debug_log(f"超時設置: {'啟用' if enabled else '停用'}, {seconds} 秒")
|
||||||
|
|
||||||
|
def get_auto_focus_enabled(self) -> bool:
|
||||||
|
"""獲取是否啟用自動聚焦到輸入框"""
|
||||||
|
return self.get('auto_focus_enabled', True) # 預設啟用
|
||||||
|
|
||||||
|
def set_auto_focus_enabled(self, enabled: bool) -> None:
|
||||||
|
"""設置是否啟用自動聚焦到輸入框"""
|
||||||
|
self.update_partial_config({'auto_focus_enabled': enabled})
|
||||||
|
debug_log(f"自動聚焦設置: {'啟用' if enabled else '停用'}")
|
||||||
|
|
||||||
def reset_settings(self) -> None:
|
def reset_settings(self) -> None:
|
||||||
"""重置所有設定到預設值"""
|
"""重置所有設定到預設值"""
|
||||||
try:
|
try:
|
||||||
@ -214,4 +223,4 @@ class ConfigManager:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"重置設定失敗: {e}")
|
debug_log(f"重置設定失敗: {e}")
|
||||||
raise
|
raise
|
||||||
|
@ -33,64 +33,73 @@ class FeedbackWindow(QMainWindow):
|
|||||||
self.result = None
|
self.result = None
|
||||||
self.i18n = get_i18n_manager()
|
self.i18n = get_i18n_manager()
|
||||||
self.mcp_timeout_seconds = timeout_seconds # MCP 傳入的超時時間
|
self.mcp_timeout_seconds = timeout_seconds # MCP 傳入的超時時間
|
||||||
|
|
||||||
# 初始化組件
|
# 初始化組件
|
||||||
self.config_manager = ConfigManager()
|
self.config_manager = ConfigManager()
|
||||||
|
|
||||||
# 載入保存的語言設定
|
# 載入保存的語言設定
|
||||||
saved_language = self.config_manager.get_language()
|
saved_language = self.config_manager.get_language()
|
||||||
if saved_language:
|
if saved_language:
|
||||||
self.i18n.set_language(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()
|
||||||
|
|
||||||
# 設置窗口狀態保存的防抖計時器
|
# 設置窗口狀態保存的防抖計時器
|
||||||
self._save_timer = QTimer()
|
self._save_timer = QTimer()
|
||||||
self._save_timer.setSingleShot(True)
|
self._save_timer.setSingleShot(True)
|
||||||
self._save_timer.timeout.connect(self._delayed_save_window_position)
|
self._save_timer.timeout.connect(self._delayed_save_window_position)
|
||||||
self._save_delay = 500 # 500ms 延遲,避免過於頻繁的保存
|
self._save_delay = 500 # 500ms 延遲,避免過於頻繁的保存
|
||||||
|
|
||||||
# 設置UI
|
# 設置UI
|
||||||
self._setup_ui()
|
self._setup_ui()
|
||||||
self._setup_shortcuts()
|
self._setup_shortcuts()
|
||||||
self._connect_signals()
|
self._connect_signals()
|
||||||
|
|
||||||
debug_log("主窗口初始化完成")
|
debug_log("主窗口初始化完成")
|
||||||
|
|
||||||
# 如果啟用了超時,自動開始倒數計時
|
# 如果啟用了超時,自動開始倒數計時
|
||||||
self.start_timeout_if_enabled()
|
self.start_timeout_if_enabled()
|
||||||
|
|
||||||
|
# 設置定時器在窗口顯示後自動聚焦到輸入框(如果啟用)
|
||||||
|
if self.config_manager.get_auto_focus_enabled():
|
||||||
|
self._focus_timer = QTimer()
|
||||||
|
self._focus_timer.setSingleShot(True)
|
||||||
|
self._focus_timer.timeout.connect(self._auto_focus_input)
|
||||||
|
self._focus_timer.start(300) # 延遲300ms確保窗口和UI元素完全加載
|
||||||
|
else:
|
||||||
|
debug_log("自動聚焦已停用")
|
||||||
|
|
||||||
def _setup_ui(self) -> None:
|
def _setup_ui(self) -> None:
|
||||||
"""設置用戶介面"""
|
"""設置用戶介面"""
|
||||||
self.setWindowTitle(t('app.title'))
|
self.setWindowTitle(t('app.title'))
|
||||||
self.setMinimumSize(400, 300) # 大幅降低最小窗口大小限制,允許用戶自由調整
|
self.setMinimumSize(400, 300) # 大幅降低最小窗口大小限制,允許用戶自由調整
|
||||||
self.resize(1200, 900)
|
self.resize(1200, 900)
|
||||||
|
|
||||||
# 智能視窗定位
|
# 智能視窗定位
|
||||||
self._apply_window_positioning()
|
self._apply_window_positioning()
|
||||||
|
|
||||||
# 中央元件
|
# 中央元件
|
||||||
central_widget = QWidget()
|
central_widget = QWidget()
|
||||||
self.setCentralWidget(central_widget)
|
self.setCentralWidget(central_widget)
|
||||||
|
|
||||||
# 主布局
|
# 主布局
|
||||||
main_layout = QVBoxLayout(central_widget)
|
main_layout = QVBoxLayout(central_widget)
|
||||||
main_layout.setSpacing(8)
|
main_layout.setSpacing(8)
|
||||||
main_layout.setContentsMargins(16, 8, 16, 12)
|
main_layout.setContentsMargins(16, 8, 16, 12)
|
||||||
|
|
||||||
# 頂部專案目錄信息
|
# 頂部專案目錄信息
|
||||||
self._create_project_header(main_layout)
|
self._create_project_header(main_layout)
|
||||||
|
|
||||||
# 分頁區域
|
# 分頁區域
|
||||||
self._create_tab_area(main_layout)
|
self._create_tab_area(main_layout)
|
||||||
|
|
||||||
# 操作按鈕
|
# 操作按鈕
|
||||||
self._create_action_buttons(main_layout)
|
self._create_action_buttons(main_layout)
|
||||||
|
|
||||||
# 應用深色主題
|
# 應用深色主題
|
||||||
self._apply_dark_style()
|
self._apply_dark_style()
|
||||||
|
|
||||||
def _create_project_header(self, layout: QVBoxLayout) -> None:
|
def _create_project_header(self, layout: QVBoxLayout) -> None:
|
||||||
"""創建專案目錄頭部信息"""
|
"""創建專案目錄頭部信息"""
|
||||||
# 創建水平布局來放置專案目錄和倒數計時器
|
# 創建水平布局來放置專案目錄和倒數計時器
|
||||||
@ -159,7 +168,7 @@ class FeedbackWindow(QMainWindow):
|
|||||||
self._update_countdown_visibility()
|
self._update_countdown_visibility()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _create_tab_area(self, layout: QVBoxLayout) -> None:
|
def _create_tab_area(self, layout: QVBoxLayout) -> None:
|
||||||
"""創建分頁區域"""
|
"""創建分頁區域"""
|
||||||
# 創建滾動區域來包裝整個分頁組件
|
# 創建滾動區域來包裝整個分頁組件
|
||||||
@ -220,21 +229,21 @@ class FeedbackWindow(QMainWindow):
|
|||||||
width: 0px;
|
width: 0px;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
self.tab_widget = QTabWidget()
|
self.tab_widget = QTabWidget()
|
||||||
self.tab_widget.setMinimumHeight(150) # 降低分頁組件最小高度
|
self.tab_widget.setMinimumHeight(150) # 降低分頁組件最小高度
|
||||||
# 設置分頁組件的大小策略,確保能觸發滾動
|
# 設置分頁組件的大小策略,確保能觸發滾動
|
||||||
self.tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
self.tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
|
||||||
# 初始化分頁管理器
|
# 初始化分頁管理器
|
||||||
self.tab_manager = TabManager(
|
self.tab_manager = TabManager(
|
||||||
self.tab_widget,
|
self.tab_widget,
|
||||||
self.project_dir,
|
self.project_dir,
|
||||||
self.summary,
|
self.summary,
|
||||||
self.combined_mode,
|
self.combined_mode,
|
||||||
self.layout_orientation
|
self.layout_orientation
|
||||||
)
|
)
|
||||||
|
|
||||||
# 創建分頁
|
# 創建分頁
|
||||||
self.tab_manager.create_tabs()
|
self.tab_manager.create_tabs()
|
||||||
|
|
||||||
@ -243,21 +252,21 @@ class FeedbackWindow(QMainWindow):
|
|||||||
|
|
||||||
# 將分頁組件放入滾動區域
|
# 將分頁組件放入滾動區域
|
||||||
scroll_area.setWidget(self.tab_widget)
|
scroll_area.setWidget(self.tab_widget)
|
||||||
|
|
||||||
layout.addWidget(scroll_area, 1)
|
layout.addWidget(scroll_area, 1)
|
||||||
|
|
||||||
def _create_action_buttons(self, layout: QVBoxLayout) -> None:
|
def _create_action_buttons(self, layout: QVBoxLayout) -> None:
|
||||||
"""創建操作按鈕"""
|
"""創建操作按鈕"""
|
||||||
button_layout = QHBoxLayout()
|
button_layout = QHBoxLayout()
|
||||||
button_layout.addStretch()
|
button_layout.addStretch()
|
||||||
|
|
||||||
# 取消按鈕
|
# 取消按鈕
|
||||||
self.cancel_button = QPushButton(t('buttons.cancel'))
|
self.cancel_button = QPushButton(t('buttons.cancel'))
|
||||||
self.cancel_button.clicked.connect(self._cancel_feedback)
|
self.cancel_button.clicked.connect(self._cancel_feedback)
|
||||||
self.cancel_button.setFixedSize(130, 40)
|
self.cancel_button.setFixedSize(130, 40)
|
||||||
apply_widget_styles(self.cancel_button, "secondary_button")
|
apply_widget_styles(self.cancel_button, "secondary_button")
|
||||||
button_layout.addWidget(self.cancel_button)
|
button_layout.addWidget(self.cancel_button)
|
||||||
|
|
||||||
# 提交按鈕
|
# 提交按鈕
|
||||||
self.submit_button = QPushButton(t('buttons.submit'))
|
self.submit_button = QPushButton(t('buttons.submit'))
|
||||||
self.submit_button.clicked.connect(self._submit_feedback)
|
self.submit_button.clicked.connect(self._submit_feedback)
|
||||||
@ -265,19 +274,19 @@ class FeedbackWindow(QMainWindow):
|
|||||||
self.submit_button.setDefault(True)
|
self.submit_button.setDefault(True)
|
||||||
apply_widget_styles(self.submit_button, "success_button")
|
apply_widget_styles(self.submit_button, "success_button")
|
||||||
button_layout.addWidget(self.submit_button)
|
button_layout.addWidget(self.submit_button)
|
||||||
|
|
||||||
layout.addLayout(button_layout)
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
def _setup_shortcuts(self) -> None:
|
def _setup_shortcuts(self) -> None:
|
||||||
"""設置快捷鍵"""
|
"""設置快捷鍵"""
|
||||||
# Ctrl+Enter (主鍵盤) 提交回饋
|
# Ctrl+Enter (主鍵盤) 提交回饋
|
||||||
submit_shortcut_main = QShortcut(QKeySequence("Ctrl+Return"), self)
|
submit_shortcut_main = QShortcut(QKeySequence("Ctrl+Return"), self)
|
||||||
submit_shortcut_main.activated.connect(self._submit_feedback)
|
submit_shortcut_main.activated.connect(self._submit_feedback)
|
||||||
|
|
||||||
# Ctrl+Enter (數字鍵盤) 提交回饋
|
# Ctrl+Enter (數字鍵盤) 提交回饋
|
||||||
submit_shortcut_keypad = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Enter), self)
|
submit_shortcut_keypad = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Enter), self)
|
||||||
submit_shortcut_keypad.activated.connect(self._submit_feedback)
|
submit_shortcut_keypad.activated.connect(self._submit_feedback)
|
||||||
|
|
||||||
# macOS 支援 Cmd+Return (主鍵盤)
|
# macOS 支援 Cmd+Return (主鍵盤)
|
||||||
submit_shortcut_mac_main = QShortcut(QKeySequence("Meta+Return"), self)
|
submit_shortcut_mac_main = QShortcut(QKeySequence("Meta+Return"), self)
|
||||||
submit_shortcut_mac_main.activated.connect(self._submit_feedback)
|
submit_shortcut_mac_main.activated.connect(self._submit_feedback)
|
||||||
@ -285,19 +294,19 @@ class FeedbackWindow(QMainWindow):
|
|||||||
# macOS 支援 Cmd+Enter (數字鍵盤)
|
# macOS 支援 Cmd+Enter (數字鍵盤)
|
||||||
submit_shortcut_mac_keypad = QShortcut(QKeySequence(Qt.Modifier.META | Qt.Key.Key_Enter), self)
|
submit_shortcut_mac_keypad = QShortcut(QKeySequence(Qt.Modifier.META | Qt.Key.Key_Enter), self)
|
||||||
submit_shortcut_mac_keypad.activated.connect(self._submit_feedback)
|
submit_shortcut_mac_keypad.activated.connect(self._submit_feedback)
|
||||||
|
|
||||||
# Escape 取消回饋
|
# Escape 取消回饋
|
||||||
cancel_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
|
cancel_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
|
||||||
cancel_shortcut.activated.connect(self._cancel_feedback)
|
cancel_shortcut.activated.connect(self._cancel_feedback)
|
||||||
|
|
||||||
def _connect_signals(self) -> None:
|
def _connect_signals(self) -> None:
|
||||||
"""連接信號"""
|
"""連接信號"""
|
||||||
# 連接語言變更信號
|
# 連接語言變更信號
|
||||||
self.language_changed.connect(self._refresh_ui_texts)
|
self.language_changed.connect(self._refresh_ui_texts)
|
||||||
|
|
||||||
# 連接分頁管理器的信號
|
# 連接分頁管理器的信號
|
||||||
self.tab_manager.connect_signals(self)
|
self.tab_manager.connect_signals(self)
|
||||||
|
|
||||||
def _apply_dark_style(self) -> None:
|
def _apply_dark_style(self) -> None:
|
||||||
"""應用深色主題"""
|
"""應用深色主題"""
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
@ -378,40 +387,40 @@ class FeedbackWindow(QMainWindow):
|
|||||||
border-color: #005a9e;
|
border-color: #005a9e;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
def _on_layout_change_requested(self, combined_mode: bool, orientation: str) -> None:
|
def _on_layout_change_requested(self, combined_mode: bool, orientation: str) -> None:
|
||||||
"""處理佈局變更請求(模式和方向同時變更)"""
|
"""處理佈局變更請求(模式和方向同時變更)"""
|
||||||
try:
|
try:
|
||||||
# 保存當前內容
|
# 保存當前內容
|
||||||
current_data = self.tab_manager.get_feedback_data()
|
current_data = self.tab_manager.get_feedback_data()
|
||||||
|
|
||||||
# 記住當前分頁索引
|
# 記住當前分頁索引
|
||||||
current_tab_index = self.tab_widget.currentIndex()
|
current_tab_index = self.tab_widget.currentIndex()
|
||||||
|
|
||||||
# 保存新設置
|
# 保存新設置
|
||||||
self.combined_mode = combined_mode
|
self.combined_mode = combined_mode
|
||||||
self.layout_orientation = orientation
|
self.layout_orientation = orientation
|
||||||
self.config_manager.set_layout_mode(combined_mode)
|
self.config_manager.set_layout_mode(combined_mode)
|
||||||
self.config_manager.set_layout_orientation(orientation)
|
self.config_manager.set_layout_orientation(orientation)
|
||||||
|
|
||||||
# 重新創建分頁
|
# 重新創建分頁
|
||||||
self.tab_manager.set_layout_mode(combined_mode)
|
self.tab_manager.set_layout_mode(combined_mode)
|
||||||
self.tab_manager.set_layout_orientation(orientation)
|
self.tab_manager.set_layout_orientation(orientation)
|
||||||
self.tab_manager.create_tabs()
|
self.tab_manager.create_tabs()
|
||||||
|
|
||||||
# 恢復內容
|
# 恢復內容
|
||||||
self.tab_manager.restore_content(
|
self.tab_manager.restore_content(
|
||||||
current_data["interactive_feedback"],
|
current_data["interactive_feedback"],
|
||||||
current_data["command_logs"],
|
current_data["command_logs"],
|
||||||
current_data["images"]
|
current_data["images"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# 重新連接信號
|
# 重新連接信號
|
||||||
self.tab_manager.connect_signals(self)
|
self.tab_manager.connect_signals(self)
|
||||||
|
|
||||||
# 刷新UI文字
|
# 刷新UI文字
|
||||||
self._refresh_ui_texts()
|
self._refresh_ui_texts()
|
||||||
|
|
||||||
# 恢復到設定頁面(通常是倒數第二個分頁)
|
# 恢復到設定頁面(通常是倒數第二個分頁)
|
||||||
if self.combined_mode:
|
if self.combined_mode:
|
||||||
# 合併模式:回饋、命令、設置、關於
|
# 合併模式:回饋、命令、設置、關於
|
||||||
@ -419,107 +428,107 @@ class FeedbackWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
# 分離模式:回饋、摘要、命令、設置、關於
|
# 分離模式:回饋、摘要、命令、設置、關於
|
||||||
settings_tab_index = 3
|
settings_tab_index = 3
|
||||||
|
|
||||||
# 確保索引在有效範圍內
|
# 確保索引在有效範圍內
|
||||||
if settings_tab_index < self.tab_widget.count():
|
if settings_tab_index < self.tab_widget.count():
|
||||||
self.tab_widget.setCurrentIndex(settings_tab_index)
|
self.tab_widget.setCurrentIndex(settings_tab_index)
|
||||||
|
|
||||||
mode_text = "合併模式" if combined_mode else "分離模式"
|
mode_text = "合併模式" if combined_mode else "分離模式"
|
||||||
orientation_text = "(水平布局)" if orientation == "horizontal" else "(垂直布局)"
|
orientation_text = "(水平布局)" if orientation == "horizontal" else "(垂直布局)"
|
||||||
if combined_mode:
|
if combined_mode:
|
||||||
mode_text += orientation_text
|
mode_text += orientation_text
|
||||||
debug_log(f"佈局已切換到: {mode_text}")
|
debug_log(f"佈局已切換到: {mode_text}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"佈局變更失敗: {e}")
|
debug_log(f"佈局變更失敗: {e}")
|
||||||
QMessageBox.warning(self, t('errors.title'), t('errors.interfaceReloadError', error=str(e)))
|
QMessageBox.warning(self, t('errors.title'), t('errors.interfaceReloadError', error=str(e)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _on_reset_settings_requested(self) -> None:
|
def _on_reset_settings_requested(self) -> None:
|
||||||
"""處理重置設定請求"""
|
"""處理重置設定請求"""
|
||||||
try:
|
try:
|
||||||
# 重置配置管理器的所有設定
|
# 重置配置管理器的所有設定
|
||||||
self.config_manager.reset_settings()
|
self.config_manager.reset_settings()
|
||||||
|
|
||||||
# 重置應用程式狀態
|
# 重置應用程式狀態
|
||||||
self.combined_mode = False # 重置為分離模式
|
self.combined_mode = False # 重置為分離模式
|
||||||
self.layout_orientation = 'vertical' # 重置為垂直布局
|
self.layout_orientation = 'vertical' # 重置為垂直布局
|
||||||
|
|
||||||
# 重新設置語言為預設
|
# 重新設置語言為預設
|
||||||
self.i18n.set_language('zh-TW')
|
self.i18n.set_language('zh-TW')
|
||||||
|
|
||||||
# 保存當前內容
|
# 保存當前內容
|
||||||
current_data = self.tab_manager.get_feedback_data()
|
current_data = self.tab_manager.get_feedback_data()
|
||||||
|
|
||||||
# 重新創建分頁
|
# 重新創建分頁
|
||||||
self.tab_manager.set_layout_mode(self.combined_mode)
|
self.tab_manager.set_layout_mode(self.combined_mode)
|
||||||
self.tab_manager.set_layout_orientation(self.layout_orientation)
|
self.tab_manager.set_layout_orientation(self.layout_orientation)
|
||||||
self.tab_manager.create_tabs()
|
self.tab_manager.create_tabs()
|
||||||
|
|
||||||
# 恢復內容
|
# 恢復內容
|
||||||
self.tab_manager.restore_content(
|
self.tab_manager.restore_content(
|
||||||
current_data["interactive_feedback"],
|
current_data["interactive_feedback"],
|
||||||
current_data["command_logs"],
|
current_data["command_logs"],
|
||||||
current_data["images"]
|
current_data["images"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# 重新連接信號
|
# 重新連接信號
|
||||||
self.tab_manager.connect_signals(self)
|
self.tab_manager.connect_signals(self)
|
||||||
|
|
||||||
# 重新載入設定分頁的狀態
|
# 重新載入設定分頁的狀態
|
||||||
if self.tab_manager.settings_tab:
|
if self.tab_manager.settings_tab:
|
||||||
self.tab_manager.settings_tab.reload_settings_from_config()
|
self.tab_manager.settings_tab.reload_settings_from_config()
|
||||||
|
|
||||||
# 刷新UI文字
|
# 刷新UI文字
|
||||||
self._refresh_ui_texts()
|
self._refresh_ui_texts()
|
||||||
|
|
||||||
# 重新應用視窗定位(使用重置後的設定)
|
# 重新應用視窗定位(使用重置後的設定)
|
||||||
self._apply_window_positioning()
|
self._apply_window_positioning()
|
||||||
|
|
||||||
# 切換到設定分頁顯示重置結果
|
# 切換到設定分頁顯示重置結果
|
||||||
settings_tab_index = 3 # 分離模式下設定分頁是第4個(索引3)
|
settings_tab_index = 3 # 分離模式下設定分頁是第4個(索引3)
|
||||||
if settings_tab_index < self.tab_widget.count():
|
if settings_tab_index < self.tab_widget.count():
|
||||||
self.tab_widget.setCurrentIndex(settings_tab_index)
|
self.tab_widget.setCurrentIndex(settings_tab_index)
|
||||||
|
|
||||||
# 顯示成功訊息
|
# 顯示成功訊息
|
||||||
QMessageBox.information(
|
QMessageBox.information(
|
||||||
self,
|
self,
|
||||||
t('settings.reset.successTitle'),
|
t('settings.reset.successTitle'),
|
||||||
t('settings.reset.successMessage'),
|
t('settings.reset.successMessage'),
|
||||||
QMessageBox.Ok
|
QMessageBox.Ok
|
||||||
)
|
)
|
||||||
|
|
||||||
debug_log("設定重置成功")
|
debug_log("設定重置成功")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"重置設定失敗: {e}")
|
debug_log(f"重置設定失敗: {e}")
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
self,
|
self,
|
||||||
t('errors.title'),
|
t('errors.title'),
|
||||||
t('settings.reset.error', error=str(e)),
|
t('settings.reset.error', error=str(e)),
|
||||||
QMessageBox.Ok
|
QMessageBox.Ok
|
||||||
)
|
)
|
||||||
|
|
||||||
def _submit_feedback(self) -> None:
|
def _submit_feedback(self) -> None:
|
||||||
"""提交回饋"""
|
"""提交回饋"""
|
||||||
# 獲取所有回饋數據
|
# 獲取所有回饋數據
|
||||||
data = self.tab_manager.get_feedback_data()
|
data = self.tab_manager.get_feedback_data()
|
||||||
|
|
||||||
self.result = data
|
self.result = data
|
||||||
debug_log(f"回饋提交: 文字長度={len(data['interactive_feedback'])}, "
|
debug_log(f"回饋提交: 文字長度={len(data['interactive_feedback'])}, "
|
||||||
f"命令日誌長度={len(data['command_logs'])}, "
|
f"命令日誌長度={len(data['command_logs'])}, "
|
||||||
f"圖片數量={len(data['images'])}")
|
f"圖片數量={len(data['images'])}")
|
||||||
|
|
||||||
# 關閉窗口
|
# 關閉窗口
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def _cancel_feedback(self) -> None:
|
def _cancel_feedback(self) -> None:
|
||||||
"""取消回饋收集"""
|
"""取消回饋收集"""
|
||||||
debug_log("取消回饋收集")
|
debug_log("取消回饋收集")
|
||||||
self.result = ""
|
self.result = ""
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def force_close(self) -> None:
|
def force_close(self) -> None:
|
||||||
"""強制關閉視窗(用於超時處理)"""
|
"""強制關閉視窗(用於超時處理)"""
|
||||||
debug_log("強制關閉視窗(超時)")
|
debug_log("強制關閉視窗(超時)")
|
||||||
@ -644,7 +653,7 @@ class FeedbackWindow(QMainWindow):
|
|||||||
# 倒數計時器只在啟用超時時顯示
|
# 倒數計時器只在啟用超時時顯示
|
||||||
self.countdown_label.setVisible(self.timeout_enabled)
|
self.countdown_label.setVisible(self.timeout_enabled)
|
||||||
self.countdown_display.setVisible(self.timeout_enabled)
|
self.countdown_display.setVisible(self.timeout_enabled)
|
||||||
|
|
||||||
def _refresh_ui_texts(self) -> None:
|
def _refresh_ui_texts(self) -> None:
|
||||||
"""刷新界面文字"""
|
"""刷新界面文字"""
|
||||||
self.setWindowTitle(t('app.title'))
|
self.setWindowTitle(t('app.title'))
|
||||||
@ -660,11 +669,11 @@ class FeedbackWindow(QMainWindow):
|
|||||||
|
|
||||||
# 更新分頁文字
|
# 更新分頁文字
|
||||||
self.tab_manager.update_tab_texts()
|
self.tab_manager.update_tab_texts()
|
||||||
|
|
||||||
def _apply_window_positioning(self) -> None:
|
def _apply_window_positioning(self) -> None:
|
||||||
"""根據用戶設置應用視窗定位策略"""
|
"""根據用戶設置應用視窗定位策略"""
|
||||||
always_center = self.config_manager.get_always_center_window()
|
always_center = self.config_manager.get_always_center_window()
|
||||||
|
|
||||||
if always_center:
|
if always_center:
|
||||||
# 總是中心顯示模式:使用保存的大小(如果有的話),然後置中
|
# 總是中心顯示模式:使用保存的大小(如果有的話),然後置中
|
||||||
self._restore_window_size_only()
|
self._restore_window_size_only()
|
||||||
@ -678,22 +687,22 @@ class FeedbackWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
# 沒有保存的位置,移到中心
|
# 沒有保存的位置,移到中心
|
||||||
self._move_to_primary_screen_center()
|
self._move_to_primary_screen_center()
|
||||||
|
|
||||||
def _is_window_visible(self) -> bool:
|
def _is_window_visible(self) -> bool:
|
||||||
"""檢查視窗是否在任何螢幕的可見範圍內"""
|
"""檢查視窗是否在任何螢幕的可見範圍內"""
|
||||||
from PySide6.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
|
|
||||||
window_rect = self.frameGeometry()
|
window_rect = self.frameGeometry()
|
||||||
|
|
||||||
for screen in QApplication.screens():
|
for screen in QApplication.screens():
|
||||||
if screen.availableGeometry().intersects(window_rect):
|
if screen.availableGeometry().intersects(window_rect):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _move_to_primary_screen_center(self) -> None:
|
def _move_to_primary_screen_center(self) -> None:
|
||||||
"""將視窗移到主螢幕中心"""
|
"""將視窗移到主螢幕中心"""
|
||||||
from PySide6.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
|
|
||||||
screen = QApplication.primaryScreen()
|
screen = QApplication.primaryScreen()
|
||||||
if screen:
|
if screen:
|
||||||
screen_geometry = screen.availableGeometry()
|
screen_geometry = screen.availableGeometry()
|
||||||
@ -702,7 +711,7 @@ class FeedbackWindow(QMainWindow):
|
|||||||
window_geometry.moveCenter(center_point)
|
window_geometry.moveCenter(center_point)
|
||||||
self.move(window_geometry.topLeft())
|
self.move(window_geometry.topLeft())
|
||||||
debug_log("視窗已移到主螢幕中心")
|
debug_log("視窗已移到主螢幕中心")
|
||||||
|
|
||||||
def _restore_window_size_only(self) -> bool:
|
def _restore_window_size_only(self) -> bool:
|
||||||
"""只恢復視窗大小(不恢復位置)"""
|
"""只恢復視窗大小(不恢復位置)"""
|
||||||
try:
|
try:
|
||||||
@ -714,7 +723,7 @@ class FeedbackWindow(QMainWindow):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"恢復視窗大小失敗: {e}")
|
debug_log(f"恢復視窗大小失敗: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _restore_last_position(self) -> bool:
|
def _restore_last_position(self) -> bool:
|
||||||
"""嘗試恢復上次保存的視窗位置和大小"""
|
"""嘗試恢復上次保存的視窗位置和大小"""
|
||||||
try:
|
try:
|
||||||
@ -727,18 +736,18 @@ class FeedbackWindow(QMainWindow):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"恢復視窗位置失敗: {e}")
|
debug_log(f"恢復視窗位置失敗: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _save_window_position(self) -> None:
|
def _save_window_position(self) -> None:
|
||||||
"""保存當前視窗位置和大小"""
|
"""保存當前視窗位置和大小"""
|
||||||
try:
|
try:
|
||||||
always_center = self.config_manager.get_always_center_window()
|
always_center = self.config_manager.get_always_center_window()
|
||||||
|
|
||||||
# 獲取當前幾何信息
|
# 獲取當前幾何信息
|
||||||
current_geometry = {
|
current_geometry = {
|
||||||
'width': self.width(),
|
'width': self.width(),
|
||||||
'height': self.height()
|
'height': self.height()
|
||||||
}
|
}
|
||||||
|
|
||||||
if not always_center:
|
if not always_center:
|
||||||
# 智能定位模式:同時保存位置
|
# 智能定位模式:同時保存位置
|
||||||
current_geometry['x'] = self.x()
|
current_geometry['x'] = self.x()
|
||||||
@ -747,45 +756,86 @@ class FeedbackWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
# 總是中心顯示模式:只保存大小,不保存位置
|
# 總是中心顯示模式:只保存大小,不保存位置
|
||||||
debug_log(f"已保存視窗大小: {current_geometry['width']}x{current_geometry['height']} (總是中心顯示模式)")
|
debug_log(f"已保存視窗大小: {current_geometry['width']}x{current_geometry['height']} (總是中心顯示模式)")
|
||||||
|
|
||||||
# 獲取現有配置,只更新需要的部分
|
# 獲取現有配置,只更新需要的部分
|
||||||
saved_geometry = self.config_manager.get_window_geometry() or {}
|
saved_geometry = self.config_manager.get_window_geometry() or {}
|
||||||
saved_geometry.update(current_geometry)
|
saved_geometry.update(current_geometry)
|
||||||
|
|
||||||
self.config_manager.set_window_geometry(saved_geometry)
|
self.config_manager.set_window_geometry(saved_geometry)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"保存視窗狀態失敗: {e}")
|
debug_log(f"保存視窗狀態失敗: {e}")
|
||||||
|
|
||||||
def resizeEvent(self, event) -> None:
|
def resizeEvent(self, event) -> None:
|
||||||
"""窗口大小變化事件"""
|
"""窗口大小變化事件"""
|
||||||
super().resizeEvent(event)
|
super().resizeEvent(event)
|
||||||
# 窗口大小變化時始終保存(無論是否設置為中心顯示)
|
# 窗口大小變化時始終保存(無論是否設置為中心顯示)
|
||||||
if hasattr(self, 'config_manager'):
|
if hasattr(self, 'config_manager'):
|
||||||
self._schedule_save_window_position()
|
self._schedule_save_window_position()
|
||||||
|
|
||||||
def moveEvent(self, event) -> None:
|
def moveEvent(self, event) -> None:
|
||||||
"""窗口位置變化事件"""
|
"""窗口位置變化事件"""
|
||||||
super().moveEvent(event)
|
super().moveEvent(event)
|
||||||
# 窗口位置變化只在智能定位模式下保存
|
# 窗口位置變化只在智能定位模式下保存
|
||||||
if hasattr(self, 'config_manager') and not self.config_manager.get_always_center_window():
|
if hasattr(self, 'config_manager') and not self.config_manager.get_always_center_window():
|
||||||
self._schedule_save_window_position()
|
self._schedule_save_window_position()
|
||||||
|
|
||||||
def _schedule_save_window_position(self) -> None:
|
def _schedule_save_window_position(self) -> None:
|
||||||
"""調度窗口位置保存(防抖機制)"""
|
"""調度窗口位置保存(防抖機制)"""
|
||||||
if hasattr(self, '_save_timer'):
|
if hasattr(self, '_save_timer'):
|
||||||
self._save_timer.start(self._save_delay)
|
self._save_timer.start(self._save_delay)
|
||||||
|
|
||||||
def _delayed_save_window_position(self) -> None:
|
def _delayed_save_window_position(self) -> None:
|
||||||
"""延遲保存窗口位置(防抖機制的實際執行)"""
|
"""延遲保存窗口位置(防抖機制的實際執行)"""
|
||||||
self._save_window_position()
|
self._save_window_position()
|
||||||
|
|
||||||
|
def _auto_focus_input(self) -> None:
|
||||||
|
"""自動聚焦到輸入框"""
|
||||||
|
try:
|
||||||
|
# 確保窗口已經顯示並激活
|
||||||
|
self.raise_()
|
||||||
|
self.activateWindow()
|
||||||
|
|
||||||
|
# 獲取回饋輸入框(修正邏輯)
|
||||||
|
feedback_input = None
|
||||||
|
|
||||||
|
# 檢查是否有tab_manager
|
||||||
|
if not hasattr(self, 'tab_manager'):
|
||||||
|
debug_log("tab_manager 不存在")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 檢查 feedback_tab(無論是合併模式還是分離模式)
|
||||||
|
if hasattr(self.tab_manager, 'feedback_tab') and self.tab_manager.feedback_tab:
|
||||||
|
if hasattr(self.tab_manager.feedback_tab, 'feedback_input'):
|
||||||
|
feedback_input = self.tab_manager.feedback_tab.feedback_input
|
||||||
|
debug_log("找到feedback_tab中的輸入框")
|
||||||
|
else:
|
||||||
|
debug_log("feedback_tab存在但沒有feedback_input屬性")
|
||||||
|
else:
|
||||||
|
debug_log("沒有找到feedback_tab")
|
||||||
|
|
||||||
|
# 設置焦點到輸入框
|
||||||
|
if feedback_input:
|
||||||
|
feedback_input.setFocus()
|
||||||
|
feedback_input.raise_() # 確保輸入框可見
|
||||||
|
debug_log("已自動聚焦到輸入框")
|
||||||
|
else:
|
||||||
|
debug_log("未找到回饋輸入框,無法自動聚焦")
|
||||||
|
# 打印調試信息
|
||||||
|
if hasattr(self, 'tab_manager'):
|
||||||
|
debug_log(f"tab_manager 屬性: {dir(self.tab_manager)}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"自動聚焦失敗: {e}")
|
||||||
|
import traceback
|
||||||
|
debug_log(f"詳細錯誤: {traceback.format_exc()}")
|
||||||
|
|
||||||
def closeEvent(self, event) -> None:
|
def closeEvent(self, event) -> None:
|
||||||
"""窗口關閉事件"""
|
"""窗口關閉事件"""
|
||||||
# 最終保存視窗狀態(大小始終保存,位置根據設置決定)
|
# 最終保存視窗狀態(大小始終保存,位置根據設置決定)
|
||||||
self._save_window_position()
|
self._save_window_position()
|
||||||
|
|
||||||
# 清理分頁管理器
|
# 清理分頁管理器
|
||||||
self.tab_manager.cleanup()
|
self.tab_manager.cleanup()
|
||||||
event.accept()
|
event.accept()
|
||||||
debug_log("主窗口已關閉")
|
debug_log("主窗口已關閉")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user