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
|
@@ -7,12 +7,23 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class VariableModel(QAbstractTableModel):
|
|
10
|
+
"""
|
|
11
|
+
變數模型:負責管理與顯示 Python 全域變數
|
|
12
|
+
Variable model: manages and displays Python global variables
|
|
13
|
+
"""
|
|
10
14
|
def __init__(self, parent=None):
|
|
11
15
|
super().__init__(parent)
|
|
12
|
-
self.variables = []
|
|
16
|
+
self.variables = [] # 儲存變數資訊 [名稱, 型別, 值字串, 真實值]
|
|
17
|
+
# Store variable info [name, type, repr(value), actual value]
|
|
13
18
|
|
|
14
19
|
def update_data(self):
|
|
20
|
+
"""
|
|
21
|
+
更新變數清單,從全域變數中擷取
|
|
22
|
+
Update variable list from globals()
|
|
23
|
+
"""
|
|
15
24
|
parent_widget = self.parent()
|
|
25
|
+
# 避免在 table 正在互動時更新,造成衝突
|
|
26
|
+
# Avoid updating while table is in active state
|
|
16
27
|
if parent_widget and getattr(parent_widget, "table", None):
|
|
17
28
|
if parent_widget.table.state() != QTableView.State.NoState:
|
|
18
29
|
return
|
|
@@ -22,17 +33,21 @@ class VariableModel(QAbstractTableModel):
|
|
|
22
33
|
self.variables = [
|
|
23
34
|
[name, type(value).__name__, repr(value), value]
|
|
24
35
|
for name, value in vars_dict.items()
|
|
25
|
-
if not name.startswith("__") # 過濾內建變數
|
|
36
|
+
if not name.startswith("__") # 過濾內建變數 / filter out built-in variables
|
|
26
37
|
]
|
|
27
38
|
self.endResetModel()
|
|
28
39
|
|
|
29
40
|
def rowCount(self, parent=None):
|
|
41
|
+
# 回傳變數數量 / return number of variables
|
|
30
42
|
return len(self.variables)
|
|
31
43
|
|
|
32
44
|
def columnCount(self, parent=None):
|
|
45
|
+
# 固定三欄:名稱、型別、值 / fixed 3 columns: name, type, value
|
|
33
46
|
return 3
|
|
34
47
|
|
|
35
48
|
def data(self, index, role=Qt.ItemDataRole.DisplayRole):
|
|
49
|
+
# 提供表格顯示的資料
|
|
50
|
+
# Provide data for table display
|
|
36
51
|
if not index.isValid() or not (0 <= index.row() < len(self.variables)):
|
|
37
52
|
return None
|
|
38
53
|
if role == Qt.ItemDataRole.DisplayRole:
|
|
@@ -40,6 +55,8 @@ class VariableModel(QAbstractTableModel):
|
|
|
40
55
|
return None
|
|
41
56
|
|
|
42
57
|
def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
|
|
58
|
+
# 設定表頭文字 (支援多語系)
|
|
59
|
+
# Set header labels (multi-language support)
|
|
43
60
|
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
|
44
61
|
return [
|
|
45
62
|
language_wrapper.language_word_dict.get("variable_inspector_var_name"),
|
|
@@ -49,14 +66,20 @@ class VariableModel(QAbstractTableModel):
|
|
|
49
66
|
return None
|
|
50
67
|
|
|
51
68
|
def flags(self, index):
|
|
69
|
+
# 設定欄位屬性,僅允許「值」欄可編輯
|
|
70
|
+
# Set column flags, only "value" column is editable
|
|
52
71
|
if not index.isValid() or not (0 <= index.row() < len(self.variables)):
|
|
53
72
|
return Qt.ItemFlag.NoItemFlags
|
|
54
73
|
base = super().flags(index)
|
|
55
|
-
if index.column() == 2: # 只允許編輯值欄
|
|
74
|
+
if index.column() == 2: # 只允許編輯值欄 / only value column editable
|
|
56
75
|
base |= Qt.ItemFlag.ItemIsEditable
|
|
57
76
|
return base
|
|
58
77
|
|
|
59
78
|
def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
|
|
79
|
+
"""
|
|
80
|
+
更新變數值,並同步到全域變數
|
|
81
|
+
Update variable value and sync to globals()
|
|
82
|
+
"""
|
|
60
83
|
if role == Qt.ItemDataRole.EditRole and index.column() == 2:
|
|
61
84
|
|
|
62
85
|
if value == "":
|
|
@@ -68,13 +91,19 @@ class VariableModel(QAbstractTableModel):
|
|
|
68
91
|
|
|
69
92
|
var_name = self.variables[index.row()][0]
|
|
70
93
|
try:
|
|
94
|
+
# 嘗試將輸入轉換為 Python 物件
|
|
95
|
+
# Try to evaluate input as Python object
|
|
71
96
|
new_value = ast.literal_eval(value)
|
|
72
97
|
except Exception:
|
|
98
|
+
# 若失敗則當作字串處理
|
|
99
|
+
# If failed, treat as string
|
|
73
100
|
new_value = value
|
|
74
101
|
|
|
75
102
|
if new_value == self.variables[index.row()][3]:
|
|
76
103
|
return False
|
|
77
104
|
|
|
105
|
+
# 更新全域變數與模型資料
|
|
106
|
+
# Update globals and model data
|
|
78
107
|
globals()[var_name] = new_value
|
|
79
108
|
self.variables[index.row()][2] = repr(new_value)
|
|
80
109
|
self.variables[index.row()][3] = new_value
|
|
@@ -82,29 +111,44 @@ class VariableModel(QAbstractTableModel):
|
|
|
82
111
|
return True
|
|
83
112
|
return False
|
|
84
113
|
|
|
114
|
+
|
|
85
115
|
class VariableProxy(QSortFilterProxyModel):
|
|
116
|
+
"""
|
|
117
|
+
過濾代理模型:支援搜尋與編輯轉發
|
|
118
|
+
Proxy model: supports filtering and forwards editing
|
|
119
|
+
"""
|
|
86
120
|
def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
|
|
121
|
+
# 將編輯操作轉發到原始模型
|
|
122
|
+
# Forward editing to source model
|
|
87
123
|
return self.sourceModel().setData(
|
|
88
124
|
self.mapToSource(index), value, role
|
|
89
125
|
)
|
|
90
126
|
|
|
127
|
+
|
|
91
128
|
class VariableInspector(QWidget):
|
|
129
|
+
"""
|
|
130
|
+
變數檢視器:提供 GUI 介面顯示與搜尋全域變數
|
|
131
|
+
Variable inspector: GUI interface to display and search global variables
|
|
132
|
+
"""
|
|
92
133
|
def __init__(self):
|
|
93
134
|
super().__init__()
|
|
94
135
|
self.setWindowTitle(language_wrapper.language_word_dict.get("variable_inspector_title"))
|
|
95
136
|
layout = QVBoxLayout(self)
|
|
96
137
|
|
|
138
|
+
# 搜尋框 / search box
|
|
97
139
|
self.search_box = QLineEdit()
|
|
98
140
|
self.search_box.setPlaceholderText(language_wrapper.language_word_dict.get("variable_inspector_search"))
|
|
99
141
|
layout.addWidget(QLabel(language_wrapper.language_word_dict.get("variable_inspector_search")))
|
|
100
142
|
layout.addWidget(self.search_box)
|
|
101
143
|
|
|
144
|
+
# 模型與代理模型 / model and proxy model
|
|
102
145
|
self.model = VariableModel(parent=self)
|
|
103
146
|
self.proxy_model = VariableProxy(self)
|
|
104
147
|
self.proxy_model.setSourceModel(self.model)
|
|
105
|
-
self.proxy_model.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
|
106
|
-
self.proxy_model.setFilterKeyColumn(0)
|
|
148
|
+
self.proxy_model.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) # 不分大小寫 / case-insensitive
|
|
149
|
+
self.proxy_model.setFilterKeyColumn(0) # 僅針對變數名稱過濾 / filter by variable name
|
|
107
150
|
|
|
151
|
+
# 表格顯示 / table view
|
|
108
152
|
self.table = QTableView()
|
|
109
153
|
self.table.setModel(self.proxy_model)
|
|
110
154
|
self.table.horizontalHeader().setStretchLastSection(True)
|
|
@@ -113,10 +157,13 @@ class VariableInspector(QWidget):
|
|
|
113
157
|
)
|
|
114
158
|
layout.addWidget(self.table, stretch=1)
|
|
115
159
|
|
|
160
|
+
# 綁定搜尋框輸入事件 / bind search box input
|
|
116
161
|
self.search_box.textChanged.connect(
|
|
117
162
|
self.proxy_model.setFilterFixedString
|
|
118
163
|
)
|
|
119
164
|
|
|
165
|
+
# 定時更新變數清單 (每 500ms)
|
|
166
|
+
# Periodically update variable list (every 500ms)
|
|
120
167
|
self.timer = QTimer(self)
|
|
121
168
|
self.timer.timeout.connect(self.model.update_data)
|
|
122
|
-
self.timer.start(500)
|
|
169
|
+
self.timer.start(500)
|
|
@@ -6,19 +6,33 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class SetAIDialog(QWidget):
|
|
9
|
+
"""
|
|
10
|
+
設定 AI 模型的對話框
|
|
11
|
+
Dialog for configuring AI model settings
|
|
12
|
+
"""
|
|
9
13
|
|
|
10
14
|
def __init__(self):
|
|
11
15
|
jeditor_logger.info("Init SetAIDialog")
|
|
12
16
|
super().__init__()
|
|
17
|
+
|
|
18
|
+
# Base URL 輸入欄位 / Base URL input field
|
|
13
19
|
self.base_url_label = QLabel(language_wrapper.language_word_dict.get("base_url_label"))
|
|
14
20
|
self.base_url_input = QLineEdit()
|
|
21
|
+
|
|
22
|
+
# API Key 輸入欄位 / API Key input field
|
|
15
23
|
self.api_key_label = QLabel(language_wrapper.language_word_dict.get("api_key_label"))
|
|
16
24
|
self.api_key_input = QLineEdit()
|
|
25
|
+
|
|
26
|
+
# Chat Model 輸入欄位 / Chat model input field
|
|
17
27
|
self.chat_model_label = QLabel(language_wrapper.language_word_dict.get("ai_model_label"))
|
|
18
28
|
self.chat_model_input = QLineEdit()
|
|
29
|
+
|
|
30
|
+
# 新增 AI 設定按鈕 / Button to add AI configuration
|
|
19
31
|
self.add_ai_info_button = QPushButton()
|
|
20
32
|
self.add_ai_info_button.setText(language_wrapper.language_word_dict.get("add_ai_model_pushbutton"))
|
|
21
33
|
self.add_ai_info_button.clicked.connect(self.update_ai_config)
|
|
34
|
+
|
|
35
|
+
# 使用 GridLayout 排版 / Use GridLayout for layout
|
|
22
36
|
self.grid_layout = QGridLayout()
|
|
23
37
|
self.grid_layout.addWidget(self.base_url_label, 0, 0)
|
|
24
38
|
self.grid_layout.addWidget(self.base_url_input, 0, 1)
|
|
@@ -27,18 +41,31 @@ class SetAIDialog(QWidget):
|
|
|
27
41
|
self.grid_layout.addWidget(self.chat_model_label, 2, 0)
|
|
28
42
|
self.grid_layout.addWidget(self.chat_model_input, 2, 1)
|
|
29
43
|
self.grid_layout.addWidget(self.add_ai_info_button, 3, 1)
|
|
44
|
+
|
|
45
|
+
# 設定視窗標題 / Set window title
|
|
30
46
|
self.setWindowTitle(language_wrapper.language_word_dict.get("add_ai_model_title"))
|
|
31
47
|
self.setLayout(self.grid_layout)
|
|
32
48
|
|
|
33
49
|
def update_ai_config(self):
|
|
50
|
+
"""
|
|
51
|
+
更新 AI 設定,將使用者輸入的 base_url、api_key、chat_model
|
|
52
|
+
儲存到 ai_config.choosable_ai 中
|
|
53
|
+
Update AI configuration with user inputs (base_url, api_key, chat_model)
|
|
54
|
+
"""
|
|
34
55
|
base_url = self.base_url_input.text().strip()
|
|
35
56
|
api_key = self.api_key_input.text().strip()
|
|
36
57
|
chat_model = self.chat_model_input.text().strip()
|
|
58
|
+
|
|
37
59
|
if base_url and chat_model:
|
|
60
|
+
# 更新設定字典 / Update configuration dictionary
|
|
38
61
|
ai_config.choosable_ai.update(
|
|
39
62
|
{"AI_model": {"base_url": base_url, "api_key": api_key, "chat_model": chat_model}}
|
|
40
63
|
)
|
|
41
64
|
else:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
65
|
+
# 若缺少必要欄位,顯示警告訊息框
|
|
66
|
+
# Show warning message box if required fields are missing
|
|
67
|
+
QMessageBox.warning(
|
|
68
|
+
self,
|
|
69
|
+
language_wrapper.language_word_dict.get("set_ai_model_warring_title"),
|
|
70
|
+
language_wrapper.language_word_dict.get("set_ai_model_warring_text")
|
|
71
|
+
)
|
|
@@ -5,31 +5,64 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class CreateFileDialog(QWidget):
|
|
8
|
+
"""
|
|
9
|
+
建立新檔案的對話框
|
|
10
|
+
Dialog for creating a new file
|
|
11
|
+
"""
|
|
8
12
|
|
|
9
13
|
def __init__(self):
|
|
10
14
|
jeditor_logger.info("Init CreateFileDialog")
|
|
11
15
|
super().__init__()
|
|
16
|
+
|
|
17
|
+
# 垂直佈局 (上到下)
|
|
18
|
+
# Vertical layout (top to bottom)
|
|
12
19
|
self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
|
|
20
|
+
|
|
21
|
+
# 檔案名稱輸入框 / File name input field
|
|
13
22
|
self.file_name_input = QLineEdit()
|
|
23
|
+
|
|
24
|
+
# 建立檔案按鈕 / Create file button
|
|
14
25
|
self.create_file_button = QPushButton()
|
|
15
26
|
self.create_file_button.setText(language_wrapper.language_word_dict.get("create_file_dialog_pushbutton"))
|
|
16
27
|
self.create_file_button.clicked.connect(self.create_file)
|
|
28
|
+
|
|
29
|
+
# 水平佈局 (放置按鈕) / Horizontal layout (for button)
|
|
17
30
|
self.box_h_layout = QHBoxLayout()
|
|
18
31
|
self.box_h_layout.addWidget(self.create_file_button)
|
|
32
|
+
|
|
33
|
+
# 將元件加入主佈局 / Add widgets to main layout
|
|
19
34
|
self.box_layout.addWidget(self.file_name_input)
|
|
20
35
|
self.box_layout.addLayout(self.box_h_layout)
|
|
36
|
+
|
|
37
|
+
# 設定視窗標題 / Set window title
|
|
21
38
|
self.setWindowTitle(language_wrapper.language_word_dict.get("create_file_dialog_pushbutton"))
|
|
22
39
|
self.setLayout(self.box_layout)
|
|
23
40
|
|
|
24
41
|
def create_file(self):
|
|
42
|
+
"""
|
|
43
|
+
建立檔案的邏輯:
|
|
44
|
+
1. 檢查輸入是否為空
|
|
45
|
+
2. 若為空,顯示警告訊息
|
|
46
|
+
3. 否則建立新檔案並關閉對話框
|
|
47
|
+
File creation logic:
|
|
48
|
+
1. Check if input is empty
|
|
49
|
+
2. If empty, show warning message
|
|
50
|
+
3. Otherwise, create new file and close dialog
|
|
51
|
+
"""
|
|
25
52
|
jeditor_logger.info("CreateFileDialog create_file")
|
|
26
53
|
file_name = self.file_name_input.text().strip()
|
|
54
|
+
|
|
27
55
|
if file_name == "":
|
|
56
|
+
# 若未輸入檔名,顯示提示訊息
|
|
57
|
+
# Show warning if no file name is entered
|
|
28
58
|
create_file_message_box = QMessageBox(self)
|
|
29
59
|
create_file_message_box.setText(
|
|
30
|
-
language_wrapper.language_word_dict.get("input_file_name_dialog_pushbutton")
|
|
60
|
+
language_wrapper.language_word_dict.get("input_file_name_dialog_pushbutton")
|
|
61
|
+
)
|
|
31
62
|
create_file_message_box.show()
|
|
32
63
|
else:
|
|
64
|
+
# 建立新檔案 (若存在則覆蓋)
|
|
65
|
+
# Create new file (overwrite if exists)
|
|
33
66
|
with open(file_name, "w+") as file:
|
|
34
67
|
file.write("")
|
|
35
|
-
self.close()
|
|
68
|
+
self.close() # 關閉對話框 / close dialog
|
|
@@ -12,6 +12,8 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
|
|
|
12
12
|
from je_editor.utils.venv_check.check_venv import check_and_choose_venv
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
+
# 僅在型別檢查時匯入,避免循環依賴
|
|
16
|
+
# Only imported during type checking to avoid circular imports
|
|
15
17
|
from je_editor.pyside_ui.main_ui.main_editor import EditorMain
|
|
16
18
|
|
|
17
19
|
from PySide6.QtWidgets import QFileDialog
|
|
@@ -22,14 +24,16 @@ from je_editor.utils.file.open.open_file import read_file
|
|
|
22
24
|
|
|
23
25
|
def choose_file_get_open_file_path(parent_qt_instance: EditorMain) -> None:
|
|
24
26
|
"""
|
|
27
|
+
開啟檔案並將內容載入編輯器
|
|
25
28
|
Open file and set code edit content
|
|
26
|
-
:param parent_qt_instance: Pyside parent
|
|
29
|
+
:param parent_qt_instance: Pyside 主視窗 / Pyside parent
|
|
27
30
|
:return: None
|
|
28
31
|
"""
|
|
29
32
|
jeditor_logger.info("open_file_dialog.py choose_file_get_open_file_path"
|
|
30
33
|
f" parent_qt_instance: {parent_qt_instance}")
|
|
31
34
|
widget = parent_qt_instance.tab_widget.currentWidget()
|
|
32
35
|
if isinstance(widget, EditorWidget):
|
|
36
|
+
# 開啟檔案選擇對話框 / Open file dialog
|
|
33
37
|
file_path = QFileDialog().getOpenFileName(
|
|
34
38
|
parent=parent_qt_instance,
|
|
35
39
|
dir=str(Path.cwd()),
|
|
@@ -37,27 +41,40 @@ def choose_file_get_open_file_path(parent_qt_instance: EditorMain) -> None:
|
|
|
37
41
|
HTML file (*.html);;
|
|
38
42
|
File (*.*)"""
|
|
39
43
|
)[0]
|
|
44
|
+
|
|
40
45
|
if file_path is not None and file_path != "":
|
|
46
|
+
# 檢查檔案是否已經開啟 / Check if file already opened
|
|
41
47
|
if file_is_open_manager_dict.get(str(Path(file_path)), None) is not None:
|
|
42
48
|
widget.tab_manager.setCurrentWidget(
|
|
43
49
|
widget.tab_manager.findChild(EditorWidget, str(Path(file_path).name)))
|
|
44
50
|
return
|
|
45
51
|
else:
|
|
52
|
+
# 記錄已開啟檔案 / Register opened file
|
|
46
53
|
file_is_open_manager_dict.update({file_path: str(Path(file_path).name)})
|
|
54
|
+
|
|
55
|
+
# 設定目前檔案路徑 / Set current file path
|
|
47
56
|
widget.current_file = file_path
|
|
57
|
+
# 讀取檔案內容 / Read file content
|
|
48
58
|
file_content = read_file(file_path)[1]
|
|
49
|
-
widget.code_edit.setPlainText(
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
widget.code_edit.setPlainText(file_content)
|
|
60
|
+
|
|
61
|
+
# 啟動自動儲存執行緒 / Start auto-save thread
|
|
52
62
|
if widget.current_file is not None and widget.code_save_thread is None:
|
|
53
63
|
init_new_auto_save_thread(widget.current_file, widget)
|
|
54
64
|
else:
|
|
55
65
|
widget.code_save_thread.file = widget.current_file
|
|
66
|
+
|
|
67
|
+
# 更新使用者設定中的最後開啟檔案 / Update last opened file in user settings
|
|
56
68
|
user_setting_dict.update({"last_file": str(widget.current_file)})
|
|
69
|
+
# 更新分頁標題 / Rename tab title
|
|
57
70
|
widget.rename_self_tab()
|
|
58
71
|
|
|
59
72
|
|
|
60
73
|
def choose_dir_get_dir_path(parent_qt_instance: EditorMain) -> None:
|
|
74
|
+
"""
|
|
75
|
+
選擇資料夾並更新工作目錄與專案樹
|
|
76
|
+
Choose directory and update working dir and project tree
|
|
77
|
+
"""
|
|
61
78
|
jeditor_logger.info("open_file_dialog.py choose_dir_get_dir_path"
|
|
62
79
|
f" parent_qt_instance: {parent_qt_instance}")
|
|
63
80
|
dir_path = QFileDialog().getExistingDirectory(parent=parent_qt_instance, )
|
|
@@ -65,19 +82,30 @@ def choose_dir_get_dir_path(parent_qt_instance: EditorMain) -> None:
|
|
|
65
82
|
check_path = Path(dir_path)
|
|
66
83
|
else:
|
|
67
84
|
return
|
|
85
|
+
|
|
68
86
|
if check_path.exists() and check_path.is_dir():
|
|
87
|
+
# 更新工作目錄 / Update working directory
|
|
69
88
|
parent_qt_instance.working_dir = dir_path
|
|
70
89
|
os.chdir(dir_path)
|
|
90
|
+
|
|
91
|
+
# 更新所有編輯器的專案樹與環境檢查 / Update project tree and check env for all editors
|
|
71
92
|
for code_editor in range(parent_qt_instance.tab_widget.count()):
|
|
72
93
|
widget = parent_qt_instance.tab_widget.widget(code_editor)
|
|
73
94
|
if isinstance(widget, EditorWidget):
|
|
74
95
|
widget.project_treeview.setRootIndex(widget.project_treeview_model.index(dir_path))
|
|
75
96
|
widget.code_edit.check_env()
|
|
97
|
+
|
|
98
|
+
# 設定虛擬環境路徑 / Set virtual environment path
|
|
76
99
|
if sys.platform in ["win32", "cygwin", "msys"]:
|
|
77
100
|
venv_path = Path(os.getcwd() + "/venv/Scripts")
|
|
78
101
|
else:
|
|
79
102
|
venv_path = Path(os.getcwd() + "/venv/bin")
|
|
103
|
+
|
|
80
104
|
parent_qt_instance.python_compiler = check_and_choose_venv(venv_path)
|
|
105
|
+
|
|
106
|
+
# 重新讀取使用者設定並套用啟動設定 / Reload user settings and apply startup settings
|
|
81
107
|
read_user_setting()
|
|
82
108
|
parent_qt_instance.startup_setting()
|
|
83
|
-
|
|
109
|
+
|
|
110
|
+
# 重設語言設定 / Reset language
|
|
111
|
+
language_wrapper.reset_language(user_setting_dict.get("language", "English"))
|
|
@@ -8,7 +8,10 @@ from je_editor.pyside_ui.code.auto_save.auto_save_manager import file_is_open_ma
|
|
|
8
8
|
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
+
# 僅在型別檢查時匯入,避免循環依賴
|
|
12
|
+
# Only imported during type checking to avoid circular imports
|
|
11
13
|
from je_editor.pyside_ui.main_ui.main_editor import EditorMain
|
|
14
|
+
|
|
12
15
|
from PySide6.QtWidgets import QFileDialog
|
|
13
16
|
|
|
14
17
|
from je_editor.pyside_ui.main_ui.editor.editor_widget import EditorWidget
|
|
@@ -17,13 +20,20 @@ from je_editor.utils.file.save.save_file import write_file
|
|
|
17
20
|
|
|
18
21
|
def choose_file_get_save_file_path(parent_qt_instance: EditorMain) -> bool:
|
|
19
22
|
"""
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
開啟「另存新檔」對話框,將編輯器內容儲存到檔案
|
|
24
|
+
Open "Save As" dialog and save editor content to file
|
|
25
|
+
:param parent_qt_instance: Pyside 主視窗 / Pyside parent
|
|
26
|
+
:return: 是否成功儲存檔案 / whether file was saved successfully
|
|
22
27
|
"""
|
|
23
28
|
jeditor_logger.info("save_file_dialog.py choose_file_get_save_file_path"
|
|
24
29
|
f" parent_qt_instance: {parent_qt_instance}")
|
|
30
|
+
|
|
31
|
+
# 取得目前分頁的編輯器元件
|
|
32
|
+
# Get current tab's editor widget
|
|
25
33
|
widget = parent_qt_instance.tab_widget.currentWidget()
|
|
34
|
+
|
|
26
35
|
if isinstance(widget, EditorWidget):
|
|
36
|
+
# 開啟檔案儲存對話框 / Open file save dialog
|
|
27
37
|
file_path = QFileDialog().getSaveFileName(
|
|
28
38
|
parent=parent_qt_instance,
|
|
29
39
|
dir=os.getcwd(),
|
|
@@ -31,15 +41,27 @@ def choose_file_get_save_file_path(parent_qt_instance: EditorMain) -> bool:
|
|
|
31
41
|
HTML file (*.html);;
|
|
32
42
|
File (*.*)"""
|
|
33
43
|
)[0]
|
|
44
|
+
|
|
45
|
+
# 確認使用者有選擇檔案路徑 / Ensure user selected a file path
|
|
34
46
|
if file_path is not None and file_path != "":
|
|
47
|
+
# 更新目前檔案路徑 / Update current file path
|
|
35
48
|
widget.current_file = file_path
|
|
49
|
+
|
|
50
|
+
# 將編輯器內容寫入檔案 / Write editor content to file
|
|
36
51
|
write_file(file_path, widget.code_edit.toPlainText())
|
|
52
|
+
|
|
53
|
+
# 更新已開啟檔案管理字典 / Update opened file manager dictionary
|
|
37
54
|
path = Path(file_path)
|
|
38
55
|
file_is_open_manager_dict.update({str(path): str(path.name)})
|
|
56
|
+
|
|
57
|
+
# 若有自動儲存執行緒,更新其監控的檔案與編輯器
|
|
58
|
+
# If auto-save thread exists, update its file and editor reference
|
|
39
59
|
if widget.code_save_thread is not None:
|
|
40
60
|
widget.code_save_thread.file = file_path
|
|
41
61
|
widget.code_save_thread.editor = widget.code_edit
|
|
62
|
+
|
|
63
|
+
# 更新分頁標題 / Update tab title
|
|
42
64
|
widget.rename_self_tab()
|
|
43
65
|
return True
|
|
44
66
|
return False
|
|
45
|
-
return False
|
|
67
|
+
return False
|
|
@@ -5,20 +5,45 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class SearchResultBox(QWidget):
|
|
8
|
+
"""
|
|
9
|
+
搜尋結果對話框
|
|
10
|
+
Search result dialog box
|
|
11
|
+
"""
|
|
8
12
|
|
|
9
13
|
def __init__(self):
|
|
10
14
|
jeditor_logger.info("Init SearchResultBox")
|
|
11
15
|
super().__init__()
|
|
16
|
+
|
|
17
|
+
# 垂直佈局 (上到下)
|
|
18
|
+
# Vertical layout (top to bottom)
|
|
12
19
|
self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
|
|
20
|
+
|
|
21
|
+
# 搜尋輸入框 / Search input field
|
|
13
22
|
self.search_input = QLineEdit()
|
|
23
|
+
|
|
24
|
+
# 下一個搜尋按鈕 / Next search button
|
|
14
25
|
self.search_next_button = QPushButton()
|
|
15
26
|
self.search_next_button.setText(language_wrapper.language_word_dict.get("search_next_dialog_pushbutton"))
|
|
27
|
+
|
|
28
|
+
# 上一個搜尋按鈕 / Previous search button
|
|
16
29
|
self.search_back_button = QPushButton()
|
|
17
30
|
self.search_back_button.setText(language_wrapper.language_word_dict.get("search_back_dialog_pushbutton"))
|
|
31
|
+
|
|
32
|
+
# 水平佈局 (放置前後搜尋按鈕)
|
|
33
|
+
# Horizontal layout (for back/next buttons)
|
|
18
34
|
self.box_h_layout = QHBoxLayout()
|
|
19
35
|
self.box_h_layout.addWidget(self.search_back_button)
|
|
20
36
|
self.box_h_layout.addWidget(self.search_next_button)
|
|
37
|
+
|
|
38
|
+
# 將元件加入主佈局
|
|
39
|
+
# Add widgets to main layout
|
|
21
40
|
self.box_layout.addWidget(self.search_input)
|
|
22
41
|
self.box_layout.addLayout(self.box_h_layout)
|
|
42
|
+
|
|
43
|
+
# 設定視窗標題
|
|
44
|
+
# Set window title
|
|
23
45
|
self.setWindowTitle("Search Result")
|
|
24
|
-
|
|
46
|
+
|
|
47
|
+
# 套用主佈局
|
|
48
|
+
# Apply main layout
|
|
49
|
+
self.setLayout(self.box_layout)
|
|
@@ -5,20 +5,45 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class SearchBox(QWidget):
|
|
8
|
+
"""
|
|
9
|
+
搜尋框元件
|
|
10
|
+
Search box widget
|
|
11
|
+
"""
|
|
8
12
|
|
|
9
13
|
def __init__(self):
|
|
10
14
|
jeditor_logger.info("Init SearchBox")
|
|
11
15
|
super().__init__()
|
|
16
|
+
|
|
17
|
+
# 垂直佈局 (上到下)
|
|
18
|
+
# Vertical layout (top to bottom)
|
|
12
19
|
self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
|
|
20
|
+
|
|
21
|
+
# 搜尋輸入框 / Search input field
|
|
13
22
|
self.search_input = QLineEdit()
|
|
23
|
+
|
|
24
|
+
# 下一個搜尋按鈕 / Next search button
|
|
14
25
|
self.search_next_button = QPushButton()
|
|
15
26
|
self.search_next_button.setText(language_wrapper.language_word_dict.get("search_next_dialog_pushbutton"))
|
|
27
|
+
|
|
28
|
+
# 上一個搜尋按鈕 / Previous search button
|
|
16
29
|
self.search_back_button = QPushButton()
|
|
17
30
|
self.search_back_button.setText(language_wrapper.language_word_dict.get("search_back_dialog_pushbutton"))
|
|
31
|
+
|
|
32
|
+
# 水平佈局 (放置前後搜尋按鈕)
|
|
33
|
+
# Horizontal layout (for back/next buttons)
|
|
18
34
|
self.box_h_layout = QHBoxLayout()
|
|
19
35
|
self.box_h_layout.addWidget(self.search_back_button)
|
|
20
36
|
self.box_h_layout.addWidget(self.search_next_button)
|
|
37
|
+
|
|
38
|
+
# 將元件加入主佈局
|
|
39
|
+
# Add widgets to main layout
|
|
21
40
|
self.box_layout.addWidget(self.search_input)
|
|
22
41
|
self.box_layout.addLayout(self.box_h_layout)
|
|
42
|
+
|
|
43
|
+
# 設定視窗標題 (支援多語系)
|
|
44
|
+
# Set window title (multi-language support)
|
|
23
45
|
self.setWindowTitle(language_wrapper.language_word_dict.get("search_box_dialog_title"))
|
|
24
|
-
|
|
46
|
+
|
|
47
|
+
# 套用主佈局
|
|
48
|
+
# Apply main layout
|
|
49
|
+
self.setLayout(self.box_layout)
|
|
@@ -34,10 +34,10 @@ class DiffViewerWidget(QWidget):
|
|
|
34
34
|
|
|
35
35
|
# === View → Theme switching (exclusive) / 主題切換(單選模式) ===
|
|
36
36
|
theme_group = QActionGroup(self)
|
|
37
|
-
theme_group.setExclusive(True) # 確保只能選擇一個主題
|
|
37
|
+
theme_group.setExclusive(True) # 確保只能選擇一個主題 / ensure only one theme can be selected
|
|
38
38
|
|
|
39
|
-
dark_action = QAction("Dark Mode", self, checkable=True)
|
|
40
|
-
light_action = QAction("Light Mode", self, checkable=True)
|
|
39
|
+
dark_action = QAction("Dark Mode", self, checkable=True) # 深色模式 / dark mode
|
|
40
|
+
light_action = QAction("Light Mode", self, checkable=True) # 淺色模式 / light mode
|
|
41
41
|
|
|
42
42
|
theme_group.addAction(dark_action)
|
|
43
43
|
theme_group.addAction(light_action)
|
|
@@ -55,8 +55,8 @@ class DiffViewerWidget(QWidget):
|
|
|
55
55
|
|
|
56
56
|
# === Layout / 版面配置 ===
|
|
57
57
|
layout = QVBoxLayout(self)
|
|
58
|
-
layout.setMenuBar(self.menubar)
|
|
59
|
-
layout.addWidget(self.viewer)
|
|
58
|
+
layout.setMenuBar(self.menubar) # 把選單列放在上方 / put menu bar at the top
|
|
59
|
+
layout.addWidget(self.viewer) # 把差異檢視器放在主要區域 / add diff viewer in main area
|
|
60
60
|
|
|
61
61
|
def open_repo(self):
|
|
62
62
|
"""
|
|
@@ -67,16 +67,16 @@ class DiffViewerWidget(QWidget):
|
|
|
67
67
|
if not path:
|
|
68
68
|
return
|
|
69
69
|
try:
|
|
70
|
-
repo = Repo(path)
|
|
71
|
-
diff_text = repo.git.diff()
|
|
70
|
+
repo = Repo(path) # 嘗試載入 Git 專案 / try to load Git repo
|
|
71
|
+
diff_text = repo.git.diff() # 取得差異文字 / get diff text
|
|
72
72
|
if not diff_text.strip():
|
|
73
|
-
# 如果沒有差異,顯示提示訊息
|
|
73
|
+
# 如果沒有差異,顯示提示訊息 / show info if no changes
|
|
74
74
|
QMessageBox.information(self, "Info", "No changes in repo.")
|
|
75
75
|
else:
|
|
76
|
-
# 如果有差異,顯示在 viewer 中
|
|
76
|
+
# 如果有差異,顯示在 viewer 中 / show diff in viewer
|
|
77
77
|
self.viewer.set_diff_text(diff_text)
|
|
78
78
|
except Exception as e:
|
|
79
|
-
# 如果開啟失敗,顯示錯誤訊息
|
|
79
|
+
# 如果開啟失敗,顯示錯誤訊息 / show error if failed
|
|
80
80
|
QMessageBox.critical(self, "Error", f"Failed to open repo:\n{e}")
|
|
81
81
|
|
|
82
82
|
def set_theme(self, mode: str):
|
|
@@ -87,4 +87,4 @@ class DiffViewerWidget(QWidget):
|
|
|
87
87
|
if mode == "dark":
|
|
88
88
|
self.viewer.set_dark_theme()
|
|
89
89
|
else:
|
|
90
|
-
self.viewer.set_light_theme()
|
|
90
|
+
self.viewer.set_light_theme()
|