PsychoPy-Scene
English | 简体中文
Build PsychoPy experiments as reusable Scenes.
A behavioral experiment is a composition of scenes. Writing an experiment program only takes two steps: create scenes and show them.
Before:
while running:
stimulus.draw()
win.flip()
keys = keyboard.getKeys()
if keys: ...
if timeout: ...
After:
@duration(3)
@close_on("key_space")
@hardware_keyboard()
@ctx.scene
def fixation(condition):
return stimulus
result = fixation.show(condition)
Installation
pip install "psychopy-scene>=0.4,<0.5"
Quick Start
Context
A Context contains resources shared between scenes, such as the drawing Window and the ExperimentHandler.
Creating a context is the first step when writing an experiment:
from psychopy import visual, data
from psychopy_scene import Context
ctx = Context(win=visual.Window(), exp=data.ExperimentHandler())
Scene
Use ctx.scene to create a Scene, and use decorators to modify its behavior:
from psychopy import visual
from psychopy.hardware import keyboard
from psychopy_scene import Scene
from psychopy_scene.decorator import close_on, duration, hardware_keyboard
# create stimuli
stim_1 = visual.TextStim(ctx.win, text="Hello")
stim_2 = visual.TextStim(ctx.win, text="World")
# create scenes
@duration(1)
@ctx.scene
def demo_1(color: str, ori: float):
print("it will be called before the first flip")
stim_1.color = color
stim_2.ori = ori
return stim_1, stim_2
@close_on("key_space")
@hardware_keyboard()
@ctx.scene
class demo_2:
scene: Scene
def __call__(self, text: str):
stim_1.text = text
return stim_1
def on_key_space(self, evt: keyboard.KeyPress):
self.scene.data["rt"] = (
evt.tDown - self.scene.data["start_time"]
)
# show scenes
data_1 = demo_1.show(color="red", ori=45)
data_2 = demo_2.show(text="test")
Timing-related decorators override one another because they work by setting the scene.timer attribute.
This can be useful when the presentation duration needs to vary:
@duration(1)
@ctx.scene
def demo():
return stim
data = demo.use(duration(0.5)).show()
Sometimes you may want to add default behavior to every Scene. This can be done by wrapping ctx.scene. For example, to terminate the experiment when Esc is pressed:
def wrapped_scene(comp):
return (
ctx.scene(comp)
.use(hardware_keyboard())
.on("key_escape", lambda _: core.quit())
)
@wrapped_scene
def demo():
return stim
Data
A Scene records the moment it appeared on screen — the first frame flip timestamp (stimulus onset) — as data["start_time"]. It is always available, with no setup:
@close_on("key_f", "key_j")
@hardware_keyboard()
@ctx.scene
def demo():
return stim
data = demo.show() # just a dict
onset = data["start_time"]
Every frame flip timestamp is also delivered as the value of the drawn/redraw events, so you can collect exactly what you need without paying for storage you don't use:
@ctx.scene
class demo:
scene: Scene
def __call__(self):
return stim
def on_redraw(self, t: float):
self.scene.data["last_flip"] = t
If you want the full list of flip timestamps, opt in with the record_frames decorator:
from psychopy_scene.decorator import record_frames
data = demo.use(record_frames).show()
frame_times = data["frame_times"]
You can also collect custom data manually:
@hardware_keyboard()
@ctx.scene
class demo:
scene: Scene
def __call__(self):
return stim
def on_key_f(self, evt: keyboard.KeyPress):
self.scene.data["pressed_duration"] = evt.duration
data = demo.show()
duration = data["pressed_duration"]
Events
Events represent specific points in a Scene's presentation lifecycle, such as a key press or mouse click.
To perform an action when an event occurs, register a callback for that event:
scene.on("show", lambda _: print("do something"))
These are the built-in Scene events and their timing:
graph TD
Init --> s((show)) --> First-draw --> d((drawn)) --> c{should draw?}
c -->|No| Stop
c -->|Yes| Timer-check --> Re-draw --> r((redraw)) --> c
The drawn and redraw events carry the corresponding flip timestamp as their value. The first one is also stored in data["start_time"].
Input-related decorators can provide additional events. For example, hardware_keyboard provides the key_space event, allowing callbacks registered with scene.on("key_space", ...) to run when the Space key is pressed.
These decorators listen to the redraw event and use scene.emit("key_space", ...) to emit additional events.
Examples
PsychoPy-Scene lets you split an experiment into a series of configurable Scenes. This makes it straightforward to build higher-level abstractions such as trials and blocks, while exposing only the parameters relevant to each abstraction.
Trial
from psychopy import visual
from psychopy_scene import Context
from psychopy_scene.decorator import duration
def trial(ctx: Context, sec=1):
stim = visual.TextStim(ctx.win, text="")
scene = ctx.scene(lambda: stim).use(duration(sec))
data = scene.show()
ctx.record(time=data["start_time"])
Block
from psychopy import visual
from psychopy_scene import Context
from psychopy_scene.decorator import duration
def trial(ctx: Context):
stim = visual.TextStim(ctx.win, text="")
scene = ctx.scene(lambda: stim).use(duration(1))
data = scene.show()
ctx.record(time=data["start_time"])
win = visual.Window()
data = []
for block_index in range(10):
ctx = Context(win)
ctx.exp.extraInfo["block_index"] = block_index
trial(ctx)
block_data = ctx.exp.getAllEntries()
data.extend(block_data)
Metadata
Release files for psychopy-scene 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| psychopy_scene-0.4.0.tar.gz | 18.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| psychopy_scene-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.2 kB
Release files / psychopy_scene-0.4.0.tar.gz
| Download URL | psychopy_scene-0.4.0.tar.gz |
|---|---|
| Size | 18.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4611e99f8d179aa13fffed538e1588af909b0ac3aed45f6a0878ff4f9d5d3836
|
|
BLAKE2b-256 checksum How to use checksums |
f3e9a92795a21e636f4f0790ced0e21e1493009f245f876a7ba19c9a3274ca27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency logRelease files / psychopy_scene-0.4.0-py3-none-any.whl
| Download URL | psychopy_scene-0.4.0-py3-none-any.whl |
|---|---|
| Size | 18.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fca2a1507d1b443432a66c9801e72e08e2db22711c95cc3cd5f76ea82043ea89
|
|
BLAKE2b-256 checksum How to use checksums |
6b0cc46687f5e3b7b460d1bdf6d01eb1b080b29b674f87c93b1a33a7e127189e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency log