config#

Experiment configuration registry.

This module defines two classes:

ConfigRegister

A generic key/value registry with optional type validation and per-key change callbacks. Used as a building block by ExperimentConfig.

ExperimentConfig

Experiment-aware extension of ConfigRegister with default parameters (subject / session / task / LED pattern / duration), a BIDS-style path layout (experiment_dirdata_dirbids_dir), JSON load/save, and an attached HardwareManager.

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)
class mesofield.config.ConfigRegister[source]#

Bases: object

A registry that maintains configuration values with optional type validation.

__init__()[source]#
register(key, default=None, type_hint=None, description='', category='general')[source]#

Register a configuration parameter with metadata.

Parameters:
  • key (str)

  • default (Any)

  • type_hint (Type | None)

  • description (str)

  • category (str)

Return type:

None

get(key, default=None)[source]#

Get a configuration value.

Parameters:
Return type:

Any

set(key, value)[source]#

Set a configuration value with type validation.

Parameters:
Return type:

None

has(key)[source]#

Check if a key exists in the registry.

Parameters:

key (str)

Return type:

bool

keys()[source]#

Get all registered keys.

Return type:

List[str]

items()[source]#

Get all key-value pairs.

Return type:

Dict[str, Any]

get_metadata(key)[source]#

Get metadata for a key.

Parameters:

key (str)

Return type:

Dict[str, Any]

register_callback(key, callback)[source]#

Register a callback for when a key’s value changes.

Parameters:
Return type:

None

register_choices(key, choices)[source]#

Register a list of selectable choices for a configuration key.

Parameters:
Return type:

None

get_choices(key)[source]#

Return the list of choices for key, or None if none are registered.

Parameters:

key (str)

Return type:

List[Any] | None

clear()[source]#

Clear all configurations.

Return type:

None

class mesofield.config.ExperimentConfig[source]#

Bases: ConfigRegister

Generate and store experiment parameters using a configuration registry.

ExperimentConfig extends ConfigRegister with experiment-aware defaults (subject / session / task, LED pattern, duration, etc.), a BIDS-style path layout (experiment_dir / data_dir / bids_dir), and integration with a HardwareManager.

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")
__init__(path=None)[source]#
Parameters:

path (str | None)

set(key, value)[source]#

Set config values with field-specific normalization where needed.

Parameters:
Return type:

None

load_hardware(yaml_path)[source]#

Load (or reload) a hardware YAML configuration.

This replaces the current HardwareManager with a new one pointed at yaml_path. Devices are not initialised until HardwareManager.initialize() is called (which is normally done by initialize_hardware).

Parameters:

yaml_path (str)

Return type:

None

property experiment_dir: str#

Get the experiment directory (base directory).

property data_dir: str#

Get the data directory (experiment_dir/data).

property save_dir: str#

Get the save directory (legacy alias for data_dir).

property subject: str#

Get the subject ID.

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 task: str#

Get the task ID.

property start_on_trigger: bool#

Get whether to start on trigger.

property sequence_duration: int#

Get the sequence duration in seconds.

property trial_duration: int#

Get the trial duration in seconds.

property num_trials: int#

Calculate the number of trials.

build_sequence(camera)[source]#

Build a useq.MDASequence sized to this experiment.

The loop count is derived from num_meso_frames when set, or from camera.sampling_rate * sequence_duration otherwise. All HardwareManager fields 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 DataProducer whose sampling_rate drives the default loop count.

Returns:

A ready-to-run MDASequence with a zero-interval time plan.

Return type:

MDASequence

property bids_dir: str#

Dynamic construct of BIDS directory path

property dataframe#

Convert parameters to a pandas DataFrame.

property psychopy_filename: str#

Get the PsychoPy experiment filename.

property psychopy_path: str#

Get the PsychoPy script path.

property psychopy_save_path: str#

Get the PsychoPy save path.

property psychopy_parameters: dict#

Get parameters for PsychoPy.

property led_pattern: list[str]#

Get the LED pattern.

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"). When None, the file is placed directly under bids_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 (see Procedure.define_config()). Unlike load_json() it does not touch the hardware YAML path – scripted hardware is supplied directly via Procedure.define_hardware().

data may be a @dataclass instance or any mapping. Both the flat and the Configuration/Subjects shapes 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 the Configuration / Subjects / DisplayKeys shape, then points _json_file_path at 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

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 to experiment.json.

Parameters:

subject_id (str)

Return type:

None

add_parameter(name, default, type_hint)[source]#

Add a subject-scoped parameter to every subject and DisplayKeys.

Registers the parameter in the config registry, fills it on every subject in subjects, appends it to display_keys, and persists the additions to experiment.json.

Parameters:
Return type:

None