Skip to content

Commit a2fd647

Browse files
committed
add Creating Custom Components in docs
1 parent 7448315 commit a2fd647

File tree

6 files changed

+1000
-343
lines changed

6 files changed

+1000
-343
lines changed

docs/creating_custom_components.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Creating Custom Components in Ethopy
2+
3+
This guide provides an introduction to extending Ethopy with your own custom components. Ethopy's modular design allows you to create specialized experiments by implementing three core component types:
4+
5+
1. **Experiments**: Define the overall experimental flow and state transitions
6+
2. **Stimuli**: Create visual, auditory, or other sensory presentations
7+
3. **Behaviors**: Handle animal interactions and responses
8+
9+
## Core Architecture
10+
11+
Ethopy uses a modular architecture where each component has specific responsibilities:
12+
13+
14+
- **Experiments** manage the overall flow of your task using a state machine
15+
- **Stimuli** control what is presented to the subject
16+
- **Behaviors** track and validate responses
17+
- **Interfaces** communicate with hardware
18+
- **Loggers** record data to the database
19+
20+
All components integrate with the DataJoint database system to store parameters and results.
21+
22+
## Example Components
23+
24+
We provide three detailed examples to help you understand how to create your own components:
25+
26+
### 1. [Match Port Experiment](match_port_example.md)
27+
28+
The Match Port experiment implements a 2-Alternative Forced Choice (2AFC) task where animals need to choose the correct port based on stimuli. This example demonstrates:
29+
30+
- Creating a complex state machine with multiple states
31+
- Implementing state transitions based on animal behavior
32+
- Managing adaptive difficulty through staircase methods
33+
- Handling reward, punishment, and intertrial periods
34+
35+
### 2. [Dot Stimulus](dot_stimulus_example.md)
36+
37+
The Dot stimulus provides a simple visual element that can be displayed at different positions and sizes. This example shows:
38+
39+
- Defining stimulus parameters in a DataJoint table
40+
- Calculating screen positions based on monitor resolution
41+
- Managing the lifecycle of visual elements
42+
- Implementing timing-based presentation
43+
44+
### 3. [MultiPort Behavior](multi_port_behavior_example.md)
45+
46+
The MultiPort behavior handles interactions with multiple response ports. This example illustrates:
47+
48+
- Tracking which ports an animal interacts with
49+
- Validating responses based on experimental conditions
50+
- Managing reward delivery at specific ports
51+
- Recording response history and outcomes
52+
53+
## Component Integration
54+
55+
These three component types work together to create a complete experimental setup:
56+
57+
1. The **Experiment** defines the sequence of states (e.g., ready → trial → reward)
58+
2. The **Stimulus** determines what the animal sees or hears in each state
59+
3. The **Behavior** handler tracks and validates the animal's responses
60+
61+
For example, in a typical trial:
62+
- The experiment enters the "Trial" state
63+
- The stimulus presents a visual cue
64+
- The behavior handler detects when the animal responds
65+
- The experiment transitions to "Reward" or "Punish" based on the response
66+
- The cycle continues to the next trial
67+
68+
## Creating Your Own Components
69+
70+
To create your own custom components:
71+
72+
1. **Start with the examples**: Use the provided examples as templates
73+
2. **Understand the lifecycle**: Each component type has specific initialization, operation, and cleanup methods
74+
3. **Define database tables**: Create appropriate DataJoint tables for your parameters
75+
4. **Implement required methods**: Each component type has essential methods that must be implemented
76+
5. **Test incrementally**: Start with simple implementations and add complexity gradually
77+
78+
The detailed documentation for each example provides step-by-step guidance for implementing your own versions.
79+
80+
## Next Steps
81+
82+
Explore each example in detail:
83+
84+
- [Experiment](match_port_example.md) for state machine implementation
85+
- [Stimulus](dot_stimulus_example.md) for visual stimulus creation
86+
- [Behavior](multi_port_behavior_example.md) for response handling
87+
88+
These examples provide a foundation for understanding how to extend Ethopy with custom components tailored to your specific experimental needs.

docs/dot_stimulus_example.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Creating a Custom Stimulus: Dot Example
2+
3+
This guide explains how to create a custom stimulus in Ethopy by examining the `dot.py` example, which implements a simple dot stimulus for visual experiments.
4+
5+
## Overview of the Dot Stimulus
6+
7+
The Dot stimulus displays a configurable dot (rectangular or oval) on the screen at specified coordinates and sizes. This type of stimulus is commonly used in:
8+
9+
- Visual attention studies
10+
- Simple detection tasks
11+
- Eye movement tracking experiments
12+
- Timing response experiments
13+
14+
## Defining the Stimulus Database Table
15+
16+
Each stimulus requires defining a database table to store its parameters. Here's how the Dot stimulus defines its table:
17+
18+
```python
19+
@stimulus.schema
20+
class Dot(Stimulus, dj.Manual):
21+
definition = """
22+
# This class handles the presentation of area mapping Bar stimulus
23+
-> stimulus.StimCondition
24+
---
25+
bg_level : tinyblob # 0-255 baground color
26+
dot_level : tinyblob # 0-255 dot color
27+
dot_x : float # (fraction of monitor width, 0 for center, from -0.5 to 0.5) position of dot on x axis
28+
dot_y : float # (fraction of monitor width, 0 for center) position of dot on y axis
29+
dot_xsize : float # fraction of monitor width, width of dots
30+
dot_ysize : float # fraction of monitor width, height of dots
31+
dot_shape : enum('rect','oval') # shape of the dot
32+
dot_time : float # (sec) time of each dot persists
33+
"""
34+
```
35+
36+
This table definition:
37+
- Uses the `@stimulus.schema` decorator to associate with the database
38+
- Inherits from both `Stimulus` (base class) and `dj.Manual` (DataJoint table class)
39+
- Defines a foreign key relationship with `stimulus.StimCondition` (parent table)
40+
- Specifies parameters for the dot's appearance:
41+
- Background and dot colors
42+
- Position (x, y coordinates as fractions of screen width)
43+
- Size (width and height as fractions of screen width)
44+
- Shape (rectangular or oval)
45+
- Duration (how long the dot persists)
46+
47+
## Implementing the Stimulus Class
48+
49+
The Dot class implements several key methods to control the stimulus lifecycle:
50+
51+
```python
52+
def __init__(self):
53+
super().__init__()
54+
self.cond_tables = ['Dot']
55+
self.required_fields = ['dot_x', 'dot_y', 'dot_xsize', 'dot_ysize', 'dot_time']
56+
self.default_key = {'bg_level': 1,
57+
'dot_level': 0, # degrees
58+
'dot_shape': 'rect'}
59+
```
60+
61+
### 1. `__init__()` method
62+
63+
This initializes the stimulus and defines:
64+
65+
- `self.cond_tables`: List of condition tables this stimulus uses (just 'Dot' in this case)
66+
- `self.required_fields`: Parameters that must be provided for this stimulus
67+
- `self.default_key`: Default values for optional parameters
68+
69+
### 2. `prepare()` method
70+
71+
```python
72+
def prepare(self, curr_cond):
73+
self.curr_cond = curr_cond
74+
self.fill_colors.background = self.curr_cond['bg_level']
75+
self.Presenter.set_background_color(self.curr_cond['bg_level'])
76+
width = self.monitor.resolution_x
77+
height = self.monitor.resolution_y
78+
x_start = self.curr_cond['dot_x'] * 2
79+
y_start = self.curr_cond['dot_y'] * 2 * width/height
80+
self.rect = (x_start - self.curr_cond['dot_xsize'],
81+
y_start - self.curr_cond['dot_ysize']*width/height,
82+
x_start + self.curr_cond['dot_xsize'],
83+
y_start + self.curr_cond['dot_ysize']*width/height)
84+
```
85+
86+
This method:
87+
1. Stores the current condition parameters
88+
2. Sets the background color
89+
3. Calculates the dot's position and size based on:
90+
- Monitor resolution (to maintain aspect ratio)
91+
- Condition parameters for position and size
92+
- Conversion from normalized coordinates to actual screen coordinates
93+
4. Creates a rectangle tuple (left, top, right, bottom) for drawing
94+
95+
### 3. `start()` method
96+
97+
```python
98+
def start(self):
99+
super().start()
100+
self.Presenter.draw_rect(self.rect, self.curr_cond['dot_level'])
101+
```
102+
103+
This method:
104+
1. Calls the parent class's start method (which initializes timing)
105+
2. Draws the rectangle using the precalculated coordinates and the specified color
106+
107+
### 4. `present()` method
108+
109+
```python
110+
def present(self):
111+
if self.timer.elapsed_time() > self.curr_cond['dot_time']*1000:
112+
self.in_operation = False
113+
self.Presenter.fill(self.fill_colors.background)
114+
```
115+
116+
This method:
117+
1. Checks if the dot's display time has elapsed (converting seconds to milliseconds)
118+
2. If the time has elapsed:
119+
- Marks the stimulus as no longer in operation
120+
- Fills the screen with the background color (removing the dot)
121+
122+
### 5. `stop()` and `exit()` methods
123+
124+
```python
125+
def stop(self):
126+
self.log_stop()
127+
self.Presenter.fill(self.fill_colors.background)
128+
self.in_operation = False
129+
130+
def exit(self):
131+
self.Presenter.fill(self.fill_colors.background)
132+
super().exit()
133+
```
134+
135+
These methods handle cleanup:
136+
- `stop()`: Called when the stimulus needs to be stopped during operation
137+
- Logs the stop event
138+
- Clears the screen
139+
- Marks the stimulus as no longer in operation
140+
141+
- `exit()`: Called when the experiment is ending
142+
- Clears the screen
143+
- Calls the parent class's exit method for additional cleanup
144+
145+
## Stimulus Lifecycle
146+
147+
The dot stimulus follows this lifecycle:
148+
149+
1. **Initialization**: Set up parameters and default values
150+
2. **Preparation**: Calculate positioning based on current conditions
151+
3. **Start**: Draw the dot on the screen
152+
4. **Present**: Monitor timing and remove the dot when its time expires
153+
5. **Stop/Exit**: Clean up resources and reset the display
154+
155+
## How to Create Your Own Stimulus
156+
157+
To create your own stimulus:
158+
159+
1. **Define a database table** with appropriate parameters for your stimulus
160+
2. **Create a stimulus class** that inherits from the base `Stimulus` class
161+
3. **Specify required fields and defaults** in the `__init__` method
162+
4. **Implement preparation logic** to set up your stimulus based on conditions
163+
5. **Create presentation methods** that control how the stimulus appears:
164+
- `start()`: Initial display
165+
- `present()`: Continuous updates (if needed)
166+
- `stop()` and `exit()`: Cleanup
167+
168+
Your stimulus should handle:
169+
- **Positioning** on the screen (considering aspect ratio)
170+
- **Timing** of presentation
171+
- **Transitions** stimulus conditions between states
172+
- **Cleanup** to ensure the display returns to a neutral state
173+
174+
By following this pattern, you can create diverse visual stimuli for behavioral experiments, from simple dots and shapes to complex moving patterns or interactive elements.

0 commit comments

Comments
 (0)