struct2ui 0.1.0__tar.gz → 0.2.0__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.
- {struct2ui-0.1.0/src/struct2ui.egg-info → struct2ui-0.2.0}/PKG-INFO +144 -5
- struct2ui-0.2.0/README.md +253 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/pyproject.toml +1 -1
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/editor.py +221 -64
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/exporters/bin_emitter.py +2 -1
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/exporters/c_emitter.py +10 -11
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/schema.py +207 -15
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/ui/renderers.py +2 -13
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/ui/tables.py +235 -207
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/ui/widgets.py +61 -4
- {struct2ui-0.1.0 → struct2ui-0.2.0/src/struct2ui.egg-info}/PKG-INFO +144 -5
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui.egg-info/SOURCES.txt +2 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_c_emitter.py +122 -69
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_c_parser.py +26 -0
- struct2ui-0.2.0/tests/test_choices.py +175 -0
- struct2ui-0.2.0/tests/test_editor.py +151 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_float_precision.py +20 -1
- struct2ui-0.2.0/tests/test_int_checkbox.py +47 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_save.py +3 -3
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_value_readback.py +36 -1
- struct2ui-0.1.0/README.md +0 -114
- struct2ui-0.1.0/tests/test_editor.py +0 -75
- {struct2ui-0.1.0 → struct2ui-0.2.0}/LICENSE +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/setup.cfg +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/__init__.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/exporters/__init__.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/exporters/c_parser.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/exporters/elf_verifier.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/exporters/json_format.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/c2j.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/elf.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/export_notes_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/flowchart_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/refresh_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/report_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/save_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/save_as_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/settings_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/icons/widgets_24dp_000000_FILL0_wght400_GRAD0_opsz24.png +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui/ui/__init__.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui.egg-info/dependency_links.txt +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui.egg-info/requires.txt +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/src/struct2ui.egg-info/top_level.txt +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_bin_emitter.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_elf_verifier.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_json_format.py +0 -0
- {struct2ui-0.1.0 → struct2ui-0.2.0}/tests/test_load_report.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: struct2ui
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Render C struct / JSON schema as editable PySide6 UI, export to C/JSON/bin
|
|
5
5
|
Author: Jay
|
|
6
6
|
License: MIT License
|
|
@@ -88,8 +88,8 @@ from struct2ui import StructEditor
|
|
|
88
88
|
app = QtWidgets.QApplication([])
|
|
89
89
|
|
|
90
90
|
editor = StructEditor(
|
|
91
|
-
flow_file="abc.json", # pipeline definition file
|
|
92
|
-
cfg_dir="cfg_t", # modules dir: *.json holding struct/enum/typedef
|
|
91
|
+
flow_file="abc.json", # pipeline definition file (optional)
|
|
92
|
+
cfg_dir="cfg_t", # modules dir: *.json holding struct/enum/typedef (optional)
|
|
93
93
|
)
|
|
94
94
|
editor.resize(480, 600)
|
|
95
95
|
editor.show()
|
|
@@ -97,6 +97,144 @@ editor.show()
|
|
|
97
97
|
app.exec_()
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
## JSON Schema Reference
|
|
101
|
+
|
|
102
|
+
This is the core of the library: you describe your C types in JSON and the UI is
|
|
103
|
+
generated from it. Files live in the **modules directory** (`cfg_dir`), one
|
|
104
|
+
`*.json` per logical group. Each file is a flat object whose keys are either
|
|
105
|
+
**type names** (your structs / enums) or the reserved key `typedefs`.
|
|
106
|
+
|
|
107
|
+
### File layout
|
|
108
|
+
|
|
109
|
+
```jsonc
|
|
110
|
+
{
|
|
111
|
+
// optional free-form metadata (ignored by the loader, allowed anywhere)
|
|
112
|
+
"version": "1.0",
|
|
113
|
+
"description": "audio EQ parameters",
|
|
114
|
+
|
|
115
|
+
// type aliases: map a custom C type to a primitive
|
|
116
|
+
"typedefs": { "gain_t": "int32_t", "freq_t": "float" },
|
|
117
|
+
|
|
118
|
+
// a struct definition
|
|
119
|
+
"eq_cfg_t": {
|
|
120
|
+
"type": "struct",
|
|
121
|
+
"items": [
|
|
122
|
+
{ "name": "enabled", "type": "uint8_t", "value": 1 },
|
|
123
|
+
{ "name": "gain", "type": "gain_t", "value": 0,
|
|
124
|
+
"min": -12, "max": 12, "step": 1, "unit": "dB",
|
|
125
|
+
"tip": "output gain", "when": { "enabled": 1 } }
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// an enum definition
|
|
130
|
+
"mode_t": {
|
|
131
|
+
"type": "enum",
|
|
132
|
+
"items": { "OFF": 0, "LOW": 1, "HIGH": 2 }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Top-level blocks
|
|
138
|
+
|
|
139
|
+
| Block type | Keys | Notes |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| `struct` | `type`, `items` (list of field specs) | `items` is required |
|
|
142
|
+
| `enum` | `type`, `items` (object `{NAME: value}`) | reads back the enumerator name |
|
|
143
|
+
| `typedefs` | object `{alias: real_type}` | resolved before field building |
|
|
144
|
+
|
|
145
|
+
Free-form metadata keys are allowed and ignored at any level: `version`,
|
|
146
|
+
`description`, `author`, `comment`, `note`.
|
|
147
|
+
|
|
148
|
+
### Field properties (inside a struct's `items`)
|
|
149
|
+
|
|
150
|
+
Each entry in `items` is a **field spec**. Only the following keys are
|
|
151
|
+
recognized — any other key triggers an "unknown keyword" error (with a
|
|
152
|
+
"Did you mean …?" hint):
|
|
153
|
+
|
|
154
|
+
| Property | Applies to | Description |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `name` | all | **Required.** C field name. |
|
|
157
|
+
| `type` | all | **Required.** C type or a `typedefs` alias (e.g. `int32_t`, `float`, `char`, an enum/struct name). |
|
|
158
|
+
| `value` | all | Default value. For arrays, a list (per element) or a scalar (applied to every element). |
|
|
159
|
+
| `count` | arrays | Makes the field an array. Integer, a `#define` name, an expression (`N - 1`), or a list for multi-dim (`[3, 4]`, stored flat as 12). |
|
|
160
|
+
| `min` / `max` | int, float | Numeric bounds. Validated `min <= max`. Drive spin-box / slider / dial ranges. |
|
|
161
|
+
| `step` | int, float | Increment, must be `> 0`. |
|
|
162
|
+
| `decimals` | float | Number of fractional digits shown/stored. Non-negative integer; defaults to digits implied by `step`. |
|
|
163
|
+
| `unit` | all | Unit suffix shown in the label, e.g. `gain (dB)`. |
|
|
164
|
+
| `tip` | all | Tooltip text. |
|
|
165
|
+
| `choices` | int, float, `char[N]` | Discrete value set rendered as a combo (see below). |
|
|
166
|
+
| `when` | all | Conditional enable/disable (see below). |
|
|
167
|
+
| `widget` | all | Override the auto-picked editor (see table below). |
|
|
168
|
+
|
|
169
|
+
### `widget` values
|
|
170
|
+
|
|
171
|
+
When omitted, the widget is inferred from `type` (and from `min/max`,
|
|
172
|
+
`choices`). Override it explicitly with `widget`:
|
|
173
|
+
|
|
174
|
+
| `widget` | Valid for | Renders as |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| `checkbox` | int | Check box (also auto-picked when `min:0, max:1`). |
|
|
177
|
+
| `toggle` | bool/int | Toggle push-button. |
|
|
178
|
+
| `combo` | int, float, `char[N]` | Combo box; pair with `choices`. |
|
|
179
|
+
| `slider` | int | Slider with min/max labels (requires `min` & `max`). |
|
|
180
|
+
| `dial` | int | Rotary dial popup (requires `min` & `max`). |
|
|
181
|
+
| `file` | array | Path label + Browse button; loads array values from a text file. |
|
|
182
|
+
| `multiline` | array | Single-line shell that pops up a multi-line editor. |
|
|
183
|
+
| `table` | struct array | Force grid layout (one column per struct member). |
|
|
184
|
+
|
|
185
|
+
Numeric fields without an explicit `widget` become a `QSpinBox` /
|
|
186
|
+
`QDoubleSpinBox`; scalar arrays become a comma-separated line edit.
|
|
187
|
+
|
|
188
|
+
### `choices` (discrete values)
|
|
189
|
+
|
|
190
|
+
A non-enum integer / float / `char[N]` field can be constrained to a fixed set.
|
|
191
|
+
Two forms are accepted:
|
|
192
|
+
|
|
193
|
+
```jsonc
|
|
194
|
+
"choices": [0, 1, 2, 3] // label = str(value)
|
|
195
|
+
"choices": [{ "Off": 0 }, { "Low": 1 }, { "High": 2 }] // explicit labels
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The combo **reads back the underlying value** (not the label), so C / JSON / bin
|
|
199
|
+
export still emits the number (or string for `char[N]`). Audits enforce: each
|
|
200
|
+
value within `[min, max]` when declared, and `value` (the default) must be one
|
|
201
|
+
of the choices.
|
|
202
|
+
|
|
203
|
+
### `when` (conditional enable/disable)
|
|
204
|
+
|
|
205
|
+
A field can be greyed out unless other fields hold specific values. `when` is an
|
|
206
|
+
object mapping a **sibling field name** to its required value; all entries must
|
|
207
|
+
match (logical AND):
|
|
208
|
+
|
|
209
|
+
```jsonc
|
|
210
|
+
{ "name": "cutoff", "type": "float", "when": { "enabled": 1, "mode": "HIGH" } }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The dependency name is matched against the dotted leaf name in scope. When a
|
|
214
|
+
dependency is not found, the field stays enabled.
|
|
215
|
+
|
|
216
|
+
### `count` expressions & multi-dimensional arrays
|
|
217
|
+
|
|
218
|
+
`count` may be an integer, a `#define` constant name, or an integer expression
|
|
219
|
+
of those using `+ - * / % ()` (e.g. `MAX_BAND_NUM - 1`). A list makes a
|
|
220
|
+
multi-dimensional array that is **stored flat** to match the C ABI:
|
|
221
|
+
|
|
222
|
+
```jsonc
|
|
223
|
+
{ "name": "matrix", "type": "float", "count": [3, 4] } // 12 contiguous floats, shown as [3][4]
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`char[N]` is treated as a C string by default (use `widget` to override).
|
|
227
|
+
|
|
228
|
+
### Validation
|
|
229
|
+
|
|
230
|
+
Loading runs three layers of checks, surfaced in the **load report** panel:
|
|
231
|
+
|
|
232
|
+
- **Keyword spelling** — unknown keys report "Did you mean `tip`?" style hints.
|
|
233
|
+
- **Semantic audits** — `min <= max`, `step > 0`, `count > 0`, choices within
|
|
234
|
+
bounds, default is one of the choices.
|
|
235
|
+
- **Pipeline cross-validation** — values in the pipeline file are checked
|
|
236
|
+
against the schema (range, choices, enum membership).
|
|
237
|
+
|
|
100
238
|
## Embedding into an Existing UI
|
|
101
239
|
|
|
102
240
|
`StructEditor` is a regular `QWidget`; just put it into a layout:
|
|
@@ -120,9 +258,10 @@ class MyWindow(QtWidgets.QMainWindow):
|
|
|
120
258
|
|
|
121
259
|
| Parameter | Description |
|
|
122
260
|
| --- | --- |
|
|
123
|
-
| `flow_file` | Path to the pipeline JSON file (pipeline definition) |
|
|
124
|
-
| `cfg_dir` | Modules directory holding `*.json` (struct / enum / typedef definitions) |
|
|
261
|
+
| `flow_file` | Path to the pipeline JSON file (pipeline definition), defaults to `None`; pick it in the UI when omitted |
|
|
262
|
+
| `cfg_dir` | Modules directory holding `*.json` (struct / enum / typedef definitions), defaults to `None`; pick it in the UI when omitted |
|
|
125
263
|
| `parent` | Qt parent object, defaults to `None` |
|
|
264
|
+
| `elf_path` | Path to an ELF for layout verification, defaults to `None`; pick it in the UI when omitted |
|
|
126
265
|
| `appearance` | Button appearance overrides, `{key: {'mode': ..., 'icon': ..., 'text': ...}}` |
|
|
127
266
|
| `settings_org` | QSettings organization name, defaults to `'struct2ui'` |
|
|
128
267
|
| `settings_app` | QSettings application name, defaults to `'StructEditor'` |
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# struct2ui
|
|
2
|
+
|
|
3
|
+
Render C structs / JSON schemas as an **editable PySide6 UI**, and convert freely between **C / JSON / bin**. Built for algorithm parameter tuning: describe a C interface in JSON, auto-generate a Qt form, edit it, then export back to C source, JSON, or binary.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **JSON → UI**: describe C structs / enums / arrays with minimal JSON and auto-render Qt widgets (int→QSpinBox, float→QDoubleSpinBox, enum→QComboBox, etc.).
|
|
8
|
+
- **Embeddable**: `StructEditor` is a plain `QWidget` that drops into any PySide6 / PyQt UI.
|
|
9
|
+
- **Multi-format export**: edited results export to C source, JSON, or binary; C source can also be parsed back into a schema.
|
|
10
|
+
- **Validation**: keyword spell-checking (with “Did you mean X?” hints), semantic audits (`min<=max`, `step>0`), and pipeline cross-validation.
|
|
11
|
+
- **Qt-binding agnostic**: built on [Qt.py](https://github.com/mottosso/Qt.py) — works with PySide6 / PyQt6 / PySide2 / PyQt5.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install struct2ui
|
|
17
|
+
|
|
18
|
+
# Pick a Qt binding (choose one)
|
|
19
|
+
pip install "struct2ui[pyside6]"
|
|
20
|
+
pip install "struct2ui[pyqt6]"
|
|
21
|
+
|
|
22
|
+
# Enable ELF layout verification (optional)
|
|
23
|
+
pip install "struct2ui[elf]"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
> The library itself only depends on `Qt.py`; you must install a Qt binding (PySide6 / PyQt6 / etc.) yourself.
|
|
27
|
+
> ELF verification depends on `pyelftools`, installed via the `[elf]` extra.
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from Qt import QtWidgets
|
|
33
|
+
from struct2ui import StructEditor
|
|
34
|
+
|
|
35
|
+
app = QtWidgets.QApplication([])
|
|
36
|
+
|
|
37
|
+
editor = StructEditor(
|
|
38
|
+
flow_file="abc.json", # pipeline definition file (optional)
|
|
39
|
+
cfg_dir="cfg_t", # modules dir: *.json holding struct/enum/typedef (optional)
|
|
40
|
+
)
|
|
41
|
+
editor.resize(480, 600)
|
|
42
|
+
editor.show()
|
|
43
|
+
|
|
44
|
+
app.exec_()
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## JSON Schema Reference
|
|
48
|
+
|
|
49
|
+
This is the core of the library: you describe your C types in JSON and the UI is
|
|
50
|
+
generated from it. Files live in the **modules directory** (`cfg_dir`), one
|
|
51
|
+
`*.json` per logical group. Each file is a flat object whose keys are either
|
|
52
|
+
**type names** (your structs / enums) or the reserved key `typedefs`.
|
|
53
|
+
|
|
54
|
+
### File layout
|
|
55
|
+
|
|
56
|
+
```jsonc
|
|
57
|
+
{
|
|
58
|
+
// optional free-form metadata (ignored by the loader, allowed anywhere)
|
|
59
|
+
"version": "1.0",
|
|
60
|
+
"description": "audio EQ parameters",
|
|
61
|
+
|
|
62
|
+
// type aliases: map a custom C type to a primitive
|
|
63
|
+
"typedefs": { "gain_t": "int32_t", "freq_t": "float" },
|
|
64
|
+
|
|
65
|
+
// a struct definition
|
|
66
|
+
"eq_cfg_t": {
|
|
67
|
+
"type": "struct",
|
|
68
|
+
"items": [
|
|
69
|
+
{ "name": "enabled", "type": "uint8_t", "value": 1 },
|
|
70
|
+
{ "name": "gain", "type": "gain_t", "value": 0,
|
|
71
|
+
"min": -12, "max": 12, "step": 1, "unit": "dB",
|
|
72
|
+
"tip": "output gain", "when": { "enabled": 1 } }
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// an enum definition
|
|
77
|
+
"mode_t": {
|
|
78
|
+
"type": "enum",
|
|
79
|
+
"items": { "OFF": 0, "LOW": 1, "HIGH": 2 }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Top-level blocks
|
|
85
|
+
|
|
86
|
+
| Block type | Keys | Notes |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `struct` | `type`, `items` (list of field specs) | `items` is required |
|
|
89
|
+
| `enum` | `type`, `items` (object `{NAME: value}`) | reads back the enumerator name |
|
|
90
|
+
| `typedefs` | object `{alias: real_type}` | resolved before field building |
|
|
91
|
+
|
|
92
|
+
Free-form metadata keys are allowed and ignored at any level: `version`,
|
|
93
|
+
`description`, `author`, `comment`, `note`.
|
|
94
|
+
|
|
95
|
+
### Field properties (inside a struct's `items`)
|
|
96
|
+
|
|
97
|
+
Each entry in `items` is a **field spec**. Only the following keys are
|
|
98
|
+
recognized — any other key triggers an "unknown keyword" error (with a
|
|
99
|
+
"Did you mean …?" hint):
|
|
100
|
+
|
|
101
|
+
| Property | Applies to | Description |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| `name` | all | **Required.** C field name. |
|
|
104
|
+
| `type` | all | **Required.** C type or a `typedefs` alias (e.g. `int32_t`, `float`, `char`, an enum/struct name). |
|
|
105
|
+
| `value` | all | Default value. For arrays, a list (per element) or a scalar (applied to every element). |
|
|
106
|
+
| `count` | arrays | Makes the field an array. Integer, a `#define` name, an expression (`N - 1`), or a list for multi-dim (`[3, 4]`, stored flat as 12). |
|
|
107
|
+
| `min` / `max` | int, float | Numeric bounds. Validated `min <= max`. Drive spin-box / slider / dial ranges. |
|
|
108
|
+
| `step` | int, float | Increment, must be `> 0`. |
|
|
109
|
+
| `decimals` | float | Number of fractional digits shown/stored. Non-negative integer; defaults to digits implied by `step`. |
|
|
110
|
+
| `unit` | all | Unit suffix shown in the label, e.g. `gain (dB)`. |
|
|
111
|
+
| `tip` | all | Tooltip text. |
|
|
112
|
+
| `choices` | int, float, `char[N]` | Discrete value set rendered as a combo (see below). |
|
|
113
|
+
| `when` | all | Conditional enable/disable (see below). |
|
|
114
|
+
| `widget` | all | Override the auto-picked editor (see table below). |
|
|
115
|
+
|
|
116
|
+
### `widget` values
|
|
117
|
+
|
|
118
|
+
When omitted, the widget is inferred from `type` (and from `min/max`,
|
|
119
|
+
`choices`). Override it explicitly with `widget`:
|
|
120
|
+
|
|
121
|
+
| `widget` | Valid for | Renders as |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| `checkbox` | int | Check box (also auto-picked when `min:0, max:1`). |
|
|
124
|
+
| `toggle` | bool/int | Toggle push-button. |
|
|
125
|
+
| `combo` | int, float, `char[N]` | Combo box; pair with `choices`. |
|
|
126
|
+
| `slider` | int | Slider with min/max labels (requires `min` & `max`). |
|
|
127
|
+
| `dial` | int | Rotary dial popup (requires `min` & `max`). |
|
|
128
|
+
| `file` | array | Path label + Browse button; loads array values from a text file. |
|
|
129
|
+
| `multiline` | array | Single-line shell that pops up a multi-line editor. |
|
|
130
|
+
| `table` | struct array | Force grid layout (one column per struct member). |
|
|
131
|
+
|
|
132
|
+
Numeric fields without an explicit `widget` become a `QSpinBox` /
|
|
133
|
+
`QDoubleSpinBox`; scalar arrays become a comma-separated line edit.
|
|
134
|
+
|
|
135
|
+
### `choices` (discrete values)
|
|
136
|
+
|
|
137
|
+
A non-enum integer / float / `char[N]` field can be constrained to a fixed set.
|
|
138
|
+
Two forms are accepted:
|
|
139
|
+
|
|
140
|
+
```jsonc
|
|
141
|
+
"choices": [0, 1, 2, 3] // label = str(value)
|
|
142
|
+
"choices": [{ "Off": 0 }, { "Low": 1 }, { "High": 2 }] // explicit labels
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The combo **reads back the underlying value** (not the label), so C / JSON / bin
|
|
146
|
+
export still emits the number (or string for `char[N]`). Audits enforce: each
|
|
147
|
+
value within `[min, max]` when declared, and `value` (the default) must be one
|
|
148
|
+
of the choices.
|
|
149
|
+
|
|
150
|
+
### `when` (conditional enable/disable)
|
|
151
|
+
|
|
152
|
+
A field can be greyed out unless other fields hold specific values. `when` is an
|
|
153
|
+
object mapping a **sibling field name** to its required value; all entries must
|
|
154
|
+
match (logical AND):
|
|
155
|
+
|
|
156
|
+
```jsonc
|
|
157
|
+
{ "name": "cutoff", "type": "float", "when": { "enabled": 1, "mode": "HIGH" } }
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The dependency name is matched against the dotted leaf name in scope. When a
|
|
161
|
+
dependency is not found, the field stays enabled.
|
|
162
|
+
|
|
163
|
+
### `count` expressions & multi-dimensional arrays
|
|
164
|
+
|
|
165
|
+
`count` may be an integer, a `#define` constant name, or an integer expression
|
|
166
|
+
of those using `+ - * / % ()` (e.g. `MAX_BAND_NUM - 1`). A list makes a
|
|
167
|
+
multi-dimensional array that is **stored flat** to match the C ABI:
|
|
168
|
+
|
|
169
|
+
```jsonc
|
|
170
|
+
{ "name": "matrix", "type": "float", "count": [3, 4] } // 12 contiguous floats, shown as [3][4]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`char[N]` is treated as a C string by default (use `widget` to override).
|
|
174
|
+
|
|
175
|
+
### Validation
|
|
176
|
+
|
|
177
|
+
Loading runs three layers of checks, surfaced in the **load report** panel:
|
|
178
|
+
|
|
179
|
+
- **Keyword spelling** — unknown keys report "Did you mean `tip`?" style hints.
|
|
180
|
+
- **Semantic audits** — `min <= max`, `step > 0`, `count > 0`, choices within
|
|
181
|
+
bounds, default is one of the choices.
|
|
182
|
+
- **Pipeline cross-validation** — values in the pipeline file are checked
|
|
183
|
+
against the schema (range, choices, enum membership).
|
|
184
|
+
|
|
185
|
+
## Embedding into an Existing UI
|
|
186
|
+
|
|
187
|
+
`StructEditor` is a regular `QWidget`; just put it into a layout:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from Qt import QtWidgets
|
|
191
|
+
from struct2ui import StructEditor
|
|
192
|
+
|
|
193
|
+
class MyWindow(QtWidgets.QMainWindow):
|
|
194
|
+
def __init__(self):
|
|
195
|
+
super().__init__()
|
|
196
|
+
editor = StructEditor(
|
|
197
|
+
"abc.json", "cfg_t",
|
|
198
|
+
settings_org="MyCompany", # custom QSettings scope to
|
|
199
|
+
settings_app="MyApp", # avoid clashing with the host app
|
|
200
|
+
)
|
|
201
|
+
self.setCentralWidget(editor)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Constructor Parameters
|
|
205
|
+
|
|
206
|
+
| Parameter | Description |
|
|
207
|
+
| --- | --- |
|
|
208
|
+
| `flow_file` | Path to the pipeline JSON file (pipeline definition), defaults to `None`; pick it in the UI when omitted |
|
|
209
|
+
| `cfg_dir` | Modules directory holding `*.json` (struct / enum / typedef definitions), defaults to `None`; pick it in the UI when omitted |
|
|
210
|
+
| `parent` | Qt parent object, defaults to `None` |
|
|
211
|
+
| `elf_path` | Path to an ELF for layout verification, defaults to `None`; pick it in the UI when omitted |
|
|
212
|
+
| `appearance` | Button appearance overrides, `{key: {'mode': ..., 'icon': ..., 'text': ...}}` |
|
|
213
|
+
| `settings_org` | QSettings organization name, defaults to `'struct2ui'` |
|
|
214
|
+
| `settings_app` | QSettings application name, defaults to `'StructEditor'` |
|
|
215
|
+
|
|
216
|
+
## Export API
|
|
217
|
+
|
|
218
|
+
The low-level export functions can be used standalone, without any UI:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from struct2ui.schema import SchemaRegistry
|
|
222
|
+
from struct2ui.exporters import (
|
|
223
|
+
emit_c, # sections + registry -> C source string
|
|
224
|
+
dumps_json, # -> JSON string
|
|
225
|
+
emit_bin, # -> binary bytes
|
|
226
|
+
merge_abi, # merge ABI info
|
|
227
|
+
verify_sections, # verify .bin layout against an ELF
|
|
228
|
+
parse_c_source, # C source -> parse result
|
|
229
|
+
build_schema_dict,
|
|
230
|
+
)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Architecture
|
|
234
|
+
|
|
235
|
+
| Layer | Module | Responsibility |
|
|
236
|
+
| --- | --- | --- |
|
|
237
|
+
| Schema (pure data, no Qt) | `struct2ui.schema` | Parse `*.json` into a Field tree; spell-checking, semantic audits, pipeline cross-validation |
|
|
238
|
+
| UI rendering | `struct2ui.ui` | `WidgetFactory`, `FormRenderer`/`TreeRenderer`, array tables, `when` conditional binding |
|
|
239
|
+
| Export | `struct2ui.exporters` | C / JSON / bin export, C source reverse parsing, ELF verification |
|
|
240
|
+
| Top-level widget | `struct2ui.StructEditor` | Path bar / action buttons / content area; load report panel; QSettings path memory |
|
|
241
|
+
|
|
242
|
+
## Development
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
pip install -e ".[dev]" --no-build-isolation
|
|
246
|
+
python -m pytest
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Tests live under `tests/` and drive real Qt widgets headlessly on the offscreen platform.
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
[MIT](LICENSE) © Jay
|