Reputation: 11
I am trying to use the Coder view to make an experiment that involves feedback and multiple conditional statements. How do you do this though? The task will ultimately involve 4 math problems, and participants will be allowed to have up to 3 attempts for each problem. The structure should be something like this...
Loop 1: goes through the 4 problems
Loop 2: allows up to 3 attempts per problem
Loop 3: if the response is correct, then say "correct" and move onto the next problem; else, say "incorrect" and ask if they would like to try again or move on
This is my first time using Python and I can't work out the kinks in the code. There is no error message that is returned, but rather, the code does not register the response and thus the task is frozen in place at the prompt screen. The code is below. I didn't include the libraries and other setup.
t=0
nProblem=4
nAttempt=3
while currentProb <= nProblem:
problemTimer.reset()
attempt = 1
# *prompt* updates
prompt.setAutoDraw(True)
prompt.setText(u'Problem prompt will go here.\n\nType in your answer and press enter to submit.', log=False)
while attempt <= nAttempt:
response = []
# *response* updates
core.wait(1) #LB - using this in place of that enormous if-statement
event.clearEvents(eventType='keyboard')
theseKeys = event.getKeys(keyList=['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'])
# check for quit:
if "escape" in theseKeys:
endExpNow = True
if ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0') in theseKeys: # subject responds with number value
response.append(theseKeys) # will tack on responses to previous responses
while event.getKeys(keyList=['return'])==[]:
# *timer* updates
if t <= 0.0:
# keep track of start time/frame for later
timer.setAutoDraw(True)
#elif timer.status == STARTED and t >= (0.0 + (600-win.monitorFramePeriod*0.75)): #most of one frame period left
#timer.setAutoDraw(False)
#if timer.status == STARTED: # only update if being drawn
timer.setText(str(round((600+routineTimer.getTime())/60,1)) , log=False)
# *minutesleft* updates
if t >= 0.0:
# keep track of start time/frame for later
minutesleft.setAutoDraw(True)
#elif minutesleft.status == STARTED and t >= (0.0 + (600-win.monitorFramePeriod*0.75)): #most of one frame period left
#minutesleft.setAutoDraw(False)
numberlist=event.getKeys(keyList=['1','2','3','4','5','6','7','8','9','0','backspace','return'])
for number in numberlist:
#if key isn't backspace, add key pressed to the string
if number !='backspace':
response.append(number)
#otherwise, take the last letter off the string
elif len(text)>=0:
response.remove(numberlist[number-1])
#continually redraw text onscreen until return pressed
answer = visual.TextStim(win, text=response,color="black",pos=(0,-100))
answer.draw()
win.flip()
event.clearEvents()
if len(theseKeys) > 0: # at least one key was pressed
response.keys.extend(theseKeys) # storing all keys
response.rt.append(response.clock.getTime())
#check for quit
if "escape" in theseKeys:
endExpNow = True
if response == '9999': # was this correct?
correctAns = 1
else:
correctAns = 0
if theseKeys == 'enter':
response.keys.extend(theseKeys) # storing all keys
response.rt.append(attemptresponse.clock.getTime())
if correctAns == 1:
attempt += 888 #ends and goes to next problem
currentProb += 1
dataFile.write(attempt,attemptresponse,theseKeys,response,correctAns) #output separated by commas
#dataFile.write('PID COND PROB ATT TIME RESP\n')
response_correct.draw()
win.flip()
event.waitKeys()
if correctAns == 0:
attempt += 1 #LB = was previously setting to 1 forever
dataFile.write(attempt-1,attemptresponse,theseKeys,response,correctAns) #output separated by commas
response_incorrect.draw()
win.flip()
theseKeys = event.getKeys(keyList=['q','space'])
if theseKeys == 'q':
continueRoutine = False
if theseKeys == 'space':
prompt.draw()
win.flip()
event.waitKeys()
Upvotes: 1
Views: 1193
Reputation: 5683
I cannot write up your complete code but hopefully point out a few things to get you most of the way.
theseKeys = event.getKeys(keyList=['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'])
if "escape" in theseKeys:
Here theseKeys
can only contain the stuff in keyList
so "escape" is never there. Extend the keyList with "escape" - and possibly "enter" which you use later.
if ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0') in theseKeys:
theseKeys
is a list and when you do "(x,y,z) in list", it will look for an element (x,y,z) and not any occurrances of x, y and z. If you have your keyList as above, you KNOW that any non-escape response is one of these so it's unneccessary. So do
theseKeys = event.getKeys(keyList=['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'escape'])
if "escape" in theseKeys:
core.quit() # assuming that psychopy.core is imported
elif "enter" in theseKeys:
# Something something
else:
response.append(theseKeys[0]) # OBS: pick the first response
It really looks like you want to use a for
loop rather than a while
loop. Using for-loops, you don't have to keep track of the current loop number. So rather than
while currentProb <= nProblem:
do
for currentProb in range(nProblem):
Then the increment of currentProb
happens automatically and the loop is terminated when it should. This is much more elegant when possible.
I'm a bit in doubt about whether you would want to use event.waitKeys()
rather than than event.getKeys()
in a while-loop, and use the maxWait
argument if you want to control waiting time and use a core.Clock()
if you want to track timing. The while loop is ok if you want to animate something but if not, the event.waitKeys()
would be much simpler and safer.
And lastly, while you try to make this work, use a lot of print
statements to check the actual contents of theseKeys
and other variables. It's the best way to debug and capture gotchas.
Upvotes: 1