In part 2, we’re going to build on the basics from part 1. The end result will be a simple survey that shows your participant a statement, and they’ll rate how much they agree with these statements on a scale of 1-9. I put a rudimentary method for recording their responses at the end, but you’ll want to wait for later posts to learn a method that’s more automated.
Initial set up
# Set up. Importing packages and creating a window to display stimuli in.
from psychopy import visual, event
import time
window = visual.Window(monitor="testMonitor", fullscr=True)
You should recognize what these lines are doing from Part 1. The next few lines of set up are also just appropriating things covered in part 1 for a different purpose.
q1 = visual.TextStim(window, text='I get bored very easily.')
q2 = visual.TextStim(window, text='Waiting in lines makes me upset.')
q3 = visual.TextStim(window, text='I need constant entertainment, conversation, or mental stimulation of some kind or I get aggravated.')
Here, creating an bunch of text stimuli with “visual.TextStim” like last time for us to “.draw()” and “window.flip()” for our participants. Before moving on to that, though, I’m going to create some instructions to show alongside each of these statements.
instruction = visual.TextStim(window, text='Rate how much you agree with this statement on a scale of 1-9', pos=(0,-0.8))
This “instruction” variable can be drawn onto the same visual scene with other stimuli. I don’t want them to be on top of each other, though, so I specified that I want this one to have a position (“pos”) equal to the coordinates (0, -0.8). This means, “Don’t move it either left or right, but lower it 80% downward.”
Now all you have to do is draw the first question along with the instruction text, and “flip” it.
q1.draw()
instruction.draw()
window.flip()
User input
Now for one of psychopy’s most useful functions:
answer = event.waitKeys(keyList=['1','2','3','4','5','6','7','8','9'])
This line creates a variable called “answer” but it calls on a function that essentially waits for a keyboard response before its value is actually assigned. You have to specify which keys are eligible for a response. You don’t want your participants pressing whatever keys they want! To do this, you set the argument “keyList” equal to a list of elements corresponding to keys you want participants to be allowed to press. A “list” is an important data type that is enclosed in square brackets “[ ]” with each element separated with commas. You’ll learn a lot more about functions, lists, and loops if you keep going, and you’ll tear up over how easy they make your life.
Recording the response
For now, we’ll record participants responses in perhaps the crudest way possible, with the “print()” function:
print "Answer 1 = " , answer[0]
You could just put “print(answer)” and the “answer[0]” value that was recorded in response to the first question will be reported in the output window after the study is done. It’s helpful though to label stuff that you print out. To string together different elements to print together, like the character string “Answer 1 = ” and the value assigned to “message”, you simply put them together in the “print()” parentheses separated plus signs.
(If you’re wondering why there’s an “[0]” after “answer”, it’s because “answer” is recorded as a list and the individual elements of a list can be accessed with indexes, which I’m about to go over in a few paragraphs.)
Rinse and repeat?
One thing you could do at this point is simply copy and paste the code that poses the first question, collects, and prints the response, but change all the relevant names like so:
q2.draw()
instruction.draw()
window.flip()
answer = event.waitKeys(keyList=['1','2','3','4','5','6','7','8','9'])
print "Answer 2 = " , answer[0]
q3.draw()
instruction.draw()
window.flip()
answer = event.waitKeys(keyList=['1','2','3','4','5','6','7','8','9'])
print "Answer 1 = " , answer[0]
Notice that “answer” is going to get assigned a new value every time you call the “event.waitKeys()” function. You don’t have to create separate “answer1”, “answer2”, “answer3” variables for every response the participant gives… unless you like doing really redundant things like that.
“for” loops
Speaking of redundancy, by far one of the biggest time (and headache) savers (and headache inducers, for beginners) are “for” loops. “for” loops essentially work their way through a list and perform the same action on each element of the list.
Here’s a simple example:
myList = ['apples','oranges','bananas']
for i in range(len(myList)):
print 'I like ' , myList[i]
The first line creates a list called “myList” with three elements, ‘apples’, ‘oranges’, ‘bananas’. The second line, in plain English, says, “For each element in myList, i, I want you to…” When you loop through lists, python’s a little O.C.D. about how you phrase things. It likes you to use “range()”. Whatever number you put inside the parentheses will correspond to the amount of times to repeat the loop. I put “len(myList)” inside here, which translates to “the length of myList”. After doing this, the “for” loop will start doing the same thing to each item in “myList” in order and, within the loop, you can reference the item that you’re on with “myList[i]”. Elements in lists start with an index of 0, then continue from there. So, if I put “myList[0]” in the program, it would come back as ‘apples’. If I put “myList[1]” it’d come back as ‘oranges’. When the “for” loop is running, the “i” becomes a variable that can stand in for whatever index the loop is on at the moment.
Every line you want the loop to execute (for each element in the list it’s looping through) has to be indented underneath. When you’re done with stuff that’s part of the for loop, you stop indenting and just write new commands as you normally would.
Taking advantage of the “for” loop
Instead of writing out the 5 lines of code it takes to draw the current question, the instructions to show along with it, to display them together, wait for a response, then print it FOR EACH QUESTION IN THE SURVEY, you can just use a “for” loop.
questions = [q1,q2,q3]
for i in range(len(questions)):
questions[i].draw()
instruction.draw()
window.flip()
answer = event.waitKeys(keyList=['1','2','3','4','5','6','7','8','9'])
print "Answer ",i+1," = ",answer[0]
The first line puts all the survey questions in a list to loop through. The second line says you want to loop through each element in that list and perform all the lines indented below that line on each element in the list. Instead of saying “q1.draw()”, you put “questions[i].draw()”. When the loop starts at 0, this will read as “questions[0].draw()” which is another way of saying “q1.draw” because “q1” is the first (or “zeroth” element in the list). The next time around, it’ll read as “questions[1].draw()” which is another way of saying “q2.draw()” because “q2″ is the second (first right after zeroth” element on the list.
If you think it’s annoying that python starts with “zero” when it’s indexing things, you’re not alone. That’s the way things are though. After all the other lines are repeated — the drawing, the flipping — I even have to print the answers with “i+1” because “i” is always actually one number behind if you start your questions at “1” like a normal person. When you are telling python to reach for the Nth item in a list, though, you have to keep in mind that python starts counting at 0, always
Finishing touches
If you were to copy and paste all the relevant code covered so far, it’d work, but things would be a little rough around the edges. To improve the participant’s experience, there are some things you could add to the script yourself using concepts we’ve already covered: (a) An initial set of instructions informing them of what they’ll be doing during the study, (b) a slight pause between questions, so they don’t (seemingly) simultaneously change as soon as you press a button, (c) put a screen at the end that says something like, “You have now completed the study. Please inform the researcher on duty,” and a secret key to press to close out the program once the participant has left.
Below is all the code you’d end up with if you followed along and changed things where appropriate (i.e., incorporated the “for” loop instead of the earlier, more redundant method). I’ve also added comments explaining what each line is doing.
# Set up. Importing packages and creating a window to display stimuli in.
from psychopy import visual, event
import time
window = visual.Window(monitor="testMonitor", fullscr=True)
# These are the survey questions
q1 = visual.TextStim(window, text='I get bored very easily.')
q2 = visual.TextStim(window, text='Waiting in lines makes me upset.')
q3 = visual.TextStim(window, text='I need constant entertainment, conversation, or mental stimulation of some kind or I get aggravated.')
# This is a text stimulus to display beneath each survey question.
instruction = visual.TextStim(window, text='Rate how much you agree with this statement on a scale of 1-9', pos=(0,-0.8))
# Puts all the survey questions in a list to loop through
questions = [q1,q2,q3]
# For each element in "questions", do the following...
for i in range(len(questions)):
# draw the current question, question number "i" and the instructions beneath
questions[i].draw()
instruction.draw()
# show these to the participant
window.flip()
# wait for, and record which button they press
answer = event.waitKeys(keyList=['1','2','3','4','5','6','7','8','9'])
# print out which button they pressed
print "Answer ",i+1," = ",answer[0]
#after this "print" command, the for loop will do this all over again but on
# the next element in "questions" until there is nothing else to show