Thanks to some amazing help from this forum, I have been able to randomize the order of presentation of my stimuli following sequentially a list from an excel sheet. I’ll just quickly put here the link to the post if it helps: Pseudorandomization for fMRI - #5 by jonathan.kominsky
So, I have an Excel sheet with the order of presentation of my conditions: happy, sad, ambiguous, and control. Each trial lasts 5.6s comprising two routines: presentation of stimuli, and rating.
However, I need to add null events (fixation cross) of different lengths depending on what is written on the Excel sheet (see picture) - it is the output of Optseq2!
So when it is sad, control, happy, or ambiguous, my code goes find the right images and set them in the routine presentation of stimuli, but when it is NE, I need to display a fixation cross for the time on the left (1.4s, 7s, 5.6s… NB: which is a multiple of the MRI TR).
How can I make conditional timing for fixation cross? Is it possible?
How can I do something like: if condition == happy, show the routine presentation of stimuli + rating, and if condition == NE, don’t show the routines, and only show a fixation cross for x seconds (Reminder: the excel sheet is a sequential order)!
if countdownTimer.getTime() <= 0:
continueRoutine = False
That code is fairly modular, you can kind of fit it in or around your existing code without too much difficulty.
There are a few different ways to do it.
One is to have something which sets the image autoDraw property of the images to False, which means it shouldn’t appear. You can put this in your Before Routine code but you may need to add it to the Each Frame code as well. If the fixation cross normally has an offset time, you could set the autoDraw property to True in the Each Frame code (or just call its draw() function directly in that code), which should override the duration time-out.
Another approach is to have an actual branching path where the fixation-only trial is a separate trial altogether. However, that might mess with your timing because there isn’t really a way to do it without creating an additional trial in the loop that will take up at least one frame. So I think for an fMRI experiment specifically it’s probably best to just have the one trial type and control what is or isn’t drawn in.
At some point, I actually thought about this: I set the opacity of all the elements in a trial to 0 when it was NE, and then to 1 when it was happy, sad, ambiguous, or control, and it kind of works (although there are lots of mistakes), but the problem with that is that it means that all NE trials have also to be 5.6s (the length of other trials). But I need them to have a conditional timing depending on what is written in the other column!
If I understand correctly, what you suggested is that I put a conditional timing on the fixation cross at the beginning of the trial, and that would overwrite all the images etc and the other routine in my loop called rating? Because sometimes the fixation cross has to be there for 2s and then goes to another trial, but sometimes 7s, and the trial is always 5.6s, so how can the fixation cross stay 7s for instance and overwrite everything else? (I don’t understand the draw function maybe!)
Right. Basically, you can create layers of conditional timers. Looking closely I see that the length of the NE trial can be longer than the 5.6 seconds of every other trial type, so you probably want two different timers, one for the fixation cross and one for the trial as a whole, and independent code that controls whether the other stimuli appear.
Before routine:
fixationCountdown = core.Clock()
trialCountdown = core.Clock()
if thisOrder_loop['condition'] != 'NE':
fixationCountdown.addTime(2.0)
# if 2s is how long the fixation should appear in a regular trial
else:
fixationCountdown.addTime(20)
# arbitrary but should be longer than longest NE trial, which will end
# before the fixation disappears
trialCountdown.addTime(thisOrder_loop['time_presentation'])
# since you have this column even for the regular trials, you can just use it unconditionally
Each frame:
if fixationCountdown.getTime() <= 0:
fixation.autoDraw = False # hides the fixation
# you could also add code that shows the other stimuli here or just do it
# w/the builder
if trialCountdown.getTime() <= 0:
continueRoutine = False # ends the trial
if thisOrder_loop['condition'] == 'NE':
# some code that hides everything but the fixation cross
Even easier. You just create a counter and decrement it in your each frame code. I’m assuming you’re working with a 60Hz display here (60 frames per second), but there are also ways to get the framerate from within PsychoPy if necessary (look at the ‘visual.Window’ documentation in the reference on the PsychoPy website).
fixationCountdown = 0
trialCountdown = 0
fps = 60 # I'm just assuming this for now
if thisOrder_loop['condition'] != 'NE':
fixationCountdown = 2*fps
# if 2s is how long the fixation should appear in a regular trial
else:
fixationCountdown = 20*fps
# arbitrary but should be longer than longest NE trial, which will end
# before the fixation disappears
trialCountdown = thisOrder_loop['time_presentation']*fps
# since you have this column even for the regular trials, you can just use it unconditionally
Each frame:
fixationCountdown -= 1
trialCountdown -= 1
# decrements each of the counters by 1.
if fixationCountdown <= 0:
fixation.autoDraw = False # hides the fixation
# you could also add code that shows the other stimuli here or just do it
# w/the builder
if trialCountdown <= 0:
continueRoutine = False # ends the trial
if thisOrder_loop['condition'] == 'NE':
# some code that hides everything but the fixation cross