Python Tutorial: Level 3

Created for the OVGU Cognitive Neuroscience Master's course H.3: Projektseminar Fall 2019

Updated August 2020

Taught by Reshanne Reeder

Before you start this section, make sure you have downloaded the latest version of PsychoPy for your OS from the PsychoPy website.

You can download a standalone version of PsychoPy, but it can be a huge pain in the ass to do this, especially if you've never done it before and are not prepared to waste an entire day figuring it out.

My recommendation is to first install Anaconda, a platform that comes with everything you need to start programming experiments. Then, follow the extremely simple instructions (you literally do 3 things) on the PsychoPy download page under Anaconda and Miniconda. You just copy and paste three lines into the Anacdonda Prompt (like the Windows Powershell or Linux Terminal). This will create a psychopy environment in Anacdonda, all in about 10 minutes.

After you have created your psychopy environment, go into your Anaconda Navigator. You'll see two environments now: base(root) and psychopy. Click the psychopy environment, and you will already see several programs you can potentially install or launch. Scroll down to "Spyder", click "install". This will take less than a minute. Now you can launch Spyder.

To go back into your psychopy environment at any time in the future, simply go into the Anaconda Navigator, click "psychopy", then launch Spyder.

To find out whether you have downloaded everything correctly, you should open Spyder within the psychopy Anaconda environment and try to import a function from the PsychoPy library. Type into your Spyder console:

In [1]:
from psychopy import visual
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html

You might see a message from pygame, like the one above, or you might just see your machine think for a moment and then complete the process with no feedback. You will get an error message if something went wrong.

In this level, we will first create the layout of everything you will need to have in your script to program a simple psychology experiment. Then, we will fill in the blanks as we learn how to write it all in a way that python can understand. The basic structure for any psychopy experiment is as follows:

In [2]:
#=====================
#IMPORT MODULES
#=====================
#-import numpy and/or numpy functions *
#-import psychopy functions
#-import file save functions
#-(import other functions as necessary: os...)

#=====================
#PATH SETTINGS
#=====================
#-define the main directory where you will keep all of your experiment files
#-define the directory where you will save your data
#-if you will be presenting images, define the image directory
#-check that these directories exist

#=====================
#COLLECT PARTICIPANT INFO
#=====================
#-create a dialogue box that will collect current participant number, age, gender, 
    #handedness
#get date and time
#-create a unique filename for the data

#=====================
#STIMULUS AND TRIAL SETTINGS
#=====================
#-number of trials and blocks *
#-stimulus names (and stimulus extensions, if images) *
#-stimulus properties like size, orientation, location, duration *
#-start message text *

#=====================
#PREPARE CONDITION LISTS
#=====================
#-check if files to be used during the experiment (e.g., images) exist
#-create counterbalanced list of all conditions *

#=====================
#PREPARE DATA COLLECTION LISTS
#=====================
#-create an empty list for correct responses (e.g., "on this trial, a response of X is 
    #correct") *
#-create an empty list for participant responses (e.g., "on this trial, response was a 
    #X") *
#-create an empty list for response accuracy collection (e.g., "was participant 
    #correct?") *
#-create an empty list for response time collection *
#-create an empty list for recording the order of stimulus identities *
#-create an empty list for recording the order of stimulus properties *

#=====================
#CREATION OF WINDOW AND STIMULI
#=====================
#-define the monitor settings using psychopy functions
#-define the window (size, color, units, fullscreen mode) using psychopy functions
#-define experiment start text unsing psychopy functions
#-define block (start)/end text using psychopy functions
#-define stimuli using psychopy functions
#-create response time clock
#-make mouse pointer invisible

#=====================
#START EXPERIMENT
#=====================
#-present start message text
#-allow participant to begin experiment with button press

#=====================
#BLOCK SEQUENCE
#=====================
#-for loop for nBlocks *
    #-present block start message
    #-randomize order of trials here *
    #-reset response time clock here
    
    #=====================
    #TRIAL SEQUENCE
    #=====================    
    #-for loop for nTrials *
        #-set stimuli and stimulus properties for the current trial
        #-empty keypresses
        
        #=====================
        #START TRIAL
        #=====================   
        #-draw stimulus
        #-flip window
        #-wait time (stimulus duration)
        #-draw stimulus
        #-...
        
        #-collect subject response for that trial
        #-collect subject response time for that trial
        #-collect accuracy for that trial
        
#======================
# END OF EXPERIMENT
#======================        
#-write data to a file
#-close window
#-quit experiment

We are going to fill in each section with real code as we learn, until we have a complete functioning experiment. You can already fill in a lot with the knowledge you have from the previous levels (this is denoted with a * on each line you can replace with real code). This is your task for exercise 1:

Let's start with how to import psychopy modules. You are already familiar with importing the numpy library (import numpy as np). You do not want to import the whole psychopy package because this will import thousands of modules you don't need. Instead, use the following syntax to import only the modules you will use in your experiment:

