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.
Files changed (79) hide show
  1. chartflow/__init__.py +28 -0
  2. chartflow/_verify_importexport.py +169 -0
  3. chartflow/edit/__init__.py +119 -0
  4. chartflow/edit/code_editor.py +1007 -0
  5. chartflow/edit/code_folder.py +523 -0
  6. chartflow/edit/completer.py +2652 -0
  7. chartflow/edit/context_analyzer.py +521 -0
  8. chartflow/edit/demo.py +469 -0
  9. chartflow/edit/line_number_area.py +548 -0
  10. chartflow/edit/minimap.py +581 -0
  11. chartflow/edit/python_editor.py +509 -0
  12. chartflow/edit/syntax_highlighter.py +291 -0
  13. chartflow/edit/test_editor.py +190 -0
  14. chartflow/flow/__init__.py +31 -0
  15. chartflow/flow/_demo.py +98 -0
  16. chartflow/flow/chart.py +1101 -0
  17. chartflow/flow/editor.py +375 -0
  18. chartflow/flow/flow.py +6 -0
  19. chartflow/flow/qchart.py +1250 -0
  20. chartflow/flow/test/__init__.py +0 -0
  21. chartflow/flow/test/test_chart.py +537 -0
  22. chartflow/flow/test_decorator_sync.py +102 -0
  23. chartflow/fsm/__init__.py +84 -0
  24. chartflow/fsm/decorators.py +395 -0
  25. chartflow/fsm/demo.py +381 -0
  26. chartflow/fsm/demo_pyqt6.py +881 -0
  27. chartflow/fsm/demo_tree.py +46 -0
  28. chartflow/fsm/logic.py +447 -0
  29. chartflow/fsm/meta.py +569 -0
  30. chartflow/fsm/scheduler.py +427 -0
  31. chartflow/fsm/stage.py +2079 -0
  32. chartflow/fsm/stdio.py +66 -0
  33. chartflow/fsm/test/__init__.py +7 -0
  34. chartflow/fsm/test/test_decorators.py +210 -0
  35. chartflow/fsm/test/test_events.py +202 -0
  36. chartflow/fsm/test/test_integration.py +596 -0
  37. chartflow/fsm/test/test_logic.py +371 -0
  38. chartflow/fsm/test/test_meta.py +192 -0
  39. chartflow/fsm/test/test_runtime_param.py +336 -0
  40. chartflow/fsm/test/test_scheduler.py +280 -0
  41. chartflow/fsm/test/test_stage.py +314 -0
  42. chartflow/fsm/test_behavior_events.py +263 -0
  43. chartflow/fsm/test_nested_parallel.py +80 -0
  44. chartflow/fsm/test_stage_spec.py +448 -0
  45. chartflow/fsm/utils.py +34 -0
  46. chartflow/node/__init__.py +66 -0
  47. chartflow/node/_node_base.py +727 -0
  48. chartflow/node/_node_interaction.py +488 -0
  49. chartflow/node/_node_interface.py +426 -0
  50. chartflow/node/_node_markers.py +648 -0
  51. chartflow/node/_node_ports.py +536 -0
  52. chartflow/node/_port_interface.py +111 -0
  53. chartflow/node/arrow_item.py +287 -0
  54. chartflow/node/color_utils.py +113 -0
  55. chartflow/node/connection_item.py +939 -0
  56. chartflow/node/demo.py +258 -0
  57. chartflow/node/demo_arrow_drag.py +46 -0
  58. chartflow/node/demo_decorator.py +63 -0
  59. chartflow/node/demo_ports.py +69 -0
  60. chartflow/node/enums.py +35 -0
  61. chartflow/node/example.py +84 -0
  62. chartflow/node/layout_utils.py +307 -0
  63. chartflow/node/main_window.py +196 -0
  64. chartflow/node/menu_bar.py +240 -0
  65. chartflow/node/node_canvas.py +1730 -0
  66. chartflow/node/node_item.py +754 -0
  67. chartflow/node/popmenu.py +472 -0
  68. chartflow/node/port_item.py +591 -0
  69. chartflow/node/qnode_editor.py +73 -0
  70. chartflow/node/selection_rect.py +53 -0
  71. chartflow/node/style_panel.py +615 -0
  72. chartflow/node/styles.py +63 -0
  73. chartflow/node/test_qnode.py +2336 -0
  74. chartflow/showcase.py +528 -0
  75. chartflow/theme.py +508 -0
  76. chartflow-0.1.dist-info/METADATA +23 -0
  77. chartflow-0.1.dist-info/RECORD +79 -0
  78. chartflow-0.1.dist-info/WHEEL +5 -0
  79. chartflow-0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,240 @@
