psychopy.org | Reference | Downloads | Github

Ending a loop with a certain keystroke OR correct response


#1

To start - I know only the very basics of Python. This is my second time patchworking bits of code together that I’ve seen around!

The aim is to design an experiment that presents a stimulus that participants can respond to with one of two keys, ‘q’ or ‘p’. The stimulus presentation is looped to be presented five times. Response ‘q’ should only end the loop 1 in 5 times - for which they get a point. If three consecutive responses fail to end the loop then the ppt loses a point. Response ‘p’ should end the loop every time for which they get no points.

At the moment, the code I’ve written doesn’t end the loop at all based on key press - it runs through until it has cycled through all five presentations of the stimulus.

My guess is that the problem lies somewhere with the lines:

if response == ‘q’

and

if response == ‘p’

Hope someone can guide me in the right direction!

Thanks in advance

Chris


#2

Hi @CEKinch, try

if response.keys == 'q':
#etc

If you are storing all keys in your keboard component, then try:

if 'q' in response.keys:  # Because it stores a list of keys

#3

Hey,

Thanks for the reply!

I’ve just tried both suggestions and neither have seemed to work - I’m at a real loss haha :sweat:


#4

You can do some diagnostic work by adding “print(response.keys)” at the top of this. That will tell you what exactly is being stored in response.keys when this code is being executed (it will appear in the output window). Does it say ‘p’ or ‘q’ in that output?


#5

Hi @jonathan.kominsky

Just added the print command and in the out put window I can see the string:

[‘p’]
[‘p’]
[‘q’]
[‘q’]

This corresponds to the keys I entered. Does that tell you anything helpful?

Cheers


#6

It tells me that

if 'p' in response.keys:

Should definitely work! Try using that kind of if statement again, and have it print something if true. That will help you narrow down whether it’s the conditional or what it’s supposed to be doing that’s failing. If that doesn’t work, a kludgy alternative would be

if response.keys[0] == 'p':

Or “is ‘p’:” if == doesn’t work, but “in” should absolutely get the job done here.


#7

Thanks for the suggestions again @jonathan.kominsky

I’ve tried the alterations you’ve suggested but it still doesn’t seem to end the trial_loop. This is how I’ve left it:

Capture2

Is it possibly a problem with what I have set up in builder? The loop properties are:

LoopType: fullRandom
nReps$: 1


#8

OK, let’s try some more diagnostic output with print(). This is a good practice in general: If you aren’t sure where something is failing, make it tell you everything it’s doing.

print(response.keys)

if response.corr == False:
    if 'q' in response.keys:
        print("Incorrect and q pressed")
        failed_attempts += 1
        print("failed_attempts = " + str(failed_attempts))
    if failed_attempts >= 3:
        score -= 1
        trial_loop.finished = True

elif response.corr == True:
    trial_loop.finished = True
    if 'q' in response.keys:
        print("Correct and q pressed")
        score += 1
        action = 1
    elif 'p' in response.keys:
        print("Correct and p pressed")
        action = 2

print("trial_loop.finished is " + str(trial_loop.finished))

This is all stuff you would delete from the final version, but it will help you narrow down what’s going on while you’re debugging it. It will tell you which line of this set of conditionals is getting tripped, and it will tell you whether trial_loop.finished is being set correctly.

There are a bunch of possible outputs you could get from this. If you don’t get the “Correct/Incorrect and [letter] pressed” text, then you know that it’s not determining response.corr as expected. If you get that, but failed_attempts gives you strange values, then it’s resetting the value of failed_attempts sometime between loops. If it’s setting trial_loop.finished to the correct value but not ending immediately, then the issue is that the “finished” property isn’t immediately triggering the end of the loop for some reason.

Once we’ve isolated what’s going on, it’ll be easier to figure out how to fix it, or at least where to look in the builder.


#9

So after running that code and entering the responses ‘p’, ‘p’, ‘q’, ‘q’ the out put window returned this:

[‘p’]
trial_loop.finished is False
[‘p’]
trial_loop.finished is False
[‘q’]
Incorrect and q pressed
failed_attempts = 1
trial_loop.finished is False
[‘q’]
Incorrect and q pressed
failed_attempts = 2
trial_loop.finished is False

No correct/incorrect string for ‘p’ key presses and trial_loop.finished is being set as false which is wrong.

Failed attempts seems to be ticking up as expected and three incorrect responses successfully end the trial_loop:

[‘q’]
Incorrect and q pressed
failed_attempts = 1
trial_loop.finished is False
[‘q’]
Incorrect and q pressed
failed_attempts = 2
trial_loop.finished is False
[‘q’]
Incorrect and q pressed
failed_attempts = 3
trial_loop.finished is True

Another issue I’ve spotted is that it seems that it doesn’t randomise the presentation of the stimuli from my excel file but I don’t think that’s a coding problem :sleepy:


#10

Ah, this narrows down our problem nicely! The issue seems to be that response.corr is always False, regardless of which key you pressed.

There’s no conditional statement for when response.corr == False and ‘p’ is pressed, and we don’t have any print output there, which is why you’re not getting any extra text when you press p. That’s also why it’s not setting trial_loop.finished to True except when you amass three failed attempts, because the only other way to do that is if response.corr == True.

So that probably has something to do with the keyboard response object and how it’s coding correct/incorrect. I actually don’t know how that works, but that’s where I’d start looking.


#11

The mystery has been solved!

So I think there’s two key things to the resolution. The first is I changed the code slightly and added the conditional statement for response.corr == False when ‘p’ is pressed and made sure the nested if statements were formatted properly. This fixed the issue of ‘p’ not ending the loop - the code now looks like this:


if response.corr == False:
    if 'q' in response.keys:
        print("Incorrect and q pressed") #these print lines will get deleted
        failed_attempts += 1
        print("failed_attempts = " + str(failed_attempts))
        if failed_attempts >= 3:
            score -= 1
            action = 3
            trial_loop.finished = True
    elif 'p' in response.keys:
        print("Incorrect and p pressed")
        trial_loop.finished = True

if response.corr == True:
    if 'q' in response.keys:
        print("Correct and q pressed")
        score += 1
        action = 1
        trial_loop.finished = True
    elif 'p' in response.keys:
        print("Correct and p pressed")
        action = 2
        trial_loop.finished = True

The second issue (and I’m quietly proud of myself for figuring this out!) is that my response object was storing ‘all keys’. So once I had pressed ‘q’, that was stored in the list and ticked up failed_attempts every time even if I pressed ‘p’. Once I changed it to only store ‘last key’ the problem was resolved!

@jonathan.kominsky thanks for the assist on this and steering me in the right direction - I’ve definitely learnt a lot!