chartflow 0.1__tar.gz

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 (83) hide show
  1. chartflow-0.1/PKG-INFO +23 -0
  2. chartflow-0.1/chartflow/__init__.py +28 -0
  3. chartflow-0.1/chartflow/_verify_importexport.py +169 -0
  4. chartflow-0.1/chartflow/edit/__init__.py +119 -0
  5. chartflow-0.1/chartflow/edit/code_editor.py +1007 -0
  6. chartflow-0.1/chartflow/edit/code_folder.py +523 -0
  7. chartflow-0.1/chartflow/edit/completer.py +2652 -0
  8. chartflow-0.1/chartflow/edit/context_analyzer.py +521 -0
  9. chartflow-0.1/chartflow/edit/demo.py +469 -0
  10. chartflow-0.1/chartflow/edit/line_number_area.py +548 -0
  11. chartflow-0.1/chartflow/edit/minimap.py +581 -0
  12. chartflow-0.1/chartflow/edit/python_editor.py +509 -0
  13. chartflow-0.1/chartflow/edit/syntax_highlighter.py +291 -0
  14. chartflow-0.1/chartflow/edit/test_editor.py +190 -0
  15. chartflow-0.1/chartflow/flow/__init__.py +31 -0
  16. chartflow-0.1/chartflow/flow/_demo.py +98 -0
  17. chartflow-0.1/chartflow/flow/chart.py +1101 -0
  18. chartflow-0.1/chartflow/flow/editor.py +375 -0
  19. chartflow-0.1/chartflow/flow/flow.py +6 -0
  20. chartflow-0.1/chartflow/flow/qchart.py +1250 -0
  21. chartflow-0.1/chartflow/flow/test/__init__.py +0 -0
  22. chartflow-0.1/chartflow/flow/test/test_chart.py +537 -0
  23. chartflow-0.1/chartflow/flow/test_decorator_sync.py +102 -0
  24. chartflow-0.1/chartflow/fsm/__init__.py +84 -0
  25. chartflow-0.1/chartflow/fsm/decorators.py +395 -0
  26. chartflow-0.1/chartflow/fsm/demo.py +381 -0
  27. chartflow-0.1/chartflow/fsm/demo_pyqt6.py +881 -0
  28. chartflow-0.1/chartflow/fsm/demo_tree.py +46 -0
  29. chartflow-0.1/chartflow/fsm/logic.py +447 -0
  30. chartflow-0.1/chartflow/fsm/meta.py +569 -0
  31. chartflow-0.1/chartflow/fsm/scheduler.py +427 -0
  32. chartflow-0.1/chartflow/fsm/stage.py +2079 -0
  33. chartflow-0.1/chartflow/fsm/stdio.py +66 -0
  34. chartflow-0.1/chartflow/fsm/test/__init__.py +7 -0
  35. chartflow-0.1/chartflow/fsm/test/test_decorators.py +210 -0
  36. chartflow-0.1/chartflow/fsm/test/test_events.py +202 -0
  37. chartflow-0.1/chartflow/fsm/test/test_integration.py +596 -0
  38. chartflow-0.1/chartflow/fsm/test/test_logic.py +371 -0
  39. chartflow-0.1/chartflow/fsm/test/test_meta.py +192 -0
  40. chartflow-0.1/chartflow/fsm/test/test_runtime_param.py +336 -0
  41. chartflow-0.1/chartflow/fsm/test/test_scheduler.py +280 -0
  42. chartflow-0.1/chartflow/fsm/test/test_stage.py +314 -0
  43. chartflow-0.1/chartflow/fsm/test_behavior_events.py +263 -0
  44. chartflow-0.1/chartflow/fsm/test_nested_parallel.py +80 -0
  45. chartflow-0.1/chartflow/fsm/test_stage_spec.py +448 -0
  46. chartflow-0.1/chartflow/fsm/utils.py +34 -0
  47. chartflow-0.1/chartflow/node/__init__.py +66 -0
  48. chartflow-0.1/chartflow/node/_node_base.py +727 -0
  49. chartflow-0.1/chartflow/node/_node_interaction.py +488 -0
  50. chartflow-0.1/chartflow/node/_node_interface.py +426 -0
  51. chartflow-0.1/chartflow/node/_node_markers.py +648 -0
  52. chartflow-0.1/chartflow/node/_node_ports.py +536 -0
  53. chartflow-0.1/chartflow/node/_port_interface.py +111 -0
  54. chartflow-0.1/chartflow/node/arrow_item.py +287 -0
  55. chartflow-0.1/chartflow/node/color_utils.py +113 -0
  56. chartflow-0.1/chartflow/node/connection_item.py +939 -0
  57. chartflow-0.1/chartflow/node/demo.py +258 -0
  58. chartflow-0.1/chartflow/node/demo_arrow_drag.py +46 -0
  59. chartflow-0.1/chartflow/node/demo_decorator.py +63 -0
  60. chartflow-0.1/chartflow/node/demo_ports.py +69 -0
  61. chartflow-0.1/chartflow/node/enums.py +35 -0
  62. chartflow-0.1/chartflow/node/example.py +84 -0
  63. chartflow-0.1/chartflow/node/layout_utils.py +307 -0
  64. chartflow-0.1/chartflow/node/main_window.py +196 -0
  65. chartflow-0.1/chartflow/node/menu_bar.py +240 -0
  66. chartflow-0.1/chartflow/node/node_canvas.py +1730 -0
  67. chartflow-0.1/chartflow/node/node_item.py +754 -0
  68. chartflow-0.1/chartflow/node/popmenu.py +472 -0
  69. chartflow-0.1/chartflow/node/port_item.py +591 -0
  70. chartflow-0.1/chartflow/node/qnode_editor.py +73 -0
  71. chartflow-0.1/chartflow/node/selection_rect.py +53 -0
  72. chartflow-0.1/chartflow/node/style_panel.py +615 -0
  73. chartflow-0.1/chartflow/node/styles.py +63 -0
  74. chartflow-0.1/chartflow/node/test_qnode.py +2336 -0
  75. chartflow-0.1/chartflow/showcase.py +528 -0
  76. chartflow-0.1/chartflow/theme.py +508 -0
  77. chartflow-0.1/chartflow.egg-info/PKG-INFO +23 -0
  78. chartflow-0.1/chartflow.egg-info/SOURCES.txt +81 -0
  79. chartflow-0.1/chartflow.egg-info/dependency_links.txt +1 -0
  80. chartflow-0.1/chartflow.egg-info/requires.txt +4 -0
  81. chartflow-0.1/chartflow.egg-info/top_level.txt +1 -0
  82. chartflow-0.1/setup.cfg +4 -0
  83. chartflow-0.1/setup.py +27 -0
