Skip to main content

Lightweight experiment framework for PsychoPy

Project description

PsychoPy-Scene

PyPI - Version PyPI - Downloads

English | 简体中文

This project is a lightweight experiment framework for PsychoPy, source code only 200 lines.

[!NOTE] this project aim to provide a new way to build PsychoPy experiments, only provide the basic API and encourage developers to develop on top of this project.

Features

  • Lightweight: Only 1 file, no extra dependencies
  • Type-safe: All parameters are type annotated
  • Newcomer-friendly: Only the concepts of Context and Scene are required to get started.

Install

pip install psychopy-scene

or copy the psychopy_scene folder directly to the root directory of your project.

Get Started

Context

Experiment context Context means this experiment's global settings, including environment parameters and task parameters. The first step to writing an experiment is to create an experiment context.

from psychopy_scene import Context
from psychopy.visual import Window
from psychopy.monitors import Monitor
from psychopy.data import ExperimentHandler

# create monitor
monitor = Monitor(
    name="testMonitor",
    width=52.65,
    distance=57,
)
monitor.setSizePix((1920, 1080))

# create window
win = Window(
    monitor=monitor,
    units="deg",
    fullscr=False,
    size=(800, 600),
)

# create experiment context
ctx = Context(
    win,
    exp=ExperimentHandler(extraInfo={"subject": "test"}),
)

Scene

The experiment can be seen as a composition of a series of scenes, only 2 steps are required to write an experiment program:

  1. create scene
  2. write scene presentation logic

scene provides some configuration parameters:

  • duration:seconds unit
  • close_on:the event to close scene, such as key_f means pressing the f key to close the scene
  • on_key_[name]:when the keyboard key is pressed, execute the function
  • on_mouse_[name]:when the mouse button is clicked, execute the function
  • on_scene_[name]:when the scene reaches a specific stage, execute the function

Creating a scene only requires a function that accepts stimulus parameters and returns the stimulus:

from psychopy.visual import TextStim

# create stimulus
stim_1 = TextStim(ctx.win, text="Hello")
stim_2 = TextStim(ctx.win, text="World")

# create scene
@ctx.scene(
    duration=1,
    close_on=["key_f", "mouse_right"],
    on_key_escape=lambda: print("escape key was pressed"),
    on_mouse_left=lambda: print("left mouse button was pressed"),
    on_scene_drawn=lambda: print("it will be called after first drawing"),
    on_scene_frame=lambda: print("it will be called each frame"),
)
def demo(color: str, ori: float): # it will be used as on_scene_setup
    print('it will be called before first drawing')
    stim_1.color = color
    stim_2.ori = ori
    return stim_1, stim_2

# show scene
demo.show(color="red", ori=45)

scene can also be configured dynamically, it is useful in some cases, such as presentations with variable duration:

@ctx.scene()
def demo():
    return stim
demo.config(duration=0.5).show()

this ctx.scene method is a shortcut for demo.config, so they are equivalent.

Event

An event represents a specific moment in the program's running, such as pressing a key or clicking the mouse. To execute some operation when an event occurs, we need to add a callback function for it:

demo = ctx.scene(close_on="key_f") # or
demo = ctx.scene(on_key_f=lambda: demo.close()) # or
demo = ctx.scene().on("key_f", lambda: demo.close())

[!NOTE] each event can only have one callback function, adding repeatedly will raise an error.

each callback parameter name should follow the format on_[type]_[name]. Now we support the following events:

type name
scene setup、drawn、frame
key any、other keys is same as returned by Keyboard.getKeys
mouse left、right、middle

these events will be triggered in the following order after the show method is executed:

graph TD
initialize --> on_scene_setup --> first-draw --> on_scene_drawn --> c{whether to draw}
c -->|no| stop-draw
c -->|yes| on_scene_frame --> re-draw --> _["on_key_[name]<br>on_mouse_[name]"] --> c

Data

scene will collect data automatically in showing:

name description
show_time first drawing timestamp
events interaction events list: keyboard events and mouse events

we can access these data by scene.get:

@ctx.scene(close_on=["key_f", "key_j"])
def demo():
    return stim
demo.show()
close_event = demo.get("events")[-1]
close_key = close_event.key.value
close_time = close_event.timestamp - demo.get('show_time')

we can also collect data manually:

@ctx.scene(
    on_key_f=lambda: demo.set('rt', core.getTime() - demo.get('show_time')),
)
def demo():
    return stim
demo.show()
rt = demo.get('rt')

Shortcut

Context also provides some shortcut methods:

ctx.text('Welcome to the experiment!', pos=(0, 0)).show() # show static text
ctx.fixation(1).show()
ctx.blank(1).show()
ctx.addRow(a='', b=1, c=True) # collect data to ExperimentHandler

Best Practices

Separation of context and task

It is recommended to write the task as a function, pass the experimental context as the first parameter, the task-specific parameters as the rest of the parameters, and return the experimental data.

from psychopy_scene import Context

def task(ctx: Context, duration: float):
    from psychopy.visual import TextStim

    stim = TextStim(ctx.win, text="")
    scene = ctx.scene(duration, on_scene_setup=lambda: stim)
    scene.show()
    ctx.addRow(time=scene.get('show_time'))

Focus only on task-related logic

Task functions should not contain any logic that is not related to the task itself, for example:

  • Introductory and closing statements
  • Number of blocks
  • Data processing, analysis, presentation of results

If there are no data dependencies between blocks, it is recommended to write the task function as a single block. For experiments that require the presentation of multiple blocks, consider the following example.

from psychopy_scene import Context
from psychopy.visual import Window

def task(ctx: Context):
    from psychopy.visual import TextStim

    stim = TextStim(ctx.win, text="")
    scene = ctx.scene(1, on_scene_setup=lambda: stim)
    scene.show()
    ctx.addRow(time=scene.get('show_time'))

win = Window()
data = []
for block_index in range(10):
    ctx = Context(win)
    ctx.exp.extraInfo['block_index'] = block_index
    task(ctx)
    block_data = ctx.exp.getAllEntries()
    data.extends(block_data)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

psychopy_scene-0.1.1.tar.gz (21.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

psychopy_scene-0.1.1-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

Details for the file psychopy_scene-0.1.1.tar.gz.

File metadata

  • Download URL: psychopy_scene-0.1.1.tar.gz
  • Upload date:
  • Size: 21.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for psychopy_scene-0.1.1.tar.gz
Algorithm Hash digest
SHA256 286f4fa3b927de548197f84c9efc44dac99a6f80a6e6f2364711da8b3a8458f0
MD5 3bb285dac30c7ddf2f90c392f523665f
BLAKE2b-256 0e24301a770f72270b9c01a8260944f8fb75cefa9b24c88e04fb721c48036121

See more details on using hashes here.

Provenance

The following attestation bundles were made for psychopy_scene-0.1.1.tar.gz:

Publisher: pypi.yaml on bluebones-team/psychopy-scene

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file psychopy_scene-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: psychopy_scene-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for psychopy_scene-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 66f561b2ca3bbc94c2f10cc2a3320c98f77381f4a48fba97e6211dff8ac8a7af
MD5 1c6b006cf0853bee6e21d560ba8142f3
BLAKE2b-256 7bb3d53ae9cd4cd6958842e509d6dbabc171155d3de73ff696d56bb6a817098e

See more details on using hashes here.

Provenance

The following attestation bundles were made for psychopy_scene-0.1.1-py3-none-any.whl:

Publisher: pypi.yaml on bluebones-team/psychopy-scene

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page