psychopy_device#
PsychoPy stimulus device.
Launches a PsychoPy experiment script as a subprocess on the shared
SubprocessStimulusDevice engine, so the
Procedure drives it through the same
arm/start/stop/shutdown lifecycle and stdout readiness handshake as
MousePortal and any other external-app stimulus device.
PsychoPy is operator-in-the-loop: unlike MousePortal (which launches silently
at arm), it launches at start and overrides the base’s presentation
hooks to show a “launching” dialog, surface a startup failure with the script’s
output, and gate recording behind a focused “press to start” dialog forced over
PsychoPy’s full-screen window. The readiness handshake is mandatory
(require_ready = True): if PSYCHOPY_READY never arrives, the run fails
rather than recording against a stimulus that never started.
PsychoPy is not a DataProducer: it never emits on
signals.data. signals.started fires on the PSYCHOPY_READY handshake;
signals.finished fires on stop / subprocess exit.
Config convention (standardized with MousePortal): the script and parameters
live in the ExperimentConfig (experiment.json); the hardware.yaml psychopy
stanza carries only subprocess plumbing (type / python_exe /
ready_timeout). Which script runs is keyed by the selected task: a
top-level PsychoPy block maps {task: filename} (scripts declare their
task by embedding task-{name} in the filename), and
ExperimentConfig.psychopy_path resolves the entry for the current task,
falling back to the legacy single psychopy_filename when no map is present.
prepare() reads psychopy_path / psychopy_parameters and is unaware of
this resolution. Parameters are handed to the script as a base64 JSON argv
token so the PsychoPy interpreter needs only the stdlib to decode them (no
mesofield import). The matching offline parser is registered under the
psychopy tag in mesofield.datakit.sources and bound here as
PsychoPyDevice.Parser for the documented dispatch convention.
- class mesofield.devices.psychopy_device.PsychoPyDevice[source]#
Bases:
SubprocessStimulusDeviceStimulus device that launches a PsychoPy script as a subprocess.
- serves_task(task, config)[source]#
Serve a task iff the task->script map has an entry for it.
With no map (legacy single-script experiments) PsychoPy serves every task, preserving the old behavior.
- Return type:
- prepare(config)[source]#
Resolve the script path and serialize parameters for the subprocess.
Parameters are sent as base64-encoded JSON (a single safe argv token) so the PsychoPy interpreter decodes them with only the stdlib – the script rebuilds an attribute namespace, e.g.
config = types.SimpleNamespace(**json.loads(base64.b64decode(sys.argv[1]))).- Return type:
None
- preflight()[source]#
Return an actionable error string to abort launch, or None to proceed.
- Return type:
str | None
- present_launching()[source]#
Show a non-blocking ‘waiting for PSYCHOPY_READY’ indicator.
- Return type:
None
- dismiss_launching()[source]#
Dismiss the indicator shown by
present_launching(). No-op default.- Return type:
None
- present_failure(message, detail='')[source]#
Surface a launch/handshake failure to the operator.
detailcarries the child’s last output (seeSubprocessSupervisor.output_tail). Logs by default; a GUI subclass shows a dialog.
- confirm_ready_to_record()[source]#
Focused ‘PsychoPy ready – press to start recording’ gate.
Forced to the foreground over PsychoPy’s full-screen window so a spacebar press lands on this dialog (OK is the default button), not on PsychoPy. Dismissing it returns control to the Procedure, which then starts the recording devices; the operator then presses spacebar in the PsychoPy window to begin the stimulus (cameras lead; timelines are aligned post-hoc). Returns
False(Cancel) to abort the run.- Return type: