TrialHandler – a PsychoPy tutorial

In this tutorial, you will learn how to use the PsychoPy function TrialHandler to create trials and correct responses to your targets in these trials. PsychoPy is an application for creating experiments for Psychology experiments. The application is written in Python, an easy programming language to learn. You can learn more about PsychoPy in my two previous posts (Free and Useful software – PsychoPy and Python apps and libraries…”).

TrialHandler Tutorial

We start by importing data from PsychoPy. In the data, we found the routine we want to use (e.g., TrialHandler):

Preparatory Work

First, we start out by importing data, as previously mentioned.

from psychopy import dataCode language: Python (python)

We start with a simple list of trials. In this example, the subjects will categorize digits as odd or even (like in a cross-modal oddball task). Here we will create the target list with digits from 1-8:

visual_targets = [digit for digit in xrange(1,9)]Code language: Python (python)

Note that you can create the same list using a for loop::

visual_targets = []

for digit in range(1, 9):
    visual_targets.append(digit)

Let’s create a new list containing a dictionary for each trial. Each dictionary (i.e., trial) contains the keys ‘Target’ and ‘CorrectResponse’. This is where we store each Target and its correct response. In the example, the response keys can be ‘z’ and ‘x’ (odd and even, respectively)
We start to loop through our list of targets,

argets_responses = [] 

for target in visual_targets: 
    if target %2 == 0: 
         correct_response = 'x' 
    else: 
         correct_response = 'z' 
    targets_responses.append({'Target':target, 'CorrectResponse':correct_response})Code language: Python (python)

TrialHandler

Now that we have our list with the targets and correct responses, we can use that list with PsychoPy’s TrialHandler. In our example, we are going to randomize our targets and present them 160 times

trials = data.TrialHandler(targets_responses,20, method='random')Code language: Python (python)

We also want to record what the subjects responded if a correct response (Accuracy) was given:

trials.data.addDataType('Response')
trials.data.addDataType('Accuracy')Code language: Python (python)

Window, stimuli, & timing

When we run the experiment, we can do a for-loop. However, we will create a window first (we need somewhere to present our targets).
Therefore, we need to import visuals and create a window. Typically, you would import everything at the beginning of your script.

from psychopy import visual 

experiment_window = visual.Window(size=(800,600),winType='pyglet',fullscr=False,
         screen=0, monitor='testMonitor',
         color="black", colorSpace='rgb')Code language: Python (python)

We also need to create an object for the text stimuli. We use the routine TextStim for this:

screen_text = visual.TextStim(experiment_window,text=None, 
              alignHoriz="center", color = 'white')Code language: JavaScript (javascript)

We are almost there… However, we need to add timing for the stimuli. We use Clock from the core for this and create a trial time. Furthermore, we use events to collect responses from the keyboard.

from psychopy import core, event 

trial_timer = core.Clock()Code language: Python (python)

Presenting stimuli

We are now ready to start our experiment loop. TrialHandler makes it possible to loop through each trial we created with it.
At the beginning of the loop, we set the current_time to zero, trial_still_running to True, and reset the trial_timer. This is done to present our target for 400ms after a 400ms fixation cross. The target is followed by a response window of 1000ms in which the fixation cross is presented again.  In the loop, the response will be collected in a list (i.e., “responded”). The first item of this list is controlled against the correct response (i.e., trial[‘CorrectResponse’]).

for trial in trials:
    current_time = 0
    trial_still_running = True
    trial_timer.reset()

    while trial_still_running:
        current_time = trial_timer.getTime()

        if current_time <=.4:
            screen_text.setText('+')
            screen_text.draw()

        elif current_time >= .4 and current_time <=.8:
            screen_text.setText(trial['Target'])
            screen_text.draw()

        elif current_time >= .8 and current_time <=1.8:
            screen_text.setText('+')
            screen_text.draw()
            responded = event.getKeys()

        elif responded:
            if trial['CorrectResponse'] == responded[0]:
                accuracy = 1

            else: accuracy = 0

        elif current_time >= 1.8:
            if not responded:
                accuracy = 0

            trial_still_running = False

        experiment_window.flip()Code language: PHP (php)

Here’s a snippet of the experiment we just created:

  • Save

We end by closing the window and quitting:

experiment_window.close()
core.quit()Code language: Python (python)

Data also contains routines for saving data, but I have not included that in the above script. However, if you want to store your data, add the following code at the end of the for-loop:

trials.data.add('Accuracy', accuracy)
trials.data.add('Response', responded[0])Code language: Python (python)

It is also pretty easy to save your data as an Excel-fie (e.g., .csv or .xlsx):

trials.saveAsExcel(fileName='data.csv',
                  sheetName = 'rawData',
                  stimOut=[], 
                  dataOut=['all_raw'])Code language: Python (python)

Note that if the timing is essential for your experiment, it is better to control stimulus timing by presenting them for a specified number of frames instead (see here for more information).

If you want to see a complete example of a task written in Python and PsychoPy, look at my Sustained Attention to Response Task (SART). In the SART, I randomize the stimuli with my method, however. If you need randomization with constraints, you might need to do that before using TrialHandler and use “method=’sequential'” instead.

Finally, I uploaded the complete script, including data saving, to pastebin. Hopefully, this tutorial helped you to understand how to use TrialHandler. Please let me know what you think or if something does not work as expected.

More Python Resources

Here are some more Python resources you might find helpful:

  • Save

4 thoughts on “TrialHandler – a PsychoPy tutorial”

  1. Thanks for the very helpful post. I am getting the error message “name ‘responded’ is not defined”, despite that I have this line “responded = event.getKeys() ” in the previous ‘if’ statement. Any idea why?

    1. Hey Ted,
      thank you for your comment. Glad you find the post helpful. I wrote this script some time ago but I tested it after your comment (thanks for pointing it out). I got the same error and changed all if-conditionals, except the first, to “elif”. It seems to work now. The script in the post is updated accordingly.

      1. Oh I see, thank you! I normally don’t find much help broken down in this way, I find it far better to learn from than most of the documentation out there. Do you know of any other websites/blogs with help such as this out there? Keep up the great work.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top
Share via
Copy link