statable 3.1.0__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 (117) hide show
  1. codegen/__init__.py +38 -0
  2. codegen/c_code_generator.py +1752 -0
  3. codegen/code_merger.py +371 -0
  4. codegen/code_templates.py +509 -0
  5. codegen/config.py +167 -0
  6. codegen/enum_generator.py +377 -0
  7. codegen/event_queue_generator.py +303 -0
  8. codegen/interrupt_generator.py +345 -0
  9. codegen/naming_convention.py +171 -0
  10. codegen/osal_generator.py +282 -0
  11. codegen/role_function_generator.py +1244 -0
  12. codegen/sample_data.py +269 -0
  13. codegen/state_actions_generator.py +395 -0
  14. codegen/struct_generator.py +721 -0
  15. codegen/timer_generator.py +299 -0
  16. codegen/transition_generator.py +907 -0
  17. codegen/type_mapper.py +69 -0
  18. codegen/validate/__init__.py +38 -0
  19. codegen/validate/change_actions.py +90 -0
  20. codegen/validate/change_applier.py +416 -0
  21. codegen/validate/clipboard_manager.py +47 -0
  22. codegen/validate/data/__init__.py +30 -0
  23. codegen/validate/data/action_definitions.py +200 -0
  24. codegen/validate/data/keywords.py +46 -0
  25. codegen/validate/data/prompt_templates.py +176 -0
  26. codegen/validate/data/validation_rules.py +205 -0
  27. codegen/validate/items/__init__.py +30 -0
  28. codegen/validate/items/base_validator.py +25 -0
  29. codegen/validate/items/cell_validator.py +275 -0
  30. codegen/validate/items/custom_type_validator.py +75 -0
  31. codegen/validate/items/event_validator.py +78 -0
  32. codegen/validate/items/flag_validator.py +74 -0
  33. codegen/validate/items/interrupt_validator.py +75 -0
  34. codegen/validate/items/queue_validator.py +73 -0
  35. codegen/validate/items/role_function_validator.py +175 -0
  36. codegen/validate/items/state_validator.py +88 -0
  37. codegen/validate/items/timer_validator.py +120 -0
  38. codegen/validate/items/transition_validator.py +113 -0
  39. codegen/validate/items/variable_validator.py +132 -0
  40. codegen/validate/logger.py +52 -0
  41. codegen/validate/models.py +177 -0
  42. codegen/validate/prompt_generator.py +724 -0
  43. codegen/validate/response_parser.py +154 -0
  44. codegen/validate/response_validator.py +275 -0
  45. codegen/validate/validation_dialog.py +322 -0
  46. codegen/validate/validator.py +94 -0
  47. codegen/variable_generator.py +419 -0
  48. statable/__init__.py +71 -0
  49. statable/__main__.py +49 -0
  50. statable/cli.py +251 -0
  51. statable/global_defs.py +256 -0
  52. statable/mermaid_gen.py +144 -0
  53. statable/model.py +426 -0
  54. statable/parser.py +64 -0
  55. statable/sample_data.py +435 -0
  56. statable/shared/__init__.py +24 -0
  57. statable/shared/condition_library.py +63 -0
  58. statable/shared/literal_library.py +66 -0
  59. statable/shared/role_function_library.py +150 -0
  60. statable/state_machine.py +121 -0
  61. statable/xml_io.py +1164 -0
  62. statable-3.1.0.dist-info/METADATA +151 -0
  63. statable-3.1.0.dist-info/RECORD +117 -0
  64. statable-3.1.0.dist-info/WHEEL +5 -0
  65. statable-3.1.0.dist-info/entry_points.txt +2 -0
  66. statable-3.1.0.dist-info/top_level.txt +3 -0
  67. statable_gui/__init__.py +23 -0
  68. statable_gui/action_edit_dialog.py +214 -0
  69. statable_gui/code_generation_dialog.py +486 -0
  70. statable_gui/code_generation_settings_dialog.py +522 -0
  71. statable_gui/common_widgets.py +537 -0
  72. statable_gui/condition_builder_dialog.py +500 -0
  73. statable_gui/condition_edit_dialog.py +123 -0
  74. statable_gui/condition_template_dialog.py +58 -0
  75. statable_gui/config.py +21 -0
  76. statable_gui/dialogs.py +439 -0
  77. statable_gui/event_definition_dialog.py +605 -0
  78. statable_gui/event_delivery_settings_dialog.py +191 -0
  79. statable_gui/event_queue_dialog.py +245 -0
  80. statable_gui/global_defs.py +167 -0
  81. statable_gui/global_defs_dialog.py +796 -0
  82. statable_gui/i18n/__init__.py +118 -0
  83. statable_gui/interrupt_handler_edit_dialog.py +810 -0
  84. statable_gui/layer_settings_dialog.py +180 -0
  85. statable_gui/libcntrl/__init__.py +23 -0
  86. statable_gui/libcntrl/condition_library.py +13 -0
  87. statable_gui/libcntrl/literal_library.py +13 -0
  88. statable_gui/libcntrl/literal_management_dialog.py +185 -0
  89. statable_gui/libcntrl/role_function_edit_dialog.py +145 -0
  90. statable_gui/libcntrl/role_function_library.py +13 -0
  91. statable_gui/literal_definition_dialog.py +62 -0
  92. statable_gui/logger.py +83 -0
  93. statable_gui/main_window.py +1380 -0
  94. statable_gui/matrix_table.py +413 -0
  95. statable_gui/preference_keys.py +18 -0
  96. statable_gui/preferences.py +74 -0
  97. statable_gui/role_function_dialog.py +244 -0
  98. statable_gui/sample_data.py +11 -0
  99. statable_gui/state_actions_dialog.py +398 -0
  100. statable_gui/symbol_picker.py +170 -0
  101. statable_gui/traceball.py +37 -0
  102. statable_gui/transition_editor_direct/__init__.py +15 -0
  103. statable_gui/transition_editor_direct/actions_tab.py +856 -0
  104. statable_gui/transition_editor_direct/code_widget.py +344 -0
  105. statable_gui/transition_editor_direct/coverage_analyzer.py +163 -0
  106. statable_gui/transition_editor_direct/dialog.py +335 -0
  107. statable_gui/transition_editor_direct/draft.py +270 -0
  108. statable_gui/transition_editor_direct/edit_dialogs.py +160 -0
  109. statable_gui/transition_editor_direct/overview_tab.py +173 -0
  110. statable_gui/transition_editor_direct/palette_widget.py +233 -0
  111. statable_gui/transition_editor_direct/relations_edit_dialog.py +156 -0
  112. statable_gui/transition_editor_direct/relations_tab.py +230 -0
  113. statable_gui/transition_editor_direct/system_global_dialog.py +52 -0
  114. statable_gui/transition_editor_direct/transition_actions_dialog.py +109 -0
  115. statable_gui/transition_editor_direct/transitions_tab.py +620 -0
  116. statable_gui/validation_dialog.py +285 -0
  117. statable_gui/widgets.py +1315 -0
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: statable
3
+ Version: 3.1.0
4
+ Summary: MISRA C:2012-aware state machine design and C code generation for embedded systems
5
+ Author: Hideki Akashi
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/akashi-hideki/StaTable
8
+ Project-URL: Repository, https://github.com/akashi-hideki/StaTable
9
+ Project-URL: Issues, https://github.com/akashi-hideki/StaTable/issues
10
+ Keywords: state machine,MISRA,embedded,code generation,C
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Code Generators
19
+ Classifier: Topic :: Software Development :: Embedded Systems
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Provides-Extra: gui
23
+ Requires-Dist: PySide6>=6.0; extra == "gui"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == "dev"
26
+ Requires-Dist: pycparser; extra == "dev"
27
+
28
+ # StaTable
29
+
30
+ **MISRA C:2012-aware state machine design and C code generation for embedded systems.**
31
+
32
+ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/akashi-hideki/StaTable/blob/main/LICENSE)
33
+ [![Python](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://www.python.org/)
34
+ [![Tests](https://img.shields.io/badge/Tests-1479%20PASS-green.svg)](https://github.com/akashi-hideki/StaTable)
35
+ [![MISRA](https://img.shields.io/badge/MISRA-C%3A2012-orange.svg)](https://www.misra.org.uk/)
36
+
37
+ StaTable is a Python-based tool for designing state machines and
38
+ generating **MISRA C:2012-compliant C code** for embedded systems.
39
+ It provides a CLI, a Python SDK, and a PySide6 GUI.
40
+
41
+ ---
42
+
43
+ ## Features
44
+
45
+ - **MISRA C:2012-aware C code generation** — validated with `cppcheck` + official MISRA addon
46
+ - **Multi-layer state machines** — Driver / Middleware / Application with priority execution
47
+ - **State actions (Entry / Exit / Do)** — table-driven dispatch with user-code markers
48
+ - **35 validation rules** across 11 categories with AI-assisted diagnostics
49
+ - **Marker-based user code preservation** — regenerate without losing your custom code
50
+ - **CLI + SDK + GUI** — integrate into any workflow
51
+ - **Pure Python** — easy to install and extend
52
+ - **i18n**: English and Simplified Chinese (简体中文)
53
+
54
+ ---
55
+
56
+ ## Installation
57
+
58
+ **SDK + CLI only:**
59
+
60
+ ```
61
+ pip install statable
62
+ ```
63
+
64
+ **With GUI (PySide6):**
65
+
66
+ ```
67
+ pip install "statable[gui]"
68
+ ```
69
+
70
+ **Development:**
71
+
72
+ ```
73
+ pip install "statable[dev]"
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Quick Start
79
+
80
+ ### CLI
81
+
82
+ ```
83
+ statable-cli version
84
+ statable-cli generate --xml design.xml --out generated/ --format json
85
+ statable-cli validate --xml design.xml --format json --exit-on-error
86
+ ```
87
+
88
+ ### Python SDK
89
+
90
+ ```
91
+ from statable import State, Event, Transition, StateMachine, StateType, EventKind
92
+ from statable.global_defs import GlobalDefinitions
93
+ from codegen import CCodeGenerator, CodeGenerationConfig
94
+
95
+ sm = StateMachine()
96
+ sm.add_state(State(name="Idle", type=StateType.INITIAL))
97
+ sm.add_state(State(name="Running"))
98
+ sm.add_event(Event(name="START", kind=EventKind.SIGNAL))
99
+ sm.set_initial("Idle")
100
+ sm.add_transition(Transition(source="Idle", event="START", target="Running"))
101
+
102
+ cfg = CodeGenerationConfig()
103
+ gen = CCodeGenerator(config=cfg)
104
+ files = gen.generate_all_layers([("Application", sm)], GlobalDefinitions())
105
+ gen.save_generated_code(files, "generated/")
106
+ ```
107
+
108
+ ### GUI
109
+
110
+ ```
111
+ python -m statable
112
+ ```
113
+
114
+ Menu: **Language / 语言** → switch between **English** and **简体中文**.
115
+
116
+ ---
117
+
118
+ ## Requirements
119
+
120
+ - Python 3.10 or later
121
+ - PySide6 >= 6.0 (for GUI)
122
+ - pycparser (optional, for advanced parsing)
123
+
124
+ ---
125
+
126
+ ## Links
127
+
128
+ - **Homepage**: https://github.com/akashi-hideki/StaTable
129
+ - **Documentation**: https://github.com/akashi-hideki/StaTable/blob/main/README.md
130
+ - **Issues**: https://github.com/akashi-hideki/StaTable/issues
131
+ - **Chinese README**: https://github.com/akashi-hideki/StaTable/blob/main/README_zh-CN.md
132
+
133
+ ---
134
+
135
+ ## License
136
+
137
+ Apache License 2.0 — free for commercial and personal use.
138
+
139
+ See [LICENSE](https://github.com/akashi-hideki/StaTable/blob/main/LICENSE) for details.
140
+
141
+ ---
142
+
143
+ ## Acknowledgments
144
+
145
+ - [PySide6](https://www.qt.io/qt-for-python) — Qt 6 for Python
146
+ - [cppcheck](https://cppcheck.sourceforge.io/) — static analysis
147
+ - All contributors and users
148
+
149
+ ---
150
+
151
+ If StaTable helps you, please consider giving it a ⭐ on GitHub.
@@ -0,0 +1,117 @@
1
+ codegen/__init__.py,sha256=v-CDFixuTpGGJ_vVSDQ99PluIaeXpWrqpKraYp-6zIM,1013
2
+ codegen/c_code_generator.py,sha256=ivBce808B7QsxymPI9PhMmgh1vIRMA5dvZVxl-r1V_o,70764
3
+ codegen/code_merger.py,sha256=WZOsOq3e0W4wB4lRo8Hd0yOTIxscARj9UWOkOoRB7cE,13761
4
+ codegen/code_templates.py,sha256=JoWSOSn-9G-72qHbp3fI7fYnZUUlxzhkvi3rOn4D4Qg,20513
5
+ codegen/config.py,sha256=Eux0T-SNVhxXaeca8oi9YTo_2huiVOPfWTDm1o-KgN8,5934
6
+ codegen/enum_generator.py,sha256=7-0np-RX5XfXf3V3RvwbX_rk-2F56J7mAZ6KXE4wdpI,13045
7
+ codegen/event_queue_generator.py,sha256=7fe1BRH-IShuLHL6N4sbm9aBivPS-NCOjlGXeDvvAD4,11746
8
+ codegen/interrupt_generator.py,sha256=xebAzMZ9s7GP7cAvDPuFC70chFSHQog56qBifqLfj9o,13973
9
+ codegen/naming_convention.py,sha256=Agl46ZVlAnadCZSZJi9zpenpJMO0iYfg1xVf_ywrZqY,5226
10
+ codegen/osal_generator.py,sha256=rQIkPZrmic6igXV366v-3rWS9qvlufDNHEn1rL1TWTo,10788
11
+ codegen/role_function_generator.py,sha256=PNwHZxmZfS4doQW7kEMR2IVAxnSdzXHHAx52Scqqlk4,48443
12
+ codegen/sample_data.py,sha256=V0aDFbMRP4BV6cz8Irei-zmhzMJyVqfFvjAviDHzYZE,10465
13
+ codegen/state_actions_generator.py,sha256=OkFOKKHfdwhabF-0_dDY6pQB3F91FjYPEu8980iE-04,15915
14
+ codegen/struct_generator.py,sha256=xlj9myaP-lL6sxKqqfaJxBVG-fLWmA7g6FJKeNU_rNY,30175
15
+ codegen/timer_generator.py,sha256=iOEBwV7vzsSUn8qerbR1rF8qlTzMVOACJbaVrvl1x6Q,11126
16
+ codegen/transition_generator.py,sha256=-Zxle1KVVWVDTtlhxvSlXfWv3ZTzSXFArXdLSgpXI6E,36866
17
+ codegen/type_mapper.py,sha256=gobT9_g6RGeThUd1ScgPpxr4RTb0QdwfdMHm03zr6Zg,2251
18
+ codegen/variable_generator.py,sha256=Xeo9WTdFthfV7IJiBu1h2Z8YxXHdCP24n55PenCZ0ls,16441
19
+ codegen/validate/__init__.py,sha256=s_9FZL3CAwkUKh74BOKmMWXwZPVk45niyl0v5xRT674,1023
20
+ codegen/validate/change_actions.py,sha256=zTvMM2g3x8CUNWpn4fMWuIcTWnnFdxLQMixuZlR9F9I,3068
21
+ codegen/validate/change_applier.py,sha256=bscgXnw3pyxSgr_7vViZ_R1L_P93_5YLNafe_3ny2Ag,17942
22
+ codegen/validate/clipboard_manager.py,sha256=FsgrmfLdR4AED6RSawBw2IOc7fNRL7iJKliwnz-wBVg,1266
23
+ codegen/validate/logger.py,sha256=2kS6hZBX9DX6A7sRgajWzmsCcFEHcoQqIERMyiXGx40,1610
24
+ codegen/validate/models.py,sha256=r2HBsWhcA9h3ikDT25_QVlvMmNGrpv4KACoz08gTskg,5692
25
+ codegen/validate/prompt_generator.py,sha256=_LeUOTtl280boNJmn1YnSmQk1aiSdR1dYMiuMzwEhe8,29766
26
+ codegen/validate/response_parser.py,sha256=YLgPk-77q38LCYVVgqvi5SBxueNUippMgq69VEzFg-Y,5530
27
+ codegen/validate/response_validator.py,sha256=mOuXnONmz4UiO6zdcK0_owRUw1xqftGcSymThc_MvAU,10615
28
+ codegen/validate/validation_dialog.py,sha256=lcwgALR6UxgS0Am6dPBKHUNx6ebejQhJQtsOVkOs4fU,12472
29
+ codegen/validate/validator.py,sha256=I8ZBxScKunw6NGvv9Ub58jJg_L_VmyBaY4PXMyy8nOo,3523
30
+ codegen/validate/data/__init__.py,sha256=NNcxmXtoDcYAnnEctBQRrAaRv2-JXXCwsVqDbebYxnY,649
31
+ codegen/validate/data/action_definitions.py,sha256=Wq6fb5eAmfZ1VtcCwQAicJ10NkzSDYC2LVRkz5Op6k8,9297
32
+ codegen/validate/data/keywords.py,sha256=iCuASfoDxiS-JC6U3AeHKs10SKJNasuE118AUJm979o,1675
33
+ codegen/validate/data/prompt_templates.py,sha256=gF-sxi8cwIVE7isodFaFUY1Ubf99q1_YeWOJe6u1c9Q,5075
34
+ codegen/validate/data/validation_rules.py,sha256=c1g_vb3yhyowhMJuuGaE32DTlPINMeMXHyZ9AN2Gwgc,8183
35
+ codegen/validate/items/__init__.py,sha256=fg4f0XktvE-SZl6syTZjJpHE1CEcqin09n4Y1q-ygG8,969
36
+ codegen/validate/items/base_validator.py,sha256=CwBi0HgfvAohmKM-NHwL3ShN4-0Nm29Xhvf2bxvBYjc,668
37
+ codegen/validate/items/cell_validator.py,sha256=SjYMKN5WX76FogYNnZLmtuvR1t8CtahKbL3O1EV16T4,12188
38
+ codegen/validate/items/custom_type_validator.py,sha256=nirVv9PY69xPj5zosScWjuMgqPMj1mjIdt6Ycg7rE0w,2731
39
+ codegen/validate/items/event_validator.py,sha256=9VH2FXJzqq8Gr69_93lkAfgIxbRVOtgXHhg-ujWUPP0,2889
40
+ codegen/validate/items/flag_validator.py,sha256=OkyzVt7F0J06eVD3hd-PE6c3tkgbPCnPgCVuAGXrv2U,2711
41
+ codegen/validate/items/interrupt_validator.py,sha256=dltTuTSrswgoBq9uxOI5m0ubXmdKaTBegJphmgfNLok,2887
42
+ codegen/validate/items/queue_validator.py,sha256=vhtEVicjyIYukocPHPGoD_Mz4jsIfd9i8rG9CjE4uFI,2760
43
+ codegen/validate/items/role_function_validator.py,sha256=l0lzR0u9Ec-Kb__I-aWQ5j7eYrWAxfmAGPMTuYh8baQ,6963
44
+ codegen/validate/items/state_validator.py,sha256=9ifWpZmT4lxpCur8b95dp7tVBun09Oq8PHyozhAnNAs,3374
45
+ codegen/validate/items/timer_validator.py,sha256=JwYUQvr5HMfjISPBy3-RtbLLh3iGxZLyd4O_9Da1eG8,4946
46
+ codegen/validate/items/transition_validator.py,sha256=AyZeQcS9E34LGbPK1WitAsdvPisOCae6ztad7e0Q6Pw,4339
47
+ codegen/validate/items/variable_validator.py,sha256=JPfCpBJ7LXMLBoOWf9XHOnFjCsFbIXcZaqFGEd7mbCU,4954
48
+ statable/__init__.py,sha256=TUkKfQQvXJQxcvZXTPDQx6rH80oA-safo6iOpAwYMK0,1495
49
+ statable/__main__.py,sha256=cK2t2nNiNgnhMq5k6-ck1uoR1044QGHKz0PbAYQdSMA,1337
50
+ statable/cli.py,sha256=w_3PowggeRHhOvjnk4Ik8FCSCKPO4xy_JSfmVQw-N3o,7279
51
+ statable/global_defs.py,sha256=bZa1w6NnzlEZNdY4JPjgxAN400lYKIzBupOc1AF-jsk,8084
52
+ statable/mermaid_gen.py,sha256=Vciuj9bG1V4x2PhjPr7WeOmTeGgpiaskWOXLcog13to,4746
53
+ statable/model.py,sha256=8PTDipauu2XsW3yNzb7Hoks1cz2Sp0gS1sLuBqFOAbc,15902
54
+ statable/parser.py,sha256=Li6xj49nDENA-OaorTMhVkxQkw6j0ldpq-MyS-ch0tc,2070
55
+ statable/sample_data.py,sha256=kIcSR6_D21ozV7pusutaTq87I5qbJInZKqqHjiB79_g,15773
56
+ statable/state_machine.py,sha256=0zk4jiiFhGYXuSWR4IJEl_e-AlnWEyhx6lQDd2DtK8U,5115
57
+ statable/xml_io.py,sha256=gNSX_WJBmJF-5BQbYp7dfL6LbJGVIEKHmAb8xswHeEU,48684
58
+ statable/shared/__init__.py,sha256=DId1TM2DHfGFFWwpAjN6RaOKf0ssg2Io0yRaBqR8AEo,717
59
+ statable/shared/condition_library.py,sha256=1urybzlFN2IZ8JqwISjnm-6G9P2lgn4vb8qlM7gNaVI,1971
60
+ statable/shared/literal_library.py,sha256=pw9gYTuSrds-g_p3v1ahIa6fLD1_RifHfwMPQMKTq-M,1962
61
+ statable/shared/role_function_library.py,sha256=sg4zD73e3WmWUhq50dyi_EyxHb9-dY2tH4or3h3VZZ0,5272
62
+ statable_gui/__init__.py,sha256=X2WCo34qvm9HyNciQKvWI3q3t5bs7zBFkeV_THQZNPI,703
63
+ statable_gui/action_edit_dialog.py,sha256=DcK1b8qqb6cdScZs0Ut9wtS716zd5eg0R4qV-lw3ufg,9061
64
+ statable_gui/code_generation_dialog.py,sha256=Ps-sXMudxfSolFgeEkYMy6xhmAd5V20b9gmLlKuryXE,17633
65
+ statable_gui/code_generation_settings_dialog.py,sha256=f44LFGx0-HTo5psKqY59hqnX-HRbroh3g5eZLyNJQtc,22161
66
+ statable_gui/common_widgets.py,sha256=lnsSr_rhfpAjSyzl2WuKhH200XRa-2LhJ8lqmtz72bk,21474
67
+ statable_gui/condition_builder_dialog.py,sha256=cTnyp9T62XS4Y6lV3t0t6wV8JqByx1jcIDaxUIoQ3yQ,20429
68
+ statable_gui/condition_edit_dialog.py,sha256=SCAtav-HpmMoy3cqcfDmw1e1lhELYObi0jaZeZ6a-4U,5005
69
+ statable_gui/condition_template_dialog.py,sha256=ivG46siYbz6ndmjX9SVAN2byFmnWke5fe7Y-8vTjcUM,2022
70
+ statable_gui/config.py,sha256=kYnNCJJjzAnDw2Lvv5OX-oYc_ocbC2pa0Gnh8OcN-vw,556
71
+ statable_gui/dialogs.py,sha256=Dt7qA3-Npm-dvW3dveqoxJOCqm3NosprpfuH5Qznx1w,17745
72
+ statable_gui/event_definition_dialog.py,sha256=BkUpekdJ8ysChEhrYDoBHgIS3_XdJOF_s2PcLyaKPDE,26715
73
+ statable_gui/event_delivery_settings_dialog.py,sha256=hapP6fr14W-383DghWpoyf3Dg_WIvNesyOr69eOSsgM,8114
74
+ statable_gui/event_queue_dialog.py,sha256=hM740pthIu4d-Z8H-toZj-hUMZW0X96g35P6I9Ie8A4,10578
75
+ statable_gui/global_defs.py,sha256=vFPQyiY5L5-DaEVupfjTqlw3fr22KTncpJSmECDdQ2o,4951
76
+ statable_gui/global_defs_dialog.py,sha256=JSUE9MP-s3Y6aio6-DNy7nA5EN9jdVR3sdi91NyG6Iw,35377
77
+ statable_gui/interrupt_handler_edit_dialog.py,sha256=nSxSOvfAESYPg3Tt5m99jtxjz4_aDyDnK4LTazKCF5c,34790
78
+ statable_gui/layer_settings_dialog.py,sha256=Zk2pjOUl3KH6R4r4U6T5o4_t3dtllva7DlFblSn-1LY,6056
79
+ statable_gui/literal_definition_dialog.py,sha256=YWQJ8bUYvx6PH18Z7X0cgm_6uNB7IqJscHcIExUENSg,2118
80
+ statable_gui/logger.py,sha256=qB1DmAr6estvJL4qEhT4etoHXrjc8BjFQqcgqVvxVEA,2767
81
+ statable_gui/main_window.py,sha256=PVsLlGL_ZIVB3FsxSERk6Bdp069-8sFrlYyRQxhIcwo,54238
82
+ statable_gui/matrix_table.py,sha256=Dt10xV6M15t-XZJNNrvRD9zaCFSkp9f1tbAi8JUjSss,17047
83
+ statable_gui/preference_keys.py,sha256=F4lCh4gHRsZelpc-y33mDO982KI3dpNk7-1sQq4Flzs,921
84
+ statable_gui/preferences.py,sha256=N7iy6SDfQ49C2kVSmY-QBFC-MlBGK0MXcqDvAeWG9tI,2935
85
+ statable_gui/role_function_dialog.py,sha256=JkqSOolnhAaqzz6NYwnq3KN5JvJlhMx5HMdlr6zGmSM,9264
86
+ statable_gui/sample_data.py,sha256=yj3IQxYBiuAwFA3ycr1AuMMb4FzCKnkS8C8Xfd4iMlc,245
87
+ statable_gui/state_actions_dialog.py,sha256=He7zjgtmDjkTcIFW6XHIuL-fKKq8scUo-ElFH-3xgt8,15496
88
+ statable_gui/symbol_picker.py,sha256=LOWEWykxH0viujEgf0xlpAfj6xpsdbXnxJr3XHwB_Zo,7229
89
+ statable_gui/traceball.py,sha256=uYZ0PLt1lj6yua_JwdKU0CCsyU2xcaENRq6haW4FhCU,1180
90
+ statable_gui/validation_dialog.py,sha256=ByN75eqOgn5ZmKfypDBGslR5Pl53JAS5UHrNtPL_-Po,11100
91
+ statable_gui/widgets.py,sha256=bYt2UvXJyb3jLfypc_RKBHA9fO5PT7zIS0uhawxTwAs,52321
92
+ statable_gui/i18n/__init__.py,sha256=toNNJHsox5axjPAXU_fA84fZ26bw09Qel39_vFeXYUA,3414
93
+ statable_gui/libcntrl/__init__.py,sha256=bc9tlD4c_CAEGSwxoDbSgLRi1n_gczqYnW9QtqDwkmc,546
94
+ statable_gui/libcntrl/condition_library.py,sha256=dONTfLiB8-DnxrGlUFhaSkYT0T-1UdCfOLFOMMFye_o,401
95
+ statable_gui/libcntrl/literal_library.py,sha256=OgiZfV5wazJxIh4oly5I3LnBZhkoUyX1nwF3ms80cvk,393
96
+ statable_gui/libcntrl/literal_management_dialog.py,sha256=e-VkhWU2MKnTaBhRaZFj_LVs6BxRMJAsMyoGRUMiiSY,6674
97
+ statable_gui/libcntrl/role_function_edit_dialog.py,sha256=lU4SuZr4fiVv_M4XiV5Z_jOolNCn0s0Qzl_Qvqtt_4Q,5556
98
+ statable_gui/libcntrl/role_function_library.py,sha256=00PHBVN7h0eUP5lpgxTB8KgJ-p9ZzZuIb2vRRuqIZ7s,405
99
+ statable_gui/transition_editor_direct/__init__.py,sha256=pwPq5qyQI9_AQkUZZvn59fCbTtgN8MhTFcxWEk38TZ8,330
100
+ statable_gui/transition_editor_direct/actions_tab.py,sha256=IRRrAUYvyFbWikBj2PO9zztnlQiHb4ZonKWV_JoCHNU,34372
101
+ statable_gui/transition_editor_direct/code_widget.py,sha256=ZNr3H-0PgasklItclf7TGoYOgI1kdSXK2U2BmFt0kzM,12796
102
+ statable_gui/transition_editor_direct/coverage_analyzer.py,sha256=QMBE5sS_LoROx6oWJSQGRu-GRMoPh6HTxDRAV8g5xmQ,6019
103
+ statable_gui/transition_editor_direct/dialog.py,sha256=wXwjySg67BMGl-5yHvUspb3PFr7uaBphNT9zCxS31K4,13444
104
+ statable_gui/transition_editor_direct/draft.py,sha256=DLUuZf9nHQH_CeDaBaZiHkCIVeYU34D_EXCiHMBVnW8,9159
105
+ statable_gui/transition_editor_direct/edit_dialogs.py,sha256=6aOAa4cOHdeppv3jodfcK1DLm7Rvy0ak4kMrd3EqjRI,6163
106
+ statable_gui/transition_editor_direct/overview_tab.py,sha256=w26RL1DUaXrF_ZDHixj5N3VaKhT3hDRtZ2moqQqr10s,5844
107
+ statable_gui/transition_editor_direct/palette_widget.py,sha256=w6Potl_2-hxDfLeDfbZ6DQ6eY1wrPGtuk3Hvhfh-HGc,9505
108
+ statable_gui/transition_editor_direct/relations_edit_dialog.py,sha256=hczBpUVE2n4BWfwHr3FBA8NXY1aA2tp5e_ffZ5NZ2rY,6001
109
+ statable_gui/transition_editor_direct/relations_tab.py,sha256=WYU93qprG8UdHtWTpsrs8tMr9glkJPpKMFMyj0BhEB0,8965
110
+ statable_gui/transition_editor_direct/system_global_dialog.py,sha256=YQ_Z46G7pDCkLVyOWX9viBnjk83g6D11skdUUc8_ZYM,1706
111
+ statable_gui/transition_editor_direct/transition_actions_dialog.py,sha256=mY2avGuRSg9_Vo3_S06MZyTSpZSrLebL_U05La8VGHM,4273
112
+ statable_gui/transition_editor_direct/transitions_tab.py,sha256=zhCba9PjVILwHqhouyTTanpNJrzpTNRC4w_o6xLtOSg,24459
113
+ statable-3.1.0.dist-info/METADATA,sha256=DIRUEJXm_OqF1QCAqxiKoWWxhosbgHtw5WUhiRRw9KM,4684
114
+ statable-3.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
115
+ statable-3.1.0.dist-info/entry_points.txt,sha256=fxjnfPG_VOU2Zh3FnNQt7A5o0uSYUhCdjB_alIBqYZY,51
116
+ statable-3.1.0.dist-info/top_level.txt,sha256=zbXm27ELYZVhvhikBmmsoQ8oOzbMg0L-ys6Y9PBh_VI,30
117
+ statable-3.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ statable-cli = statable.cli:main
@@ -0,0 +1,3 @@
1
+ codegen
2
+ statable
3
+ statable_gui
@@ -0,0 +1,23 @@
1
+ # statable_gui package
2
+ """\nStaTable GUI layer\n\n[v1.5 added]\n - Keep re-exports minimal\n - Qt-heavy modules (main_window etc.) should be\n explicitly imported\n"""
3
+
4
+ # Settings / environment
5
+ from .config import WINDOW_WIDTH, WINDOW_HEIGHT
6
+
7
+ # Logger (lightweight)
8
+ try:
9
+ from .logger import StaTableLogger
10
+ except ImportError:
11
+ StaTableLogger = None
12
+
13
+ __all__ = [
14
+ 'WINDOW_WIDTH',
15
+ 'WINDOW_HEIGHT',
16
+ 'StaTableLogger',
17
+ ]
18
+
19
+ # Note:
20
+ # MainWindow / StateMachineTab etc. are not re-exported.
21
+ # Loading before creating the PySide6 QApplication causes errors, so
22
+ # On the caller side, use `from statable_gui.main_window import MainWindow` to
23
+ #Explicitly import it.
@@ -0,0 +1,214 @@
1
+ from PySide6.QtCore import Qt
2
+ from PySide6.QtGui import QFont
3
+ from PySide6.QtWidgets import (
4
+ QDialog, QVBoxLayout, QHBoxLayout, QTextEdit, QComboBox,
5
+ QPushButton, QLabel, QDialogButtonBox, QMessageBox,
6
+ QSplitter, QWidget, QMenu
7
+ )
8
+
9
+ from .role_function_dialog import RoleFunctionDialog
10
+ from .global_defs import GlobalDefinitions
11
+ from .global_defs_dialog import VariableEditDialog, FlagEditDialog
12
+ from .common_widgets import TitleEditWidget
13
+ from .symbol_picker import SymbolPickerWidget
14
+ from .logger import StaTableLogger
15
+
16
+
17
+ class ActionEditDialog(QDialog):
18
+ """Dialog to edit the action of a transition (H3: qualified_name insertion support)"""
19
+
20
+ def __init__(self, parent=None, action_text="", title="",
21
+ role_functions=None, global_defs=None):
22
+ super().__init__(parent)
23
+ self.setWindowTitle(self.tr("ActionEdit"))
24
+ self.setMinimumSize(900, 650)
25
+ self.role_functions = role_functions if role_functions is not None else {}
26
+ self.global_defs = global_defs if global_defs else GlobalDefinitions()
27
+
28
+ StaTableLogger.debug(
29
+ f"ActionEditDialog.__init__: action_text_len={len(action_text)}, "
30
+ f"title='{title}', roles={len(self.role_functions)}, "
31
+ f"vars={len(self.global_defs.variables)}, "
32
+ f"flags={len(self.global_defs.flags)}"
33
+ )
34
+
35
+ main_layout = QVBoxLayout(self)
36
+
37
+ self.title_widget = TitleEditWidget(self, title=title)
38
+ main_layout.addWidget(self.title_widget)
39
+
40
+ role_bar = QHBoxLayout()
41
+ role_bar.addWidget(QLabel(self.tr("Role function:")))
42
+ self.role_combo = QComboBox()
43
+ self.refresh_role_combo()
44
+ role_bar.addWidget(self.role_combo)
45
+ insert_role_btn = QPushButton(self.tr("Insert"))
46
+ insert_role_btn.clicked.connect(self.insert_role_function)
47
+ role_bar.addWidget(insert_role_btn)
48
+ new_role_btn = QPushButton(self.tr("New role function..."))
49
+ new_role_btn.clicked.connect(self.add_new_role_function)
50
+ role_bar.addWidget(new_role_btn)
51
+ main_layout.addLayout(role_bar)
52
+
53
+ self.signature_label = QLabel(self.tr(""))
54
+ self.signature_label.setFont(QFont("Consolas", 9))
55
+ self.signature_label.setStyleSheet("color: #555;")
56
+ main_layout.addWidget(self.signature_label)
57
+
58
+ splitter = QSplitter(Qt.Horizontal)
59
+ main_layout.addWidget(splitter)
60
+
61
+ self.symbol_picker = SymbolPickerWidget(
62
+ global_defs=self.global_defs,
63
+ role_functions=self.role_functions
64
+ )
65
+ self.symbol_picker.insert_requested.connect(self.insert_symbol)
66
+ splitter.addWidget(self.symbol_picker)
67
+
68
+ right_widget = QWidget()
69
+ right_layout = QVBoxLayout(right_widget)
70
+ right_layout.addWidget(QLabel(self.tr("Action code:")))
71
+ self.action_edit = QTextEdit()
72
+ self.action_edit.setAcceptRichText(False)
73
+ self.action_edit.setPlainText(action_text)
74
+ self.action_edit.setFont(QFont("Consolas", 10))
75
+ self.action_edit.setContextMenuPolicy(Qt.CustomContextMenu)
76
+ self.action_edit.customContextMenuRequested.connect(self.show_action_context_menu)
77
+ right_layout.addWidget(self.action_edit, stretch=1)
78
+ splitter.addWidget(right_widget)
79
+
80
+ buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
81
+ buttons.accepted.connect(self._on_accept)
82
+ buttons.rejected.connect(self.reject)
83
+ main_layout.addWidget(buttons)
84
+
85
+ self.update_signature_label()
86
+ StaTableLogger.debug("ActionEditDialog.__init__ completed")
87
+
88
+ def refresh_role_combo(self):
89
+ self.role_combo.clear()
90
+ for name in self.role_functions.keys():
91
+ self.role_combo.addItem(name)
92
+ try:
93
+ self.role_combo.currentTextChanged.disconnect(self.update_signature_label)
94
+ except (RuntimeError, TypeError):
95
+ pass
96
+ self.role_combo.currentTextChanged.connect(self.update_signature_label)
97
+ StaTableLogger.debug(f"refresh_role_combo: {len(self.role_functions)} roles")
98
+
99
+ def update_signature_label(self):
100
+ func_name = self.role_combo.currentText()
101
+ if func_name:
102
+ rf = self.role_functions.get(func_name)
103
+ if rf:
104
+ # Prefer displaying qualified_name
105
+ display = getattr(rf, 'qualified_name', rf.name)
106
+ arg1 = getattr(rf, 'arg1_type', '') or ''
107
+ arg1n = getattr(rf, 'arg1_name', '') or ''
108
+ arg2 = getattr(rf, 'arg2_type', '') or ''
109
+ arg2n = getattr(rf, 'arg2_name', '') or ''
110
+ ret = getattr(rf, 'return_type', 'void') or 'void'
111
+ sig = f"{ret} {display}({arg1} {arg1n}, {arg2} {arg2n})"
112
+ self.signature_label.setText(sig)
113
+ return
114
+ self.signature_label.setText(self.tr(""))
115
+
116
+ # change: use qualified_name
117
+ def insert_role_function(self):
118
+ """\n Insert a role function into the action field\n\n - With namespace: \"Driver.Init\" reference form (no arguments)\n - Without namespace: preserve legacy form \"Sensor_Init(arg1, arg2);\"\n """
119
+ func_name = self.role_combo.currentText()
120
+ StaTableLogger.debug(f"insert_role_function: '{func_name}'")
121
+ if not func_name:
122
+ return
123
+ rf = self.role_functions.get(func_name)
124
+ if not rf:
125
+ return
126
+
127
+ qualified = getattr(rf, 'qualified_name', func_name)
128
+
129
+ if '.' in qualified:
130
+ # Namespace.Name form: insert reference only
131
+ call = qualified
132
+ else:
133
+ # Legacy form: preserve call with args
134
+ arg1 = getattr(rf, 'arg1_name', '') or ''
135
+ arg2 = getattr(rf, 'arg2_name', '') or ''
136
+ call = f"{qualified}({arg1}, {arg2});"
137
+
138
+ self.action_edit.insertPlainText(call + "\n")
139
+
140
+ def add_new_role_function(self):
141
+ StaTableLogger.debug("add_new_role_function called")
142
+ dlg = RoleFunctionDialog(self)
143
+ if dlg.exec() == QDialog.Accepted:
144
+ rf = dlg.get_role_function()
145
+ if rf.name in self.role_functions:
146
+ QMessageBox.warning(
147
+ self,
148
+ self.tr("Warning"),
149
+ self.tr("A role function with the same name already exists."),
150
+ )
151
+ return
152
+ self.role_functions[rf.name] = rf
153
+ self.refresh_role_combo()
154
+ self.update_signature_label()
155
+
156
+ def insert_symbol(self, text: str):
157
+ self.action_edit.insertPlainText(text)
158
+
159
+ def show_action_context_menu(self, pos):
160
+ selected_text = self.action_edit.textCursor().selectedText().strip()
161
+ if not selected_text:
162
+ return
163
+ menu = QMenu(self)
164
+ add_var_action = menu.addAction(
165
+ self.tr("'{0}' as global variable").format(selected_text)
166
+ )
167
+ add_flag_action = menu.addAction(
168
+ self.tr("'{0}' as event flag").format(selected_text)
169
+ )
170
+ chosen = menu.exec(self.action_edit.viewport().mapToGlobal(pos))
171
+ if chosen == add_var_action:
172
+ self.register_selected_as_variable(selected_text)
173
+ elif chosen == add_flag_action:
174
+ self.register_selected_as_flag(selected_text)
175
+
176
+ def register_selected_as_variable(self, name: str):
177
+ dlg = VariableEditDialog(self, groups=self.global_defs.variable_groups())
178
+ dlg.name_edit.setText(name)
179
+ if dlg.exec() == QDialog.Accepted:
180
+ var = dlg.get_variable()
181
+ if not var.name:
182
+ return
183
+ if any(v.name == var.name for v in self.global_defs.variables):
184
+ return
185
+ self.global_defs.variables.append(var)
186
+ self.symbol_picker.refresh_list()
187
+
188
+ def register_selected_as_flag(self, name: str):
189
+ dlg = FlagEditDialog(self, groups=self.global_defs.flag_groups())
190
+ dlg.name_edit.setText(name)
191
+ if dlg.exec() == QDialog.Accepted:
192
+ flag = dlg.get_flag()
193
+ if not flag.name:
194
+ return
195
+ if any(f.name == flag.name for f in self.global_defs.flags):
196
+ return
197
+ self.global_defs.flags.append(flag)
198
+ self.symbol_picker.refresh_list()
199
+
200
+ def _on_accept(self):
201
+ action_text = self.action_edit.toPlainText().strip()
202
+ if action_text:
203
+ first_line = action_text.split('\n')[0].strip()
204
+ auto_title = first_line[:20] + ("..." if len(first_line) > 20 else "")
205
+ else:
206
+ auto_title = self.tr("(untitled action)")
207
+ self.title_widget.ensure_title(auto_title)
208
+ self.accept()
209
+
210
+ def get_action_text(self) -> str:
211
+ return self.action_edit.toPlainText().strip()
212
+
213
+ def get_title(self) -> str:
214
+ return self.title_widget.get_title()