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