In my experiment, I have 10 trials, and at each trial, the participant has to press a key ("space") asap after seeing a change of colour in the animation. As feedback that confirms the key pressing, I'd like to move to the next trial (move to the next loop iteration) after the key was pressed. I tried to implement the idea in my code with break and continue, but it doesn't work:
for i in range(nTrials):
# Start with fixation cross
fixation.draw()
win.flip()
core.wait(2)
# Play the video for 200 frames
for Nframes in range(200):
optic_flow_movie.draw()
fixation.draw()
win.flip()
# Get Participants responses
keys = psychopy.event.getKeys(keyList=["space"],timeStamped=clock)
if (keys[0][0] == 'space') is True:
break
else:
continue
# Take only the timestamps from the variable key and store it in the variable RTs
RTs = [sublist[1:2] for sublist in keys] # This stores only the timestamps
RTs = [item for sublist in RTs for item in sublist] # This converts from list of lists to a flat list
Many thanks for your help !
It's not entirely clear what the structure of your trial is, but if you want to monitor responses during the animation, then the call to event.getKeys() needs to be embedded within the drawing loop (i.e. within for Nframes in range(200):). That way, you are checking for a keypress on every screen refresh, so the animation can be halted in realtime.
At the moment, the code shows the entire animation and only then checks the keyboard (but just once per trial). Regardless of what happens there, the next trial will begin, as that is the last code in the main trial loop (i.e. for i in range(nTrials):.
Lastly, the event module is deprecated for keyboard responses. You should really shift to the newer Keyboard class for much better performance:
https://www.psychopy.org/api/hardware/keyboard.html
https://discourse.psychopy.org/t/3-ways-to-get-keyboard-input-which-is-best/11184/3
Related
I’m trying to use the same key to flip after multiple successive stimuli in PsychoPy but it keeps flipping successive screens as well then.
What I came up with was the following slight modification of one of the examples in the manual:
from psychopy import visual, core, event
win = visual.Window(monitor="testMonitor", units="deg")
stim1 = visual.TextStim(win, text="Stim 1", pos=(0,0))
stim2 = visual.TextStim(win, text="Stim 2", pos=(0,0))
stim3 = visual.TextStim(win, text="Stim 3", pos=(0,0))
while len(event.getKeys(['space'])) <= 0:
stim1.draw()
win.flip()
event.clearEvents()
print(event.getKeys()) # test if key buffer is really empty
win.flip()
# while len(event.getKeys(['return'])) <= 0:
# stim2.draw()
# win.flip()
# event.clearEvents()
# win.flip()
while len(event.getKeys(['space'])) <= 0:
stim3.draw()
win.flip()
event.clearEvents()
win.flip()
win.close()
core.quit()
However if I hit space on the first screen, it doesn’t just flip this screen. Instead it will flash stim3 for a split second and then immediately terminate. If on the other hand I use a different key for the second stimulus (replace 'space' with 'return' in the loop condition), that works fine.
My first suspicion was that event.clearEvents() somehow failed to clear the event buffer, so the space event from the first stimulus remained saved and also flipped the second stimulus. However in that case the stim3 shouldn’t be shown at all because the loop condition would immediately fail. Also, if I print out event.getKeys() at the indicated position above, it appears to be empty. Moreover, if I use a different key between the two space-triggered stimuli (remove comments from the stim2 section), suddenly all keys work just fine.
Does anybody have an idea what I’m doing wrong?
(Btw if you’re wondering: I modified the manual example by changing the loop where the program waits for input from a while True loop into while len(event.getKeys(['space'])) <= 0 which eliminates the need of using the exact same condition to break. I tested it with the original idiom just to be sure and the behaviour is the exact same as with my version.)
After some more testing found that deleting the win.flip()'s after each event.clearEvent() fixes the issue. I’m not quite sure why the additional flips (which I thought were necessary to clear the screen before drawing the new stimulus) would cause such a behaviour though. Can somebody explain?
I am having a hard time understanding the window.timeout() function. To be more specific, I am toying with a "snake" game in python:
s = curses.initscr()
curses.curs_set(0)
w = curses.newwin()
w.timeout(100)
while True:
move snake until it hits the wall
I understand that in this case, timeout(100) determines how fast the snake "moves", i.e. printing out new characters on the screen. However, I got stuck when I want to amend the code so that it waits until someone press "start". I wrote something like:
w.timeout(100)
while True:
if w.getch() is not start:
stay at the initial screen
else:
while True:
move the snake until it hits the wall
However, in this case, the timeout(100) seems to govern how long each time the program waits for w.getch(), not how long to wait between each time the snake moves. Also, I notice that in the first example, the timeout is declared at the top, outside the while loop. This looks weird to me because normally if I want to pause a while loop, I would put sleep() at the bottom inside the while loop.
If you want to pause between snake moves, you could use napms to wait a given number of milliseconds (and unlike sleep, does not interfere with screen updates). Setting w.timeout to 100 (milliseconds) is probably too long. If you're not concerned with reading function-keys, you could use nodelay to set the w.getch to non-blocking, relying on the napms to slow down the loop.
Regarding the followup comment: in ncurses, the wtimeout function sets a property of the window named _delay, which acts within the getch function, ultimately passed to a timed-wait function that would return early if there's data to be read.
I'm creating an experiment using the PsychoPy coder and am not sure how to implement multiple mouse clicks. In the experiment, there are multiple targets and distractors and during a response period, individuals are to select the targets. However, I currently have it where if you click on one of the targets, you get a correct message, otherwise you get an incorrect message. I also have a function that will wait for a mouse click to find the reaction time and will give the response after the mouse click. How do I add multiple mouse clicks for multiple targets?
def waitForMouse(mouse):
mouse.clickReset()
buttons = mouse.getPressed()
while buttons[0] == False: #wait for mouse click
buttons, times = mouse.getPressed(getTime=True) #get reaction time when mouse clicked
return times[0]
if clock.getTime() >= stimDuration: #start of response period
ResponsePrompt.draw() #indicate to participant to select targets
win.flip()
rt = waitForMouse(myMouse)
if myMouse.isPressedIn(target[0]) or myMouse.isPressedIn(target[1]):
CorrectResp.draw() #selected one of the correct targets
win.flip()
core.wait(2) #allow them to read it
else:
IncorrectResp.draw() #answered incorrectly
win.flip()
core.wait(2)
Presumably you have some way of knowing that the participant is "done." You could just do a while loop until exit condition was met. You would just cycle through your mouse function collecting data and appending it to some list that you will later save until the exit condition is met. The basic pseudo-code approach would be something like:
while 1:
catchData = getMouseClickStuff()
<append catchData to whatever list it should be stored to>
if <endConditionMet>:
break
I am creating an experiment using Psychopy builder.
The participant is presented with an image containing numbers, e.g. 10 and 20.
They enter what they think is the mean of the numbers, 15 in this case, and then press the spacebar to move on to the next image.
I am trying to have it so there is a display/box on screen that shows them their entry, as with larger numbers in the hundreds of thousands and millions I think they might lose track of what they have pressed.
The ability to change their entry would be excellent also, but really I am most interested in them being able to see it on screen.
In builder I can't find a way to do this, and the ratings scale is not appropriate for huge numbers.
I found these solutions in code to do something that sounds like it:
http://www.psychopy.org/wiki/home.php/Snippets/LiveUpdatingText
However when I try to add them using the code insert function , or just adding them to the compiled script the screen locks up when I try to run the experiment. I am a novice at python, and am not sure where to start fixing this. Is what I'm trying to do possible?
I'm happy to provide some example code from the compiled builder experiment.
Thanks in advance!
Those code snippets are designed for Coder, where you control everything that is happening and when. The same thing can be done in Builder, but you will have to amend the code to fit in with Builder's event loop cycle. i.e. Builder does certain things at the start of an experiment, on every trial, on every screen refresh and so on. So you can't just insert this sort of code without modification, because, for example, it attempts to wait indefinitely for a keypress. Builder meanwhile, is checking the keyboard every screen refresh (typically at 60 Hz), so if you try to wait indefinitely for a keypress in code, you'll be halting Builder from doing everything else it needs to do.
In essence, you just need to break up the code into snippets that go in the appropriate tab in a Builder Code Component (for code to be executed at experiment start, on each frame, and so on), and avoid indefinite functions like event.waitKeys() in favour of instantaneous checking via event.getKeys()
e.g. to adapt the second example from Jonas Lindeløv, in the "Begin Routine" tab, put:
chars = list('0123456789.') # the valid characters
meanText = '' # start with an empty answer on each trial
In the "Each Frame" tab, put something like:
response = event.getKeys() # get a list of keys pressed at this instant
if len(response) > 0: # if there was one,
key = response[0] # just convenient shorthand
if key in chars:
meanText = meanText + response[0]
elif key == 'space':
meanText = meanText + ' '
elif key == 'backspace' and len(meanText) > 0:
meanText = meanText[:-1]
elif key == 'return':
thisExp.addData('Answer', meanText) # save the response
continueRoutine = False # finish this trial
# update the appropriate text stimulus with the current response value:
insertNameOfYourTextStimulusComponent.text = meanText
i'm currently developping a program that runs on a robot. The program needs to add positions to a list but you have to push a button to add a new position. This has to be inside a while loop. But when i press the button the function repeats itself multiple times instead of just 1. Here is the part of code:
while not self.done():
if self._io_left_lower.state:
self._gripper_left.open()
elif self._io_left_upper.state:
self._gripper_left.close()
if self._io_right_lower.state:
self._gripper_right.open()
elif self._io_right_upper.state:
self._gripper_right.close()
if self._io_left_nav.button0:
print("repeats")
#self._mapping.append([self._limb_left.joint_angles(), self._gripper_left.position()])
if self._io_right_nav.button0:
self._mapping.append([self._limb_right.joint_angles(), self._gripper_right.position()])
if self._io_left_nav.button1:
self.stop()
As you can see when the self._io_left_nav.button0 is pressed it will now just print 'repeats' several times but it has to to be printed just onces. same for the self._io_right_nav.button0.
If button0 is a physical button and your loop is polling the button state then it is normal that many iterations run in the time your button is being pressed. You have several options to solve this situation, two of them are:
Do not use a loop check the button but hardware interruptions used to keep an updated representation of your state.
If you still want to use your loop, you should detect button state changes instead checking its current state at each iteration. That may require your program to implement a noise filter.
A very simple way to implement the second option is to have a button state variable with a timestamp associated: At each loop iteration, if the difference between the current time and the timestamp is big enought then you should check if the current state is the same than the one stored in the variable. If not then you have detected a change. In any case, at this point you should update your state-timestamp variable.
This is an example:
from time import time
prevState = (False,time())
while not self.done():
current = time()
if self._io_left_nav.button0 and (current-prevState[1])*1000>2: #For example, 2 ms
if self._io_left_nav.button0 != prevState[0]:
print("repeats")
prevState = (self._io_left_nav.button0,time())
I presume that's some kind of flag, that the button was pressed.
If you don't clear the state, the condition in the if statement will evaluate true. Again, and again....