🏁 Getting Started with psyflow

Welcome to psyflow, a lightweight framework for building PsychoPy experiments with modular, chainable components. This guide walks you through installation, basic setup, and running your first trial.

1. Installation

Ensure you have PsychoPy installed:

pip install psychopy

Then install psyflow (replace with actual package name if on PyPI):

pip install psyflow

Or if you’re working from source:

git clone https://github.com/your-org/psyflow.git
cd psyflow
pip install -e .

2. Basic Experiment Structure

A typical psyflow experiment has these core steps:

  1. Configure experiment settings (TaskSettings)

  2. Collect participant info (SubInfo)

  3. Build stimuli (StimBank)

  4. Define triggers (a dictionary of codes + TriggerSender)

  5. Create a trial (StimUnit)

  6. Run the trial and collect data

3. Configure Your Task

from psyflow import TaskSettings

config = {
    "total_blocks": 2,
    "total_trials": 20,
    "seed_mode": "same_within_sub",
    "key_list": ["left", "right"],
    "conditions": ["reward", "neutral"],
    "bg_color": "black"
}
settings = TaskSettings.from_dict(config)

Later, after collecting subject_id:

settings.add_subinfo({"subject_id": "001", "session_name": "A"})

This creates settings.block_seed, settings.log_file, and settings.res_file.

4. Collect Participant Info

import yaml
from psyflow import SubInfo

config = yaml.safe_load(open("subinfo.yaml"))
collector = SubInfo(config)
subinfo = collector.collect()   # opens GUI
# e.g., {'subject_id':'001', 'session_name':'A'}

Pass subinfo into settings via add_subinfo() above.

5. Build Your Stimuli

from psyflow import StimBank
from psychopy.visual import TextStim, Circle

stim_bank = StimBank(win)
@stim_bank.define("fix")
def make_fix(win):
    return TextStim(win, text="+", color="white")
stim_bank.add_from_dict({
    "target": {"type":"circle","radius":0.5,"fillColor":"red"}
})
stim_bank.preload_all()

Retrieve with:

fix = stim_bank.get("fix")
tgt = stim_bank.get("target")

6. Set Up Triggers

import yaml
from psyflow import TriggerSender

with open("triggers.yaml") as f:
    triggers = yaml.safe_load(f)

sender = TriggerSender(lambda code: port.write(bytes([code])))

7. Create & Run a Trial

from psyflow import StimUnit

trial = StimUnit("T1", win, kb, triggersender=sender)
trial \
  .add_stim(fix, tgt) \
  .on_start(lambda u: u.send_trigger(triggers["fix_onset"])) \
  .capture_response(
     keys=["left","right"],
     duration=1.0,
     onset_trigger=triggers["fix_onset"],
     response_trigger={"left":triggers["resp_L"], "right":triggers["resp_R"]},
     timeout_trigger=triggers["timeout"],
     correct_keys=["left"],
     highlight_stim={"left": highlight_left, "right": highlight_right}
  ) \
  .on_end(lambda u: print("Result:", u.state)) \
  .run(frame_based=True)

Inspect trial.state, save or append to data file.

8. Putting It All Together

Combine loops over blocks and trials:

for b in range(settings.total_blocks):
    # generate conditions, run trial sequence...
    pass

Use settings.res_file to write headers and trial.to_dict() for rows.

Congratulations—you’ve run your first psyflow trial! Explore additional features in each class’s documentation to customize timing, logging, and more. Happy experimenting!