In [ ]:
from psychopy import core, gui, visual, event

"core" contains various functions used for timing and quitting the experiment.
"gui" allows you to create a dialog box to collect participant information
"visual" allows you to draw various stimuli
"event" allows you to collect responses

You may end up using more modules, but these are the most often used for a simple, fully-functioning experiment.

You will also have to import (a) file saving module(s). You can save your data in various formats, the most common being csv (comma-separated values), pickle (python's own filetype), and JSON (which is being used with increasing popularity due to cross-platform readability). We will go over the syntax to save files in these various formats, but first you will need to import them, for example:

In [4]:
import json

Finally, a useful module you will need to navigate directories (for finding images, saving data, etc.) is "os":

In [4]:
import os

"os" stands for "operating system", and it allows you to define and change the current working directory, and list files in a directory, among many other things (although these are the main ones used for our purposes). You can check the current working directory with this function:

In [5]:
os.getcwd()
Out[5]:
'/home/shoarly/Documents/pytutorial'

The "current working directory" should be the directory from which you are running your experiment script. You can change the directory with the "change directory" function:

In [6]:
os.chdir('/home/shoarly/Documents/pytutorial/exp') #if you are in Windows, the syntax should be '\\home\\shoarly\\Documents\\...

At this point, if you have not already created the directory, python will give you an error message along the lines of "this directory does not exist". If there is no error message, now if you ask what the current working directory is, you get the changed path:

In [7]:
os.getcwd()
Out[7]:
'/home/shoarly/Documents/pytutorial/exp'

If you have images saved in this directory that you want to present in your experiment, you will have to refer to the precise path for every image. Because the main directory should be the same for all images, you can get rid of a lot of repeated text by defining a main experiment directory:

In [8]:
main_dir = os.getcwd()

Then you can add more paths to the main directory as needed:

In [9]:
image_dir = os.path.join(main_dir,'imagies')
data_dir = os.path.join(main_dir,'data')

print(image_dir)
print(data_dir)
/home/shoarly/Documents/pytutorial/exp/imagies
/home/shoarly/Documents/pytutorial/exp/data

As you can see, you can predefine several directories that would manually take a lot of text to create. Also, because paths can get very long, this increases your chances of making a mistake when you type them out manually. Finally, it is useful to use "os" functions as much as possible to avoid restricting your paths to a particular slash: / is used in Linux systems and \ is used in Windows systems. If you program your experiment in Linux and run it in Windows (or vice versa), the difference between slashes will keep your os from finding directories.

To find out if a path exists, use the following syntax:

In [10]:
os.path.isdir(image_dir)
Out[10]:
False

Unlike the "change directory" function, you can define directories for your experiment that do not exist yet -- this will just give you a boolean "False". To make sure that you do not commit an error late in the experiment because you forgot to create a directory (or you mistyped a path name), you can add this bit of code after you define your paths:

In [11]:
if not os.path.isdir(image_dir):
    raise Exception("Could not find the path!")
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-11-e45bfb6e709c> in <module>
      1 if not os.path.isdir(image_dir):
----> 2     raise Exception("Could not find the path!")

Exception: Could not find the path!

This will terminate the experiment and give you the output shown above. So let's create the directory "data". Then, to get the "images" directory, you can download it from my github page. This will give you a directory of 10 images.

In [12]:
image_dir = os.path.join(main_dir,'images')
data_dir = os.path.join(main_dir,'data')

It is important to check (before running your experiment) whether all the experimental images exist. You can do this a few different ways (you will try out another method in the exercises), but here is one option: first, write out the list of images you want to present:

In [13]:
pics = ['face01.jpg','face02.jpg','face03.jpg','face04.jpg','face05.jpg','face06.jpg','face07.jpg','face08.jpg','face09.jpg','face10.jpg']

Then, list all the images that are inside the "images" directory:

In [14]:
os.listdir(image_dir)
Out[14]:
['face08.jpg',
 'face06.jpg',
 'face10.jpg',
 'face02.jpg',
 'face03.jpg',
 'face04.jpg',
 'face05.jpg',
 'face07.jpg',
 'face01.jpg',
 'face09.jpg']

You can sort the list in alphabetical order using "sorted":

In [15]:
sorted(os.listdir(image_dir))
Out[15]:
['face01.jpg',
 'face02.jpg',
 'face03.jpg',
 'face04.jpg',
 'face05.jpg',
 'face06.jpg',
 'face07.jpg',
 'face08.jpg',
 'face09.jpg',
 'face10.jpg']

Give it a variable name:

In [16]:
ims_in_dir = sorted(os.listdir(image_dir))

Then only go on with the experiment if the two lists are equal:

In [17]:
if not pics == ims_in_dir:
    raise Exception("The image lists do not match up!")