chartflow 0.1__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.
- chartflow/__init__.py +28 -0
- chartflow/_verify_importexport.py +169 -0
- chartflow/edit/__init__.py +119 -0
- chartflow/edit/code_editor.py +1007 -0
- chartflow/edit/code_folder.py +523 -0
- chartflow/edit/completer.py +2652 -0
- chartflow/edit/context_analyzer.py +521 -0
- chartflow/edit/demo.py +469 -0
- chartflow/edit/line_number_area.py +548 -0
- chartflow/edit/minimap.py +581 -0
- chartflow/edit/python_editor.py +509 -0
- chartflow/edit/syntax_highlighter.py +291 -0
- chartflow/edit/test_editor.py +190 -0
- chartflow/flow/__init__.py +31 -0
- chartflow/flow/_demo.py +98 -0
- chartflow/flow/chart.py +1101 -0
- chartflow/flow/editor.py +375 -0
- chartflow/flow/flow.py +6 -0
- chartflow/flow/qchart.py +1250 -0
- chartflow/flow/test/__init__.py +0 -0
- chartflow/flow/test/test_chart.py +537 -0
- chartflow/flow/test_decorator_sync.py +102 -0
- chartflow/fsm/__init__.py +84 -0
- chartflow/fsm/decorators.py +395 -0
- chartflow/fsm/demo.py +381 -0
- chartflow/fsm/demo_pyqt6.py +881 -0
- chartflow/fsm/demo_tree.py +46 -0
- chartflow/fsm/logic.py +447 -0
- chartflow/fsm/meta.py +569 -0
- chartflow/fsm/scheduler.py +427 -0
- chartflow/fsm/stage.py +2079 -0
- chartflow/fsm/stdio.py +66 -0
- chartflow/fsm/test/__init__.py +7 -0
- chartflow/fsm/test/test_decorators.py +210 -0
- chartflow/fsm/test/test_events.py +202 -0
- chartflow/fsm/test/test_integration.py +596 -0
- chartflow/fsm/test/test_logic.py +371 -0
- chartflow/fsm/test/test_meta.py +192 -0
- chartflow/fsm/test/test_runtime_param.py +336 -0
- chartflow/fsm/test/test_scheduler.py +280 -0
- chartflow/fsm/test/test_stage.py +314 -0
- chartflow/fsm/test_behavior_events.py +263 -0
- chartflow/fsm/test_nested_parallel.py +80 -0
- chartflow/fsm/test_stage_spec.py +448 -0
- chartflow/fsm/utils.py +34 -0
- chartflow/node/__init__.py +66 -0
- chartflow/node/_node_base.py +727 -0
- chartflow/node/_node_interaction.py +488 -0
- chartflow/node/_node_interface.py +426 -0
- chartflow/node/_node_markers.py +648 -0
- chartflow/node/_node_ports.py +536 -0
- chartflow/node/_port_interface.py +111 -0
- chartflow/node/arrow_item.py +287 -0
- chartflow/node/color_utils.py +113 -0
- chartflow/node/connection_item.py +939 -0
- chartflow/node/demo.py +258 -0
- chartflow/node/demo_arrow_drag.py +46 -0
- chartflow/node/demo_decorator.py +63 -0
- chartflow/node/demo_ports.py +69 -0
- chartflow/node/enums.py +35 -0
- chartflow/node/example.py +84 -0
- chartflow/node/layout_utils.py +307 -0
- chartflow/node/main_window.py +196 -0
- chartflow/node/menu_bar.py +240 -0
- chartflow/node/node_canvas.py +1730 -0
- chartflow/node/node_item.py +754 -0
- chartflow/node/popmenu.py +472 -0
- chartflow/node/port_item.py +591 -0
- chartflow/node/qnode_editor.py +73 -0
- chartflow/node/selection_rect.py +53 -0
- chartflow/node/style_panel.py +615 -0
- chartflow/node/styles.py +63 -0
- chartflow/node/test_qnode.py +2336 -0
- chartflow/showcase.py +528 -0
- chartflow/theme.py +508 -0
- chartflow-0.1.dist-info/METADATA +23 -0
- chartflow-0.1.dist-info/RECORD +79 -0
- chartflow-0.1.dist-info/WHEEL +5 -0
- chartflow-0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python编辑器主Widget
|
|
3
|
+
整合所有组件,提供完整的Python代码编辑功能
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from PyQt6.QtWidgets import (
|
|
7
|
+
QWidget, QVBoxLayout, QHBoxLayout, QSplitter,
|
|
8
|
+
QFrame, QLabel, QStatusBar, QToolBar, QPushButton,
|
|
9
|
+
QApplication
|
|
10
|
+
)
|
|
11
|
+
from PyQt6.QtCore import Qt, pyqtSignal, QTimer, QSize
|
|
12
|
+
from PyQt6.QtGui import QFont, QIcon, QAction, QKeySequence
|
|
13
|
+
from typing import Optional, Callable
|
|
14
|
+
|
|
15
|
+
from .code_editor import CodeEditor
|
|
16
|
+
from .minimap import MinimapPanel, CodeOverview
|
|
17
|
+
from .context_analyzer import ContextAnalyzer
|
|
18
|
+
from ..theme import PALETTE
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PythonEditor(QWidget):
|
|
22
|
+
"""
|
|
23
|
+
Python编辑器主控件
|
|
24
|
+
|
|
25
|
+
功能:
|
|
26
|
+
- Python语法高亮
|
|
27
|
+
- 智能代码补全(类似PyCharm)
|
|
28
|
+
- 上下文分析(支持隐藏代码CA和CB)
|
|
29
|
+
- 代码折叠(Ctrl++ / Ctrl+-)
|
|
30
|
+
- 行号显示
|
|
31
|
+
- 缩略图滑块
|
|
32
|
+
|
|
33
|
+
使用示例:
|
|
34
|
+
editor = PythonEditor()
|
|
35
|
+
editor.set_context_code(
|
|
36
|
+
code_a="class A(CL):\\n NAME = ''\\n ...",
|
|
37
|
+
code_b="def myfn(self, c:OuterClassRef, k: KnowRef, *refs):\\n v = 0",
|
|
38
|
+
prefix=" " # 4空格缩进
|
|
39
|
+
)
|
|
40
|
+
editor.set_code("# 在这里编辑代码\\nself.do_something()")
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# 信号
|
|
44
|
+
text_modified = pyqtSignal() # 文本修改
|
|
45
|
+
cursor_position_changed = pyqtSignal(int, int) # 光标位置改变 (line, column)
|
|
46
|
+
completions_ready = pyqtSignal(list) # 补全建议就绪
|
|
47
|
+
|
|
48
|
+
def __init__(self, parent=None):
|
|
49
|
+
super().__init__(parent)
|
|
50
|
+
|
|
51
|
+
# 隐藏代码上下文
|
|
52
|
+
self._context_code_a: str = "" # 类定义代码 (CA)
|
|
53
|
+
self._context_code_b: str = "" # 函数定义代码 (CB)
|
|
54
|
+
self._context_prefix: str = "" # 前缀缩进
|
|
55
|
+
|
|
56
|
+
# 设置UI
|
|
57
|
+
self._setup_ui()
|
|
58
|
+
self._setup_toolbar()
|
|
59
|
+
self._setup_statusbar()
|
|
60
|
+
self._setup_connections()
|
|
61
|
+
|
|
62
|
+
def _setup_ui(self):
|
|
63
|
+
"""设置UI"""
|
|
64
|
+
# 主布局
|
|
65
|
+
self._main_layout = QVBoxLayout(self)
|
|
66
|
+
self._main_layout.setContentsMargins(0, 0, 0, 0)
|
|
67
|
+
self._main_layout.setSpacing(0)
|
|
68
|
+
|
|
69
|
+
# 创建分割器
|
|
70
|
+
self._splitter = QSplitter(Qt.Orientation.Horizontal)
|
|
71
|
+
self._splitter.setHandleWidth(2)
|
|
72
|
+
|
|
73
|
+
# 创建代码编辑器
|
|
74
|
+
self._editor = CodeEditor()
|
|
75
|
+
self._editor.text_modified.connect(self._on_text_modified)
|
|
76
|
+
self._editor.cursor_position_changed.connect(self._on_cursor_position_changed)
|
|
77
|
+
|
|
78
|
+
# 创建缩略图面板
|
|
79
|
+
self._minimap = MinimapPanel()
|
|
80
|
+
self._minimap.navigate_to_line.connect(self._on_navigate_to_line)
|
|
81
|
+
self._minimap.viewport_scroll.connect(self._on_viewport_scroll)
|
|
82
|
+
|
|
83
|
+
# 添加到分割器
|
|
84
|
+
self._splitter.addWidget(self._editor)
|
|
85
|
+
self._splitter.addWidget(self._minimap)
|
|
86
|
+
|
|
87
|
+
# 设置分割比例
|
|
88
|
+
self._splitter.setSizes([600, 120])
|
|
89
|
+
self._splitter.setStretchFactor(0, 1)
|
|
90
|
+
self._splitter.setStretchFactor(1, 0)
|
|
91
|
+
|
|
92
|
+
# 添加到主布局
|
|
93
|
+
self._main_layout.addWidget(self._splitter)
|
|
94
|
+
|
|
95
|
+
# 设置样式:暗色外壳 + 框住的浅色编辑面板(语法高亮为浅色配色)
|
|
96
|
+
self.setStyleSheet(f"""
|
|
97
|
+
PythonEditor {{
|
|
98
|
+
background-color: {PALETTE.window};
|
|
99
|
+
}}
|
|
100
|
+
QSplitter::handle {{
|
|
101
|
+
background-color: {PALETTE.border};
|
|
102
|
+
}}
|
|
103
|
+
QSplitter::handle:hover {{
|
|
104
|
+
background-color: {PALETTE.accent};
|
|
105
|
+
}}
|
|
106
|
+
""")
|
|
107
|
+
|
|
108
|
+
def _setup_toolbar(self):
|
|
109
|
+
"""设置工具栏"""
|
|
110
|
+
self._toolbar = QToolBar()
|
|
111
|
+
self._toolbar.setMovable(False)
|
|
112
|
+
self._toolbar.setFixedHeight(32)
|
|
113
|
+
|
|
114
|
+
# 折叠按钮
|
|
115
|
+
self._btn_fold_all = QPushButton("折叠全部")
|
|
116
|
+
self._btn_fold_all.setFixedHeight(24)
|
|
117
|
+
self._btn_fold_all.setToolTip("Ctrl+Shift+- 折叠所有代码块")
|
|
118
|
+
self._btn_fold_all.clicked.connect(self._editor.fold_all)
|
|
119
|
+
|
|
120
|
+
self._btn_unfold_all = QPushButton("展开全部")
|
|
121
|
+
self._btn_unfold_all.setFixedHeight(24)
|
|
122
|
+
self._btn_unfold_all.setToolTip("Ctrl+Shift++ 展开所有代码块")
|
|
123
|
+
self._btn_unfold_all.clicked.connect(self._editor.unfold_all)
|
|
124
|
+
|
|
125
|
+
# 添加按钮到工具栏
|
|
126
|
+
self._toolbar.addWidget(self._btn_fold_all)
|
|
127
|
+
self._toolbar.addWidget(self._btn_unfold_all)
|
|
128
|
+
|
|
129
|
+
# 添加分隔线
|
|
130
|
+
self._toolbar.addSeparator()
|
|
131
|
+
|
|
132
|
+
# 添加标签
|
|
133
|
+
self._lbl_position = QLabel("行 1, 列 1")
|
|
134
|
+
self._lbl_position.setStyleSheet(f"color: {PALETTE.text_dim}; padding: 0 10px;")
|
|
135
|
+
self._toolbar.addWidget(self._lbl_position)
|
|
136
|
+
|
|
137
|
+
# 添加到主布局
|
|
138
|
+
self._main_layout.insertWidget(0, self._toolbar)
|
|
139
|
+
|
|
140
|
+
# 设置工具栏样式(暗色外壳,按钮随主题)
|
|
141
|
+
self._toolbar.setStyleSheet(f"""
|
|
142
|
+
QToolBar {{
|
|
143
|
+
background-color: {PALETTE.panel};
|
|
144
|
+
border: none;
|
|
145
|
+
border-bottom: 1px solid {PALETTE.border};
|
|
146
|
+
spacing: 5px;
|
|
147
|
+
padding: 3px 6px;
|
|
148
|
+
}}
|
|
149
|
+
QToolBar QLabel {{ color: {PALETTE.text_dim}; }}
|
|
150
|
+
QPushButton {{
|
|
151
|
+
background-color: {PALETTE.surface};
|
|
152
|
+
color: {PALETTE.text};
|
|
153
|
+
border: 1px solid {PALETTE.border};
|
|
154
|
+
border-radius: 4px;
|
|
155
|
+
padding: 4px 12px;
|
|
156
|
+
font-size: 12px;
|
|
157
|
+
}}
|
|
158
|
+
QPushButton:hover {{
|
|
159
|
+
background-color: {PALETTE.accent_soft};
|
|
160
|
+
border-color: {PALETTE.accent};
|
|
161
|
+
color: {PALETTE.accent_hover};
|
|
162
|
+
}}
|
|
163
|
+
QPushButton:pressed {{
|
|
164
|
+
background-color: {PALETTE.accent_pressed};
|
|
165
|
+
color: #ffffff;
|
|
166
|
+
}}
|
|
167
|
+
""")
|
|
168
|
+
|
|
169
|
+
def _setup_statusbar(self):
|
|
170
|
+
"""设置状态栏"""
|
|
171
|
+
self._statusbar = QStatusBar()
|
|
172
|
+
self._statusbar.setFixedHeight(22)
|
|
173
|
+
self._statusbar.setStyleSheet(f"""
|
|
174
|
+
QStatusBar {{
|
|
175
|
+
background-color: {PALETTE.panel};
|
|
176
|
+
border-top: 1px solid {PALETTE.border};
|
|
177
|
+
font-size: 11px;
|
|
178
|
+
color: {PALETTE.text_dim};
|
|
179
|
+
}}
|
|
180
|
+
""")
|
|
181
|
+
|
|
182
|
+
# 添加状态标签
|
|
183
|
+
self._lbl_status = QLabel("就绪")
|
|
184
|
+
self._lbl_status.setStyleSheet(f"color: {PALETTE.text_dim}; padding: 0 5px;")
|
|
185
|
+
self._statusbar.addWidget(self._lbl_status)
|
|
186
|
+
|
|
187
|
+
# 添加到主布局
|
|
188
|
+
self._main_layout.addWidget(self._statusbar)
|
|
189
|
+
|
|
190
|
+
def _setup_connections(self):
|
|
191
|
+
"""设置信号连接"""
|
|
192
|
+
# 定时更新缩略图
|
|
193
|
+
self._update_timer = QTimer()
|
|
194
|
+
self._update_timer.timeout.connect(self._update_minimap)
|
|
195
|
+
self._update_timer.start(500) # 每500ms更新一次
|
|
196
|
+
|
|
197
|
+
def set_context_code(self, code_a: str = "", code_b: str = "", prefix: str = ""):
|
|
198
|
+
"""
|
|
199
|
+
设置上下文代码
|
|
200
|
+
|
|
201
|
+
这是本编辑器的核心功能。可以设置隐藏的上下文代码,这些代码
|
|
202
|
+
不会显示在编辑器中,但会参与语法分析和代码补全。
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
code_a: 类定义代码 (CA),例如:
|
|
206
|
+
class A(CL):
|
|
207
|
+
NAME = ''
|
|
208
|
+
...
|
|
209
|
+
code_b: 函数定义代码 (CB),例如:
|
|
210
|
+
def myfn(self, c:OuterClassRef, k: KnowRef, *refs):
|
|
211
|
+
v = 0
|
|
212
|
+
prefix: 前缀缩进,用于编辑器中的代码
|
|
213
|
+
"""
|
|
214
|
+
print(f"[PythonEditor.set_context_code] Setting context code")
|
|
215
|
+
print(f"[PythonEditor.set_context_code] code_a length: {len(code_a)}")
|
|
216
|
+
print(f"[PythonEditor.set_context_code] code_b length: {len(code_b)}")
|
|
217
|
+
print(f"[PythonEditor.set_context_code] prefix: {repr(prefix)}")
|
|
218
|
+
|
|
219
|
+
self._context_code_a = code_a
|
|
220
|
+
self._context_code_b = code_b
|
|
221
|
+
self._context_prefix = prefix
|
|
222
|
+
|
|
223
|
+
# 更新编辑器
|
|
224
|
+
self._editor.set_context_code(code_a, code_b, prefix)
|
|
225
|
+
|
|
226
|
+
# 更新缩略图
|
|
227
|
+
self._update_minimap()
|
|
228
|
+
|
|
229
|
+
def get_context_code(self) -> tuple:
|
|
230
|
+
"""获取上下文代码"""
|
|
231
|
+
return (self._context_code_a, self._context_code_b, self._context_prefix)
|
|
232
|
+
|
|
233
|
+
def set_code(self, code: str):
|
|
234
|
+
"""设置编辑器中的代码"""
|
|
235
|
+
self._editor.set_text(code)
|
|
236
|
+
self._update_minimap()
|
|
237
|
+
|
|
238
|
+
def get_code(self) -> str:
|
|
239
|
+
"""获取编辑器中的代码"""
|
|
240
|
+
return self._editor.get_text()
|
|
241
|
+
|
|
242
|
+
def append_code(self, code: str):
|
|
243
|
+
"""追加代码"""
|
|
244
|
+
current = self.get_code()
|
|
245
|
+
if current and not current.endswith('\n'):
|
|
246
|
+
code = '\n' + code
|
|
247
|
+
self._editor.appendPlainText(code)
|
|
248
|
+
|
|
249
|
+
def clear(self):
|
|
250
|
+
"""清空编辑器"""
|
|
251
|
+
self._editor.clear()
|
|
252
|
+
self._update_minimap()
|
|
253
|
+
|
|
254
|
+
def _update_minimap(self):
|
|
255
|
+
"""更新缩略图"""
|
|
256
|
+
# 构建完整代码用于缩略图显示
|
|
257
|
+
full_code = self._build_full_code()
|
|
258
|
+
self._minimap.set_code(full_code)
|
|
259
|
+
|
|
260
|
+
# 更新视口
|
|
261
|
+
first, last = self._editor.get_visible_lines()
|
|
262
|
+
total = self._editor.document().blockCount()
|
|
263
|
+
self._minimap.set_viewport(first, last, total)
|
|
264
|
+
|
|
265
|
+
def _build_full_code(self) -> str:
|
|
266
|
+
"""构建完整代码(CA + CB + CC,替换 <__begin__> 占位符)"""
|
|
267
|
+
# 获取可见代码(CC)
|
|
268
|
+
visible_code = self.get_code()
|
|
269
|
+
|
|
270
|
+
# 添加前缀缩进(8空格,用于类方法体内)
|
|
271
|
+
if self._context_prefix:
|
|
272
|
+
cc_lines = []
|
|
273
|
+
for line in visible_code.split('\n'):
|
|
274
|
+
cc_lines.append(self._context_prefix + line)
|
|
275
|
+
cc_code = '\n'.join(cc_lines)
|
|
276
|
+
else:
|
|
277
|
+
cc_code = visible_code
|
|
278
|
+
|
|
279
|
+
# 如果有 CA,直接替换其中的 <__begin__>
|
|
280
|
+
if self._context_code_a:
|
|
281
|
+
full_code = self._context_code_a.replace('<__begin__>', cc_code)
|
|
282
|
+
elif self._context_code_b:
|
|
283
|
+
# 兼容旧格式:使用 CB
|
|
284
|
+
full_code = self._context_code_b.replace('<__begin__>', cc_code)
|
|
285
|
+
else:
|
|
286
|
+
full_code = cc_code
|
|
287
|
+
|
|
288
|
+
return full_code
|
|
289
|
+
|
|
290
|
+
def get_full_code(self) -> str:
|
|
291
|
+
"""
|
|
292
|
+
获取完整代码(CA + CB + CC)
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
完整代码字符串,包含导入、类定义、函数定义和可见代码
|
|
296
|
+
"""
|
|
297
|
+
return self._build_full_code()
|
|
298
|
+
|
|
299
|
+
def _on_text_modified(self):
|
|
300
|
+
"""文本修改事件"""
|
|
301
|
+
self.text_modified.emit()
|
|
302
|
+
# 检查 QLabel 是否还存在(防止窗口关闭后崩溃)
|
|
303
|
+
if self._lbl_status is not None:
|
|
304
|
+
try:
|
|
305
|
+
self._lbl_status.setText("已修改")
|
|
306
|
+
QTimer.singleShot(2000, self._reset_status_label)
|
|
307
|
+
except RuntimeError:
|
|
308
|
+
# QLabel 已被删除,忽略
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
def _reset_status_label(self):
|
|
312
|
+
"""重置状态栏标签(带存在性检查)"""
|
|
313
|
+
if self._lbl_status is not None:
|
|
314
|
+
try:
|
|
315
|
+
self._lbl_status.setText("就绪")
|
|
316
|
+
except RuntimeError:
|
|
317
|
+
# QLabel 已被删除,忽略
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
def _on_cursor_position_changed(self, line: int, column: int):
|
|
321
|
+
"""光标位置改变事件"""
|
|
322
|
+
self._lbl_position.setText(f"行 {line}, 列 {column}")
|
|
323
|
+
self.cursor_position_changed.emit(line, column)
|
|
324
|
+
|
|
325
|
+
def _on_navigate_to_line(self, line: int):
|
|
326
|
+
"""导航到指定行"""
|
|
327
|
+
# 将缩略图行号转换为编辑器行号
|
|
328
|
+
editor_line = self._map_minimap_to_editor_line(line)
|
|
329
|
+
self._editor.scroll_to_line(editor_line)
|
|
330
|
+
|
|
331
|
+
def _on_viewport_scroll(self, ratio: float):
|
|
332
|
+
"""视口滚动事件"""
|
|
333
|
+
# 计算对应的行号
|
|
334
|
+
total_lines = self._editor.document().blockCount()
|
|
335
|
+
line = int(ratio * total_lines) + 1
|
|
336
|
+
self._editor.scroll_to_line(line)
|
|
337
|
+
|
|
338
|
+
def _map_minimap_to_editor_line(self, minimap_line: int) -> int:
|
|
339
|
+
"""将缩略图行号映射到编辑器行号"""
|
|
340
|
+
# 计算偏移
|
|
341
|
+
offset = 0
|
|
342
|
+
if self._context_code_a:
|
|
343
|
+
offset += len(self._context_code_a.split('\n')) + 1
|
|
344
|
+
if self._context_code_b:
|
|
345
|
+
offset += len(self._context_code_b.split('\n')) + 1
|
|
346
|
+
|
|
347
|
+
# 转换为编辑器行号
|
|
348
|
+
editor_line = minimap_line - offset
|
|
349
|
+
return max(1, editor_line)
|
|
350
|
+
|
|
351
|
+
def get_cursor_position(self) -> tuple:
|
|
352
|
+
"""获取光标位置 (line, column)"""
|
|
353
|
+
return self._editor.get_cursor_position()
|
|
354
|
+
|
|
355
|
+
def set_cursor_position(self, line: int, column: int = 0):
|
|
356
|
+
"""设置光标位置"""
|
|
357
|
+
self._editor.set_cursor_position(line, column)
|
|
358
|
+
|
|
359
|
+
def set_focus(self):
|
|
360
|
+
"""设置焦点到编辑器"""
|
|
361
|
+
self._editor.setFocus()
|
|
362
|
+
|
|
363
|
+
def fold_all(self):
|
|
364
|
+
"""折叠所有代码块"""
|
|
365
|
+
self._editor.fold_all()
|
|
366
|
+
self._lbl_status.setText("已折叠所有代码块")
|
|
367
|
+
|
|
368
|
+
def unfold_all(self):
|
|
369
|
+
"""展开所有代码块"""
|
|
370
|
+
self._editor.unfold_all()
|
|
371
|
+
self._lbl_status.setText("已展开所有代码块")
|
|
372
|
+
|
|
373
|
+
def fold_current(self):
|
|
374
|
+
"""折叠当前代码块"""
|
|
375
|
+
# 调用编辑器的折叠功能
|
|
376
|
+
self._editor._fold_current_block()
|
|
377
|
+
|
|
378
|
+
def unfold_current(self):
|
|
379
|
+
"""展开当前代码块"""
|
|
380
|
+
self._editor._unfold_current_block()
|
|
381
|
+
|
|
382
|
+
def get_editor(self) -> CodeEditor:
|
|
383
|
+
"""获取内部的CodeEditor实例"""
|
|
384
|
+
return self._editor
|
|
385
|
+
|
|
386
|
+
def set_font(self, font: QFont):
|
|
387
|
+
"""设置字体"""
|
|
388
|
+
self._editor.setFont(font)
|
|
389
|
+
|
|
390
|
+
def set_font_family(self, family: str, size: int = 12):
|
|
391
|
+
"""设置字体族和大小"""
|
|
392
|
+
font = QFont(family, size)
|
|
393
|
+
self.set_font(font)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class PythonEditorDialog(QWidget):
|
|
397
|
+
"""
|
|
398
|
+
Python编辑器对话框
|
|
399
|
+
提供独立的编辑器窗口
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
def __init__(self, title: str = "Python编辑器", parent=None):
|
|
403
|
+
super().__init__(parent)
|
|
404
|
+
|
|
405
|
+
self.setWindowTitle(title)
|
|
406
|
+
self.setMinimumSize(800, 600)
|
|
407
|
+
self.resize(900, 700)
|
|
408
|
+
|
|
409
|
+
# 创建布局
|
|
410
|
+
layout = QVBoxLayout(self)
|
|
411
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
|
412
|
+
|
|
413
|
+
# 创建编辑器
|
|
414
|
+
self.editor = PythonEditor()
|
|
415
|
+
layout.addWidget(self.editor)
|
|
416
|
+
|
|
417
|
+
# 设置样式
|
|
418
|
+
self.setStyleSheet(f"""
|
|
419
|
+
PythonEditorDialog {{
|
|
420
|
+
background-color: {PALETTE.window};
|
|
421
|
+
}}
|
|
422
|
+
""")
|
|
423
|
+
|
|
424
|
+
def set_context_code(self, code_a: str = "", code_b: str = "", prefix: str = ""):
|
|
425
|
+
"""设置上下文代码"""
|
|
426
|
+
self.editor.set_context_code(code_a, code_b, prefix)
|
|
427
|
+
|
|
428
|
+
def set_code(self, code: str):
|
|
429
|
+
"""设置代码"""
|
|
430
|
+
self.editor.set_code(code)
|
|
431
|
+
|
|
432
|
+
def get_code(self) -> str:
|
|
433
|
+
"""获取代码"""
|
|
434
|
+
return self.editor.get_code()
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class PythonEditorFactory:
|
|
438
|
+
"""Python编辑器工厂类"""
|
|
439
|
+
|
|
440
|
+
@staticmethod
|
|
441
|
+
def create_editor(parent=None,
|
|
442
|
+
code_a: str = "",
|
|
443
|
+
code_b: str = "",
|
|
444
|
+
prefix: str = "",
|
|
445
|
+
initial_code: str = "") -> PythonEditor:
|
|
446
|
+
"""
|
|
447
|
+
创建配置好的Python编辑器
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
parent: 父控件
|
|
451
|
+
code_a: 类定义代码
|
|
452
|
+
code_b: 函数定义代码
|
|
453
|
+
prefix: 前缀缩进
|
|
454
|
+
initial_code: 初始代码
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
配置好的PythonEditor实例
|
|
458
|
+
"""
|
|
459
|
+
editor = PythonEditor(parent)
|
|
460
|
+
editor.set_context_code(code_a, code_b, prefix)
|
|
461
|
+
|
|
462
|
+
if initial_code:
|
|
463
|
+
editor.set_code(initial_code)
|
|
464
|
+
|
|
465
|
+
return editor
|
|
466
|
+
|
|
467
|
+
@staticmethod
|
|
468
|
+
def create_node_editor(parent=None,
|
|
469
|
+
node_class_code: str = "",
|
|
470
|
+
handler_code: str = "",
|
|
471
|
+
initial_code: str = "") -> PythonEditor:
|
|
472
|
+
"""
|
|
473
|
+
创建用于节点编辑的Python编辑器
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
parent: 父控件
|
|
477
|
+
node_class_code: 节点类定义代码 (CA)
|
|
478
|
+
handler_code: 处理函数代码 (CB)
|
|
479
|
+
initial_code: 初始代码
|
|
480
|
+
|
|
481
|
+
Returns:
|
|
482
|
+
配置好的PythonEditor实例
|
|
483
|
+
"""
|
|
484
|
+
return PythonEditorFactory.create_editor(
|
|
485
|
+
parent=parent,
|
|
486
|
+
code_a=node_class_code,
|
|
487
|
+
code_b=handler_code,
|
|
488
|
+
prefix=" ", # 8空格缩进
|
|
489
|
+
initial_code=initial_code
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
# 便捷函数
|
|
494
|
+
def create_editor(parent=None) -> PythonEditor:
|
|
495
|
+
"""创建Python编辑器"""
|
|
496
|
+
return PythonEditor(parent)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def create_editor_with_context(code_a: str = "",
|
|
500
|
+
code_b: str = "",
|
|
501
|
+
prefix: str = "",
|
|
502
|
+
initial_code: str = "",
|
|
503
|
+
parent=None) -> PythonEditor:
|
|
504
|
+
"""创建带上下文的Python编辑器"""
|
|
505
|
+
editor = PythonEditor(parent)
|
|
506
|
+
editor.set_context_code(code_a, code_b, prefix)
|
|
507
|
+
if initial_code:
|
|
508
|
+
editor.set_code(initial_code)
|
|
509
|
+
return editor
|