Trying to change opacity of stimulus overtime while recording button press

Description of the problem:

I am coding a psychophysics experiment where at the beginning of each trial, a grey square is displayed on the screen. Under the grey square is a stimulus, but it is hidden from the subject. Over the course of the trial, the grey square becomes more transparent, slowly unveiling the stimulus drawn beneath at a constant rate. The goal for the subject is to press the space bar as soon as they can identify the stimulus that was previously hidden.

I am currently having trouble both simultaneously changing the opacity/transparency at a specified rate while recording a space bar press. Ideally, as soon as the subject makes a press, it will take them to a post-trial question. Any help about how to properly change opacity over time while recording a button press and moving to the next screen would be greatly appreciated! Below is the code I currently have, which is successful at changing opacity, but not recording a press and advancing.

timer.reset()
while timer.getTime() < unveil_rate * 12:
    
    if timer.getTime() < unveil_rate:
        present_true_stimulus.draw()
        mask_stimulus.opacity = 1
        mask_stimulus.draw()
        win.flip()        
        
    elif timer.getTime() >= unveil_rate and timer.getTime() < unveil_rate * 2:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .985
        mask_stimulus.draw()
        win.flip()
            
    elif  timer.getTime() >= unveil_rate * 2 and timer.getTime() < unveil_rate * 3:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .970
        mask_stimulus.draw()         
        win.flip()
        
    elif  timer.getTime() >= unveil_rate * 3 and timer.getTime() < unveil_rate * 4:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .955
        mask_stimulus.draw()
        win.flip()
        
    elif  timer.getTime() >= unveil_rate * 4 and timer.getTime() < unveil_rate * 5:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .940
        mask_stimulus.draw()
        win.flip()            

    elif  timer.getTime() > unveil_rate * 5 and timer.getTime() < unveil_rate * 6:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .925
        mask_stimulus.draw()
        win.flip()    
        
    elif  timer.getTime() > unveil_rate * 6 and timer.getTime() < unveil_rate * 7:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .910
        mask_stimulus.draw()
        win.flip()
        
    elif  timer.getTime() > unveil_rate * 7 and timer.getTime() < unveil_rate * 8:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .895
        mask_stimulus.draw()
        win.flip()                

    elif  timer.getTime() > unveil_rate * 8 and timer.getTime() > unveil_rate * 9:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .88
        mask_stimulus.draw()
        win.flip()                

    elif timer.getTime() > unveil_rate * 9 and timer.getTime() < unveil_rate * 10:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .865
        mask_stimulus.draw()
        win.flip()                

    elif  timer.getTime() > unveil_rate * 10 and timer.getTime() < unveil_rate * 11:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .850
        mask_stimulus.draw()
        win.flip()                

    elif  timer.getTime() > unveil_rate * 12:
        present_true_stimulus.draw()
        mask_stimulus.opacity = .835
        mask_stimulus.draw()
        win.flip()                
         
key = event.waitKeys()
if key[0] in ['space']:
    break

Hi, welcome along.

A coding tip: present_true_stimulus.draw(), mask_stimulus.draw() and win.flip() are all unconditional in your code (i.e. they aren’t affected by the time). So remove all of those from within your if and elif clauses and just put them in once, after all of the elifs. A key principle of programming is DRY: Don’t Repeat Yourself. If you find yourself typing the same thing over and over again, it is usually a sign the code can be done more efficiently.

Also, you repeatedly call timer.getTime(). There is a chance that the actual value will be infinitesimally larger between the start of the loop iteration and the end, which could affect your logic. But also it is wasteful to keep asking what the time is. Just do it once per iteration and store the result:

while timer.getTime() < unveil_rate * 12:
    
    current_time = timer.getTime() # just check once

    if current_time < unveil_rate:
        mask_stimulus.opacity = 1
    elif current_time  >= unveil_rate and current_time < unveil_rate * 2:
        mask_stimulus.opacity = .985
    elif # etc
    elif # etc

    # now just have this once:
    present_true_stimulus.draw()
    mask_stimulus.draw()         
    win.flip()

Making the code shorter might help reveal the issue: you need to be checking for key presses within the same while loop that you do your drawing in, not after that loop has completed. i.e. the current check is outside the indentation for all of the drawing. The key thing here is that you won’t want to use .waitKeys(), as by its nature, it will pause your drawing loop while it waits for a response. Instead, use .getKeys(), which does just an instantaneous check of the keyboard. So it won’t interrupt your drawing loop, but now you will need to check whether a key was actually pressed or not.

Lastly, don’t use the event module at all: it has been deprecated in favour of the much higher-performance Keyboard class. The docs are here:

https://www.psychopy.org/api/hardware/keyboard.html

1 Like

Hi Michael,

Thank you so much for your response and suggestions. I have implemented them and now the code is properly working. I really appreciate it!