Task Methods & Attributes
Full reference for TaskBase — every method you can override or call, and every
attribute you can read, from inside your own task. For a walkthrough of building a
task from scratch, see the Task Development Guide.
Methods you must override
Method |
Called |
Use it to |
|---|---|---|
|
Once, before the trial loop begins. |
Configure hardware, pre-compute stimuli, open files, etc. |
|
Once at the start of every trial. |
Build and run the trial. |
|
Immediately after each trial finishes. |
Score performance, update adaptive parameters, call |
|
Once after the trial loop ends (session finished, forced stop, or error). |
Close files, stop hardware, release resources. |
create_trial with Bpod
Each call starts with an empty state machine. Add states with self.bpod.add_state
— when the method returns, the state machine is sent and run automatically, and
after_trial is called once it finishes. Without Bpod, you’re responsible for
creating and running the trial logic yourself.
after_trial must register “water”
Always call self.register_value("water", ...) with the amount of water consumed
during the trial. The system sums it across the session to get total water
consumption — if that total falls below a configurable threshold, an alarm is
triggered (adjustable in the Settings tab of the GUI).
Methods you can call
Do not override these — call them from inside your task’s own methods.
Registering controller events
If you’re using Bpod, all four of these are called automatically — you never need
to call them yourself. With BEHAVIOR_CONTROLLER set to OTHER, call them with
the right timestamps to populate trial_data (see below).
Method |
Args |
When to call it ( |
|---|---|---|
|
|
At the beginning of each trial. |
|
|
At the end of each trial. |
|
|
Whenever you enter a state. |
|
|
To register any other controller event. |
raspberry_timestamp: the Raspberry Pi’s own clock — get it withtime_utils.now_timestamp().controller_timestamp: read from the microcontroller’s own clock (e.g. an Arduino’smillis(), converted to seconds), if you’re talking to one and it reports its own timing.If you’re not syncing against an external clock (no microcontroller, or one that doesn’t report its own timing),
controller_timestampis exactly the same asraspberry_timestamp.
register_start_trial uses the two timestamps together to compute a clock offset
(raspberry_timestamp - controller_timestamp), used to convert every later
controller_timestamp back to Raspberry Pi absolute time.
Registering a Raspberry-side event
def register_raspberry_event(self, name: str, raspberry_timestamp: float) -> None
For events generated by the Raspberry Pi itself, asynchronously from the controller
— e.g. executing a direct function, or a camera/touchscreen detection. Pass
time_utils.now_timestamp() as raspberry_timestamp.
Registering a value
def register_value(self, name: str, value: Any) -> None
Saves a custom value to the current trial row in the CSV — call it inside
after_trial.
self.register_value("correct", 1)
self.register_value("response_time", 0.432)
Executing a direct function
def execute_function(self, i: int) -> None
Runs the direct function registered at index i (1-99). See
Direct, Audio & Video Functions for how those get registered and
the other ways they can be triggered.
self.execute_function(1) # executes the function registered at index 1
Attributes you can read
Attribute |
Type |
Description |
|---|---|---|
|
|
Name of the task (the class name). |
|
|
Name of the subject running the session. |
|
|
Name of the system, as defined in the Settings tab. |
|
|
Bpod interface — state machine construction, sending, etc. Mainly used inside |
|
|
Arduino interface, for tasks using an Arduino instead of Bpod. |
|
|
Session parameters defined in the training protocol. Read and write its attributes to implement adaptive training. |
|
|
Camera attached to the operant box ( |
|
|
Output pin control ( |
|
|
The current trial number, starting from 1. |
|
|
Date string of the current session, set once when it starts. |
|
|
|
|
|
Set to |
|
|
|
|
|
|
self.info
Human-readable description of the task, set once (usually in __init__). Shown to
the user when selecting the task in the GUI.
self.info = """
My Task
-------
Describe what the task does and how it progresses here.
"""
self.calibrations
Holds the task’s calibrations — call its methods to convert between hardware values and real-world units (see Custom Calibrations).
gain = self.calibrations.sound_calibration.get_sound_gain(
speaker=1, dB=70.0, sound_name="white_noise"
)
self.should_stop (read-only property)
True once any stopping condition is met: the trial/time limit (Manual mode
only — Auto mode runs until the subject leaves the corridor, not for a fixed
number of trials), force_stop, or stop_button_pressed.
Needed for a task with its own waiting loop
Trial/time limits and force_stop are only checked between trials, by the
task’s own run loop — they can’t interrupt a create_trial that’s blocked inside
its own waiting loop. If your task doesn’t use Bpod and waits on its own
condition inside create_trial, poll self.should_stop so the loop actually
exits when asked to:
def create_trial(self):
while not self.should_stop:
... # wait for your own condition
self.trial_data (dict)
Populated automatically at the end of each trial — available inside after_trial.
Keys always present:
"date"(str),"trial"(int),"subject"(str),"task"(str),"system_name"(str)"TRIAL_START"/"TRIAL_END"(float): absolute timestamps (UNIX epoch seconds)"ordered_list_of_events"(list[str]): event names in the order they occurred (e.g.["Port1In", "Port1Out", "Port1In"])
Keys added per state visited (Bpod or manual):
"STATE_<name>_START"/"STATE_<name>_END"(list[float]): timestamps of every entry/exit for that state — a list because the same state can be visited more than once per trial.
Keys added per event type:
"<EventName>"(list[float]): timestamps of every occurrence of that event (e.g."Port1In","Port1Out").
When using Bpod, all state and event keys are filled in automatically. Without
Bpod, call register_enter_state and register_controller_event /
register_raspberry_event yourself to populate them.
Keys added by you: any name passed to register_value inside after_trial.
def after_trial(self):
if self.trial_data.get("correct") == 1:
self.settings.difficulty += 1
t_start = self.trial_data["STATE_stimulus_START"][0]
t_end = self.trial_data["STATE_stimulus_END"][0]
response_time = t_end - t_start