1
+ """
2
+ 菜单栏模块 - 提供文件、编辑、视图等菜单
3
+ """
4
+
5
+ from chartflow.node.layout_utils import ViewportFitter
6
+ from chartflow.node.layout_utils import DenseLayoutEngine
7
+
8
+
9
+ from typing import Optional, Callable
10
+
11
+ from PyQt6.QtWidgets import QMenuBar, QMenu, QFileDialog, QMessageBox
12
+ from PyQt6.QtCore import pyqtSignal, QObject
13
+ from PyQt6.QtGui import QKeySequence, QAction
14
+
15
+
16
+ class MenuBar(QMenuBar):
17
+ """
18
+ 菜单栏 - 提供文件、编辑、视图等菜单
19
+ """
20
+
21
+ # 信号
22
+ newGraphRequested = pyqtSignal()
23
+ openGraphRequested = pyqtSignal(str)
24
+ saveGraphRequested = pyqtSignal()
25
+ saveAsGraphRequested = pyqtSignal(str)
26
+ exportGraphRequested = pyqtSignal()
27
+ importGraphRequested = pyqtSignal(str)
28
+ exitRequested = pyqtSignal()
29
+
30
+ def __init__(self, parent: Optional['QWidget'] = None) -> None:
31
+ super().__init__(parent)
32
+
33
+ self._createFileMenu()
34
+ self._createEditMenu()
35
+ self._createViewMenu()
36
+ self._createLayoutMenu()
37
+
38
+ def _createFileMenu(self) -> None:
39
+ """创建文件菜单"""
40
+ file_menu = self.addMenu("File")
41
+
42
+ # New
43
+ new_action = QAction("New", self)
44
+ new_action.setShortcut(QKeySequence.StandardKey.New)
45
+ new_action.triggered.connect(self.newGraphRequested.emit)
46
+ file_menu.addAction(new_action)
47
+
48
+ # Open
49
+ open_action = QAction("Open...", self)
50
+ open_action.setShortcut(QKeySequence.StandardKey.Open)
51
+ open_action.triggered.connect(self._onOpen)
52
+ file_menu.addAction(open_action)
53
+
54
+ file_menu.addSeparator()
55
+
56
+ # Save
57
+ save_action = QAction("Save", self)
58
+ save_action.setShortcut(QKeySequence.StandardKey.Save)
59
+ save_action.triggered.connect(self.saveGraphRequested.emit)
60
+ file_menu.addAction(save_action)
61
+
62
+ # Save As
63
+ save_as_action = QAction("Save As...", self)
64
+ save_as_action.setShortcut(QKeySequence.StandardKey.SaveAs)
65
+ save_as_action.triggered.connect(self._onSaveAs)
66
+ file_menu.addAction(save_as_action)
67
+
68
+ file_menu.addSeparator()
69
+
70
+ # Export
71
+ export_action = QAction("Export...", self)
72
+ export_action.triggered.connect(self._onExport)
73
+ file_menu.addAction(export_action)
74
+
75
+ # Import
76
+ import_action = QAction("Import...", self)
77
+ import_action.triggered.connect(self._onImport)
78
+ file_menu.addAction(import_action)
79
+
80
+ file_menu.addSeparator()
81
+
82
+ # Exit
83
+ exit_action = QAction("Exit", self)
84
+ exit_action.setShortcut(QKeySequence.StandardKey.Quit)
85
+ exit_action.triggered.connect(self.exitRequested.emit)
86
+ file_menu.addAction(exit_action)
87
+
88
+ def _createEditMenu(self) -> None:
89
+ """创建编辑菜单"""
90
+ edit_menu = self.addMenu("Edit")
91
+
92
+ # Delete
93
+ delete_action = QAction("Delete", self)
94
+ delete_action.setShortcut(QKeySequence.StandardKey.Delete)
95
+ delete_action.triggered.connect(self._onDelete)
96
+ edit_menu.addAction(delete_action)
97
+
98
+ # Select All
99
+ select_all_action = QAction("Select All", self)
100
+ select_all_action.setShortcut(QKeySequence.StandardKey.SelectAll)
101
+ select_all_action.triggered.connect(self._onSelectAll)
102
+ edit_menu.addAction(select_all_action)
103
+
104
+ def _createViewMenu(self) -> None:
105
+ """创建视图菜单"""
106
+ view_menu = self.addMenu("View")
107
+
108
+ # Zoom In
109
+ zoom_in_action = QAction("Zoom In", self)
110
+ zoom_in_action.setShortcut(QKeySequence.StandardKey.ZoomIn)
111
+ zoom_in_action.triggered.connect(self._onZoomIn)
112
+ view_menu.addAction(zoom_in_action)
113
+
114
+ # Zoom Out
115
+ zoom_out_action = QAction("Zoom Out", self)
116
+ zoom_out_action.setShortcut(QKeySequence.StandardKey.ZoomOut)
117
+ zoom_out_action.triggered.connect(self._onZoomOut)
118
+ view_menu.addAction(zoom_out_action)
119
+
120
+ # Fit to Viewport
121
+ fit_viewport_action = QAction("Fit to Viewport", self)
122
+ fit_viewport_action.triggered.connect(self._onFitViewport)
123
+ view_menu.addAction(fit_viewport_action)
124
+
125
+ # Reset Zoom
126
+ reset_zoom_action = QAction("Reset Zoom", self)
127
+ reset_zoom_action.triggered.connect(self._onResetZoom)
128
+ view_menu.addAction(reset_zoom_action)
129
+
130
+ def _createLayoutMenu(self) -> None:
131
+ """创建布局菜单"""
132
+ layout_menu = self.addMenu("Layout")
133
+
134
+ # Compact
135
+ compact_action = QAction("Compact", self)
136
+ compact_action.triggered.connect(lambda: self._onLayout("compact"))
137
+ layout_menu.addAction(compact_action)
138
+
139
+ # Circular
140
+ circular_action = QAction("Circular", self)
141
+ circular_action.triggered.connect(lambda: self._onLayout("circular"))
142
+ layout_menu.addAction(circular_action)
143
+
144
+ # Grid
145
+ grid_action = QAction("Grid", self)
146
+ grid_action.triggered.connect(lambda: self._onLayout("grid"))
147
+ layout_menu.addAction(grid_action)
148
+
149
+ # Hierarchical
150
+ hierarchical_action = QAction("Hierarchical", self)
151
+ hierarchical_action.triggered.connect(lambda: self._onLayout("hierarchical"))
152
+ layout_menu.addAction(hierarchical_action)
153
+
154
+ # Force Directed
155
+ force_action = QAction("Force Directed", self)
156
+ force_action.triggered.connect(lambda: self._onLayout("force"))
157
+ layout_menu.addAction(force_action)
158
+
159
+ def _onOpen(self) -> None:
160
+ """打开文件"""
161
+ file_path, _ = QFileDialog.getOpenFileName(
162
+ self, "Open Graph", "", "JSON Files (*.json);;All Files (*)"
163
+ )
164
+ if file_path:
165
+ self.openGraphRequested.emit(file_path)
166
+
167
+ def _onSaveAs(self) -> None:
168
+ """另存为"""
169
+ file_path, _ = QFileDialog.getSaveFileName(
170
+ self, "Save Graph As", "", "JSON Files (*.json);;All Files (*)"
171
+ )
172
+ if file_path:
173
+ self.saveAsGraphRequested.emit(file_path)
174
+
175
+ def _onExport(self) -> None:
176
+ """导出"""
177
+ file_path, _ = QFileDialog.getSaveFileName(
178
+ self, "Export Graph", "", "JSON Files (*.json);;All Files (*)"
179
+ )
180
+ if file_path:
181
+ self.exportGraphRequested.emit()
182
+
183
+ def _onImport(self) -> None:
184
+ """导入"""
185
+ file_path, _ = QFileDialog.getOpenFileName(
186
+ self, "Import Graph", "", "JSON Files (*.json);;All Files (*)"
187
+ )
188
+ if file_path:
189
+ self.importGraphRequested.emit(file_path)
190
+
191
+ def _onDelete(self) -> None:
192
+ """删除选中"""
193
+ # 通过父窗口获取画布并删除选中项
194
+ parent = self.parent()
195
+ if parent and hasattr(parent, 'canvas'):
196
+ canvas = parent.canvas
197
+ # 删除选中的节点
198
+ for node in canvas.nodes[:]:
199
+ if node.isSelected():
200
+ canvas.removeNode(node)
201
+
202
+ def _onSelectAll(self) -> None:
203
+ """全选"""
204
+ parent = self.parent()
205
+ if parent and hasattr(parent, 'canvas'):
206
+ canvas = parent.canvas
207
+ for node in canvas.nodes:
208
+ node.setSelected(True)
209
+
210
+ def _onZoomIn(self) -> None:
211
+ """放大"""
212
+ parent = self.parent()
213
+ if parent and hasattr(parent, 'canvas'):
214
+ parent.canvas.scale(1.1, 1.1)
215
+
216
+ def _onZoomOut(self) -> None:
217
+ """缩小"""
218
+ parent = self.parent()
219
+ if parent and hasattr(parent, 'canvas'):
220
+ parent.canvas.scale(0.9, 0.9)
221
+
222
+ def _onFitViewport(self) -> None:
223
+ """适应视口"""
224
+ parent = self.parent()
225
+ if parent and hasattr(parent, 'canvas'):
226
+ fitter = ViewportFitter(parent.canvas)
227
+ fitter.fitViewport()
228
+
229
+ def _onResetZoom(self) -> None:
230
+ """重置缩放"""
231
+ parent = self.parent()
232
+ if parent and hasattr(parent, 'canvas'):
233
+ parent.canvas.resetTransform()
234
+
235
+ def _onLayout(self, strategy: str) -> None:
236
+ """执行布局"""
237
+ parent = self.parent()
238
+ if parent and hasattr(parent, 'canvas'):
239
+ engine = DenseLayoutEngine(parent.canvas)
240
+ engine.autoLayout(strategy)