psychopy.org | Reference | Downloads | Github

Wait without pausing code

Hi,

I have a code for presenting an image and getting a response. I am going to use this with TMS, triggering the TMS via parallel port after a specific time (different for each trial) in between the participant seeing the image and responding.
Previously I used event.waitKeys() but have switched the event.getKeys() to allow the code to run while getting the response. I still only want the image to appear for a maximum of 3 seconds so have included the while loop.
I want the image to appear and then after a certain time to send the parallel port signal while simultaneously allowing participants to respond (and not only after the signal has been sent - as it is likely on some trials they will respond before as I am basing the timing of the TMS pulse on their average RT).
I was wondering how I can do this without using core.wait() as this also pauses the code. I am currently trying timer but this seems to be repeating the parallel port section of the code several times for each image.

This is the code I currently have:

    image[i].draw()
    win.flip()
    presentationTime = clock.getTime()
    t_end=time.time()+3
    while time.time() < t_end:
        keys = event.getKeys(keyList = validResponse.values())
        if keys is None:
            keys=[]
            keys.append("no key")
            response = 'no key'
        if len(keys) >0:
            resp=keys[0]
            responseTime = clock.getTime()
            break
        TrialValue=np.random.choice(jitter, replace=False)
        t = Timer(TrialValue, trigger)
        t.start()
        pulse_time =  clock.getTime()
        windll.inpout32.Out32(portaddress, 0)

“”"

Thanks,
Emma

Hi Emma, could you describe in pseudo-code exactly what sequence of events you want to happen?

Hi Michael,

Thanks for replying.

I want to present a word cue (not shown here - this is fine). I then want to present an image, which participants respond yes or no to depending on whether it matches the word cue. As soon as the image appears, I want it to wait a specified amount of time before a TMS pulse is sent. I have created a set of values to wait which I have saved in the variable jitter. I want the code to select one of these from random, without replacement, for each trial. I want it to wait this long, send the pulse (get the time to ensure it is working properly) and then reset the pins. I have saved sending the parallel port signal in the def trigger.

However, as soon as the image is presented, I also want participants to be able to respond. And I want them to be able to respond for 3 seconds before it times out and moves onto the next trial. The time values to send the TMS pulse are all less than 1 second and should occur in the first second that participants are able to respond.

Therefore I don’t want to pause the code before sending the TMS pulse as this will affect getting the responses from participants and I want to analyse their reaction times.

I hope this makes sense.
Thanks,
Emma

Something like this:

# trial-level loop:
for image in images:
    # presumably also show the word cue here:

    # then draw the image:
    image.draw()
    win.flip()

    # decide when to send the pulse on this trial:
    pulse_time = np.random.choice(jitter, replace=False)
    pulse_sent = False

    # set the end time for the trial:
    t_end = clock.getTime() + 3
    t = 0 # need an initial value

    # within-trial loop:
    while t < t_end:

        # send pulse once per trial:
        if not pulse_sent and t >= pulse_time:
            windll.inpout32.Out32(portaddress, 0)
            pulse_sent = True
        
        # monitor key presses
        keys = event.getKeys(keyList = validResponse.values())
        if keys is None:
            keys = []
            keys.append('no key')
            response = 'no key'
        if len(keys) > 0:
            resp = keys[0]
            responseTime = clock.getTime()
            break

        # need some way to slow the loop:
        time.sleep(0.001) # pause for 1 ms

        # for checking if we are finished:
        t = clock.getTime()

      

Note that you shouldn’t let the loop run at unconstrained speed. If we do that, it will happily run at potentially millions of iterations per second, starving the computer of CPU resources. At some point, the OS would then break in and steal the CPU back, causing unpredictable pauses in your code. So you need to be a good citizen and give some time back to the system. This is why PsychoPy code often puts the win.flip() within the loop, so that a few milliseconds are freed up on each iteration. But above, we are just drawing once and then explicitly pausing for 1 ms per iteration on every iteration using time.sleep().

Just a note on your use of numpy.random.choice(): I don’t think that will do what you want here re sampling without replacement. The replacement parameter can only work within a single call to the function. It doesn’t have any way of storing what happened on previous calls, so it is quite possible (and in fact, almost certain) that you will get the same value returned on subsequent calls of this function.

If you want to have sampling without replacement, then you need to somehow maintain the state of previous choices across trials yourself. A simple way to do this is to randomly shuffle a list of possible values, and then on each trial, pop() one out. Popping is sequential, but that is fine if the list has been shuffled. Because the list gets shorter each time a value is removed for use, you can’t sample the same value again on subsequent trials. e.g:

# before the trial loop starts, create some list of values,
# at least equal in length to the number of trials:
jitter = [0.25, 0.5, 0.75, 1.0, 1.25] 

# randomise it:
np.shuffle(jitter)


# then at the start of each trial, extract one value:
trial_value = jitter.pop()
# the list is now shorter by one entry, so entry can't be selected again

That is really helpful, thank you so much.
I noticed recently that some of the values were being repeated so I will definitely give that a try.
Thanks,
Emma