Press "Enter" to skip to content

TrialHandler – a PsychoPy tutorial

In this tutorial you will get to know 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 data we found the routine we want to use (e.g., TrialHandler):

Prepatory work

from psychopy import data

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

visual_targets = [digit for digit in xrange(1,9)]

Note that, you can create the same list with a for-loop:

visual_targets = []

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

Lets continue with creating a new list containing a dictionary for each trial. Each dictionairy (i.e., trial) contain the keys ‘Target’ and ‘CorrectResponse’. This is were 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,

targets_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})

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 randomize our targets and present them 160 times

trials = data.TrialHandler(targets_responses,20, method='random')

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

trials.data.addDataType('Response')
trials.data.addDataType('Accuracy')

Window, stimuli, & timing

When we run the experiment we can just do a for-loop. However, we are going to create a window first (we need to somewhere to present our targets).
Therefore, we need to import visual and create a window. Typically, you would import everything in 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')

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')

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

from psychopy import core, event
trial_timer = core.Clock()

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 with setting the current_time to zero, trial_still_running to True and reset the trial_timer. This is done so that we can 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

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()
digit-categorization-task-psychopy-trialhandler
Output the above script produces (shortened down, of course).

We end by closing the window and quitting:

experiment_window.close()
core.quit()

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])

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'])

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

If you want to see a more 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. That is, if you need randomization with constraints you might need to do that prior to using TrialHandler and, also, 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.

 

4 Comments

  1. Ted Ted

    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?

    • 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.

      • Ted Ted

        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.

        • Hey,

          There are some, yes. I think Damien J. Mannions teaching resources are super great (). There are some more. I may put together a post listing them. I did a post for the JEPS Bulletin, also, but in that post I use Expyriment to create a Flanker task (Python Programming in Psychology…).

Leave a Reply

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.

%d bloggers like this: