Preset templates
Parameterised presets with substitution and forEach expansion
Reference for FixoPresetTemplate — parameterised preset patterns the user fills in via a two-page wizard to produce a complete FixoPreset ready to build.
Why preset templates exist
Building a 24-actuator hexapod preset by clicking "Add actuator" 24 times is repetitive. A preset template captures the actuator pattern (3 joints × N legs), parameterises the leg count, and lets the wizard fill in the rest. Output: a fully-populated preset, board chosen, actuators tagged, stanza wired up via the matching stanza template.
Built-in templates
Three preset templates ship with libfixo:
rc-car
| Parameters | board (board-type), name (string) |
| Actuators | 2 motors tagged ["wheel", "left"] and ["wheel", "right"] |
| Stanza | One drive stanza using differential-drive stanza template |
| Result | Drives with CarSteeringWidget on Remote |
tracked-vehicle
| Parameters | board (board-type), name (string) |
| Actuators | 2 actuators tagged ["track", "left"] and ["track", "right"] |
| Stanza | One drive stanza using tracked-drive stanza template |
hexapod
| Parameters | board (board-type), name (string), legCount (int, default 6) |
| Actuators | legCount × 3 actuators per leg: coxa, femur, tibia tagged accordingly |
| Stanza | One walk stanza using hexapod-walk stanza template |
| Notes | Crank legCount to 4 for a quadruped or 8 for an octapod |
Substitution language
The template's preset body is a JSON tree with placeholders. The instantiator does three things:
1. {{paramName}} substitution
Anywhere in any string field. The user-supplied parameter value is interpolated:
{
"name": "{{name}}",
"board": "{{board}}"
}
Becomes, with name = "Hexy" and board = "arduino-mega2560":
{
"name": "Hexy",
"board": "arduino-mega2560"
}
If a placeholder doesn't resolve to a known parameter, it's left intact — useful for placeholders meant to be filled in by the consumer at a later stage (e.g. stanza-template references).
2. Typed parameters
Each template declares parameters with a type and optional default:
"parameters": {
"board": {"type": "board-type", "default": "arduino-mega2560"},
"name": {"type": "string"},
"legCount": {"type": "int", "default": 6}
}
Supported types:
type |
Wizard widget | Notes |
|---|---|---|
"board-type" |
Board dropdown populated from FixoBoardTypeBook |
The chosen value substitutes into {{board}} |
"int" |
QSpinBox |
Default applied if missing; non-integer input rejected |
"string" |
QLineEdit |
Default applied if missing |
3. forEachActuators expansion
For per-leg / per-joint patterns, you don't write 18 actuators by hand. Instead:
"forEachActuators": [
{
"countParameter": "legCount",
"pattern": {
"type": "rc-servo",
"name": "leg_{{index}}_coxa",
"semanticTags": ["leg_{{index}}", "coxa"]
}
},
{
"countParameter": "legCount",
"pattern": {
"type": "rc-servo",
"name": "leg_{{index}}_femur",
"semanticTags": ["leg_{{index}}", "femur"]
}
},
{
"countParameter": "legCount",
"pattern": {
"type": "rc-servo",
"name": "leg_{{index}}_tibia",
"semanticTags": ["leg_{{index}}", "tibia"]
}
}
]
The pattern is duplicated N = parameterValues[countParameter] times, with {{index}} substituting 0…N-1 per iteration. With legCount = 6 you get 18 actuators tagged leg_0_coxa, leg_0_femur, leg_0_tibia, leg_1_coxa, …, leg_5_tibia.
What is not supported
- Arithmetic:
{{legs * joints_per_leg}}is not a thing. Use a single int parameter for the count, or fix the per-iteration count (the hexapod has three joints per leg, so it uses threeforEachActuatorsrules — one per joint). - Conditional fields: there's no
{{if}}/{{unless}}. Author per-board variants by templating the JSON wholesale. - Generators:
{{generate:fn(expr)}}is also out of scope.
The first pass is intentionally minimal; the goal is "make 80 % of common cases trivial without a Turing-complete templating layer". Custom templates that need arithmetic should pre-compute and expose the result as a separate parameter.
Stanza-template references
A preset template's stanzas typically reference a stanza template by name. The instantiator emits a stub stanza with lobeType prefixed "template:":
"stanzas": [
{"id": "walk", "lobeType": "template:hexapod-walk"}
]
The consumer (typically the preset editor or FixoRuntime setup) sees the template: prefix and expands it via FixoStanzaTemplateBook::get("hexapod-walk")::instantiate(stanzaId, actuators). That step runs auto-connect against the just-expanded actuators, so the resulting FixoStanza has its mapping populated correctly.
This indirection means a preset template doesn't need to repeat the stanza-template's port list — it just names the template and lets the auto-connect do the wiring.
Wizard
The Studio's FixoPresetManager exposes a From template button that pushes FixoPresetTemplateWizardActivity:
| Page | Content |
|---|---|
| 1 | Template list with descriptions; user picks one |
| 2 | Parameter form: typed inputs per parameter (board dropdown, int spinbox, string line edit), all seeded with defaults; preset Name field seeded from template name with collision check against FixoPresetBook |
On Accept, the wizard:
- Validates parameters (type + presence)
- Calls
FixoPresetTemplate::instantiate(parameterValues, nameOverride) - Adds the resulting
FixoPresettoFixoPresetBook - Pops back to the manager, with the new preset selected
See Use a preset template for the user-side walkthrough, and Author a preset template for writing your own.
Custom templates
Drop a fixo.json with fixoType: "preset-template" into <personality>/fixo/preset-templates/. FixoLibrary::scan() picks it up; it appears in the wizard alongside the built-ins.
The recommended workflow is duplicate-to-edit: open FixoPresetTemplateManager in the Studio, duplicate the closest built-in, rename, edit the JSON.
API
struct FixoPresetTemplate {
QString name, title, description;
bool builtin{false};
QMap<QString, ParameterSpec> parameters;
FixoPreset basePreset; // tree with placeholders
QVector<ForEachActuators> forEachActuators;
FixoPreset instantiate(
const QMap<QString, QVariant> ¶meterValues,
const QString &nameOverride = {}) const;
};
class FixoPresetTemplateBook : public FixoBook {
QStringList list();
FixoPresetTemplate get(const QString &name);
bool create(const QString &name);
bool rename(const QString &oldName, const QString &newName);
bool remove(const QString &name);
bool importFile(const QString &path);
bool exportFile(const QString &name, const QString &path);
bool isBuiltin(const QString &name);
};
Source code
Each header has the name of its class, in the folder that the list names.
FixoPresetTemplate:libfixo/fixo/preset/FixoPresetTemplateBook:libfixo/fixo/library/FixoPresetTemplateWizardActivity:libfixo/fixo/studio/
See also
fixo.jsonschema — preset-template- Stanza Templates — what
template:references expand to - Use a preset template — user-side walkthrough
- Author a preset template — write your own