je-editor 0.0.223__py3-none-any.whl → 0.0.224__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of je-editor might be problematic. Click here for more details.
- je_editor/git_client/commit_graph.py +7 -7
- je_editor/git_client/git_action.py +0 -7
- je_editor/pyside_ui/browser/browser_widget.py +24 -11
- je_editor/pyside_ui/browser/main_browser_widget.py +40 -27
- je_editor/pyside_ui/code/auto_save/auto_save_manager.py +34 -2
- je_editor/pyside_ui/code/auto_save/auto_save_thread.py +19 -6
- je_editor/pyside_ui/code/code_format/pep8_format.py +53 -9
- je_editor/pyside_ui/code/code_process/code_exec.py +88 -52
- je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +116 -55
- je_editor/pyside_ui/code/running_process_manager.py +19 -1
- je_editor/pyside_ui/code/shell_process/shell_exec.py +71 -48
- je_editor/pyside_ui/code/syntax/python_syntax.py +45 -10
- je_editor/pyside_ui/code/syntax/syntax_setting.py +40 -12
- je_editor/pyside_ui/code/textedit_code_result/code_record.py +34 -12
- je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +53 -6
- je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +30 -3
- je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +35 -2
- je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +33 -5
- je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +25 -3
- je_editor/pyside_ui/dialog/search_ui/search_error_box.py +26 -1
- je_editor/pyside_ui/dialog/search_ui/search_text_box.py +26 -1
- je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +11 -11
- je_editor/pyside_ui/git_ui/git_client/commit_table.py +46 -8
- je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +49 -15
- je_editor/pyside_ui/git_ui/git_client/graph_view.py +64 -20
- je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +20 -5
- je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +20 -1
- je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +56 -41
- je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +45 -6
- je_editor/pyside_ui/main_ui/console_widget/console_gui.py +44 -12
- je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +34 -13
- je_editor/pyside_ui/main_ui/dock/destroy_dock.py +33 -2
- je_editor/pyside_ui/main_ui/editor/editor_widget.py +104 -20
- je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +34 -7
- je_editor/pyside_ui/main_ui/editor/process_input.py +38 -11
- je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +46 -11
- je_editor/pyside_ui/main_ui/main_editor.py +175 -37
- je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +51 -28
- je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +80 -22
- je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +70 -17
- je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +34 -3
- je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +41 -1
- je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +100 -42
- je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +57 -7
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +50 -4
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +52 -6
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +44 -4
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +23 -1
- je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +37 -12
- je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +44 -7
- je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +126 -28
- je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +65 -1
- je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +18 -1
- je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +33 -3
- je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +38 -11
- je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +39 -2
- je_editor/start_editor.py +26 -1
- je_editor/utils/encodings/python_encodings.py +101 -98
- je_editor/utils/file/open/open_file.py +36 -19
- je_editor/utils/file/save/save_file.py +35 -14
- je_editor/utils/json/json_file.py +29 -14
- je_editor/utils/json_format/json_process.py +33 -2
- je_editor/utils/logging/loggin_instance.py +38 -8
- je_editor/utils/multi_language/multi_language_wrapper.py +29 -4
- je_editor/utils/redirect_manager/redirect_manager_class.py +49 -11
- je_editor/utils/venv_check/check_venv.py +45 -15
- {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/METADATA +1 -1
- {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/RECORD +71 -71
- {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/WHEEL +0 -0
- {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/licenses/LICENSE +0 -0
- {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/top_level.txt +0 -0
|
@@ -11,33 +11,60 @@ from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class FullEditorWidget(QWidget):
|
|
14
|
+
"""
|
|
15
|
+
FullEditorWidget 提供一個完整的單檔編輯器介面,
|
|
16
|
+
包含程式碼編輯區、捲動支援,以及關閉時自動儲存功能。
|
|
17
|
+
|
|
18
|
+
FullEditorWidget provides a full single-file editor interface,
|
|
19
|
+
including code editing area, scroll support, and auto-save on close.
|
|
20
|
+
"""
|
|
14
21
|
|
|
15
22
|
def __init__(self, current_file: str):
|
|
23
|
+
# 初始化時記錄日誌 / Log initialization
|
|
16
24
|
jeditor_logger.info(f"Init FullEditorWidget current_file: {current_file}")
|
|
17
25
|
super().__init__()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
#
|
|
26
|
+
|
|
27
|
+
# ---------------- Init variable 初始化變數 ----------------
|
|
28
|
+
self.current_file = current_file # 目前編輯的檔案路徑 / Current editing file path
|
|
29
|
+
|
|
30
|
+
# ---------------- Attributes 屬性設定 ----------------
|
|
31
|
+
# 設定關閉時自動刪除物件,釋放記憶體
|
|
32
|
+
# Delete object on close to free memory
|
|
21
33
|
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
22
|
-
|
|
34
|
+
|
|
35
|
+
# ---------------- UI 初始化 ----------------
|
|
23
36
|
self.grid_layout = QGridLayout(self)
|
|
24
37
|
self.setWindowTitle(language_wrapper.language_word_dict.get("application_name"))
|
|
25
|
-
|
|
38
|
+
|
|
39
|
+
# 建立程式碼編輯器並放入捲動區域
|
|
40
|
+
# Create code editor and put inside scroll area
|
|
26
41
|
self.code_edit = CodeEditor(self)
|
|
27
42
|
self.code_edit_scroll_area = QScrollArea()
|
|
28
43
|
self.code_edit_scroll_area.setWidgetResizable(True)
|
|
29
44
|
self.code_edit_scroll_area.setViewportMargins(0, 0, 0, 0)
|
|
30
45
|
self.code_edit_scroll_area.setWidget(self.code_edit)
|
|
46
|
+
|
|
47
|
+
# 將編輯器加入版面配置
|
|
48
|
+
# Add editor to layout
|
|
31
49
|
self.grid_layout.addWidget(self.code_edit_scroll_area, 0, 0)
|
|
32
|
-
|
|
50
|
+
|
|
51
|
+
# 設定字體樣式 (從使用者設定檔讀取)
|
|
52
|
+
# Set font style (from user settings)
|
|
33
53
|
self.code_edit.setStyleSheet(
|
|
34
54
|
f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
|
|
35
55
|
f"font-family: {user_setting_dict.get('font', 'Lato')};"
|
|
36
56
|
)
|
|
37
57
|
|
|
38
58
|
def closeEvent(self, event) -> None:
|
|
59
|
+
"""
|
|
60
|
+
覆寫 closeEvent,在關閉視窗時自動儲存檔案內容。
|
|
61
|
+
Override closeEvent to auto-save file content when closing window.
|
|
62
|
+
"""
|
|
39
63
|
jeditor_logger.info(f"FullEditorWidget closeEvent event: {event}")
|
|
40
64
|
path = Path(self.current_file)
|
|
41
65
|
if path.exists() and path.is_file():
|
|
66
|
+
# 將編輯器內容寫回檔案 / Write editor content back to file
|
|
42
67
|
write_file(self.current_file, self.code_edit.toPlainText())
|
|
43
|
-
|
|
68
|
+
# 呼叫父類別的 closeEvent,完成 Qt 預設流程
|
|
69
|
+
# Call parent closeEvent to complete Qt default process
|
|
70
|
+
super().closeEvent(event)
|
|
@@ -1,57 +1,83 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
1
|
+
from __future__ import annotations # 允許未來版本的型別註解功能 / Enable postponed evaluation of type annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING # 用於避免循環匯入,僅在型別檢查時載入 / Used to avoid circular imports, only loaded during type checking
|
|
4
4
|
|
|
5
|
-
from PySide6.QtCore import Qt
|
|
5
|
+
from PySide6.QtCore import Qt # Qt 核心模組 / Qt core module
|
|
6
6
|
from PySide6.QtWidgets import QWidget, QLineEdit, QBoxLayout, QPushButton, QHBoxLayout
|
|
7
|
+
# 匯入 PySide6 的 GUI 元件 / Import GUI widgets from PySide6
|
|
7
8
|
|
|
8
9
|
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
10
|
+
# 專案內的日誌紀錄器 / Project's logger instance
|
|
9
11
|
|
|
10
12
|
if TYPE_CHECKING:
|
|
11
13
|
from je_editor.pyside_ui.main_ui.main_editor import EditorWidget
|
|
14
|
+
# 僅在型別檢查時匯入 EditorWidget,避免循環依賴 / Import only for type checking to avoid circular dependency
|
|
12
15
|
|
|
13
16
|
from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
|
|
17
|
+
# 多語系支援工具 / Multi-language wrapper for UI text
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
class ProcessInput(QWidget):
|
|
21
|
+
"""
|
|
22
|
+
ProcessInput 是一個輸入視窗,允許使用者輸入指令並傳送到不同的子程序 (program/shell/debugger)。
|
|
23
|
+
ProcessInput is an input widget that allows users to send commands to different subprocesses.
|
|
24
|
+
"""
|
|
17
25
|
|
|
18
26
|
def __init__(self, main_window: EditorWidget, process_type: str = "debugger"):
|
|
27
|
+
# 初始化時記錄日誌 / Log initialization
|
|
19
28
|
jeditor_logger.info("Init ProcessInput "
|
|
20
29
|
f"main_window: {main_window} "
|
|
21
30
|
f"process_type: {process_type}")
|
|
22
31
|
super().__init__()
|
|
23
|
-
|
|
32
|
+
|
|
33
|
+
# 設定當視窗關閉時自動刪除資源
|
|
34
|
+
# Set attribute to delete widget on close
|
|
24
35
|
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
self.
|
|
28
|
-
self.
|
|
29
|
-
self.
|
|
36
|
+
|
|
37
|
+
# === UI 設定 / UI Setup ===
|
|
38
|
+
self.main_window = main_window # 儲存主視窗參考 / Store reference to main window
|
|
39
|
+
self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom) # 垂直佈局 / Vertical layout
|
|
40
|
+
self.command_input = QLineEdit() # 輸入框 / Input field
|
|
41
|
+
self.send_command_button = QPushButton() # 傳送按鈕 / Send button
|
|
42
|
+
|
|
43
|
+
# 設定按鈕文字 (多語系) / Set button text (multi-language)
|
|
30
44
|
self.send_command_button.setText(language_wrapper.language_word_dict.get("process_input_send_command"))
|
|
45
|
+
|
|
46
|
+
# 水平佈局,放置按鈕 / Horizontal layout for button
|
|
31
47
|
self.box_h_layout = QHBoxLayout()
|
|
32
48
|
self.box_h_layout.addWidget(self.send_command_button)
|
|
49
|
+
|
|
50
|
+
# 將元件加入主佈局 / Add widgets to main layout
|
|
33
51
|
self.box_layout.addWidget(self.command_input)
|
|
34
52
|
self.box_layout.addLayout(self.box_h_layout)
|
|
53
|
+
|
|
54
|
+
# 根據 process_type 設定不同的標題與功能 / Configure behavior based on process_type
|
|
35
55
|
if process_type == "program":
|
|
36
56
|
self.setWindowTitle(language_wrapper.language_word_dict.get("editor_program_input_title_label"))
|
|
37
57
|
self.send_command_button.clicked.connect(self.program_send_command)
|
|
38
58
|
elif process_type == "shell":
|
|
39
59
|
self.setWindowTitle(language_wrapper.language_word_dict.get("editor_shell_input_title_label"))
|
|
40
60
|
self.send_command_button.clicked.connect(self.shell_send_command)
|
|
41
|
-
else:
|
|
61
|
+
else: # 預設為 debugger / Default: debugger
|
|
42
62
|
self.setWindowTitle(language_wrapper.language_word_dict.get("editor_debugger_input_title_label"))
|
|
43
63
|
self.send_command_button.clicked.connect(self.debugger_send_command)
|
|
64
|
+
# 切換主視窗的顯示頁面到 debugger 結果 / Switch main window tab to debugger result
|
|
44
65
|
self.main_window.code_difference_result.setCurrentWidget(self.main_window.debugger_result)
|
|
66
|
+
|
|
67
|
+
# 設定主佈局 / Apply layout
|
|
45
68
|
self.setLayout(self.box_layout)
|
|
46
69
|
|
|
70
|
+
# === Debugger 指令傳送 / Send command to debugger ===
|
|
47
71
|
def debugger_send_command(self):
|
|
48
72
|
jeditor_logger.info("EditorWidget debugger_send_command")
|
|
49
73
|
if self.main_window.exec_python_debugger is not None:
|
|
50
74
|
process_stdin = self.main_window.exec_python_debugger.process.stdin
|
|
51
75
|
if process_stdin is not None:
|
|
76
|
+
# 將輸入框文字編碼後寫入子程序 stdin / Write encoded input to subprocess stdin
|
|
52
77
|
process_stdin.write(self.command_input.text().encode() + b"\n")
|
|
53
78
|
process_stdin.flush()
|
|
54
79
|
|
|
80
|
+
# === Shell 指令傳送 / Send command to shell ===
|
|
55
81
|
def shell_send_command(self):
|
|
56
82
|
jeditor_logger.info("EditorWidget shell_send_command")
|
|
57
83
|
if self.main_window.exec_shell is not None:
|
|
@@ -60,10 +86,11 @@ class ProcessInput(QWidget):
|
|
|
60
86
|
process_stdin.write(self.command_input.text().encode() + b"\n")
|
|
61
87
|
process_stdin.flush()
|
|
62
88
|
|
|
89
|
+
# === Program 指令傳送 / Send command to program ===
|
|
63
90
|
def program_send_command(self):
|
|
64
91
|
jeditor_logger.info("EditorWidget program_send_command")
|
|
65
92
|
if self.main_window.exec_program is not None:
|
|
66
93
|
process_stdin = self.main_window.exec_program.process.stdin
|
|
67
94
|
if process_stdin is not None:
|
|
68
95
|
process_stdin.write(self.command_input.text().encode() + b"\n")
|
|
69
|
-
process_stdin.flush()
|
|
96
|
+
process_stdin.flush()
|
|
@@ -1,43 +1,78 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
1
|
+
from __future__ import annotations # 啟用未來版本的型別註解功能 / Enable postponed evaluation of type annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING # 僅在型別檢查時使用,避免循環匯入 / Used only for type checking to avoid circular imports
|
|
4
4
|
|
|
5
|
-
from IPython.lib import guisupport
|
|
6
|
-
from PySide6.QtWidgets import QWidget, QGridLayout
|
|
7
|
-
from qtconsole.inprocess import QtInProcessKernelManager
|
|
8
|
-
from qtconsole.rich_jupyter_widget import RichJupyterWidget
|
|
5
|
+
from IPython.lib import guisupport # 提供與 Qt GUI 整合的支援 / Provides support for integrating IPython with Qt GUI
|
|
6
|
+
from PySide6.QtWidgets import QWidget, QGridLayout # PySide6 視窗元件 / PySide6 widgets
|
|
7
|
+
from qtconsole.inprocess import QtInProcessKernelManager # 管理內嵌的 Jupyter kernel / Manages an in-process Jupyter kernel
|
|
8
|
+
from qtconsole.rich_jupyter_widget import RichJupyterWidget # Jupyter 富文本控制台元件 / Rich Jupyter console widget
|
|
9
9
|
|
|
10
|
-
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
10
|
+
from je_editor.utils.logging.loggin_instance import jeditor_logger # 專案內的日誌紀錄器 / Project logger
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from je_editor.pyside_ui.main_ui.main_editor import EditorMain
|
|
14
|
+
# 僅在型別檢查時匯入 EditorMain,避免循環依賴 / Import only for type checking to avoid circular dependency
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class IpythonWidget(QWidget):
|
|
18
|
+
"""
|
|
19
|
+
IpythonWidget 類別
|
|
20
|
+
- 在 PySide6 GUI 中嵌入一個 Jupyter/IPython 控制台
|
|
21
|
+
- 提供互動式 Python 環境,方便在應用程式內直接執行程式碼
|
|
22
|
+
IpythonWidget class
|
|
23
|
+
- Embeds a Jupyter/IPython console inside a PySide6 GUI
|
|
24
|
+
- Provides an interactive Python environment within the application
|
|
25
|
+
"""
|
|
17
26
|
|
|
18
27
|
def __init__(self, main_window: EditorMain):
|
|
28
|
+
# 初始化時記錄日誌 / Log initialization
|
|
19
29
|
jeditor_logger.info(f"Init IpythonWidget main_window: {main_window}")
|
|
20
30
|
super().__init__()
|
|
31
|
+
|
|
32
|
+
# 建立網格佈局 / Create grid layout
|
|
21
33
|
self.grid_layout = QGridLayout()
|
|
34
|
+
|
|
35
|
+
# 取得 Qt 應用程式實例 (Qt4 API,但可在 PySide6 中使用) / Get Qt application instance
|
|
22
36
|
app = guisupport.get_app_qt4()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
self.
|
|
37
|
+
|
|
38
|
+
# === 建立並啟動 Jupyter Kernel / Create and start Jupyter kernel ===
|
|
39
|
+
self.kernel_manager = QtInProcessKernelManager() # 內嵌 kernel 管理器 / In-process kernel manager
|
|
40
|
+
self.kernel_manager.start_kernel() # 啟動 kernel / Start kernel
|
|
41
|
+
self.kernel = self.kernel_manager.kernel # 取得 kernel 實例 / Get kernel instance
|
|
42
|
+
|
|
43
|
+
# 建立 kernel client 並啟動通訊管道 / Create kernel client and start communication channels
|
|
26
44
|
self.kernel_client = self.kernel_manager.client()
|
|
27
45
|
self.kernel_client.start_channels()
|
|
46
|
+
|
|
47
|
+
# === 建立 Jupyter 控制台元件 / Create Jupyter console widget ===
|
|
28
48
|
self.jupyter_widget = RichJupyterWidget()
|
|
29
49
|
self.jupyter_widget.kernel_manager = self.kernel_manager
|
|
30
50
|
self.jupyter_widget.kernel_client = self.kernel_client
|
|
51
|
+
|
|
52
|
+
# 將控制台加入佈局 (佔滿整個視窗) / Add console widget to layout (fill entire window)
|
|
31
53
|
self.grid_layout.addWidget(self.jupyter_widget, 0, 0, -1, -1)
|
|
54
|
+
|
|
55
|
+
# 啟動 Qt 事件迴圈,讓 Jupyter 控制台能正常運作 / Start Qt event loop for Jupyter console
|
|
32
56
|
guisupport.start_event_loop_qt4(app)
|
|
33
57
|
|
|
58
|
+
# 設定主佈局 / Apply layout
|
|
34
59
|
self.setLayout(self.grid_layout)
|
|
35
60
|
|
|
36
61
|
def close(self):
|
|
62
|
+
"""
|
|
63
|
+
覆寫 close 方法,確保關閉時正確釋放資源
|
|
64
|
+
Override close method to properly release resources
|
|
65
|
+
"""
|
|
37
66
|
jeditor_logger.info("IpythonWidget close")
|
|
67
|
+
|
|
68
|
+
# 停止 kernel client 的通訊管道 / Stop kernel client channels
|
|
38
69
|
if self.kernel_client:
|
|
39
70
|
self.kernel_client.stop_channels()
|
|
71
|
+
|
|
72
|
+
# 重啟並關閉 kernel,確保乾淨退出 / Restart and shutdown kernel for clean exit
|
|
40
73
|
if self.kernel_manager:
|
|
41
74
|
self.kernel_manager.restart_kernel()
|
|
42
75
|
self.kernel_manager.shutdown_kernel()
|
|
43
|
-
|
|
76
|
+
|
|
77
|
+
# 呼叫父類別的 close 方法 / Call parent close method
|
|
78
|
+
super().close()
|
|
@@ -4,38 +4,66 @@ import sys
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Dict, Type
|
|
6
6
|
|
|
7
|
+
# 匯入 Jedi 設定,用於 Python 自動補全與分析
|
|
8
|
+
# Import Jedi settings for Python auto-completion and analysis
|
|
7
9
|
import jedi.settings
|
|
10
|
+
|
|
11
|
+
# 匯入 PySide6 (Qt for Python) 的核心模組
|
|
12
|
+
# Import PySide6 core modules
|
|
8
13
|
from PySide6.QtCore import QTimer, QEvent
|
|
9
14
|
from PySide6.QtGui import QFontDatabase, QIcon, Qt, QTextCharFormat
|
|
10
15
|
from PySide6.QtWidgets import QMainWindow, QWidget, QTabWidget
|
|
16
|
+
|
|
17
|
+
# 匯入 Qt Material 主題工具
|
|
18
|
+
# Import Qt Material style tools
|
|
11
19
|
from qt_material import QtStyleTools
|
|
12
20
|
|
|
21
|
+
# 匯入專案內部模組 (自訂 UI 與功能)
|
|
22
|
+
# Import project-specific modules (custom UI and features)
|
|
13
23
|
from je_editor.pyside_ui.browser.browser_widget import BrowserWidget
|
|
14
24
|
from je_editor.pyside_ui.browser.main_browser_widget import MainBrowserWidget
|
|
15
25
|
from je_editor.pyside_ui.code.auto_save.auto_save_manager import init_new_auto_save_thread, file_is_open_manager_dict
|
|
16
26
|
from je_editor.pyside_ui.main_ui.editor.editor_widget import EditorWidget
|
|
17
27
|
from je_editor.pyside_ui.main_ui.menu.set_menu_bar import set_menu_bar
|
|
18
|
-
from je_editor.pyside_ui.main_ui.save_settings.user_color_setting_file import
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
from je_editor.pyside_ui.main_ui.save_settings.user_color_setting_file import (
|
|
29
|
+
write_user_color_setting,
|
|
30
|
+
read_user_color_setting,
|
|
31
|
+
update_actually_color_dict,
|
|
32
|
+
actually_color_dict
|
|
33
|
+
)
|
|
34
|
+
from je_editor.pyside_ui.main_ui.save_settings.user_setting_file import (
|
|
35
|
+
user_setting_dict,
|
|
36
|
+
read_user_setting,
|
|
21
37
|
write_user_setting
|
|
38
|
+
)
|
|
22
39
|
from je_editor.pyside_ui.main_ui.system_tray.extend_system_tray import ExtendSystemTray
|
|
23
40
|
from je_editor.utils.file.open.open_file import read_file
|
|
24
41
|
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
25
42
|
from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
|
|
26
43
|
from je_editor.utils.redirect_manager.redirect_manager_class import redirect_manager_instance
|
|
27
44
|
|
|
45
|
+
# 定義一個字典,用來存放可擴充的 Tab (標籤頁)
|
|
46
|
+
# Define a dictionary to store extendable tabs
|
|
28
47
|
EDITOR_EXTEND_TAB: Dict[str, Type[QWidget]] = {}
|
|
29
48
|
|
|
30
49
|
|
|
31
50
|
class EditorMain(QMainWindow, QtStyleTools):
|
|
51
|
+
"""
|
|
52
|
+
主編輯器視窗類別
|
|
53
|
+
Main editor window class
|
|
54
|
+
繼承 QMainWindow 與 QtStyleTools
|
|
55
|
+
"""
|
|
32
56
|
|
|
33
57
|
def __init__(self, debug_mode: bool = False, show_system_tray_ray: bool = False):
|
|
58
|
+
# 初始化時記錄 log
|
|
59
|
+
# Log initialization
|
|
34
60
|
jeditor_logger.info(f"Init EditorMain "
|
|
35
61
|
f"debug_mode: {debug_mode} "
|
|
36
62
|
f"show_system_tray_ray: {show_system_tray_ray}")
|
|
37
63
|
super(EditorMain, self).__init__()
|
|
38
|
-
|
|
64
|
+
|
|
65
|
+
# 初始化變數
|
|
66
|
+
# Initialize variables
|
|
39
67
|
self.file_menu = None
|
|
40
68
|
self.code_result = None
|
|
41
69
|
self.code_edit = None
|
|
@@ -45,77 +73,130 @@ class EditorMain(QMainWindow, QtStyleTools):
|
|
|
45
73
|
self.font_menu = None
|
|
46
74
|
self.working_dir = None
|
|
47
75
|
self.show_system_tray_ray = show_system_tray_ray
|
|
48
|
-
|
|
49
|
-
#
|
|
76
|
+
|
|
77
|
+
# 讀取使用者設定
|
|
78
|
+
# Read user settings
|
|
50
79
|
read_user_setting()
|
|
51
|
-
|
|
80
|
+
|
|
81
|
+
# 設定語言 (多語系支援)
|
|
82
|
+
# Set language (multi-language support)
|
|
52
83
|
language_wrapper.reset_language(user_setting_dict.get("language", "English"))
|
|
53
|
-
|
|
84
|
+
|
|
85
|
+
# Jedi 設定:關閉快取解析器,避免執行緒問題
|
|
86
|
+
# Jedi settings: disable fast parser for thread safety
|
|
54
87
|
jedi.settings.fast_parser = False
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
#
|
|
88
|
+
jedi.settings.case_insensitive_completion = False # 關閉大小寫不敏感補全 / Disable case-insensitive completion
|
|
89
|
+
|
|
90
|
+
# Python 編譯器 (可由使用者指定)
|
|
91
|
+
# Python compiler (can be set by user)
|
|
58
92
|
self.python_compiler = None
|
|
93
|
+
|
|
94
|
+
# 除錯模式
|
|
59
95
|
# Debug mode
|
|
60
96
|
self.debug_mode: bool = debug_mode
|
|
61
|
-
|
|
97
|
+
|
|
98
|
+
# Windows 系統專用:設定應用程式 ID
|
|
99
|
+
# Windows only: set application ID
|
|
62
100
|
self.id = language_wrapper.language_word_dict.get("application_name")
|
|
63
101
|
if sys.platform in ["win32", "cygwin", "msys"]:
|
|
64
102
|
from ctypes import windll
|
|
65
103
|
windll.shell32.SetCurrentProcessExplicitAppUserModelID(self.id)
|
|
66
|
-
|
|
104
|
+
|
|
105
|
+
# 設定 Python 輸出不緩衝
|
|
106
|
+
# Set Python output unbuffered
|
|
67
107
|
os.environ["PYTHONUNBUFFERED"] = "1"
|
|
68
|
-
|
|
108
|
+
|
|
109
|
+
# 自動儲存執行緒
|
|
110
|
+
# Auto-save thread
|
|
69
111
|
self.auto_save_thread = None
|
|
70
|
-
|
|
112
|
+
|
|
113
|
+
# 預設編碼
|
|
114
|
+
# Default encoding
|
|
71
115
|
self.encoding = "utf-8"
|
|
116
|
+
|
|
117
|
+
# 讀取使用者顏色設定
|
|
118
|
+
# Read user color settings
|
|
72
119
|
read_user_color_setting()
|
|
73
|
-
|
|
120
|
+
|
|
121
|
+
# 字型資料庫
|
|
122
|
+
# Font database
|
|
74
123
|
self.font_database = QFontDatabase()
|
|
75
|
-
|
|
124
|
+
|
|
125
|
+
# 建立 TabWidget (多分頁編輯器)
|
|
126
|
+
# Create TabWidget (multi-tab editor)
|
|
76
127
|
self.tab_widget = QTabWidget()
|
|
77
|
-
self.tab_widget.setTabsClosable(True)
|
|
128
|
+
self.tab_widget.setTabsClosable(True) # 可關閉分頁 / Tabs closable
|
|
78
129
|
self.tab_widget.setAttribute(Qt.WidgetAttribute.WA_AlwaysShowToolTips, on=False)
|
|
79
130
|
self.tab_widget.tabCloseRequested.connect(self.close_tab)
|
|
80
|
-
|
|
131
|
+
|
|
132
|
+
# 建立計時器,用來處理訊息重導 (stdout/stderr)
|
|
133
|
+
# Timer for redirecting messages (stdout/stderr)
|
|
81
134
|
self.redirect_timer = QTimer(self)
|
|
82
135
|
self.redirect_timer.setInterval(10)
|
|
83
136
|
self.redirect_timer.start()
|
|
137
|
+
|
|
138
|
+
# 設定視窗標題與提示
|
|
139
|
+
# Set window title and tooltip
|
|
84
140
|
self.setWindowTitle(language_wrapper.language_word_dict.get("application_name"))
|
|
85
141
|
self.setToolTip(language_wrapper.language_word_dict.get("application_name"))
|
|
142
|
+
|
|
143
|
+
# 設定選單列
|
|
144
|
+
# Set menu bar
|
|
86
145
|
set_menu_bar(self)
|
|
87
|
-
|
|
146
|
+
|
|
147
|
+
# 設定應用程式圖示
|
|
148
|
+
# Set application icon
|
|
88
149
|
self.icon_path = Path(os.getcwd() + "/je_driver_icon.ico")
|
|
89
150
|
self.icon = QIcon(str(self.icon_path))
|
|
90
151
|
if self.icon.isNull() is False:
|
|
91
152
|
self.setWindowIcon(self.icon)
|
|
153
|
+
# 如果系統支援系統匣,則顯示圖示
|
|
154
|
+
# Show system tray icon if available
|
|
92
155
|
if ExtendSystemTray.isSystemTrayAvailable() and self.show_system_tray_ray:
|
|
93
156
|
self.system_tray = ExtendSystemTray(main_window=self)
|
|
94
157
|
self.system_tray.setIcon(self.icon)
|
|
95
158
|
self.system_tray.setVisible(True)
|
|
96
159
|
self.system_tray.show()
|
|
97
160
|
self.system_tray.setToolTip(language_wrapper.language_word_dict.get("application_name"))
|
|
98
|
-
|
|
161
|
+
|
|
162
|
+
# 設定輸出重導 (stdout/stderr)
|
|
163
|
+
# Setup output redirection (stdout/stderr)
|
|
99
164
|
redirect_manager_instance.restore_std()
|
|
100
165
|
redirect_manager_instance.set_redirect()
|
|
101
|
-
|
|
166
|
+
|
|
167
|
+
# 再次設定計時器,定期檢查輸出
|
|
168
|
+
# Setup timer again to check redirected output
|
|
102
169
|
self.redirect_timer = QTimer(self)
|
|
103
170
|
self.redirect_timer.setInterval(10)
|
|
104
171
|
self.redirect_timer.timeout.connect(self.redirect)
|
|
105
172
|
self.redirect_timer.start()
|
|
106
|
-
|
|
173
|
+
|
|
174
|
+
# 建立主要分頁:編輯器與瀏覽器
|
|
175
|
+
# Create main tabs: editor and browser
|
|
107
176
|
main_browser_widget = MainBrowserWidget()
|
|
108
177
|
self.tab_widget.addTab(EditorWidget(self), language_wrapper.language_word_dict.get("tab_name_editor"))
|
|
109
178
|
self.tab_widget.addTab(main_browser_widget, language_wrapper.language_word_dict.get("tab_name_web_browser"))
|
|
179
|
+
|
|
180
|
+
# 預設新增一個 StackOverflow 瀏覽分頁
|
|
181
|
+
# Add a default StackOverflow browser tab
|
|
110
182
|
main_browser_widget.add_browser_tab(
|
|
111
183
|
BrowserWidget(start_url="https://stackoverflow.com/", search_prefix="https://stackoverflow.com/search?q="))
|
|
112
184
|
|
|
185
|
+
# 加入擴充的自訂分頁
|
|
186
|
+
# Add extended custom tabs
|
|
113
187
|
for widget_name, widget in EDITOR_EXTEND_TAB.items():
|
|
114
188
|
self.tab_widget.addTab(widget(), widget_name)
|
|
189
|
+
|
|
190
|
+
# 設定中央元件為 TabWidget
|
|
191
|
+
# Set central widget as TabWidget
|
|
115
192
|
self.setCentralWidget(self.tab_widget)
|
|
116
|
-
|
|
193
|
+
|
|
194
|
+
# 啟動時讀取設定
|
|
195
|
+
# Load startup settings
|
|
117
196
|
self.startup_setting()
|
|
118
|
-
|
|
197
|
+
|
|
198
|
+
# 如果是 debug 模式,10 秒後自動關閉
|
|
199
|
+
# If debug mode, auto-close after 10 seconds
|
|
119
200
|
if self.debug_mode:
|
|
120
201
|
close_timer = QTimer(self)
|
|
121
202
|
close_timer.setInterval(10000)
|
|
@@ -123,61 +204,91 @@ class EditorMain(QMainWindow, QtStyleTools):
|
|
|
123
204
|
close_timer.start()
|
|
124
205
|
|
|
125
206
|
def clear_code_result(self):
|
|
207
|
+
"""
|
|
208
|
+
清除目前編輯器的輸出結果
|
|
209
|
+
Clear the current editor's output result
|
|
210
|
+
"""
|
|
126
211
|
jeditor_logger.info(f"EditorMain clear_code_result")
|
|
127
212
|
widget = self.tab_widget.currentWidget()
|
|
128
213
|
if isinstance(widget, EditorWidget):
|
|
129
214
|
widget.code_result.clear()
|
|
130
215
|
|
|
131
216
|
def redirect(self) -> None:
|
|
217
|
+
"""
|
|
218
|
+
將 stdout/stderr 的訊息導入到編輯器的輸出區域
|
|
219
|
+
Redirect stdout/stderr messages into the editor's output area
|
|
220
|
+
"""
|
|
132
221
|
jeditor_logger.info(f"EditorMain redirect")
|
|
222
|
+
# 遍歷所有分頁 (Tab),尋找 EditorWidget
|
|
223
|
+
# Iterate through all tabs to find EditorWidget
|
|
133
224
|
for code_editor in range(self.tab_widget.count()):
|
|
134
225
|
widget = self.tab_widget.widget(code_editor)
|
|
135
226
|
if isinstance(widget, EditorWidget):
|
|
136
|
-
#
|
|
227
|
+
# stdout 輸出處理
|
|
228
|
+
# Handle stdout messages
|
|
137
229
|
if not redirect_manager_instance.std_out_queue.empty():
|
|
138
230
|
output_message = redirect_manager_instance.std_out_queue.get_nowait()
|
|
139
231
|
output_message = str(output_message).strip()
|
|
140
232
|
if output_message:
|
|
141
233
|
text_cursor = self.code_result.textCursor()
|
|
142
234
|
text_format = QTextCharFormat()
|
|
235
|
+
# 設定正常輸出顏色
|
|
236
|
+
# Set normal output color
|
|
143
237
|
text_format.setForeground(actually_color_dict.get("normal_output_color"))
|
|
144
238
|
text_cursor.insertText(output_message, text_format)
|
|
145
239
|
text_cursor.insertBlock()
|
|
240
|
+
|
|
241
|
+
# stderr 錯誤輸出處理
|
|
242
|
+
# Handle stderr messages
|
|
146
243
|
if not redirect_manager_instance.std_err_queue.empty():
|
|
147
244
|
error_message = redirect_manager_instance.std_err_queue.get_nowait()
|
|
148
245
|
error_message = str(error_message).strip()
|
|
149
246
|
if error_message:
|
|
150
247
|
text_cursor = self.code_result.textCursor()
|
|
151
248
|
text_format = QTextCharFormat()
|
|
249
|
+
# 設定錯誤輸出顏色
|
|
250
|
+
# Set error output color
|
|
152
251
|
text_format.setForeground(actually_color_dict.get("error_output_color"))
|
|
153
252
|
text_cursor.insertText(error_message, text_format)
|
|
154
253
|
text_cursor.insertBlock()
|
|
155
|
-
break
|
|
254
|
+
break # 找到第一個 EditorWidget 就結束迴圈 / Stop after first EditorWidget found
|
|
156
255
|
|
|
157
256
|
def startup_setting(self) -> None:
|
|
257
|
+
"""
|
|
258
|
+
啟動時套用使用者設定 (字型、樣式、上次開啟的檔案)
|
|
259
|
+
Apply user settings on startup (fonts, styles, last opened file)
|
|
260
|
+
"""
|
|
158
261
|
jeditor_logger.info(f"EditorMain startup_setting")
|
|
159
|
-
#
|
|
262
|
+
# 設定 UI 字型與大小
|
|
263
|
+
# Set UI font and size
|
|
160
264
|
self.setStyleSheet(
|
|
161
265
|
f"font-size: {user_setting_dict.get('ui_font_size', 12)}pt;"
|
|
162
266
|
f"font-family: {user_setting_dict.get('ui_font', 'Lato')};"
|
|
163
267
|
)
|
|
164
|
-
|
|
268
|
+
|
|
269
|
+
# 套用到每個編輯器分頁
|
|
270
|
+
# Apply settings to each editor tab
|
|
165
271
|
for code_editor_count in range(self.tab_widget.count()):
|
|
166
272
|
widget = self.tab_widget.widget(code_editor_count)
|
|
167
273
|
if isinstance(widget, EditorWidget):
|
|
168
|
-
#
|
|
274
|
+
# 編輯區字型
|
|
275
|
+
# Font for code editor
|
|
169
276
|
widget.code_edit.setStyleSheet(
|
|
170
277
|
f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
|
|
171
278
|
f"font-family: {user_setting_dict.get('font', 'Lato')};"
|
|
172
279
|
)
|
|
173
|
-
#
|
|
280
|
+
# 輸出區字型
|
|
281
|
+
# Font for output area
|
|
174
282
|
widget.code_result.setStyleSheet(
|
|
175
283
|
f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
|
|
176
284
|
f"font-family: {user_setting_dict.get('font', 'Lato')};"
|
|
177
285
|
)
|
|
178
|
-
#
|
|
286
|
+
# 預設 Python 編譯器
|
|
287
|
+
# Default Python compiler
|
|
179
288
|
self.python_compiler = user_setting_dict.get("python_compiler", None)
|
|
180
|
-
|
|
289
|
+
|
|
290
|
+
# 嘗試開啟上次編輯的檔案
|
|
291
|
+
# Try to open last edited file
|
|
181
292
|
last_file = user_setting_dict.get("last_file", None)
|
|
182
293
|
if last_file is not None:
|
|
183
294
|
last_file_path = pathlib.Path(last_file)
|
|
@@ -189,32 +300,51 @@ class EditorMain(QMainWindow, QtStyleTools):
|
|
|
189
300
|
file_is_open_manager_dict.update({str(last_file_path): str(last_file_path.name)})
|
|
190
301
|
widget.rename_self_tab()
|
|
191
302
|
|
|
192
|
-
#
|
|
303
|
+
# 套用 UI 樣式 (主題)
|
|
304
|
+
# Apply UI stylesheet (theme)
|
|
193
305
|
self.apply_stylesheet(self, user_setting_dict.get("ui_style", "dark_amber.xml"))
|
|
194
|
-
#
|
|
306
|
+
# 更新顏色設定
|
|
307
|
+
# Update color settings
|
|
195
308
|
update_actually_color_dict()
|
|
196
309
|
|
|
197
310
|
def go_to_new_tab(self, file_path: Path):
|
|
311
|
+
"""
|
|
312
|
+
開啟新分頁並載入檔案
|
|
313
|
+
Open a new tab and load a file
|
|
314
|
+
"""
|
|
198
315
|
jeditor_logger.info(f"EditorMain go_to_new_tab file_path: {file_path}")
|
|
199
316
|
if file_is_open_manager_dict.get(str(file_path), None) is None:
|
|
317
|
+
# 建立新的編輯器分頁
|
|
318
|
+
# Create a new editor tab
|
|
200
319
|
editor_widget = EditorWidget(self)
|
|
201
320
|
self.tab_widget.addTab(
|
|
202
321
|
editor_widget,
|
|
203
322
|
f"{language_wrapper.language_word_dict.get('tab_menu_editor_tab_name')} "
|
|
204
|
-
f"{self.tab_widget.count()}"
|
|
323
|
+
f"{self.tab_widget.count()}"
|
|
324
|
+
)
|
|
205
325
|
self.tab_widget.setCurrentWidget(editor_widget)
|
|
206
326
|
editor_widget.open_an_file(file_path)
|
|
207
327
|
else:
|
|
328
|
+
# 如果檔案已開啟,直接切換到該分頁
|
|
329
|
+
# If file already opened, switch to that tab
|
|
208
330
|
widget: QWidget = self.tab_widget.findChild(EditorWidget, str(file_path))
|
|
209
331
|
self.tab_widget.setCurrentWidget(widget)
|
|
210
332
|
|
|
211
333
|
def closeEvent(self, event) -> None:
|
|
334
|
+
"""
|
|
335
|
+
視窗關閉事件:儲存使用者設定
|
|
336
|
+
Window close event: save user settings
|
|
337
|
+
"""
|
|
212
338
|
jeditor_logger.info("EditorMain closeEvent")
|
|
213
339
|
write_user_setting()
|
|
214
340
|
write_user_color_setting()
|
|
215
341
|
super().closeEvent(event)
|
|
216
342
|
|
|
217
343
|
def event(self, event: QEvent) -> bool:
|
|
344
|
+
"""
|
|
345
|
+
事件處理:忽略 ToolTip 類型事件
|
|
346
|
+
Event handler: ignore ToolTip events
|
|
347
|
+
"""
|
|
218
348
|
jeditor_logger.info(f"EditorMain event: {event}")
|
|
219
349
|
if event.type() == QEvent.Type.ToolTip:
|
|
220
350
|
event.ignore()
|
|
@@ -223,6 +353,10 @@ class EditorMain(QMainWindow, QtStyleTools):
|
|
|
223
353
|
return super().event(event)
|
|
224
354
|
|
|
225
355
|
def close_tab(self, index: int):
|
|
356
|
+
"""
|
|
357
|
+
關閉指定索引的分頁
|
|
358
|
+
Close tab at given index
|
|
359
|
+
"""
|
|
226
360
|
widget = self.tab_widget.widget(index)
|
|
227
361
|
if widget:
|
|
228
362
|
widget.close()
|
|
@@ -230,4 +364,8 @@ class EditorMain(QMainWindow, QtStyleTools):
|
|
|
230
364
|
|
|
231
365
|
@classmethod
|
|
232
366
|
def debug_close(cls):
|
|
233
|
-
|
|
367
|
+
"""
|
|
368
|
+
除錯模式下自動關閉程式
|
|
369
|
+
Auto-close the program in debug mode
|
|
370
|
+
"""
|
|
371
|
+
sys.exit(0)
|