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,522 @@
1
+ # statable_gui/code_generation_settings_dialog.py
2
+ """\nCode generation settings dialog\n- Tab structure: basic settings, log settings, external include, output settings\n- Generation style / table style are fixed (table-driven + array style only)\n"""
3
+
4
+ import os
5
+ import sys
6
+ import logging
7
+ from typing import Dict, Optional, List
8
+
9
+ from PySide6.QtWidgets import (
10
+ QDialog, QVBoxLayout, QHBoxLayout, QPushButton,
11
+ QLabel, QComboBox, QGroupBox, QFormLayout,
12
+ QCheckBox, QLineEdit, QFileDialog, QTabWidget,
13
+ QWidget, QMessageBox, QDialogButtonBox, QListWidget,
14
+ QListWidgetItem, QSpinBox, QAbstractItemView
15
+ )
16
+ from PySide6.QtCore import Qt
17
+
18
+ # Path settings
19
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
20
+
21
+ try:
22
+ from codegen.config import CodeGenerationConfig, ConfigManager
23
+ except ImportError:
24
+ sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'codegen'))
25
+ from config import CodeGenerationConfig, ConfigManager
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class CodeGenerationSettingsDialog(QDialog):
31
+ """Code generation settings dialog"""
32
+
33
+ def __init__(self, config_manager: Optional[ConfigManager] = None, parent=None):
34
+ super().__init__(parent)
35
+ self.config_manager = config_manager or ConfigManager()
36
+ self.config = self.config_manager.get_config()
37
+
38
+ self.setWindowTitle(self.tr("Code generation settings"))
39
+ self.setMinimumSize(600, 600)
40
+
41
+ self._setup_ui()
42
+ self._load_config()
43
+
44
+ def _setup_ui(self):
45
+ """Build UI"""
46
+ main_layout = QVBoxLayout(self)
47
+
48
+ # Tab widget
49
+ self.tab_widget = QTabWidget()
50
+ main_layout.addWidget(self.tab_widget)
51
+
52
+ # Tab
53
+ self.basic_tab = QWidget()
54
+ self._setup_basic_tab()
55
+ self.tab_widget.addTab(self.basic_tab, self.tr("Basic settings"))
56
+
57
+ # Log settings tab
58
+ self.log_tab = QWidget()
59
+ self._setup_log_tab()
60
+ self.tab_widget.addTab(self.log_tab, self.tr("Log settings"))
61
+
62
+ # Output settings tab
63
+ self.include_tab = QWidget()
64
+ self._setup_include_tab()
65
+ self.tab_widget.addTab(self.include_tab, self.tr("External include"))
66
+
67
+ self.output_tab = QWidget()
68
+ self._setup_output_tab()
69
+ self.tab_widget.addTab(self.output_tab, self.tr("Output settings"))
70
+
71
+ # Buttons
72
+ button_layout = QHBoxLayout()
73
+ button_layout.addStretch()
74
+
75
+ self.reset_btn = QPushButton(self.tr("Reset"))
76
+ self.reset_btn.clicked.connect(self._on_reset)
77
+ button_layout.addWidget(self.reset_btn)
78
+
79
+ self.cancel_btn = QPushButton(self.tr("Cancel"))
80
+ self.cancel_btn.clicked.connect(self.reject)
81
+ button_layout.addWidget(self.cancel_btn)
82
+
83
+ self.ok_btn = QPushButton(self.tr("OK"))
84
+ self.ok_btn.clicked.connect(self._on_ok)
85
+ button_layout.addWidget(self.ok_btn)
86
+
87
+ main_layout.addLayout(button_layout)
88
+
89
+ def _setup_basic_tab(self):
90
+ """Basic settings tab"""
91
+ layout = QVBoxLayout(self.basic_tab)
92
+
93
+ # ★ Project name
94
+ project_group = QGroupBox(self.tr("Project settings"))
95
+ project_layout = QFormLayout(project_group)
96
+
97
+ self.project_name_edit = QLineEdit()
98
+ self.project_name_edit.setPlaceholderText(self.tr("Example: MyProject"))
99
+ self.project_name_edit.setToolTip(self.tr("Used as the generated file name <project_name>_run.c"))
100
+ project_layout.addRow("Project name:", self.project_name_edit)
101
+
102
+ layout.addWidget(project_group)
103
+
104
+ # Generation style (fixed display)
105
+ style_group = QGroupBox(self.tr("Generation style"))
106
+ style_layout = QFormLayout(style_group)
107
+
108
+ # Generation style: fixed
109
+ style_value_label = QLabel(self.tr("Table-driven style (cell-level function + function table)"))
110
+ style_value_label.setStyleSheet("font-weight: bold;")
111
+ style_layout.addRow("Generation style:", style_value_label)
112
+
113
+ # Table style: fixed
114
+ table_value_label = QLabel(self.tr("Array style (2D array + O(1) access)"))
115
+ table_value_label.setStyleSheet("font-weight: bold;")
116
+ style_layout.addRow("Table style:", table_value_label)
117
+
118
+ # description
119
+ note_label = QLabel(
120
+ self.tr("* Fixed to a style optimized for C.\n"
121
+ " Generates cell-level transition functions, managed by a function table.")
122
+ )
123
+ note_label.setStyleSheet("color: gray; font-size: 10px; margin-left: 10px;")
124
+ style_layout.addRow("", note_label)
125
+
126
+ # OS type (this is meaningful, so it is selectable)
127
+ self.os_type_combo = QComboBox()
128
+ self.os_type_combo.addItem(self.tr("NonRTOS (bare metal)"), "non_rtos")
129
+ self.os_type_combo.addItem(self.tr("FreeRTOS"), "freertos")
130
+ self.os_type_combo.addItem(self.tr("ThreadX"), "threadx")
131
+ style_layout.addRow("OS type:", self.os_type_combo)
132
+
133
+ layout.addWidget(style_group)
134
+
135
+ # Naming convention
136
+ naming_group = QGroupBox(self.tr("Naming convention"))
137
+ naming_layout = QFormLayout(naming_group)
138
+
139
+ self.naming_prefix_edit = QLineEdit()
140
+ self.naming_prefix_edit.setPlaceholderText(self.tr("Function name prefix (optional)"))
141
+ naming_layout.addRow("Prefix:", self.naming_prefix_edit)
142
+
143
+ self.state_prefix_edit = QLineEdit()
144
+ naming_layout.addRow("State prefix:", self.state_prefix_edit)
145
+
146
+ self.event_prefix_edit = QLineEdit()
147
+ naming_layout.addRow("Event prefix:", self.event_prefix_edit)
148
+
149
+ self.flag_prefix_edit = QLineEdit()
150
+ naming_layout.addRow("Flag prefix:", self.flag_prefix_edit)
151
+
152
+ layout.addWidget(naming_group)
153
+
154
+ # Comment settings
155
+ comment_group = QGroupBox(self.tr("Comment settings"))
156
+ comment_layout = QVBoxLayout(comment_group)
157
+
158
+ self.enable_comments_check = QCheckBox(self.tr("Generate comments"))
159
+ comment_layout.addWidget(self.enable_comments_check)
160
+
161
+ self.enable_doxygen_check = QCheckBox(self.tr("Generate Doxygen-style comments"))
162
+ comment_layout.addWidget(self.enable_doxygen_check)
163
+
164
+ self.enable_markers_check = QCheckBox(self.tr("Generate user code markers"))
165
+ comment_layout.addWidget(self.enable_markers_check)
166
+
167
+ layout.addWidget(comment_group)
168
+ layout.addStretch()
169
+
170
+ def _setup_log_tab(self):
171
+ """Log settings tab"""
172
+ layout = QVBoxLayout(self.log_tab)
173
+
174
+ log_group = QGroupBox(self.tr("Debug log settings"))
175
+ log_layout = QVBoxLayout(log_group)
176
+
177
+ self.enable_debug_logs_check = QCheckBox(self.tr("Generate DEBUG logs"))
178
+ log_layout.addWidget(self.enable_debug_logs_check)
179
+
180
+ self.enable_info_logs_check = QCheckBox(self.tr("Generate INFO logs"))
181
+ log_layout.addWidget(self.enable_info_logs_check)
182
+
183
+ self.enable_error_logs_check = QCheckBox(self.tr("Generate ERROR logs"))
184
+ log_layout.addWidget(self.enable_error_logs_check)
185
+
186
+ layout.addWidget(log_group)
187
+
188
+ # Pending event limit
189
+ pending_group = QGroupBox(self.tr("Pending event settings"))
190
+ pending_layout = QFormLayout(pending_group)
191
+
192
+ self.max_pending_spin = QSpinBox()
193
+ self.max_pending_spin.setRange(1, 255)
194
+ self.max_pending_spin.setValue(16)
195
+ self.max_pending_spin.setToolTip(
196
+ self.tr("Upper limit of consecutive pending events.\n"
197
+ "Processing is aborted beyond this count to prevent infinite loops.")
198
+ )
199
+ pending_layout.addRow("Max consecutive processing:", self.max_pending_spin)
200
+
201
+ layout.addWidget(pending_group)
202
+ layout.addStretch()
203
+
204
+ def _setup_include_tab(self):
205
+ """External include settings tab"""
206
+ layout = QVBoxLayout(self.include_tab)
207
+
208
+ # External include file
209
+ include_group = QGroupBox(self.tr("External include file"))
210
+ include_layout = QVBoxLayout(include_group)
211
+
212
+ hint_label = QLabel(
213
+ self.tr("Specify a generated header such as Renesas Smart Configurator output.\n"
214
+ "The specified header is #include'd into the selected insertion target.")
215
+ )
216
+ hint_label.setStyleSheet("color: gray; font-size: 10px;")
217
+ include_layout.addWidget(hint_label)
218
+
219
+ self.include_list = QListWidget()
220
+ self.include_list.setSelectionMode(QAbstractItemView.SingleSelection)
221
+ self.include_list.setMinimumHeight(120)
222
+ include_layout.addWidget(self.include_list)
223
+
224
+ include_btn_layout = QHBoxLayout()
225
+
226
+ add_btn = QPushButton(self.tr("Add..."))
227
+ add_btn.clicked.connect(self._on_add_include)
228
+ include_btn_layout.addWidget(add_btn)
229
+
230
+ remove_btn = QPushButton(self.tr("Delete"))
231
+ remove_btn.clicked.connect(self._on_remove_include)
232
+ include_btn_layout.addWidget(remove_btn)
233
+
234
+ up_btn = QPushButton(self.tr("Move up"))
235
+ up_btn.clicked.connect(self._on_move_include_up)
236
+ include_btn_layout.addWidget(up_btn)
237
+
238
+ down_btn = QPushButton(self.tr("Move down"))
239
+ down_btn.clicked.connect(self._on_move_include_down)
240
+ include_btn_layout.addWidget(down_btn)
241
+
242
+ include_btn_layout.addStretch()
243
+ include_layout.addLayout(include_btn_layout)
244
+
245
+ layout.addWidget(include_group)
246
+
247
+ # Insertion target
248
+ target_group = QGroupBox(self.tr("Insertion target"))
249
+ target_layout = QVBoxLayout(target_group)
250
+
251
+ self.include_in_super_check = QCheckBox(self.tr("statable_all.h (for user main.c)"))
252
+ target_layout.addWidget(self.include_in_super_check)
253
+
254
+ self.include_in_role_check = QCheckBox(self.tr("Role function .c file"))
255
+ target_layout.addWidget(self.include_in_role_check)
256
+
257
+ self.include_in_transitions_check = QCheckBox(self.tr("transitions .c file"))
258
+ target_layout.addWidget(self.include_in_transitions_check)
259
+
260
+ self.include_in_common_check = QCheckBox(self.tr("common .c file"))
261
+ target_layout.addWidget(self.include_in_common_check)
262
+
263
+ layout.addWidget(target_group)
264
+ layout.addStretch()
265
+
266
+ def _setup_output_tab(self):
267
+ """Output settings tab"""
268
+ layout = QVBoxLayout(self.output_tab)
269
+
270
+ # Output settings
271
+ output_group = QGroupBox(self.tr("Output settings"))
272
+ output_layout = QFormLayout(output_group)
273
+
274
+ self.output_dir_edit = QLineEdit()
275
+ self.output_dir_edit.setPlaceholderText(self.tr("Output directory"))
276
+ self.output_dir_btn = QPushButton(self.tr("Browse..."))
277
+ self.output_dir_btn.clicked.connect(self._select_output_dir)
278
+
279
+ output_dir_layout = QHBoxLayout()
280
+ output_dir_layout.addWidget(self.output_dir_edit)
281
+ output_dir_layout.addWidget(self.output_dir_btn)
282
+
283
+ output_layout.addRow("Output directory:", output_dir_layout)
284
+
285
+ layout.addWidget(output_group)
286
+
287
+ # Folder structure
288
+ folder_group = QGroupBox(self.tr("Folder structure"))
289
+ folder_layout = QFormLayout(folder_group)
290
+
291
+ self.folder_structure_combo = QComboBox()
292
+ self.folder_structure_combo.addItem(self.tr("include/src separation (recommended)"), "by_type")
293
+ self.folder_structure_combo.addItem(self.tr("Per layer"), "by_layer")
294
+ self.folder_structure_combo.addItem(self.tr("Flat"), "flat")
295
+ folder_layout.addRow("Structure:", self.folder_structure_combo)
296
+
297
+ self.include_dir_edit = QLineEdit()
298
+ folder_layout.addRow("include folder name:", self.include_dir_edit)
299
+
300
+ self.source_dir_edit = QLineEdit()
301
+ folder_layout.addRow("src folder name:", self.source_dir_edit)
302
+
303
+ self.common_dir_edit = QLineEdit()
304
+ folder_layout.addRow("Common folder name:", self.common_dir_edit)
305
+
306
+ self.project_dir_edit = QLineEdit()
307
+ folder_layout.addRow("Project folder name:", self.project_dir_edit)
308
+
309
+ layout.addWidget(folder_group)
310
+
311
+ # Super include
312
+ super_group = QGroupBox(self.tr("Super include"))
313
+ super_layout = QVBoxLayout(super_group)
314
+
315
+ self.generate_super_check = QCheckBox(self.tr("Generate statable_all.h (for user main.c)"))
316
+ super_layout.addWidget(self.generate_super_check)
317
+
318
+ super_form = QFormLayout()
319
+ self.super_include_name_edit = QLineEdit()
320
+ super_form.addRow("File name:", self.super_include_name_edit)
321
+
322
+ self.super_include_dir_edit = QLineEdit()
323
+ super_form.addRow("Placement folder:", self.super_include_dir_edit)
324
+
325
+ super_layout.addLayout(super_form)
326
+
327
+ layout.addWidget(super_group)
328
+
329
+ # Merge settings
330
+ merge_group = QGroupBox(self.tr("Merge settings"))
331
+ merge_layout = QVBoxLayout(merge_group)
332
+
333
+ self.save_with_merge_check = QCheckBox(self.tr("Save by merging with existing file (preserve user code)"))
334
+ merge_layout.addWidget(self.save_with_merge_check)
335
+
336
+ layout.addWidget(merge_group)
337
+ layout.addStretch()
338
+
339
+ def _load_config(self):
340
+ # Project name
341
+ self.project_name_edit.setText(self.config.project_name)
342
+
343
+ # Generation style / table style are fixed, so force internal settings
344
+ self.config.generation_style = "table_driven"
345
+ self.config.table_type = "array"
346
+
347
+ # OS type
348
+ idx = self.os_type_combo.findData(self.config.os_type)
349
+ if idx >= 0:
350
+ self.os_type_combo.setCurrentIndex(idx)
351
+
352
+ # Naming convention
353
+ self.naming_prefix_edit.setText(self.config.naming_prefix)
354
+ self.state_prefix_edit.setText(self.config.state_prefix)
355
+ self.event_prefix_edit.setText(self.config.event_prefix)
356
+ self.flag_prefix_edit.setText(self.config.flag_prefix)
357
+
358
+ # comment
359
+ self.enable_comments_check.setChecked(self.config.enable_comments)
360
+ self.enable_doxygen_check.setChecked(self.config.enable_doxygen)
361
+ self.enable_markers_check.setChecked(self.config.enable_user_markers)
362
+
363
+ # Log
364
+ self.enable_debug_logs_check.setChecked(self.config.enable_debug_logs)
365
+ self.enable_info_logs_check.setChecked(self.config.enable_info_logs)
366
+ self.enable_error_logs_check.setChecked(self.config.enable_error_logs)
367
+
368
+ # Pending event
369
+ self.max_pending_spin.setValue(self.config.max_consecutive_pending_events)
370
+
371
+ # External include
372
+ self.include_list.clear()
373
+ for inc in self.config.external_includes:
374
+ self.include_list.addItem(inc)
375
+
376
+ self.include_in_super_check.setChecked(self.config.external_includes_in_super)
377
+ self.include_in_role_check.setChecked(self.config.external_includes_in_role)
378
+ self.include_in_transitions_check.setChecked(self.config.external_includes_in_transitions)
379
+ self.include_in_common_check.setChecked(self.config.external_includes_in_common)
380
+
381
+ # Output
382
+ self.output_dir_edit.setText(self.config.output_directory)
383
+ self.save_with_merge_check.setChecked(self.config.save_with_merge)
384
+
385
+ # Folder structure
386
+ idx = self.folder_structure_combo.findData(self.config.folder_structure)
387
+ if idx >= 0:
388
+ self.folder_structure_combo.setCurrentIndex(idx)
389
+
390
+ self.include_dir_edit.setText(self.config.include_dir_name)
391
+ self.source_dir_edit.setText(self.config.source_dir_name)
392
+ self.common_dir_edit.setText(self.config.common_dir_name)
393
+ self.project_dir_edit.setText(self.config.project_dir_name)
394
+
395
+ # Super include
396
+ self.generate_super_check.setChecked(self.config.generate_super_include)
397
+ self.super_include_name_edit.setText(self.config.super_include_file)
398
+ self.super_include_dir_edit.setText(self.config.super_include_dir)
399
+
400
+ def _save_config(self):
401
+ # Project name
402
+ self.config.project_name = self.project_name_edit.text().strip() or "MyProject"
403
+
404
+ # Generation style / table style fixed
405
+ self.config.generation_style = "table_driven"
406
+ self.config.table_type = "array"
407
+
408
+ # OS type
409
+ self.config.os_type = self.os_type_combo.currentData()
410
+
411
+ # Naming convention
412
+ self.config.naming_prefix = self.naming_prefix_edit.text()
413
+ self.config.state_prefix = self.state_prefix_edit.text() or "STATE"
414
+ self.config.event_prefix = self.event_prefix_edit.text() or "EVENT"
415
+ self.config.flag_prefix = self.flag_prefix_edit.text() or "FLAG"
416
+
417
+ # comment
418
+ self.config.enable_comments = self.enable_comments_check.isChecked()
419
+ self.config.enable_doxygen = self.enable_doxygen_check.isChecked()
420
+ self.config.enable_user_markers = self.enable_markers_check.isChecked()
421
+
422
+ # Log
423
+ self.config.enable_debug_logs = self.enable_debug_logs_check.isChecked()
424
+ self.config.enable_info_logs = self.enable_info_logs_check.isChecked()
425
+ self.config.enable_error_logs = self.enable_error_logs_check.isChecked()
426
+
427
+ # Pending event
428
+ self.config.max_consecutive_pending_events = self.max_pending_spin.value()
429
+
430
+ # External include
431
+ self.config.external_includes = [
432
+ self.include_list.item(i).text()
433
+ for i in range(self.include_list.count())
434
+ ]
435
+ self.config.external_includes_in_super = self.include_in_super_check.isChecked()
436
+ self.config.external_includes_in_role = self.include_in_role_check.isChecked()
437
+ self.config.external_includes_in_transitions = self.include_in_transitions_check.isChecked()
438
+ self.config.external_includes_in_common = self.include_in_common_check.isChecked()
439
+
440
+ # Output
441
+ self.config.output_directory = self.output_dir_edit.text()
442
+ self.config.save_with_merge = self.save_with_merge_check.isChecked()
443
+
444
+ # Folder structure
445
+ self.config.folder_structure = self.folder_structure_combo.currentData()
446
+ self.config.include_dir_name = self.include_dir_edit.text() or "include"
447
+ self.config.source_dir_name = self.source_dir_edit.text() or "src"
448
+ self.config.common_dir_name = self.common_dir_edit.text() or "common"
449
+ self.config.project_dir_name = self.project_dir_edit.text() or "project"
450
+
451
+ # Super include
452
+ self.config.generate_super_include = self.generate_super_check.isChecked()
453
+ self.config.super_include_file = self.super_include_name_edit.text() or "statable_all.h"
454
+ self.config.super_include_dir = self.super_include_dir_edit.text() or "common"
455
+
456
+ self.config_manager.set_config(self.config)
457
+
458
+ def _on_ok(self):
459
+ """OK button"""
460
+ self._save_config()
461
+ self.accept()
462
+
463
+ def _on_reset(self):
464
+ """Reset button"""
465
+ reply = QMessageBox.question(
466
+ self, self.tr("Confirm"), self.tr("Reset settings?"),
467
+ QMessageBox.Yes | QMessageBox.No
468
+ )
469
+ if reply == QMessageBox.Yes:
470
+ self.config_manager.reset()
471
+ self.config = self.config_manager.get_config()
472
+ self._load_config()
473
+
474
+ def _select_output_dir(self):
475
+ """Select output directory"""
476
+ current = self.output_dir_edit.text() or os.getcwd()
477
+ dir_path = QFileDialog.getExistingDirectory(
478
+ self, "Select output directory", current
479
+ )
480
+ if dir_path:
481
+ self.output_dir_edit.setText(dir_path)
482
+
483
+ # ===== External include operations =====
484
+ def _on_add_include(self):
485
+ files, _ = QFileDialog.getOpenFileNames(
486
+ self, "Select external include file", "",
487
+ "Header files (*.h);;All files (*)"
488
+ )
489
+ if files:
490
+ for f in files:
491
+ # Extract only the file name from the path (consistent with existing registration)
492
+ name = os.path.basename(f)
493
+ # Duplicate check
494
+ existing = [
495
+ self.include_list.item(i).text()
496
+ for i in range(self.include_list.count())
497
+ ]
498
+ if name not in existing:
499
+ self.include_list.addItem(name)
500
+
501
+ def _on_remove_include(self):
502
+ row = self.include_list.currentRow()
503
+ if row >= 0:
504
+ self.include_list.takeItem(row)
505
+
506
+ def _on_move_include_up(self):
507
+ row = self.include_list.currentRow()
508
+ if row > 0:
509
+ item = self.include_list.takeItem(row)
510
+ self.include_list.insertItem(row - 1, item)
511
+ self.include_list.setCurrentRow(row - 1)
512
+
513
+ def _on_move_include_down(self):
514
+ row = self.include_list.currentRow()
515
+ if 0 <= row < self.include_list.count() - 1:
516
+ item = self.include_list.takeItem(row)
517
+ self.include_list.insertItem(row + 1, item)
518
+ self.include_list.setCurrentRow(row + 1)
519
+
520
+ def get_config(self) -> CodeGenerationConfig:
521
+ """Get settings"""
522
+ return self.config