config#
Experiment configuration registry.
This module defines two classes:
ConfigRegisterA generic key/value registry with optional type validation and per-key change callbacks. Used as a building block by
ExperimentConfig.ExperimentConfigExperiment-aware extension of
ConfigRegisterwith default parameters (subject / session / task / LED pattern / duration), a BIDS-style path layout (experiment_dir→data_dir→bids_dir), JSON load/save, and an attachedHardwareManager.
Typical lifecycle:
cfg = ExperimentConfig("path/to/hardware.yaml")
cfg.load_json("path/to/experiment.json")
cfg.set("subject", "001")
seq = cfg.build_sequence(cfg.hardware.primary)
- mesofield.config.parse_task_from_filename(name)[source]#
Return the task token from a
task-{name}filename, orNone.Example:
parse_task_from_filename("Gratings_task-grat_v0.9.py") == "grat".
- class mesofield.config.ConfigRegister[source]#
Bases:
objectA registry that maintains configuration values with optional type validation.
- register(key, default=None, type_hint=None, description='', category='general')[source]#
Register a configuration parameter with metadata.
- register_choices(key, choices)[source]#
Register a list of selectable choices for a configuration key.
- class mesofield.config.ExperimentConfig[source]#
Bases:
ConfigRegisterGenerate and store experiment parameters using a configuration registry.
ExperimentConfigextendsConfigRegisterwith experiment-aware defaults (subject / session / task, LED pattern, duration, etc.), a BIDS-style path layout (experiment_dir / data_dir / bids_dir), and integration with aHardwareManager.Example
from mesofield.config import ExperimentConfig config = ExperimentConfig("path/to/hardware.yaml") # Populate from a JSON config file: config.load_json("path/to/experiment.json") config.experiment_dir = "./output" config.set("subject", "001") config.set("task", "TestTask") config.notes.append("This is a test note.") # Persist parameters and notes back to JSON: config.save_json("path/to/experiment.json")
- load_hardware(yaml_path)[source]#
Load (or reload) a hardware YAML configuration.
This replaces the current
HardwareManagerwith a new one pointed at yaml_path. Devices are not initialised untilHardwareManager.initialize()is called (which is normally done byinitialize_hardware).- Parameters:
yaml_path (str)
- Return type:
None
- load_hardware_spec(spec)[source]#
Install a rig from an in-memory mapping (e.g. an embedded
hardwareblock).Replaces the current
HardwareManagerwith one built from spec. Devices are not initialised untilHardwareManager.initialize().- Parameters:
spec (dict)
- Return type:
None
- property experiment_dir_is_set: bool#
Trueonce a caller has explicitly chosenexperiment_dir.Lets launchers (e.g.
load_procedure) apply a fallback directory only when the user/config never picked one.
- property session: str#
Get the session ID as a zero-padded BIDS string (e.g. “01”).
Formatting is enforced here so paths/filenames are always padded regardless of how the raw value was entered (GUI, JSON, etc.).
- property trial_duration: int#
Trial duration in seconds.
The selected task’s PsychoPy
trial_duration(when set) wins over the plaintrial_durationconfig value, so per-task stimulus timing is data-driven instead of hardcoded in a Procedure subclass.
- property num_trials: int#
Number of trials.
When the selected task declares a PsychoPy
trial_duration, derive it assequence_duration // trial_duration(at least 1) – the rule the legacy vis-stim Procedure hardcoded, now driven by the task map. Otherwise fall back to the storednum_trials(default 20).
- build_sequence(camera)[source]#
Build a
useq.MDASequencesized to this experiment.The loop count is derived from
num_meso_frameswhen set, or fromcamera.sampling_rate * sequence_durationotherwise. AllHardwareManagerfields are attached as sequence metadata so downstream engines (e.g.MesoEngine) can resolve the LED pattern and NI-DAQ at setup time.- Parameters:
camera (DataProducer) – The primary
DataProducerwhosesampling_ratedrives the default loop count.- Returns:
A ready-to-run
MDASequencewith a zero-interval time plan.- Return type:
MDASequence
- property dataframe#
Convert parameters to a pandas DataFrame.
- property psychopy: dict#
The task -> PsychoPy-script map.
Each value is either a bare filename (legacy) or a dict
{"file": ..., "trial_duration": ...}– the optional per-tasktrial_duration(seconds) drivesnum_trialsand is passed to the script. Use_psychopy_entry()to read either shape.
- property psychopy_path: str#
Resolve the PsychoPy script path for the currently selected task.
When a
task -> scriptmap is present (thePsychoPyblock) and has an entry for the currenttask, that script wins: an absolute path is used as-is, a relative one is resolved againstexperiment_dir. With no map (or no entry for this task) it falls back to the legacy singlepsychopy_filenameso existing one-script experiments keep working.
- make_path(suffix, extension, bids_type=None, create_dir=False)[source]#
Build a unique BIDS-style output file path.
The returned path follows the layout
<bids_dir>/[<bids_type>/]<timestamp>_sub-<id>_ses-<id>_task-<id>_<suffix>.<ext>. If a file with that name already exists,_<n>is appended to keep the path unique.- Parameters:
suffix (str) – Trailing tag added to the filename, e.g.
"images".extension (str) – File extension without the leading dot, e.g.
"jpg".bids_type (str | None) – Optional BIDS modality subdirectory under
bids_dir(e.g."func"). WhenNone, the file is placed directly underbids_dir.create_dir (bool) – When
True, parent directories are created.
- Returns:
Absolute path to the generated file.
Example
cfg.make_path("images", "jpg", "func") # -> 'C:/save_dir/data/sub-001/ses-01/func/' # '20250110_123456_sub-001_ses-01_task-example_images.jpg'
- load_json(file_path)[source]#
Load parameters from a JSON configuration file into the config object.
- Return type:
None
- load_dict(data)[source]#
Load parameters from a dataclass instance or plain mapping.
This is the programmatic counterpart to
load_json()used by scripted procedures (seeProcedure.define_config()). Unlikeload_json()it does not touch the hardware YAML path – scripted hardware is supplied directly viaProcedure.define_hardware().data may be a
@dataclassinstance or any mapping. Both the flat and theConfiguration/Subjectsshapes are accepted.- Parameters:
data (Any)
- Return type:
None
- save_json(path=None)[source]#
Persist displayed configuration values back to the JSON file.
- Parameters:
path (str | None)
- Return type:
None
- save_json_as(path)[source]#
Write the current configuration to a new JSON file and adopt it.
Unlike
save_json()(which edits an existing file in place), this serializes the full in-memory state – registry values, subjects, and DisplayKeys – into theConfiguration/Subjects/DisplayKeysshape, then points_json_file_pathat the new file so later saves land there. Lets the GUI author an experiment.json from a hardware-only session.- Parameters:
path (str)
- Return type:
None
- select_subject(subject_id)[source]#
Apply subject-specific parameters from
self.subjects.- Parameters:
subject_id (str)
- Return type:
None
- update_mouseportal(block)[source]#
Apply and persist the MousePortal config block.
Updates the in-memory registry (so the next run’s
armreads the new params) and writes the top-levelMousePortalblock back to experiment.json. Mirrors how ConfigController persists ExperimentConfig edits, but for MousePortal’s structured block.- Parameters:
block (dict)
- Return type:
None
- update_psychopy(block)[source]#
Apply and persist the task -> PsychoPy-script map.
Updates the in-memory registry (so the next run’s
armresolves the right script), re-derives thetaskdropdown choices from the map keys, and writes the top-levelPsychoPyblock back to experiment.json. Mirrorsupdate_mouseportal()for PsychoPy’s task->script map.- Parameters:
block (dict)
- Return type:
None
- update_hardware(spec)[source]#
Persist the rig as the top-level
hardwareblock of experiment.json.Makes the JSON self-contained so a relaunch needs no separate rig file.
- Parameters:
spec (dict)
- Return type:
None
- add_subject(subject_id)[source]#
Add a new subject, seeding parameters from existing subjects.
The new subject’s parameter dict is the union of keys from existing subjects with blank string values, so all subjects share a consistent parameter set and
select_subject()will accept the new entry. Persists toexperiment.json.- Parameters:
subject_id (str)
- Return type:
None