je-editor 0.0.222__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.

Files changed (75) hide show
  1. je_editor/__init__.py +2 -2
  2. je_editor/git_client/commit_graph.py +7 -7
  3. je_editor/git_client/git_action.py +0 -7
  4. je_editor/pyside_ui/browser/browser_view.py +16 -4
  5. je_editor/pyside_ui/browser/browser_widget.py +43 -29
  6. je_editor/pyside_ui/browser/main_browser_widget.py +85 -0
  7. je_editor/pyside_ui/code/auto_save/auto_save_manager.py +34 -2
  8. je_editor/pyside_ui/code/auto_save/auto_save_thread.py +19 -6
  9. je_editor/pyside_ui/code/code_format/pep8_format.py +53 -9
  10. je_editor/pyside_ui/code/code_process/code_exec.py +88 -52
  11. je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +116 -55
  12. je_editor/pyside_ui/code/running_process_manager.py +19 -1
  13. je_editor/pyside_ui/code/shell_process/shell_exec.py +71 -48
  14. je_editor/pyside_ui/code/syntax/python_syntax.py +45 -10
  15. je_editor/pyside_ui/code/syntax/syntax_setting.py +40 -12
  16. je_editor/pyside_ui/code/textedit_code_result/code_record.py +34 -12
  17. je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +53 -6
  18. je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +30 -3
  19. je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +35 -2
  20. je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +33 -5
  21. je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +25 -3
  22. je_editor/pyside_ui/dialog/search_ui/search_error_box.py +26 -1
  23. je_editor/pyside_ui/dialog/search_ui/search_text_box.py +26 -1
  24. je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +11 -11
  25. je_editor/pyside_ui/git_ui/git_client/commit_table.py +46 -8
  26. je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +49 -15
  27. je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +81 -16
  28. je_editor/pyside_ui/git_ui/git_client/graph_view.py +64 -20
  29. je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +20 -5
  30. je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +20 -1
  31. je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +56 -41
  32. je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +45 -6
  33. je_editor/pyside_ui/main_ui/console_widget/console_gui.py +44 -12
  34. je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +34 -13
  35. je_editor/pyside_ui/main_ui/dock/destroy_dock.py +33 -2
  36. je_editor/pyside_ui/main_ui/editor/editor_widget.py +104 -20
  37. je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +34 -7
  38. je_editor/pyside_ui/main_ui/editor/process_input.py +38 -11
  39. je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +46 -11
  40. je_editor/pyside_ui/main_ui/main_editor.py +180 -42
  41. je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +51 -28
  42. je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +83 -36
  43. je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +70 -17
  44. je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +35 -4
  45. je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +41 -1
  46. je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +100 -42
  47. je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +57 -7
  48. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +50 -4
  49. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +52 -6
  50. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +44 -4
  51. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +23 -1
  52. je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +37 -12
  53. je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +44 -7
  54. je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +127 -44
  55. je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +65 -1
  56. je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +18 -1
  57. je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +33 -3
  58. je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +38 -11
  59. je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +39 -2
  60. je_editor/start_editor.py +26 -1
  61. je_editor/utils/encodings/python_encodings.py +101 -98
  62. je_editor/utils/file/open/open_file.py +36 -19
  63. je_editor/utils/file/save/save_file.py +35 -14
  64. je_editor/utils/json/json_file.py +29 -14
  65. je_editor/utils/json_format/json_process.py +33 -2
  66. je_editor/utils/logging/loggin_instance.py +38 -8
  67. je_editor/utils/multi_language/multi_language_wrapper.py +29 -4
  68. je_editor/utils/redirect_manager/redirect_manager_class.py +49 -11
  69. je_editor/utils/venv_check/check_venv.py +45 -15
  70. {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/METADATA +1 -1
  71. {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/RECORD +74 -74
  72. je_editor/git_client/github.py +0 -81
  73. {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/WHEEL +0 -0
  74. {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/licenses/LICENSE +0 -0
  75. {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/top_level.txt +0 -0
@@ -32,11 +32,24 @@ from je_editor.utils.file.open.open_file import read_file
32
32
 
33
33
 
34
34
  class EditorWidget(QWidget):
35
+ """
36
+ EditorWidget 是主要的程式碼編輯器元件,包含:
37
+ - 專案檔案樹狀檢視
38
+ - 程式碼編輯區
39
+ - 執行結果、格式檢查、除錯輸出
40
+ - 自動儲存與檔案管理
41
+
42
+ EditorWidget is the main code editor widget, including:
43
+ - Project file tree view
44
+ - Code editing area
45
+ - Execution result, format check, debugger output
46
+ - Auto-save and file management
47
+ """
35
48
 
36
49
  def __init__(self, main_window: EditorMain):
37
50
  jeditor_logger.info(f"Init EditorWidget main_window: {main_window}")
38
51
  super().__init__()
39
- # Init variable
52
+ # ---------------- Init variables 初始化變數 ----------------
40
53
  self.checker: Union[PEP8FormatChecker, None] = None
41
54
  self.current_file = None
42
55
  self.tree_view_scroll_area = None
@@ -45,41 +58,51 @@ class EditorWidget(QWidget):
45
58
  self.python_compiler = None
46
59
  self.main_window = main_window
47
60
  self.tab_manager = self.main_window.tab_widget
48
- # Attr
49
- # Current execute instance if none not running
61
+
62
+ # 執行相關物件 / Execution related objects
50
63
  self.exec_program: Union[None, ExecManager] = None
51
64
  self.exec_shell: Union[None, ShellManager] = None
52
65
  self.exec_python_debugger: Union[None, ExecManager] = None
53
- # Autosave
66
+
67
+ # 自動儲存執行緒 / Auto-save thread
54
68
  self.code_save_thread: Union[CodeEditSaveThread, None] = None
55
- # UI
69
+
70
+ # ---------------- UI 初始化 ----------------
56
71
  self.grid_layout = QGridLayout(self)
57
72
  self.setWindowTitle(language_wrapper.language_word_dict.get("application_name"))
58
- # Treeview
73
+
74
+ # 建立專案檔案樹狀檢視 / Setup project tree view
59
75
  self.set_project_treeview()
60
- # Use to put full ui
76
+
77
+ # 主分割器 (左:檔案樹,右:編輯器) / Main splitter (left: tree, right: editor)
61
78
  self.full_splitter = QSplitter()
62
79
  self.full_splitter.setOrientation(Qt.Orientation.Horizontal)
63
- # Code edit and result QSplitter
80
+
81
+ # 編輯器分割器 (上:編輯器,下:輸出) / Editor splitter (top: editor, bottom: output)
64
82
  self.edit_splitter = QSplitter(self.full_splitter)
65
83
  self.edit_splitter.setOrientation(Qt.Orientation.Vertical)
66
- # code edit and code result plaintext
84
+
85
+ # 程式碼編輯器與輸出區 / Code editor and result area
67
86
  self.code_edit = CodeEditor(self)
68
87
  self.code_result = CodeRecord()
69
88
  self.code_result_cursor = self.code_result.textCursor()
89
+
90
+ # 捲動區包裝編輯器與輸出 / Scroll areas for editor and result
70
91
  self.code_edit_scroll_area = QScrollArea()
71
92
  self.code_edit_scroll_area.setWidgetResizable(True)
72
93
  self.code_edit_scroll_area.setViewportMargins(0, 0, 0, 0)
73
94
  self.code_edit_scroll_area.setWidget(self.code_edit)
95
+
74
96
  self.code_result_scroll_area = QScrollArea()
75
97
  self.code_result_scroll_area.setWidgetResizable(True)
76
98
  self.code_result_scroll_area.setViewportMargins(0, 0, 0, 0)
77
99
  self.code_result_scroll_area.setWidget(self.code_result)
78
- # Code format checker
100
+
101
+ # 格式檢查與除錯輸出 / Format check and debugger output
79
102
  self.format_check_result = CodeRecord()
80
- # Debugger
81
103
  self.debugger_result = CodeRecord()
82
- # Code result tab
104
+
105
+ # 輸出分頁 (執行結果 / 格式檢查 / 除錯) / Output tabs
83
106
  self.code_difference_result = QTabWidget()
84
107
  self.code_difference_result.addTab(
85
108
  self.code_result_scroll_area, language_wrapper.language_word_dict.get("editor_code_result"))
@@ -87,18 +110,21 @@ class EditorWidget(QWidget):
87
110
  self.format_check_result, language_wrapper.language_word_dict.get("editor_format_check"))
88
111
  self.code_difference_result.addTab(
89
112
  self.debugger_result, language_wrapper.language_word_dict.get("editor_debugger_input_title_label"))
90
- # Edit splitter
113
+
114
+ # 加入分割器 / Add widgets to splitters
91
115
  self.edit_splitter.addWidget(self.code_edit_scroll_area)
92
116
  self.edit_splitter.addWidget(self.code_difference_result)
93
117
  self.edit_splitter.setStretchFactor(0, 3)
94
118
  self.edit_splitter.setStretchFactor(1, 1)
95
119
  self.edit_splitter.setSizes([300, 100])
120
+
96
121
  self.full_splitter.addWidget(self.project_treeview)
97
122
  self.full_splitter.addWidget(self.edit_splitter)
98
123
  self.full_splitter.setStretchFactor(0, 1)
99
124
  self.full_splitter.setStretchFactor(1, 3)
100
125
  self.full_splitter.setSizes([100, 300])
101
- # Font
126
+
127
+ # 設定字體樣式 / Set font style
102
128
  self.code_edit.setStyleSheet(
103
129
  f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
104
130
  f"font-family: {user_setting_dict.get('font', 'Lato')};"
@@ -107,15 +133,23 @@ class EditorWidget(QWidget):
107
133
  f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
108
134
  f"font-family: {user_setting_dict.get('font', 'Lato')};"
109
135
  )
110
- # Add to layout
136
+
137
+ # 加入主版面配置 / Add to main layout
111
138
  self.grid_layout.addWidget(self.full_splitter)
112
139
 
140
+ # ---------------- Project Treeview ----------------
113
141
  def set_project_treeview(self) -> None:
142
+ """
143
+ 建立並設定專案檔案樹狀檢視
144
+ Setup and configure project file tree view
145
+ """
114
146
  jeditor_logger.info("EditorWidget set_project_treeview")
115
147
  self.project_treeview_model = QFileSystemModel()
116
148
  self.project_treeview_model.setRootPath(QDir.currentPath())
117
149
  self.project_treeview = QTreeView()
118
150
  self.project_treeview.setModel(self.project_treeview_model)
151
+
152
+ # 設定根目錄 (工作目錄或當前路徑) / Set root directory (working dir or current path)
119
153
  if self.main_window.working_dir is None:
120
154
  self.project_treeview.setRootIndex(
121
155
  self.project_treeview_model.index(str(Path.cwd()))
@@ -124,53 +158,87 @@ class EditorWidget(QWidget):
124
158
  self.project_treeview.setRootIndex(
125
159
  self.project_treeview_model.index(self.main_window.working_dir)
126
160
  )
161
+
162
+ # 包裝成可捲動區域 / Wrap in scroll area
127
163
  self.tree_view_scroll_area = QScrollArea()
128
164
  self.tree_view_scroll_area.setWidgetResizable(True)
129
165
  self.tree_view_scroll_area.setViewportMargins(0, 0, 0, 0)
130
166
  self.tree_view_scroll_area.setWidget(self.project_treeview)
131
167
  self.grid_layout.addWidget(self.tree_view_scroll_area, 0, 0, 0, 1)
168
+
169
+ # 點擊檔案時觸發 / Connect click event
132
170
  self.project_treeview.clicked.connect(self.treeview_click)
133
171
 
134
172
  def check_is_open(self, path: Path):
173
+ """
174
+ 檢查檔案是否已經開啟,如果已開啟則切換到該分頁。
175
+ Check if the file is already open, if yes then switch to that tab.
176
+ """
135
177
  jeditor_logger.info(f"EditorWidget check_is_open path: {path}")
136
178
  if file_is_open_manager_dict.get(str(path), None) is not None:
179
+ # 嘗試在分頁中找到對應的 EditorWidget
180
+ # Try to find the corresponding EditorWidget in tab manager
137
181
  widget: QWidget = self.tab_manager.findChild(EditorWidget, str(path))
138
182
  if widget is None:
183
+ # 如果找不到,代表之前的紀錄失效,移除紀錄
184
+ # If not found, remove stale record
139
185
  file_is_open_manager_dict.pop(str(path), None)
140
186
  else:
187
+ # 如果找到,直接切換到該分頁
188
+ # If found, switch to that tab
141
189
  self.tab_manager.setCurrentWidget(widget)
142
190
  return False
143
191
  else:
192
+ # 如果檔案未開啟,加入紀錄
193
+ # If file not open, add to open manager dict
144
194
  file_is_open_manager_dict.update({str(path): str(path)})
145
195
  return True
146
196
 
147
197
  def open_an_file(self, path: Path) -> bool:
148
198
  """
149
- :param path: open file path
150
- :return: return False if file tab exists
199
+ 開啟檔案並載入到編輯器。
200
+ Open a file and load it into the editor.
201
+
202
+ :param path: 檔案路徑 / File path
203
+ :return: 如果檔案已經開啟則回傳 False / Return False if file tab already exists
151
204
  """
152
205
  jeditor_logger.info(f"EditorWidget open_an_file path: {path}")
153
206
  if not self.check_is_open(path):
154
207
  return False
208
+
209
+ # 如果有自動儲存執行緒,暫時跳過這一輪
210
+ # If auto-save thread exists, skip this round
155
211
  if self.code_save_thread:
156
212
  self.code_save_thread.skip_this_round = True
213
+
214
+ # 讀取檔案內容 / Read file content
157
215
  file, file_content = read_file(str(path))
158
- self.code_edit.setPlainText(
159
- file_content
160
- )
216
+ self.code_edit.setPlainText(file_content)
217
+
218
+ # 更新目前檔案資訊 / Update current file info
161
219
  self.current_file = file
162
220
  self.code_edit.current_file = file
163
221
  self.code_edit.reset_highlighter()
222
+
223
+ # 更新使用者設定中的最後開啟檔案 / Update last opened file in user settings
164
224
  user_setting_dict.update({"last_file": str(self.current_file)})
225
+
226
+ # 啟動或更新自動儲存執行緒 / Start or update auto-save thread
165
227
  if self.current_file is not None and self.code_save_thread is None:
166
228
  init_new_auto_save_thread(self.current_file, self)
167
229
  else:
168
230
  self.code_save_thread.file = self.current_file
169
231
  self.code_save_thread.skip_this_round = False
232
+
233
+ # 更新分頁標籤名稱 / Update tab title
170
234
  self.rename_self_tab()
171
235
  return True
172
236
 
173
237
  def treeview_click(self) -> None:
238
+ """
239
+ 當使用者點擊檔案樹中的項目時觸發。
240
+ Triggered when user clicks an item in the project tree view.
241
+ """
174
242
  jeditor_logger.info("EditorWidget treeview_click")
175
243
  clicked_item: QFileSystemModel = self.project_treeview.selectedIndexes()[0]
176
244
  file_info: QFileInfo = self.project_treeview.model().fileInfo(clicked_item)
@@ -179,6 +247,10 @@ class EditorWidget(QWidget):
179
247
  self.open_an_file(path)
180
248
 
181
249
  def rename_self_tab(self):
250
+ """
251
+ 將目前分頁的標籤名稱改為目前檔案名稱。
252
+ Rename the current tab to the current file name.
253
+ """
182
254
  jeditor_logger.info("EditorWidget rename_self_tab")
183
255
  if self.tab_manager.currentWidget() is self:
184
256
  self.tab_manager.setTabText(
@@ -186,13 +258,20 @@ class EditorWidget(QWidget):
186
258
  self.setObjectName(str(Path(self.current_file)))
187
259
 
188
260
  def check_file_format(self):
261
+ """
262
+ 檢查目前檔案的程式碼格式 (僅支援 Python)。
263
+ Check the code format of the current file (only supports Python).
264
+ """
189
265
  if self.current_file:
190
266
  jeditor_logger.info("EditorWidget check_file_format")
191
267
  suffix_checker = Path(self.current_file).suffix
192
268
  if suffix_checker == ".py":
269
+ # 使用 PEP8 格式檢查器 / Use PEP8 format checker
193
270
  self.checker = PEP8FormatChecker(self.current_file)
194
271
  self.checker.check_all_format()
195
272
  self.format_check_result.setPlainText("")
273
+
274
+ # 顯示錯誤訊息並套用顏色 / Display errors with color formatting
196
275
  for error in self.checker.error_list:
197
276
  text_cursor = self.format_check_result.textCursor()
198
277
  text_format = QTextCharFormat()
@@ -201,12 +280,17 @@ class EditorWidget(QWidget):
201
280
  text_cursor.insertBlock()
202
281
  self.checker.error_list.clear()
203
282
  else:
283
+ # 非 Python 檔案,顯示提示訊息 / Show message if not Python file
204
284
  message_box = QMessageBox()
205
285
  message_box.setText(
206
286
  language_wrapper.language_word_dict.get("python_format_checker_only_support_python_message"))
207
287
  message_box.exec_()
208
288
 
209
289
  def close(self) -> bool:
290
+ """
291
+ 關閉編輯器,釋放資源並移除自動儲存紀錄。
292
+ Close the editor, release resources, and remove auto-save records.
293
+ """
210
294
  jeditor_logger.info("EditorWidget close")
211
295
  if self.code_save_thread is not None:
212
296
  self.code_save_thread.still_run = False
@@ -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
- # Init variable
19
- self.current_file = current_file
20
- # Attr
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
- # UI
34
+
35
+ # ---------------- UI 初始化 ----------------
23
36
  self.grid_layout = QGridLayout(self)
24
37
  self.setWindowTitle(language_wrapper.language_word_dict.get("application_name"))
25
- # code edit and code result plaintext
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
- # Font
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
- super().closeEvent(event)
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
- # Attr
32
+
33
+ # 設定當視窗關閉時自動刪除資源
34
+ # Set attribute to delete widget on close
24
35
  self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
25
- # UI setting
26
- self.main_window = main_window
27
- self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
28
- self.command_input = QLineEdit()
29
- self.send_command_button = QPushButton()
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
- self.kernel_manager = QtInProcessKernelManager()
24
- self.kernel_manager.start_kernel()
25
- self.kernel = self.kernel_manager.kernel
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
- super().close()
76
+
77
+ # 呼叫父類別的 close 方法 / Call parent close method
78
+ super().close()