chartflow-0.1/PKG-INFO ADDED
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: chartflow
3
+ Version: 0.1
4
+ Summary: Self-use. with fsm. node. chart. edit. Used for fsm and run, structure graph. use pyqt to show and interact
5
+ Author-email: 2229066748@qq.com
6
+ Maintainer: Eagle'sBaby
7
+ Maintainer-email: 2229066748@qq.com
8
+ License: Apache Licence 2.0
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: PyQt6
13
+ Requires-Dist: efr
14
+ Requires-Dist: colorama
15
+ Requires-Dist: Pillow
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: license
19
+ Dynamic: maintainer
20
+ Dynamic: maintainer-email
21
+ Dynamic: requires-dist
22
+ Dynamic: requires-python
23
+ Dynamic: summary
@@ -0,0 +1,28 @@
1
+ """
2
+ chartflow - Finite state machine + flow chart library
3
+
4
+ Standalone extraction of qwork's qfsm (state-machine engine) and qflow
5
+ (flow-chart / node-graph layer), bundled with the qnode (node canvas) and
6
+ pyedit (Python code editor) widgets they rely on, so the whole package is
7
+ self-contained.
8
+
9
+ Subpackages:
10
+ chartflow.fsm - state machine: Stage, Logic, Scheduler, decorators, events
11
+ chartflow.flow - flow charts: IChart/Chart, QFlowChart, StageEditor
12
+ chartflow.node - node canvas GUI (NodeCanvas, NodeItem, ...)
13
+ chartflow.edit - Python code editor widget (PythonEditor, ...)
14
+
15
+ Importing the top-level package re-exports the fsm and flow APIs::
16
+
17
+ from chartflow import Stage, Logic, Scheduler, QFlowChart
18
+
19
+ The node and edit widgets are imported explicitly when needed::
20
+
21
+ from chartflow.node import NodeCanvas
22
+ from chartflow.edit import PythonEditor
23
+ """
24
+
25
+ from chartflow.fsm import * # noqa: F401,F403
26
+ from chartflow.flow import * # noqa: F401,F403
27
+
28
+ __version__ = "0.1.0"
@@ -0,0 +1,169 @@
1
+ """验证 import/export 全量 bug 修复的往返(round-trip)脚本。
2
+
3
+ 对每个 bug: 构造对应画布状态 -> export -> import 到新画布 -> 断言属性保留。
4
+ """
5
+ import os
6
+ os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
7
+
8
+ import copy
9
+ from PyQt6.QtGui import QColor, QPen
10
+ from PyQt6.QtCore import Qt, QPointF
11
+ from PyQt6.QtWidgets import QApplication
12
+
13
+ from chartflow.node import (
14
+ QNodeCanvas, QNodeItem, NodeShape, ConnectionType,
15
+ )
16
+ from chartflow.node.enums import ConnectionType as CT
17
+
18
+ app = QApplication([])
19
+
20
+
21
+ def make_canvas():
22
+ c = QNodeCanvas()
23
+ c.resize(800, 600)
24
+ c.show()
25
+ app.processEvents()
26
+ return c
27
+
28
+
29
+ def roundtrip(canvas):
30
+ """export -> json -> import 到全新画布, 返回新画布。"""
31
+ import json
32
+ js = canvas.exportToJson()
33
+ new = make_canvas()
34
+ new.importData(js)
35
+ return new
36
+
37
+
38
+ def check(name, cond, detail=""):
39
+ status = "PASS" if cond else "FAIL"
40
+ print(f"[{status}] {name}" + (f" -- {detail}" if detail and not cond else ""))
41
+ if not cond:
42
+ check.failed += 1
43
+ check.failed = 0
44
+
45
+
46
+ # ---- Bug 1: decorator 往返 ----
47
+ c = make_canvas()
48
+ n = c.addNode("A", QPointF(50, 50))
49
+ n.decorator = "recursive"
50
+ n2 = c.addNode("B", QPointF(200, 50))
51
+ n2.decorator = "behavior"
52
+ new = roundtrip(c)
53
+ ns = {x.name: x for x in new._nodes}
54
+ check("Bug1 decorator 往返", ns["A"].decorator == "recursive" and ns["B"].decorator == "behavior",
55
+ f"A={ns['A'].decorator!r} B={ns['B'].decorator!r}")
56
+
57
+ # ---- Bug 2: custom 标记可见性 ----
58
+ c = make_canvas()
59
+ n = c.addNode("A", QPointF(50, 50))
60
+ n.setCustomMarkerStyle(text="★", bg_color=QColor(255, 0, 0))
61
+ n.setCustomMarkerVisible(True)
62
+ app.processEvents()
63
+ new = roundtrip(c)
64
+ nA = {x.name: x for x in new._nodes}["A"]
65
+ check("Bug2 custom 标记可见性往返",
66
+ nA._is_custom_marker_visible is True,
67
+ f"visible={nA._is_custom_marker_visible}, marker存在={nA._custom_marker is not None}")
68
+
69
+ # ---- Bug 3: single 标记可见性 ----
70
+ c = make_canvas()
71
+ n = c.addNode("A", QPointF(50, 50))
72
+ n.setSingleMarkerVisible(True)
73
+ app.processEvents()
74
+ new = roundtrip(c)
75
+ nA = {x.name: x for x in new._nodes}["A"]
76
+ check("Bug3 single 标记可见性往返", nA._is_single_marker_visible is True,
77
+ f"visible={nA._is_single_marker_visible}")
78
+
79
+ # ---- Bug 4: stroke_width<=0 (无边框) 还原为 NoPen ----
80
+ c = make_canvas()
81
+ n = c.addNode("A", QPointF(50, 50))
82
+ n.setBorder(0) # <=0 -> NoPen
83
+ assert n.pen().style() == QPen(Qt.PenStyle.NoPen).style(), "前置: setBorder(0) 应为 NoPen"
84
+ new = roundtrip(c)
85
+ nA = {x.name: x for x in new._nodes}["A"]
86
+ check("Bug4 无边框(NoPen)往返", nA.pen().style() == QPen(Qt.PenStyle.NoPen).style(),
87
+ f"pen style={nA.pen().style()}")
88
+
89
+ # ---- Bug 5: 连线 style(颜色/线宽) 往返 ----
90
+ c = make_canvas()
91
+ a = c.addNode("A", QPointF(50, 50))
92
+ b = c.addNode("B", QPointF(300, 50))
93
+ conn = c.addConnection(a, b)
94
+ conn._style.normal_color = QColor("#FF00FF")
95
+ conn._style.stroke_width = 5.0
96
+ conn._setupStyle()
97
+ new = roundtrip(c)
98
+ if new._connections:
99
+ nc = new._connections[0]
100
+ check("Bug5 连线 normal_color 往返", nc._style.normal_color.name() == "#ff00ff",
101
+ f"got {nc._style.normal_color.name()}")
102
+ check("Bug5 连线 stroke_width 往返", nc._style.stroke_width == 5.0,
103
+ f"got {nc._style.stroke_width}")
104
+ else:
105
+ check("Bug5 连线存在", False, "导入后无连线")
106
+
107
+ # ---- Bug 6: connectable=False 端点的连接被跳过(警告, 非崩溃) ----
108
+ import io, contextlib
109
+ c = make_canvas()
110
+ a = c.addNode("A", QPointF(50, 50))
111
+ b = c.addNode("B", QPointF(300, 50))
112
+ # 构造一份"带连接但 B 不可连线"的外部 JSON, 模拟手工/历史数据
113
+ js = c.exportToJson()
114
+ import json
115
+ data = json.loads(js)
116
+ # 诊断: 确认连接确实被导出
117
+ check("Bug6 诊断 连接已导出", len(data.get("connections", [])) == 1,
118
+ f"connections count={len(data.get('connections', []))}")
119
+ # 把 B 的 connectable 置 False, 但保留连接(正常流程不可能, 模拟外部数据)
120
+ for nd in data["nodes"]:
121
+ if nd["name"] == "B":
122
+ nd["connectable"] = False
123
+ buf = io.StringIO()
124
+ new = make_canvas()
125
+ with contextlib.redirect_stdout(buf):
126
+ new.importData(json.dumps(data))
127
+ out = buf.getvalue()
128
+ check("Bug6 connectable=False 跳过时打印警告",
129
+ "警告" in out and "connectable=False" in out,
130
+ f"stdout={out!r}")
131
+ # 诊断: 确认 B 导入后确实不可连线(否则警告条件根本不触发)
132
+ bn = {x.name: x for x in new._nodes}["B"]
133
+ check("Bug6 诊断 B 导入后 connectable=False", not bn.isConnectable(),
134
+ f"B connectable={bn.isConnectable()}")
135
+ # 且不应崩溃, A/B 节点都在
136
+ check("Bug6 跳过连接不崩溃",
137
+ {x.name for x in new._nodes} == {"A", "B"})
138
+
139
+ # ---- Bug 7: data 浅拷贝 -> deepcopy ----
140
+ c = make_canvas()
141
+ n = c.addNode("A", QPointF(50, 50))
142
+ n.data["targets"] = ["B", "C"]
143
+ d = c.export() # dict 形式
144
+ # 原地修改 export 返回的 dict 中的 list, 不应污染节点
145
+ d["nodes"][0]["data"]["targets"].append("D")
146
+ check("Bug7 data deepcopy 不污染原节点",
147
+ n.data["targets"] == ["B", "C"],
148
+ f"原节点 targets={n.data['targets']}")
149
+
150
+ # ---- Bug 8: 布局失败不再被误报为"非 json 非 dict" ----
151
+ # 通过让 autoLayout 抛异常来验证(模拟)。直接验证 _importDict 路径。
152
+ c = make_canvas()
153
+ flat = {"X": {"code": "1", "targets": ["Y"]}, "Y": {"code": "2", "targets": []}}
154
+ buf = io.StringIO()
155
+ new = make_canvas()
156
+ with contextlib.redirect_stdout(buf):
157
+ new.importData(copy.deepcopy(flat))
158
+ out = buf.getvalue()
159
+ # 不应出现旧的误导性文案; 布局即使失败也只是"布局失败"告警
160
+ check("Bug8 不再误报 'neither a json or a dict'",
161
+ "neither a json or a dict" not in out,
162
+ f"stdout={out!r}")
163
+
164
+ print(f"\n{'='*50}")
165
+ print(f"总计失败: {check.failed}")
166
+ if check.failed:
167
+ print("存在失败项, 请检查上方 FAIL 详情。")
168
+ else:
169
+ print("全部验证通过 ✅")
@@ -0,0 +1,119 @@
1
+ """
2
+ PyEdit - Python代码编辑器模块
3
+
4
+ 基于PyQt6和Python 3.12的高级Python代码编辑器
5
+
6
+ 功能特性:
7
+ - Python语法高亮(浅色主题)
8
+ - 智能代码补全(类似PyCharm)
9
+ - 上下文分析(支持隐藏的上下文代码)
10
+ - 代码折叠(Ctrl++ / Ctrl+-)
11
+ - 行号显示
12
+ - 缩略图滑块
13
+
14
+ 使用示例:
15
+ from chartflow.edit import PythonEditor
16
+
17
+ editor = PythonEditor()
18
+
19
+ # 设置上下文代码(隐藏但参与分析)
20
+ editor.set_context_code(
21
+ code_a="class A(CL):\\n NAME = ''\\n ...",
22
+ code_b="def myfn(self, c:OuterClassRef, k: KnowRef, *refs):\\n v = 0",
23
+ prefix=" "
24
+ )
25
+
26
+ # 设置编辑器中的代码
27
+ editor.set_code("self.do_something()")
28
+
29
+ # 获取代码
30
+ code = editor.get_code()
31
+ """
32
+
33
+ __version__ = '1.0.0'
34
+ __author__ = 'PyEdit Team'
35
+
36
+ # 主要组件
37
+ from .python_editor import (
38
+ PythonEditor,
39
+ PythonEditorDialog,
40
+ PythonEditorFactory,
41
+ create_editor,
42
+ create_editor_with_context,
43
+ )
44
+
45
+ from .code_editor import CodeEditor
46
+ from .syntax_highlighter import PythonSyntaxHighlighter
47
+ from .completer import (
48
+ CodeCompleter,
49
+ SmartCompleter,
50
+ CompletionItem,
51
+ CompletionWorker,
52
+ )
53
+ from .context_analyzer import (
54
+ ContextAnalyzer,
55
+ ContextInfo,
56
+ CodeContext,
57
+ ScopeAnalyzer,
58
+ )
59
+ from .code_folder import (
60
+ CodeFolder,
61
+ FoldRegion,
62
+ FoldMarkerPainter,
63
+ get_fold_indicator,
64
+ )
65
+ from .line_number_area import (
66
+ LineNumberArea,
67
+ LineNumberDelegate,
68
+ FoldingIndicator,
69
+ )
70
+ from .minimap import (
71
+ MinimapWidget,
72
+ MinimapSlider,
73
+ MinimapPanel,
74
+ CodeOverview,
75
+ )
76
+
77
+ __all__ = [
78
+ # 主编辑器
79
+ 'PythonEditor',
80
+ 'PythonEditorDialog',
81
+ 'PythonEditorFactory',
82
+ 'create_editor',
83
+ 'create_editor_with_context',
84
+
85
+ # 代码编辑器组件
86
+ 'CodeEditor',
87
+
88
+ # 语法高亮
89
+ 'PythonSyntaxHighlighter',
90
+
91
+ # 代码补全
92
+ 'CodeCompleter',
93
+ 'SmartCompleter',
94
+ 'CompletionItem',
95
+ 'CompletionWorker',
96
+
97
+ # 上下文分析
98
+ 'ContextAnalyzer',
99
+ 'ContextInfo',
100
+ 'CodeContext',
101
+ 'ScopeAnalyzer',
102
+
103
+ # 代码折叠
104
+ 'CodeFolder',
105
+ 'FoldRegion',
106
+ 'FoldMarkerPainter',
107
+ 'get_fold_indicator',
108
+
109
+ # 行号
110
+ 'LineNumberArea',
111
+ 'LineNumberDelegate',
112
+ 'FoldingIndicator',
113
+
114
+ # 缩略图
115
+ 'MinimapWidget',
116
+ 'MinimapSlider',
117
+ 'MinimapPanel',
118
+ 'CodeOverview',
119
+ ]