statable 3.1.1__tar.gz → 3.2.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.
- {statable-3.1.1 → statable-3.2.1}/PKG-INFO +1 -1
- {statable-3.1.1 → statable-3.2.1}/codegen/__init__.py +1 -1
- {statable-3.1.1 → statable-3.2.1}/pyproject.toml +1 -1
- {statable-3.1.1 → statable-3.2.1}/statable/__init__.py +1 -1
- {statable-3.1.1 → statable-3.2.1}/statable/cli.py +1 -1
- {statable-3.1.1 → statable-3.2.1}/statable.egg-info/PKG-INFO +1 -1
- {statable-3.1.1 → statable-3.2.1}/statable.egg-info/SOURCES.txt +4 -1
- {statable-3.1.1 → statable-3.2.1}/statable_gui/i18n/__init__.py +11 -1
- {statable-3.1.1 → statable-3.2.1}/statable_gui/main_window.py +61 -16
- statable-3.2.1/statable_gui/new_project_templates.py +133 -0
- statable-3.2.1/statable_gui/new_project_wizard.py +551 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v3_0_s1_packaging.py +9 -9
- {statable-3.1.1 → statable-3.2.1}/tests/test_v3_0_s2_public_api.py +4 -4
- {statable-3.1.1 → statable-3.2.1}/tests/test_v3_0_s3_cli.py +4 -4
- {statable-3.1.1 → statable-3.2.1}/tests/test_v3_0_s4_docs.py +5 -5
- statable-3.2.1/tests/test_v3_2_s1_wizard.py +219 -0
- {statable-3.1.1 → statable-3.2.1}/README.md +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/c_code_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/code_merger.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/code_templates.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/config.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/enum_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/event_queue_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/interrupt_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/naming_convention.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/osal_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/role_function_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/sample_data.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/state_actions_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/struct_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/timer_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/transition_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/type_mapper.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/__init__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/change_actions.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/change_applier.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/clipboard_manager.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/data/__init__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/data/action_definitions.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/data/keywords.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/data/prompt_templates.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/data/validation_rules.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/__init__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/base_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/cell_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/custom_type_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/event_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/flag_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/interrupt_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/queue_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/role_function_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/state_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/timer_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/transition_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/items/variable_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/logger.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/models.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/prompt_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/response_parser.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/response_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/validation_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/validate/validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/codegen/variable_generator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/setup.cfg +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/__main__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/global_defs.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/mermaid_gen.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/model.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/parser.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/sample_data.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/shared/__init__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/shared/condition_library.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/shared/literal_library.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/shared/role_function_library.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/state_machine.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable/xml_io.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable.egg-info/dependency_links.txt +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable.egg-info/entry_points.txt +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable.egg-info/requires.txt +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable.egg-info/top_level.txt +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/__init__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/action_edit_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/code_generation_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/code_generation_settings_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/common_widgets.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/condition_builder_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/condition_edit_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/condition_template_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/config.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/dialogs.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/event_definition_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/event_delivery_settings_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/event_queue_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/global_defs.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/global_defs_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/interrupt_handler_edit_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/layer_settings_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/libcntrl/__init__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/libcntrl/condition_library.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/libcntrl/literal_library.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/libcntrl/literal_management_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/libcntrl/role_function_edit_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/libcntrl/role_function_library.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/literal_definition_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/logger.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/matrix_table.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/preference_keys.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/preferences.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/role_function_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/sample_data.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/state_actions_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/symbol_picker.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/traceball.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/__init__.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/actions_tab.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/code_widget.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/coverage_analyzer.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/draft.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/edit_dialogs.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/overview_tab.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/palette_widget.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/relations_edit_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/relations_tab.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/system_global_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/transition_actions_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/transition_editor_direct/transitions_tab.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/validation_dialog.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/statable_gui/widgets.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_readme_consistency.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p1.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p12_10.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p12_2.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p12_5.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p12_6.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p12_7.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p12_8.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p12_9.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p2.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p3.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p4a.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_2_p4b.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_3_p1.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_4_p1_merge.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p1.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p10.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p11.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p2.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p3.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p4.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p5.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p6.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p8.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_5_p9.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_6_p1.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_6_p2.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_6_p3.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_7_p3.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_7_p4.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_7_p5_validation.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_8_p1_ai_prompt.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_8_p2_response_parser.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_8_p3_response_validator.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v2_8_p4_gui_integration.py +0 -0
- {statable-3.1.1 → statable-3.2.1}/tests/test_v3_0_g1_shared.py +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "statable"
|
|
9
|
-
version = "3.
|
|
9
|
+
version = "3.2.1"
|
|
10
10
|
description = "MISRA C:2012-aware state machine design and C code generation for embedded systems"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.10"
|
|
@@ -88,6 +88,8 @@ statable_gui/literal_definition_dialog.py
|
|
|
88
88
|
statable_gui/logger.py
|
|
89
89
|
statable_gui/main_window.py
|
|
90
90
|
statable_gui/matrix_table.py
|
|
91
|
+
statable_gui/new_project_templates.py
|
|
92
|
+
statable_gui/new_project_wizard.py
|
|
91
93
|
statable_gui/preference_keys.py
|
|
92
94
|
statable_gui/preferences.py
|
|
93
95
|
statable_gui/role_function_dialog.py
|
|
@@ -157,4 +159,5 @@ tests/test_v3_0_g1_shared.py
|
|
|
157
159
|
tests/test_v3_0_s1_packaging.py
|
|
158
160
|
tests/test_v3_0_s2_public_api.py
|
|
159
161
|
tests/test_v3_0_s3_cli.py
|
|
160
|
-
tests/test_v3_0_s4_docs.py
|
|
162
|
+
tests/test_v3_0_s4_docs.py
|
|
163
|
+
tests/test_v3_2_s1_wizard.py
|
|
@@ -14,6 +14,7 @@ Usage in gui_main.py or statable_gui/main_window.py:
|
|
|
14
14
|
"""
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
+
import sys
|
|
17
18
|
from pathlib import Path
|
|
18
19
|
from typing import Optional
|
|
19
20
|
|
|
@@ -25,7 +26,16 @@ from PySide6.QtCore import (
|
|
|
25
26
|
)
|
|
26
27
|
from PySide6.QtWidgets import QApplication
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
# [v3.2] PyInstaller frozen support
|
|
30
|
+
if getattr(sys, "frozen", False):
|
|
31
|
+
# Bundled: resources are in sys._MEIPASS/statable_gui/i18n
|
|
32
|
+
_meipass = getattr(sys, "_MEIPASS", None)
|
|
33
|
+
if _meipass:
|
|
34
|
+
I18N_DIR = Path(_meipass) / "statable_gui" / "i18n"
|
|
35
|
+
else:
|
|
36
|
+
I18N_DIR = Path(__file__).parent
|
|
37
|
+
else:
|
|
38
|
+
I18N_DIR = Path(__file__).parent
|
|
29
39
|
DEFAULT_LANG = "en"
|
|
30
40
|
SUPPORTED_LANGUAGES = ["en", "zh_CN"]
|
|
31
41
|
LANG_DISPLAY = {
|
|
@@ -39,7 +39,7 @@ from pathlib import Path
|
|
|
39
39
|
from PySide6.QtCore import Qt
|
|
40
40
|
from PySide6.QtGui import QAction
|
|
41
41
|
from PySide6.QtWidgets import (
|
|
42
|
-
QMainWindow, QTabWidget, QToolButton, QMessageBox,
|
|
42
|
+
QMainWindow, QTabWidget, QToolButton, QMessageBox, QDialog,
|
|
43
43
|
QInputDialog, QFileDialog, QDialog, QToolBar
|
|
44
44
|
)
|
|
45
45
|
|
|
@@ -211,6 +211,8 @@ class MainWindow(QMainWindow):
|
|
|
211
211
|
|
|
212
212
|
self.tab_widget = QTabWidget()
|
|
213
213
|
self.tab_widget.setTabsClosable(True)
|
|
214
|
+
# [v3.2.1] Enable drag-and-drop tab reordering
|
|
215
|
+
self.tab_widget.setMovable(True)
|
|
214
216
|
self.tab_widget.tabCloseRequested.connect(
|
|
215
217
|
self.close_tab)
|
|
216
218
|
self.setCentralWidget(self.tab_widget)
|
|
@@ -375,16 +377,17 @@ class MainWindow(QMainWindow):
|
|
|
375
377
|
|
|
376
378
|
file_menu = menubar.addMenu(self.tr("File"))
|
|
377
379
|
|
|
378
|
-
# [
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
file_menu.addAction(
|
|
380
|
+
# [v3.2] New Project Wizard (Ctrl+N)
|
|
381
|
+
new_wizard_action = QAction(self.tr("New Project..."), self)
|
|
382
|
+
new_wizard_action.setShortcut("Ctrl+N")
|
|
383
|
+
new_wizard_action.triggered.connect(self.new_project_wizard)
|
|
384
|
+
file_menu.addAction(new_wizard_action)
|
|
383
385
|
|
|
384
386
|
file_menu.addSeparator()
|
|
385
387
|
|
|
386
388
|
open_action = QAction(self.tr("Open Project..."), self)
|
|
387
|
-
|
|
389
|
+
# [v3.2.1 fix] lambda: avoid passing triggered(bool) as filepath
|
|
390
|
+
open_action.triggered.connect(lambda: self.open_project())
|
|
388
391
|
file_menu.addAction(open_action)
|
|
389
392
|
|
|
390
393
|
save_action = QAction(self.tr("Save Project..."), self)
|
|
@@ -516,7 +519,12 @@ class MainWindow(QMainWindow):
|
|
|
516
519
|
self._restart_in_progress = True
|
|
517
520
|
|
|
518
521
|
exe = _sys.executable
|
|
519
|
-
|
|
522
|
+
# [v3.2] Frozen (PyInstaller) support: sys.executable is the
|
|
523
|
+
# bundled StaTable.exe, which does not accept "-m statable".
|
|
524
|
+
if getattr(_sys, "frozen", False):
|
|
525
|
+
args = [exe]
|
|
526
|
+
else:
|
|
527
|
+
args = [exe, "-m", "statable"]
|
|
520
528
|
cwd = os.getcwd()
|
|
521
529
|
|
|
522
530
|
# Disable UI to avoid double-clicks
|
|
@@ -805,8 +813,12 @@ class MainWindow(QMainWindow):
|
|
|
805
813
|
f"Failed to save project:\n{e}")
|
|
806
814
|
return False
|
|
807
815
|
|
|
808
|
-
def open_project(self):
|
|
816
|
+
def open_project(self, filepath: str = None):
|
|
809
817
|
"""Load the whole project"""
|
|
818
|
+
# [v3.2.1 fix] Qt triggered signal passes bool; normalize to None
|
|
819
|
+
if isinstance(filepath, bool):
|
|
820
|
+
filepath = None
|
|
821
|
+
|
|
810
822
|
# [v2.3] Prompt to save before loading
|
|
811
823
|
if not self._maybe_save():
|
|
812
824
|
return
|
|
@@ -814,13 +826,14 @@ class MainWindow(QMainWindow):
|
|
|
814
826
|
StaTableLogger.debug(
|
|
815
827
|
"MainWindow.open_project called")
|
|
816
828
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
829
|
+
if filepath is None:
|
|
830
|
+
last_dir = self.prefs.last_project_dir
|
|
831
|
+
filepath, _ = QFileDialog.getOpenFileName(
|
|
832
|
+
self, "Open Project", last_dir,
|
|
833
|
+
"XML files (*.xml)")
|
|
834
|
+
if not filepath:
|
|
835
|
+
StaTableLogger.debug("Open cancelled")
|
|
836
|
+
return
|
|
824
837
|
try:
|
|
825
838
|
StaTableLogger.debug(
|
|
826
839
|
f"Calling project_from_xml: {filepath}")
|
|
@@ -1297,6 +1310,38 @@ class MainWindow(QMainWindow):
|
|
|
1297
1310
|
# New Project feature (v2.3 / F-15)
|
|
1298
1311
|
# ==================================================================
|
|
1299
1312
|
|
|
1313
|
+
def new_project_wizard(self) -> None:
|
|
1314
|
+
"""[v3.2] Launch the New Project Wizard."""
|
|
1315
|
+
if not self._maybe_save():
|
|
1316
|
+
return
|
|
1317
|
+
try:
|
|
1318
|
+
from statable_gui.new_project_wizard import NewProjectWizard
|
|
1319
|
+
except ImportError as e:
|
|
1320
|
+
QMessageBox.critical(
|
|
1321
|
+
self, "Wizard Error",
|
|
1322
|
+
f"Failed to import wizard module:\n{e}")
|
|
1323
|
+
return
|
|
1324
|
+
try:
|
|
1325
|
+
wiz = NewProjectWizard(self)
|
|
1326
|
+
if wiz.exec() != QDialog.Accepted:
|
|
1327
|
+
StaTableLogger.debug("New project wizard: cancelled")
|
|
1328
|
+
return
|
|
1329
|
+
xml_path = wiz.generate()
|
|
1330
|
+
if not xml_path:
|
|
1331
|
+
StaTableLogger.warning("Wizard returned no path")
|
|
1332
|
+
return
|
|
1333
|
+
StaTableLogger.info(f"New project generated: {xml_path}")
|
|
1334
|
+
self.open_project(filepath=xml_path)
|
|
1335
|
+
self.statusBar().showMessage(
|
|
1336
|
+
f"New project created: {xml_path}", 5000)
|
|
1337
|
+
except Exception as e:
|
|
1338
|
+
StaTableLogger.error(f"New project wizard failed: {e!r}")
|
|
1339
|
+
import traceback
|
|
1340
|
+
QMessageBox.critical(
|
|
1341
|
+
self, "Wizard Error",
|
|
1342
|
+
f"Failed to create project:\n{e}\n\n"
|
|
1343
|
+
f"{traceback.format_exc()}")
|
|
1344
|
+
|
|
1300
1345
|
def new_project(self) -> None:
|
|
1301
1346
|
"""Start a new empty project.
|
|
1302
1347
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# statable_gui/new_project_templates.py
|
|
2
|
+
"""Template definitions for the New Project Wizard."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
# Common events used across all layers
|
|
7
|
+
COMMON_EVENTS = [
|
|
8
|
+
{"name": "EV_INIT", "delivery": "queue", "kind": "signal", "description": "Initialization request"},
|
|
9
|
+
{"name": "EV_START", "delivery": "direct", "kind": "signal", "description": "Start request"},
|
|
10
|
+
{"name": "EV_STOP", "delivery": "direct", "kind": "signal", "description": "Stop request"},
|
|
11
|
+
{"name": "EV_ERROR", "delivery": "queue", "kind": "signal", "description": "Error notification"},
|
|
12
|
+
{"name": "EV_TIMEOUT", "delivery": "queue", "kind": "time", "description": "Timer expiry"},
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
# Global interrupts (shared across layers)
|
|
16
|
+
INTERRUPTS = [
|
|
17
|
+
{"name": "ISR_TIMER0", "description": "System tick timer"},
|
|
18
|
+
{"name": "ISR_UART_RX", "description": "UART receive complete"},
|
|
19
|
+
{"name": "ISR_UART_TX", "description": "UART transmit complete"},
|
|
20
|
+
{"name": "ISR_GPIO", "description": "GPIO edge detection"},
|
|
21
|
+
{"name": "ISR_ADC", "description": "ADC conversion complete"},
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
# Global variables (shared)
|
|
25
|
+
GLOBAL_VARIABLES = [
|
|
26
|
+
{"name": "g_counter", "type": "uint32_t", "description": "Loop counter"},
|
|
27
|
+
{"name": "g_error_code", "type": "int32_t", "description": "Last error code"},
|
|
28
|
+
{"name": "g_last_event", "type": "uint16_t", "description": "Last processed event ID"},
|
|
29
|
+
{"name": "g_timeout_ms", "type": "uint32_t", "description": "Timeout in milliseconds"},
|
|
30
|
+
{"name": "g_debug_level", "type": "uint8_t", "description": "Debug verbosity level"},
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Event flags (shared)
|
|
34
|
+
EVENT_FLAGS = [
|
|
35
|
+
{"name": "f_init_done", "description": "Initialization complete"},
|
|
36
|
+
{"name": "f_error", "description": "Error state active"},
|
|
37
|
+
{"name": "f_ready", "description": "Ready for operation"},
|
|
38
|
+
{"name": "f_busy", "description": "Operation in progress"},
|
|
39
|
+
{"name": "f_timeout", "description": "Timeout occurred"},
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Event queues (one per layer + 2 shared)
|
|
43
|
+
EVENT_QUEUES = [
|
|
44
|
+
{"name": "q_driver", "size": 16, "description": "Driver layer event queue"},
|
|
45
|
+
{"name": "q_middleware", "size": 16, "description": "Middleware layer event queue"},
|
|
46
|
+
{"name": "q_application", "size": 16, "description": "Application layer event queue"},
|
|
47
|
+
{"name": "q_system", "size": 32, "description": "System-wide event queue"},
|
|
48
|
+
{"name": "q_debug", "size": 8, "description": "Debug message queue"},
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Layer templates
|
|
53
|
+
LAYERS_3LAYER = [
|
|
54
|
+
{
|
|
55
|
+
"name": "Driver",
|
|
56
|
+
"priority": 1,
|
|
57
|
+
"description": "Hardware abstraction layer",
|
|
58
|
+
"states": [
|
|
59
|
+
{"name": "Driver_Init", "type": "initial", "description": "Hardware initialization"},
|
|
60
|
+
{"name": "Driver_Idle", "type": "normal", "description": "Waiting for commands"},
|
|
61
|
+
{"name": "Driver_Active", "type": "normal", "description": "Hardware active"},
|
|
62
|
+
{"name": "Driver_Error", "type": "normal", "description": "Hardware error"},
|
|
63
|
+
{"name": "Driver_Recover", "type": "normal", "description": "Recovery in progress"},
|
|
64
|
+
],
|
|
65
|
+
"role_functions": [
|
|
66
|
+
{"name": "Driver_HwInit", "description": "Initialize hardware"},
|
|
67
|
+
{"name": "Driver_Start", "description": "Start hardware operation"},
|
|
68
|
+
{"name": "Driver_Stop", "description": "Stop hardware operation"},
|
|
69
|
+
{"name": "Driver_HandleError", "description": "Handle hardware error"},
|
|
70
|
+
{"name": "Driver_Cleanup", "description": "Cleanup resources"},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "Middleware",
|
|
75
|
+
"priority": 3,
|
|
76
|
+
"description": "Business logic layer",
|
|
77
|
+
"states": [
|
|
78
|
+
{"name": "Middleware_Init", "type": "initial", "description": "Middleware initialization"},
|
|
79
|
+
{"name": "Middleware_Standby", "type": "normal", "description": "Waiting"},
|
|
80
|
+
{"name": "Middleware_Processing","type": "normal", "description": "Processing data"},
|
|
81
|
+
{"name": "Middleware_Waiting", "type": "normal", "description": "Waiting for response"},
|
|
82
|
+
{"name": "Middleware_Error", "type": "normal", "description": "Middleware error"},
|
|
83
|
+
],
|
|
84
|
+
"role_functions": [
|
|
85
|
+
{"name": "Middleware_Init", "description": "Initialize middleware"},
|
|
86
|
+
{"name": "Middleware_Process", "description": "Process request"},
|
|
87
|
+
{"name": "Middleware_Commit", "description": "Commit result"},
|
|
88
|
+
{"name": "Middleware_Rollback", "description": "Rollback on error"},
|
|
89
|
+
{"name": "Middleware_Cleanup", "description": "Cleanup resources"},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "Application",
|
|
94
|
+
"priority": 5,
|
|
95
|
+
"description": "User-facing application layer",
|
|
96
|
+
"states": [
|
|
97
|
+
{"name": "Application_Boot", "type": "initial", "description": "Boot sequence"},
|
|
98
|
+
{"name": "Application_Ready", "type": "normal", "description": "Ready for user input"},
|
|
99
|
+
{"name": "Application_Running", "type": "normal", "description": "Main operation"},
|
|
100
|
+
{"name": "Application_Paused", "type": "normal", "description": "Paused state"},
|
|
101
|
+
{"name": "Application_Shutdown", "type": "normal", "description": "Shutdown sequence"},
|
|
102
|
+
],
|
|
103
|
+
"role_functions": [
|
|
104
|
+
{"name": "Application_Init", "description": "Initialize application"},
|
|
105
|
+
{"name": "Application_Start", "description": "Start main operation"},
|
|
106
|
+
{"name": "Application_Pause", "description": "Pause operation"},
|
|
107
|
+
{"name": "Application_Resume", "description": "Resume operation"},
|
|
108
|
+
{"name": "Application_Shutdown", "description": "Shutdown application"},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
TEMPLATES = {
|
|
115
|
+
"blank": {
|
|
116
|
+
"id": "blank",
|
|
117
|
+
"name": "Empty Project",
|
|
118
|
+
"description": "One empty Application layer (existing behavior)",
|
|
119
|
+
"layers": [],
|
|
120
|
+
},
|
|
121
|
+
"basic": {
|
|
122
|
+
"id": "basic",
|
|
123
|
+
"name": "Basic (Single Layer)",
|
|
124
|
+
"description": "Application layer with 5 states + 5 events + 5 role functions",
|
|
125
|
+
"layers": [LAYERS_3LAYER[2]], # Application only
|
|
126
|
+
},
|
|
127
|
+
"three_layer": {
|
|
128
|
+
"id": "three_layer",
|
|
129
|
+
"name": "3-Layer (Driver / Middleware / Application) [Recommended]",
|
|
130
|
+
"description": "Full 3-layer structure with all placeholders",
|
|
131
|
+
"layers": LAYERS_3LAYER,
|
|
132
|
+
},
|
|
133
|
+
}
|