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
chartflow/edit/demo.py
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyEdit演示程序
|
|
3
|
+
展示Python编辑器的各种功能
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from PyQt6.QtWidgets import QInputDialog
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
from PyQt6.QtWidgets import (
|
|
11
|
+
QApplication, QMainWindow, QWidget, QVBoxLayout,
|
|
12
|
+
QHBoxLayout, QPushButton, QLabel, QTextEdit,
|
|
13
|
+
QSplitter, QFrame, QMenuBar, QMenu, QStatusBar,
|
|
14
|
+
QFileDialog, QMessageBox, QDialog, QDialogButtonBox
|
|
15
|
+
)
|
|
16
|
+
from PyQt6.QtCore import Qt, QSize
|
|
17
|
+
from PyQt6.QtGui import QFont, QAction, QKeySequence, QIcon
|
|
18
|
+
|
|
19
|
+
# 导入pyedit模块
|
|
20
|
+
from chartflow.edit import PythonEditor, PythonEditorFactory
|
|
21
|
+
from chartflow.theme import applyTheme
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DemoWindow(QMainWindow):
|
|
25
|
+
"""演示窗口"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
super().__init__()
|
|
29
|
+
|
|
30
|
+
self.setWindowTitle("PyEdit - Python代码编辑器演示")
|
|
31
|
+
self.setMinimumSize(1200, 800)
|
|
32
|
+
self.resize(1400, 900)
|
|
33
|
+
|
|
34
|
+
self._setup_ui()
|
|
35
|
+
self._setup_menubar()
|
|
36
|
+
self._setup_statusbar()
|
|
37
|
+
self._setup_shortcuts()
|
|
38
|
+
|
|
39
|
+
# 创建示例编辑器
|
|
40
|
+
self._create_example_editor()
|
|
41
|
+
|
|
42
|
+
def _setup_ui(self):
|
|
43
|
+
"""设置UI"""
|
|
44
|
+
# 中央部件
|
|
45
|
+
central_widget = QWidget()
|
|
46
|
+
self.setCentralWidget(central_widget)
|
|
47
|
+
|
|
48
|
+
# 主布局
|
|
49
|
+
layout = QVBoxLayout(central_widget)
|
|
50
|
+
layout.setContentsMargins(10, 10, 10, 10)
|
|
51
|
+
layout.setSpacing(10)
|
|
52
|
+
|
|
53
|
+
# 说明标签
|
|
54
|
+
self._lbl_info = QLabel(
|
|
55
|
+
"PyEdit - 支持上下文代码的Python编辑器 | "
|
|
56
|
+
"快捷键: Ctrl++ 展开 | Ctrl+- 折叠 | Ctrl+Shift++ 展开全部 | Ctrl+Shift+- 折叠全部 | "
|
|
57
|
+
"Ctrl+Space 代码补全"
|
|
58
|
+
)
|
|
59
|
+
self._lbl_info.setStyleSheet("""
|
|
60
|
+
QLabel {
|
|
61
|
+
background-color: #f0f0f0;
|
|
62
|
+
border: 1px solid #cccccc;
|
|
63
|
+
border-radius: 4px;
|
|
64
|
+
padding: 8px 12px;
|
|
65
|
+
font-size: 12px;
|
|
66
|
+
color: #333333;
|
|
67
|
+
}
|
|
68
|
+
""")
|
|
69
|
+
layout.addWidget(self._lbl_info)
|
|
70
|
+
|
|
71
|
+
# 分割器
|
|
72
|
+
splitter = QSplitter(Qt.Orientation.Vertical)
|
|
73
|
+
|
|
74
|
+
# 上半部分:编辑器
|
|
75
|
+
self._editor_frame = QFrame()
|
|
76
|
+
self._editor_frame.setFrameShape(QFrame.Shape.StyledPanel)
|
|
77
|
+
editor_layout = QVBoxLayout(self._editor_frame)
|
|
78
|
+
editor_layout.setContentsMargins(0, 0, 0, 0)
|
|
79
|
+
|
|
80
|
+
# 创建Python编辑器
|
|
81
|
+
self._editor = PythonEditor()
|
|
82
|
+
editor_layout.addWidget(self._editor)
|
|
83
|
+
|
|
84
|
+
splitter.addWidget(self._editor_frame)
|
|
85
|
+
|
|
86
|
+
# 下半部分:信息显示
|
|
87
|
+
self._info_frame = QFrame()
|
|
88
|
+
self._info_frame.setFrameShape(QFrame.Shape.StyledPanel)
|
|
89
|
+
info_layout = QVBoxLayout(self._info_frame)
|
|
90
|
+
info_layout.setContentsMargins(10, 10, 10, 10)
|
|
91
|
+
|
|
92
|
+
# 上下文代码显示
|
|
93
|
+
info_label = QLabel("上下文代码(隐藏但参与分析):")
|
|
94
|
+
info_label.setStyleSheet("font-weight: bold; color: #333333;")
|
|
95
|
+
info_layout.addWidget(info_label)
|
|
96
|
+
|
|
97
|
+
self._txt_context = QTextEdit()
|
|
98
|
+
self._txt_context.setReadOnly(True)
|
|
99
|
+
self._txt_context.setMaximumHeight(150)
|
|
100
|
+
self._txt_context.setStyleSheet("""
|
|
101
|
+
QTextEdit {
|
|
102
|
+
background-color: #f8f8f8;
|
|
103
|
+
border: 1px solid #dddddd;
|
|
104
|
+
border-radius: 4px;
|
|
105
|
+
padding: 8px;
|
|
106
|
+
font-family: Consolas, monospace;
|
|
107
|
+
font-size: 11px;
|
|
108
|
+
color: #666666;
|
|
109
|
+
}
|
|
110
|
+
""")
|
|
111
|
+
info_layout.addWidget(self._txt_context)
|
|
112
|
+
|
|
113
|
+
# 按钮区域
|
|
114
|
+
btn_layout = QHBoxLayout()
|
|
115
|
+
|
|
116
|
+
self._btn_show_code = QPushButton("获取编辑器代码")
|
|
117
|
+
self._btn_show_code.setToolTip("获取当前编辑器中的代码")
|
|
118
|
+
self._btn_show_code.clicked.connect(self._show_current_code)
|
|
119
|
+
btn_layout.addWidget(self._btn_show_code)
|
|
120
|
+
|
|
121
|
+
self._btn_fold_all = QPushButton("折叠全部")
|
|
122
|
+
self._btn_fold_all.setToolTip("Ctrl+Shift+-")
|
|
123
|
+
self._btn_fold_all.clicked.connect(self._editor.fold_all)
|
|
124
|
+
btn_layout.addWidget(self._btn_fold_all)
|
|
125
|
+
|
|
126
|
+
self._btn_unfold_all = QPushButton("展开全部")
|
|
127
|
+
self._btn_unfold_all.setToolTip("Ctrl+Shift++")
|
|
128
|
+
self._btn_unfold_all.clicked.connect(self._editor.unfold_all)
|
|
129
|
+
btn_layout.addWidget(self._btn_unfold_all)
|
|
130
|
+
|
|
131
|
+
self._btn_new_example = QPushButton("新示例")
|
|
132
|
+
self._btn_new_example.setToolTip("加载新的示例代码")
|
|
133
|
+
self._btn_new_example.clicked.connect(self._load_new_example)
|
|
134
|
+
btn_layout.addWidget(self._btn_new_example)
|
|
135
|
+
|
|
136
|
+
btn_layout.addStretch()
|
|
137
|
+
info_layout.addLayout(btn_layout)
|
|
138
|
+
|
|
139
|
+
splitter.addWidget(self._info_frame)
|
|
140
|
+
|
|
141
|
+
# 设置分割比例
|
|
142
|
+
splitter.setSizes([600, 200])
|
|
143
|
+
splitter.setStretchFactor(0, 1)
|
|
144
|
+
splitter.setStretchFactor(1, 0)
|
|
145
|
+
|
|
146
|
+
layout.addWidget(splitter)
|
|
147
|
+
|
|
148
|
+
def _setup_menubar(self):
|
|
149
|
+
"""设置菜单栏"""
|
|
150
|
+
menubar = self.menuBar()
|
|
151
|
+
|
|
152
|
+
# 文件菜单
|
|
153
|
+
file_menu = menubar.addMenu("文件(&F)")
|
|
154
|
+
|
|
155
|
+
new_action = QAction("新建(&N)", self)
|
|
156
|
+
new_action.setShortcut(QKeySequence.StandardKey.New)
|
|
157
|
+
new_action.triggered.connect(self._new_file)
|
|
158
|
+
file_menu.addAction(new_action)
|
|
159
|
+
|
|
160
|
+
open_action = QAction("打开(&O)...", self)
|
|
161
|
+
open_action.setShortcut(QKeySequence.StandardKey.Open)
|
|
162
|
+
open_action.triggered.connect(self._open_file)
|
|
163
|
+
file_menu.addAction(open_action)
|
|
164
|
+
|
|
165
|
+
save_action = QAction("保存(&S)", self)
|
|
166
|
+
save_action.setShortcut(QKeySequence.StandardKey.Save)
|
|
167
|
+
save_action.triggered.connect(self._save_file)
|
|
168
|
+
file_menu.addAction(save_action)
|
|
169
|
+
|
|
170
|
+
file_menu.addSeparator()
|
|
171
|
+
|
|
172
|
+
exit_action = QAction("退出(&X)", self)
|
|
173
|
+
exit_action.setShortcut(QKeySequence.StandardKey.Quit)
|
|
174
|
+
exit_action.triggered.connect(self.close)
|
|
175
|
+
file_menu.addAction(exit_action)
|
|
176
|
+
|
|
177
|
+
# 编辑菜单
|
|
178
|
+
edit_menu = menubar.addMenu("编辑(&E)")
|
|
179
|
+
|
|
180
|
+
undo_action = QAction("撤销(&U)", self)
|
|
181
|
+
undo_action.setShortcut(QKeySequence.StandardKey.Undo)
|
|
182
|
+
undo_action.triggered.connect(self._editor.get_editor().undo)
|
|
183
|
+
edit_menu.addAction(undo_action)
|
|
184
|
+
|
|
185
|
+
redo_action = QAction("重做(&R)", self)
|
|
186
|
+
redo_action.setShortcut(QKeySequence.StandardKey.Redo)
|
|
187
|
+
redo_action.triggered.connect(self._editor.get_editor().redo)
|
|
188
|
+
edit_menu.addAction(redo_action)
|
|
189
|
+
|
|
190
|
+
edit_menu.addSeparator()
|
|
191
|
+
|
|
192
|
+
fold_action = QAction("折叠当前块", self)
|
|
193
|
+
fold_action.setShortcut("Ctrl+-")
|
|
194
|
+
fold_action.triggered.connect(self._editor.fold_current)
|
|
195
|
+
edit_menu.addAction(fold_action)
|
|
196
|
+
|
|
197
|
+
unfold_action = QAction("展开当前块", self)
|
|
198
|
+
unfold_action.setShortcut("Ctrl++")
|
|
199
|
+
unfold_action.triggered.connect(self._editor.unfold_current)
|
|
200
|
+
edit_menu.addAction(unfold_action)
|
|
201
|
+
|
|
202
|
+
# 示例菜单
|
|
203
|
+
example_menu = menubar.addMenu("示例(&X)")
|
|
204
|
+
|
|
205
|
+
basic_example = QAction("基础示例", self)
|
|
206
|
+
basic_example.triggered.connect(lambda: self._load_example("basic"))
|
|
207
|
+
example_menu.addAction(basic_example)
|
|
208
|
+
|
|
209
|
+
class_example = QAction("类定义示例", self)
|
|
210
|
+
class_example.triggered.connect(lambda: self._load_example("class"))
|
|
211
|
+
example_menu.addAction(class_example)
|
|
212
|
+
|
|
213
|
+
function_example = QAction("函数示例", self)
|
|
214
|
+
function_example.triggered.connect(lambda: self._load_example("function"))
|
|
215
|
+
example_menu.addAction(function_example)
|
|
216
|
+
|
|
217
|
+
node_example = QAction("节点编辑示例", self)
|
|
218
|
+
node_example.triggered.connect(lambda: self._load_example("node"))
|
|
219
|
+
example_menu.addAction(node_example)
|
|
220
|
+
|
|
221
|
+
def _setup_statusbar(self):
|
|
222
|
+
"""设置状态栏"""
|
|
223
|
+
self.statusbar = QStatusBar()
|
|
224
|
+
self.setStatusBar(self.statusbar)
|
|
225
|
+
self.statusbar.showMessage("就绪 - 使用 Ctrl+Space 触发代码补全")
|
|
226
|
+
|
|
227
|
+
def _setup_shortcuts(self):
|
|
228
|
+
"""设置快捷键"""
|
|
229
|
+
# 折叠快捷键
|
|
230
|
+
pass # 已在菜单中设置
|
|
231
|
+
|
|
232
|
+
def _create_example_editor(self):
|
|
233
|
+
"""创建示例编辑器"""
|
|
234
|
+
self._load_example("node")
|
|
235
|
+
|
|
236
|
+
def _load_example(self, example_type: str):
|
|
237
|
+
"""加载示例"""
|
|
238
|
+
if example_type == "basic":
|
|
239
|
+
code_a = ""
|
|
240
|
+
code_b = ""
|
|
241
|
+
prefix = ""
|
|
242
|
+
initial_code = '''# 欢迎使用 PyEdit
|
|
243
|
+
# 这是一个基础的Python编辑器示例
|
|
244
|
+
|
|
245
|
+
def hello_world():
|
|
246
|
+
"""打印问候语"""
|
|
247
|
+
print("Hello, World!")
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
# 调用函数
|
|
251
|
+
hello_world()
|
|
252
|
+
'''
|
|
253
|
+
|
|
254
|
+
elif example_type == "class":
|
|
255
|
+
code_a = '''class Person:
|
|
256
|
+
"""人员类"""
|
|
257
|
+
|
|
258
|
+
species = "Homo sapiens"
|
|
259
|
+
|
|
260
|
+
def __init__(self, name: str, age: int):
|
|
261
|
+
self.name = name
|
|
262
|
+
self.age = age
|
|
263
|
+
|
|
264
|
+
def introduce(self):
|
|
265
|
+
return f"My name is {self.name}, I am {self.age} years old."
|
|
266
|
+
'''
|
|
267
|
+
code_b = ""
|
|
268
|
+
prefix = ""
|
|
269
|
+
initial_code = '''# 使用Person类
|
|
270
|
+
person = Person("Alice", 30)
|
|
271
|
+
print(person.introduce())
|
|
272
|
+
'''
|
|
273
|
+
|
|
274
|
+
elif example_type == "function":
|
|
275
|
+
code_a = ""
|
|
276
|
+
code_b = '''def process_data(data: list, callback: callable = None):
|
|
277
|
+
"""处理数据"""
|
|
278
|
+
results = []
|
|
279
|
+
for item in data:
|
|
280
|
+
processed = item * 2
|
|
281
|
+
if callback:
|
|
282
|
+
callback(processed)
|
|
283
|
+
results.append(processed)
|
|
284
|
+
return results
|
|
285
|
+
'''
|
|
286
|
+
prefix = " "
|
|
287
|
+
initial_code = '''# 在函数内部编辑
|
|
288
|
+
# 可以使用callback和results等变量
|
|
289
|
+
if callback:
|
|
290
|
+
callback(42)
|
|
291
|
+
'''
|
|
292
|
+
|
|
293
|
+
elif example_type == "node":
|
|
294
|
+
# 节点编辑示例(模拟node_widgets的用例)
|
|
295
|
+
code_a = '''class DialogueNode(BaseNode):
|
|
296
|
+
"""对话节点"""
|
|
297
|
+
|
|
298
|
+
NODE_TYPE = "dialogue"
|
|
299
|
+
NAME = "Dialogue"
|
|
300
|
+
|
|
301
|
+
# 节点属性
|
|
302
|
+
speaker = ""
|
|
303
|
+
text = ""
|
|
304
|
+
choices = []
|
|
305
|
+
|
|
306
|
+
def __init__(self):
|
|
307
|
+
super().__init__()
|
|
308
|
+
self.connections = []
|
|
309
|
+
self.variables = {}
|
|
310
|
+
|
|
311
|
+
def on_enter(self):
|
|
312
|
+
"""进入节点时调用"""
|
|
313
|
+
print(f"Entering dialogue: {self.text}")
|
|
314
|
+
|
|
315
|
+
def on_exit(self):
|
|
316
|
+
"""离开节点时调用"""
|
|
317
|
+
print(f"Exiting dialogue: {self.text}")
|
|
318
|
+
'''
|
|
319
|
+
code_b = '''def handle_connection(self, context: GameContext, player: Player, *args):
|
|
320
|
+
"""处理连接"""
|
|
321
|
+
result = None
|
|
322
|
+
selected_choice = None
|
|
323
|
+
'''
|
|
324
|
+
prefix = " " # 8空格缩进
|
|
325
|
+
initial_code = '''# 在这里编辑处理逻辑
|
|
326
|
+
# 可用的变量: context, player, result, selected_choice
|
|
327
|
+
# 可用的self属性: speaker, text, choices, connections, variables
|
|
328
|
+
|
|
329
|
+
# 显示对话选项
|
|
330
|
+
for i, choice in enumerate(self.choices):
|
|
331
|
+
print(f"{i+1}. {choice}")
|
|
332
|
+
|
|
333
|
+
# 获取玩家选择
|
|
334
|
+
selected_choice = player.get_input()
|
|
335
|
+
|
|
336
|
+
# 根据选择设置结果
|
|
337
|
+
if selected_choice == "1":
|
|
338
|
+
result = "option_1"
|
|
339
|
+
elif selected_choice == "2":
|
|
340
|
+
result = "option_2"
|
|
341
|
+
else:
|
|
342
|
+
result = "default"
|
|
343
|
+
|
|
344
|
+
# 返回结果
|
|
345
|
+
return result
|
|
346
|
+
'''
|
|
347
|
+
|
|
348
|
+
else:
|
|
349
|
+
code_a = ""
|
|
350
|
+
code_b = ""
|
|
351
|
+
prefix = ""
|
|
352
|
+
initial_code = ""
|
|
353
|
+
|
|
354
|
+
# 设置编辑器
|
|
355
|
+
self._editor.set_context_code(code_a, code_b, prefix)
|
|
356
|
+
self._editor.set_code(initial_code)
|
|
357
|
+
|
|
358
|
+
# 更新上下文显示
|
|
359
|
+
context_text = f"""=== 类定义代码 (CA) ===
|
|
360
|
+
{code_a if code_a else '(无)'}
|
|
361
|
+
|
|
362
|
+
=== 函数定义代码 (CB) ===
|
|
363
|
+
{code_b if code_b else '(无)'}
|
|
364
|
+
|
|
365
|
+
=== 前缀缩进 ===
|
|
366
|
+
{repr(prefix) if prefix else '(无)'}
|
|
367
|
+
"""
|
|
368
|
+
self._txt_context.setText(context_text)
|
|
369
|
+
|
|
370
|
+
self.statusbar.showMessage(f"已加载示例: {example_type}")
|
|
371
|
+
|
|
372
|
+
def _load_new_example(self):
|
|
373
|
+
"""加载新示例"""
|
|
374
|
+
|
|
375
|
+
examples = ["basic", "class", "function", "node"]
|
|
376
|
+
example, ok = QInputDialog.getItem(
|
|
377
|
+
self, "选择示例", "示例类型:", examples, 0, False
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
if ok and example:
|
|
381
|
+
self._load_example(example)
|
|
382
|
+
|
|
383
|
+
def _show_current_code(self):
|
|
384
|
+
"""显示当前代码"""
|
|
385
|
+
code = self._editor.get_code()
|
|
386
|
+
|
|
387
|
+
dialog = QDialog(self)
|
|
388
|
+
dialog.setWindowTitle("当前编辑器代码")
|
|
389
|
+
dialog.setMinimumSize(600, 400)
|
|
390
|
+
|
|
391
|
+
layout = QVBoxLayout(dialog)
|
|
392
|
+
|
|
393
|
+
text_edit = QTextEdit()
|
|
394
|
+
text_edit.setPlainText(code)
|
|
395
|
+
text_edit.setReadOnly(True)
|
|
396
|
+
text_edit.setStyleSheet("""
|
|
397
|
+
QTextEdit {
|
|
398
|
+
background-color: #f8f8f8;
|
|
399
|
+
border: 1px solid #dddddd;
|
|
400
|
+
border-radius: 4px;
|
|
401
|
+
padding: 8px;
|
|
402
|
+
font-family: Consolas, monospace;
|
|
403
|
+
font-size: 12px;
|
|
404
|
+
}
|
|
405
|
+
""")
|
|
406
|
+
layout.addWidget(text_edit)
|
|
407
|
+
|
|
408
|
+
btn_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
|
|
409
|
+
btn_box.rejected.connect(dialog.reject)
|
|
410
|
+
layout.addWidget(btn_box)
|
|
411
|
+
|
|
412
|
+
dialog.exec()
|
|
413
|
+
|
|
414
|
+
def _new_file(self):
|
|
415
|
+
"""新建文件"""
|
|
416
|
+
self._editor.clear()
|
|
417
|
+
self.statusbar.showMessage("新建文件")
|
|
418
|
+
|
|
419
|
+
def _open_file(self):
|
|
420
|
+
"""打开文件"""
|
|
421
|
+
file_path, _ = QFileDialog.getOpenFileName(
|
|
422
|
+
self, "打开Python文件", "", "Python文件 (*.py);;所有文件 (*.*)"
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
if file_path:
|
|
426
|
+
try:
|
|
427
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
428
|
+
code = f.read()
|
|
429
|
+
self._editor.set_code(code)
|
|
430
|
+
self.statusbar.showMessage(f"已打开: {file_path}")
|
|
431
|
+
except Exception as e:
|
|
432
|
+
QMessageBox.critical(self, "错误", f"无法打开文件:\n{str(e)}")
|
|
433
|
+
|
|
434
|
+
def _save_file(self):
|
|
435
|
+
"""保存文件"""
|
|
436
|
+
file_path, _ = QFileDialog.getSaveFileName(
|
|
437
|
+
self, "保存Python文件", "", "Python文件 (*.py);;所有文件 (*.*)"
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
if file_path:
|
|
441
|
+
try:
|
|
442
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
443
|
+
f.write(self._editor.get_code())
|
|
444
|
+
self.statusbar.showMessage(f"已保存: {file_path}")
|
|
445
|
+
except Exception as e:
|
|
446
|
+
QMessageBox.critical(self, "错误", f"无法保存文件:\n{str(e)}")
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def main():
|
|
450
|
+
"""主函数"""
|
|
451
|
+
app = QApplication(sys.argv)
|
|
452
|
+
|
|
453
|
+
# 设置应用样式
|
|
454
|
+
app.setStyle('Fusion')
|
|
455
|
+
applyTheme(app)
|
|
456
|
+
|
|
457
|
+
# 设置全局字体
|
|
458
|
+
font = QFont('Microsoft YaHei', 9)
|
|
459
|
+
app.setFont(font)
|
|
460
|
+
|
|
461
|
+
# 创建窗口
|
|
462
|
+
window = DemoWindow()
|
|
463
|
+
window.show()
|
|
464
|
+
|
|
465
|
+
sys.exit(app.exec())
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
if __name__ == '__main__':
|
|
469
|
+
main()
|