I am using the psychopy code from this publication: "Using a variant of the optomotor response as a visual defect detection assay in zebrafish". The code is a series of while loops on a countdown. Each loop should run for 10 seconds. I want to add another countdown before the first 10 seconds loop that will last for 5 seconds and will just be a black screen.
The problem that I am having is that the first 5 seconds while loop (the black screen) is overlapping with the first 10 seconds while loop. This results in the first 10 second loop only lasting for 5 seconds. Oddly enough, the rest of the loops that are supposed to last for 10 seconds each are not overlapped or disturbed.
Does anyone have any ideas why this might be happening? I don't get any errors when I run my code.
Irrelevant note: I am planning on changing the times for each loop
#import libraries needed from PsychoPy
from psychopy import visual, core, event, sys
#create a window
mywin = visual.Window([1680,1050], monitor="testMonitor", units="deg", fullscr = True)
#To run the stimulus, "save" + "run"
#Create the black screen for acclimation
TIMEBLACK = 5
black = visual.GratingStim(win=mywin, size=999, pos=[0,0], sf=0, color=[-1,-1,-1])
blacktimer = core.CountdownTimer(TIMEBLACK)
#Create OMR Stimuli
TIME = 10 #this is seconds of stimulus display and should be changed based on experimental needs (See Table 2)
SPEED = 1.04 #1.033 is adult optimal, 1.04 is larvae optimal. See table for additional speeds; correlates to 12 angular cycles for adults and 16 angular cycles for larvae.
grating = visual.RadialStim(win=mywin, mask='circle', tex='saw',size=20, color=[1,1,1],pos=[0,0],angularCycles = 4, angularRes = 3600, contrast = -1.0) #angularCycles are the number of black/white bars presented by the stimulus. Adult optimal is 12 angular cycles, larvae optimal is 16 angular cycles (See Table 1).
fixation = visual.GratingStim(win=mywin, size=999, pos=[0,0], sf=0, color=[0.5,0.5,0.5])
timer = core.CountdownTimer(TIME)
while blacktimer.getTime()>0:
black.draw()
mywin.flip()
if len(event.getKeys())>0: sys.exit(0)
event.clearEvents()
blacktimer.reset(TIMEBLACK)
#draw the stimuli and update the window. The phase is always advanced by 0.05 of a cycle.
while True: #this creates a never-ending loop
while timer.getTime()>0:
grating.setAngularPhase(SPEED, '-')
grating.draw()
mywin.flip()
if len(event.getKeys())>0: sys.exit(0)
event.clearEvents()
timer.reset(TIME)
while timer.getTime()>0:
fixation.draw()
mywin.flip()
if len(event.getKeys())>0: sys.exit(0)
event.clearEvents()
timer.reset(TIME)
while timer.getTime()>0:
grating.setAngularPhase(SPEED, '+')
grating.draw()
mywin.flip()
if len(event.getKeys())>0: sys.exit(0)
event.clearEvents()
timer.reset(TIME)
while timer.getTime()>0:
fixation.draw()
mywin.flip()
if len(event.getKeys())>0: sys.exit(0)
event.clearEvents()
timer.reset(TIME)
if len(event.getKeys())>0: break
event.clearEvents()
#cleanup. To exit, press any key.
mywin.close()
core.quit()
Here are my updates after edits. I moved everything having to do with the black screen to be before the first 10 second while loop:
#Create the black screen for acclimation
TIMEBLACK = 5
black = visual.GratingStim(win=mywin, size=999, pos=[0,0], sf=0, color=[1,-1,-1])
blacktimer = core.CountdownTimer(TIMEBLACK)
while blacktimer.getTime()>0:
black.draw()
mywin.flip()
if len(event.getKeys())>0: core.quit(0)
blacktimer.reset(TIMEBLACK)
#Create OMR Stimuli
TIME = 10 #this is seconds of stimulus display and should be changed based on experimental needs (See Table 2)
SPEED = 1.04 #1.033 is adult optimal, 1.04 is larvae optimal. See table for additional speeds; correlates to 12 angular cycles for adults and 16 angular cycles for larvae.
grating = visual.RadialStim(win=mywin, mask='circle', tex='saw',size=20, color=[1,1,1],pos=[0,0],angularCycles = 4, angularRes = 3600, contrast = -1.0) #angularCycles are the number of black/white bars presented by the stimulus. Adult optimal is 12 angular cycles, larvae optimal is 16 angular cycles (See Table 1).
fixation = visual.GratingStim(win=mywin, size=999, pos=[0,0], sf=0, color=[0.5,0.5,0.5])
timer = core.CountdownTimer(TIME)
#draw the stimuli and update the window. The phase is always advanced by 0.05 of a cycle.
while True: #this creates a never-ending loop
while timer.getTime()>0:
grating.setAngularPhase(SPEED, '-')
grating.draw()
mywin.flip()
if len(event.getKeys())>0: core.quit(0)
timer.reset(TIME)
In the original version of your code, you create the first timer like this:
blacktimer = core.CountdownTimer(TIMEBLACK) # will run for 5 s
You then create a second one like this:
timer = core.CountdownTimer(TIME) # will run for 10 s
Each timer begins counting down from the moment it is created (or reset). So the second one (timer
) will have counted down to ~ 5 seconds by the time the first (blacktimer
) has reached 0 s, meaning that by the time you start using timer
, half of its time has already elapsed.
Each subsequent time that you use timer
, it will run correctly for 10 s, as you are re-setting it immediately before each loop.
Your revised version of the code seems to address this issue.
Additional suggestions: you should avoid calling sys.exit(0)
. PsychoPy provides a more graceful way of exiting via core.quit()
, which does some tidying up on the way out, including saving any current data. Also no need to keep calling event.clearEvents()
here: if a key event had occurred, your code would have exited.
Lastly, the recommendation these days (even for experienced Python developers) is to use the Builder GUI to generate the script for you. It uses best-practice techniques to give you good timing, and uses up-to-date APIs. For example, Builder scripts no longer use the old event
module for keypress handling, instead using the much higher-performance Keyboard
object. The GUI still allows you to insert custom Python via its graphical "Code Components", for when you need functionality that the GUI doesn't provide. It takes care of ensuring the code runs at the right time (e.g. at the beginning of each trial, or on every screen refresh) and means you can focus on whatever your unique functionality is, while all the housekeeping of recording data and showing stimuli on time is taken care of for you.
You'll also find that the best support for PsychoPy is from the dedicated forum at https://discourse.psychopy.org, rather than here at StackOverflow. The PsychoPy developers themselves are much more likely to see your posts there.