Jennifer
Jennifer

Reputation: 11

psychopy builder experiment with feedback and multiple if loops

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

Answers (1)

Jonas Lindel&#248;v
Jonas Lindel&#248;v

Reputation: 5683

I cannot write up your complete code but hopefully point out a few things to get you most of the way.

Getting responses

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

Loops

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.

Waiting for responses

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

Related Questions