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
chartflow/fsm/demo.py ADDED
@@ -0,0 +1,381 @@
1
+ from __future__ import annotations
2
+ """
3
+ QFSM Demo - 完整示例程序
4
+
5
+ 展示:
6
+ 1. 基本的FSM状态切换
7
+ 2. 事件系统通信
8
+ 3. 优先级调度
9
+ 4. 实例间引用
10
+ 5. 生命周期管理
11
+
12
+ 场景: 简单的游戏AI系统
13
+ - Player: 玩家角色,可以移动和攻击
14
+ - Enemy: 敌人AI,有巡逻、追击、攻击状态
15
+ - GameManager: 游戏管理器,处理游戏事件
16
+ """
17
+
18
+
19
+ import time
20
+ from typing import Any
21
+
22
+ from chartflow.fsm.logic import Logic
23
+ from chartflow.fsm.scheduler import Scheduler
24
+
25
+
26
+ class GameEntity:
27
+ """游戏实体基类,模拟游戏对象"""
28
+ def __init__(self, name: str, x: float = 0, y: float = 0):
29
+ self.name = name
30
+ self.x = x
31
+ self.y = y
32
+ self.health = 100
33
+ self.is_active = True
34
+
35
+ def distance_to(self, other: GameEntity) -> float:
36
+ """计算到另一个实体的距离"""
37
+ return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
38
+
39
+ def __repr__(self) -> str:
40
+ return f"{self.name}({self.x:.1f}, {self.y:.1f})"
41
+
42
+
43
+ class GameManager(Logic):
44
+ """
45
+ 游戏管理器 - 最高优先级
46
+ 负责管理游戏整体状态和事件分发
47
+ """
48
+ NAME = "GameManager"
49
+ PRIORITY = 100 # 最高优先级,先执行
50
+
51
+ def __init__(self) -> None:
52
+ super().__init__(None)
53
+ self.tick_count = 0
54
+ self.game_over = False
55
+ print("[GameManager] 游戏管理器启动")
56
+
57
+ def onStart(self) -> None:
58
+ """游戏开始时调用"""
59
+ print("[GameManager] 游戏开始!")
60
+ # 监听游戏事件
61
+ self.listenFor("enemy_died", self._on_enemy_died)
62
+ self.listenFor("player_died", self._on_player_died)
63
+
64
+ def _on_enemy_died(self, data: Any) -> None:
65
+ """敌人死亡事件处理"""
66
+ enemy_name = data.get("name", "Unknown") if isinstance(data, dict) else data
67
+ print(f"[GameManager] 敌人 {enemy_name} 被消灭!")
68
+
69
+ def _on_player_died(self, data: Any) -> None:
70
+ """玩家死亡事件处理"""
71
+ print("[GameManager] 玩家死亡! 游戏结束!")
72
+ self.game_over = True
73
+
74
+ def onStep(self) -> None:
75
+ """每tick执行"""
76
+ self.tick_count += 1
77
+ if self.tick_count % 10 == 0:
78
+ print(f"[GameManager] === Tick {self.tick_count} ===")
79
+
80
+ def idle(self) -> str | None:
81
+ """空闲状态 - 检查游戏是否结束"""
82
+ if self.game_over:
83
+ return "game_over"
84
+ return None
85
+
86
+ def game_over(self) -> str | None:
87
+ """游戏结束状态"""
88
+ print("[GameManager] 游戏结束状态")
89
+ # 通知所有对象清理
90
+ self.pushEvent("game_over", None)
91
+ self.kill() # 结束管理器
92
+ return None
93
+
94
+
95
+ class Player(Logic):
96
+ """
97
+ 玩家控制器
98
+ 状态: idle -> move -> attack -> idle
99
+ """
100
+ NAME = "Player"
101
+ PRIORITY = 50
102
+
103
+ def __init__(self, entity: GameEntity) -> None:
104
+ super().__init__(entity)
105
+ self._inst: GameEntity = entity
106
+ self.target: GameEntity | None = None
107
+ self.attack_cooldown = 0
108
+ print(f"[Player] 玩家 {entity.name} 创建在 {entity}")
109
+
110
+ def onStart(self) -> None:
111
+ print(f"[Player] 玩家 {self._inst.name} 准备就绪")
112
+
113
+ def onStep(self) -> None:
114
+ """每tick更新"""
115
+ if self.attack_cooldown > 0:
116
+ self.attack_cooldown -= 1
117
+
118
+ def idle(self) -> str | None:
119
+ """空闲状态 - 寻找最近的敌人"""
120
+ # 获取所有敌人实例
121
+ enemies = self.ref("Enemy")
122
+ if enemies:
123
+ if isinstance(enemies, list):
124
+ # 找到最近的敌人
125
+ self.target = min(enemies, key=lambda e: e._inst.distance_to(self._inst))._inst
126
+ else:
127
+ self.target = enemies._inst
128
+
129
+ distance = self._inst.distance_to(self.target)
130
+ if distance > 2.0:
131
+ return "move"
132
+ else:
133
+ return "attack"
134
+ return None
135
+
136
+ def move(self) -> str | None:
137
+ """移动状态 - 向目标移动"""
138
+ if not self.target or self.target.health <= 0:
139
+ return "idle"
140
+
141
+ # 简单的移动逻辑
142
+ dx = self.target.x - self._inst.x
143
+ dy = self.target.y - self._inst.y
144
+ distance = (dx ** 2 + dy ** 2) ** 0.5
145
+
146
+ if distance <= 2.0:
147
+ return "attack"
148
+
149
+ # 移动 (归一化方向 * 速度)
150
+ speed = 1.5
151
+ if distance > 0:
152
+ self._inst.x += (dx / distance) * speed
153
+ self._inst.y += (dy / distance) * speed
154
+
155
+ print(f"[Player] 移动到 ({self._inst.x:.1f}, {self._inst.y:.1f}), "
156
+ f"目标距离: {distance:.1f}")
157
+ return "move"
158
+
159
+ def attack(self) -> str | None:
160
+ """攻击状态"""
161
+ if not self.target or self.target.health <= 0:
162
+ return "idle"
163
+
164
+ if self.attack_cooldown > 0:
165
+ return "attack"
166
+
167
+ # 执行攻击
168
+ damage = 25
169
+ self.target.health -= damage
170
+ self.attack_cooldown = 3 # 3 tick冷却
171
+
172
+ print(f"[Player] 攻击 {self.target.name} 造成 {damage} 伤害! "
173
+ f"剩余生命: {self.target.health}")
174
+
175
+ if self.target.health <= 0:
176
+ # 发送敌人死亡事件
177
+ self.pushEvent("enemy_died", {"name": self.target.name})
178
+ self.target = None
179
+ return "idle"
180
+
181
+ return "attack"
182
+
183
+
184
+ class Enemy(Logic):
185
+ """
186
+ 敌人AI
187
+ 状态: patrol -> chase -> attack -> patrol
188
+ """
189
+ NAME = "Enemy"
190
+ PRIORITY = 40
191
+
192
+ def __init__(self, entity: GameEntity, patrol_points: list[tuple[float, float]]) -> None:
193
+ super().__init__(entity)
194
+ self._inst: GameEntity = entity
195
+ self.patrol_points = patrol_points
196
+ self.current_point = 0
197
+ self.player: Player | None = None
198
+ self.attack_cooldown = 0
199
+ self.alert_range = 5.0
200
+ self.attack_range = 2.0
201
+ print(f"[Enemy] 敌人 {entity.name} 创建在 {entity}")
202
+
203
+ def onStart(self) -> None:
204
+ print(f"[Enemy] 敌人 {self._inst.name} 开始巡逻")
205
+ # 监听游戏结束事件
206
+ self.listenFor("game_over", self._on_game_over)
207
+
208
+ def _on_game_over(self, data: Any) -> None:
209
+ """游戏结束时停止"""
210
+ print(f"[Enemy] {self._inst.name} 收到游戏结束信号")
211
+ self.kill()
212
+
213
+ def onStep(self) -> None:
214
+ """每tick更新"""
215
+ if self.attack_cooldown > 0:
216
+ self.attack_cooldown -= 1
217
+
218
+ # 死亡检查
219
+ if self._inst.health <= 0:
220
+ self.kill()
221
+
222
+ def _find_player(self) -> Player | None:
223
+ """查找玩家实例"""
224
+ player = self.ref("Player")
225
+ return player[0] if isinstance(player, list) else player
226
+
227
+ def patrol(self) -> str | None:
228
+ """巡逻状态"""
229
+ # 检查玩家是否在警戒范围
230
+ player = self._find_player()
231
+ if player and player._inst.health > 0:
232
+ distance = self._inst.distance_to(player._inst)
233
+ if distance <= self.alert_range:
234
+ print(f"[Enemy] {self._inst.name} 发现玩家! 距离: {distance:.1f}")
235
+ self.player = player
236
+ return "chase"
237
+
238
+ # 巡逻移动
239
+ target = self.patrol_points[self.current_point]
240
+ dx = target[0] - self._inst.x
241
+ dy = target[1] - self._inst.y
242
+ distance = (dx ** 2 + dy ** 2) ** 0.5
243
+
244
+ if distance < 0.5:
245
+ # 到达巡逻点,前往下一个
246
+ self.current_point = (self.current_point + 1) % len(self.patrol_points)
247
+ print(f"[Enemy] {self._inst.name} 到达巡逻点,前往下一个")
248
+ else:
249
+ # 移动
250
+ speed = 0.8
251
+ self._inst.x += (dx / distance) * speed
252
+ self._inst.y += (dy / distance) * speed
253
+
254
+ return None
255
+
256
+ def chase(self) -> str | None:
257
+ """追击状态"""
258
+ if not self.player or self.player._inst.health <= 0:
259
+ return "patrol"
260
+
261
+ distance = self._inst.distance_to(self.player._inst)
262
+
263
+ # 如果距离太远,返回巡逻
264
+ if distance > self.alert_range * 1.5:
265
+ print(f"[Enemy] {self._inst.name} 丢失目标")
266
+ self.player = None
267
+ return "patrol"
268
+
269
+ # 如果在攻击范围内,切换攻击
270
+ if distance <= self.attack_range:
271
+ return "attack"
272
+
273
+ # 向玩家移动
274
+ dx = self.player._inst.x - self._inst.x
275
+ dy = self.player._inst.y - self._inst.y
276
+ speed = 1.2
277
+ self._inst.x += (dx / distance) * speed
278
+ self._inst.y += (dy / distance) * speed
279
+
280
+ print(f"[Enemy] {self._inst.name} 追击玩家到 ({self._inst.x:.1f}, {self._inst.y:.1f})")
281
+ return "chase"
282
+
283
+ def attack(self) -> str | None:
284
+ """攻击状态"""
285
+ if not self.player or self.player._inst.health <= 0:
286
+ return "patrol"
287
+
288
+ distance = self._inst.distance_to(self.player._inst)
289
+ if distance > self.attack_range:
290
+ return "chase"
291
+
292
+ if self.attack_cooldown > 0:
293
+ return "attack"
294
+
295
+ # 执行攻击
296
+ damage = 15
297
+ self.player._inst.health -= damage
298
+ self.attack_cooldown = 4
299
+
300
+ print(f"[Enemy] {self._inst.name} 攻击玩家造成 {damage} 伤害! "
301
+ f"玩家剩余生命: {self.player._inst.health}")
302
+
303
+ if self.player._inst.health <= 0:
304
+ self.pushEvent("player_died", {"name": self.player._inst.name})
305
+
306
+ return "attack"
307
+
308
+ def onStop(self) -> None:
309
+ print(f"[Enemy] 敌人 {self._inst.name} 被销毁")
310
+
311
+
312
+ def main():
313
+ """主程序 - 运行Demo"""
314
+ print("=" * 60)
315
+ print("QFSM Demo - 游戏AI状态机示例")
316
+ print("=" * 60)
317
+
318
+ # 创建调度器
319
+ scheduler = Scheduler()
320
+
321
+ # 注册所有逻辑类
322
+ scheduler.login(GameManager)
323
+ scheduler.login(Player)
324
+ scheduler.login(Enemy)
325
+
326
+ # 创建游戏管理器
327
+ game_manager = GameManager()
328
+
329
+ # 创建玩家
330
+ player_entity = GameEntity("Hero", x=0, y=0)
331
+ player = Player(player_entity)
332
+
333
+ # 创建敌人
334
+ enemy1_entity = GameEntity("Slime", x=10, y=10)
335
+ enemy1 = Enemy(enemy1_entity, patrol_points=[(10, 10), (15, 10), (15, 15), (10, 15)])
336
+
337
+ enemy2_entity = GameEntity("Goblin", x=-8, y=8)
338
+ enemy2 = Enemy(enemy2_entity, patrol_points=[(-8, 8), (-5, 8), (-5, 12), (-8, 12)])
339
+
340
+ print("\n" + "=" * 60)
341
+ print("开始游戏循环 (最多50个tick)")
342
+ print("=" * 60 + "\n")
343
+
344
+ # 游戏主循环
345
+ max_ticks = 50
346
+ tick_delay = 0.3 # 每个tick之间的延迟(秒)
347
+
348
+ for tick in range(max_ticks):
349
+ if not game_manager.alive:
350
+ print("\n[Main] 游戏管理器已停止")
351
+ break
352
+
353
+ scheduler.handle() # ← 执行一个tick
354
+
355
+ # 打印当前状态
356
+ print(f"\n[Tick {tick + 1}] 玩家HP: {player_entity.health}, "
357
+ f"敌人1 HP: {enemy1_entity.health}, 敌人2 HP: {enemy2_entity.health}")
358
+
359
+ # 检查是否所有敌人都被消灭
360
+ if not enemy1.alive and not enemy2.alive:
361
+ print("\n[Main] 所有敌人都被消灭! 胜利!")
362
+ break
363
+
364
+ # 玩家死亡检查
365
+ if player_entity.health <= 0:
366
+ print("\n[Main] 玩家死亡! 游戏结束!")
367
+ break
368
+
369
+ time.sleep(tick_delay)
370
+
371
+ print("\n" + "=" * 60)
372
+ print("Demo 结束")
373
+ print(f"总tick数: {game_manager.tick_count}")
374
+ print(f"玩家最终状态: {player_entity}")
375
+ print(f"敌人1存活: {enemy1.alive}")
376
+ print(f"敌人2存活: {enemy2.alive}")
377
+ print("=" * 60)
378
+
379
+
380
+ if __name__ == "__main__":
381
+ main()