Spacebar duration estimate

What are you trying to achieve?:
I am trying to allow for a duration estimate via spacebar. So I want the stopwatch to start when the participant hits the spacebar and then I want the stopwatch to stop at a second hit of the spacebar.

@culbertm, you could use a text stim and code component to achieve this. Create a blank text stim called ‘text’ and a code component. In the code component’s relevant tabs:

# Each routine
space = False  # Has space been pushed?
spaceRT = ''  # variable for holding RT
spaceClock = clock.Clock()  # Clock for space bar

# Each Frame
keys = event.getKeys()
if not space and 'space' in keys:  # If space has not been pushed, reset clock when pushed
    space = True
    spaceClock.reset()
elif space and 'space' in keys:  # If space is pushed again, get RT
    spaceRT = spaceClock.getTime()
    space = False 

if space:  # present text on screen after space has been pushed
    text.setText(round(spaceClock.getTime(), 3))  

# End routine
thisExp.addData('space.rt', spaceRT)


So I’m trying to do something very similar to this, but measure the total duration the space bar is held down (not time between 2 keypresses), then exit the routine upon key release. I’ve tried the following, but no luck. The trial just ends around 0.01s after the space is first pressed, but before it is actually released

Start of experiment

space_pressed = False
space_clock = clock.Clock()

Each frame

keys = event.getKeys()
if not space_pressed and 'space' in keys:
    space_pressed = True
    space_clock.reset()
    print('space press detected')
elif space_pressed and 'space' not in keys:
    space_duration = space_clock.getTime()
    print(f'space_duration = {space_duration}')
    continueRoutine = False

End routine

trials.addData('duration', space_duration)

If I remove the continueRoutine = False then the trial never terminates.

Space press detection seems to work just fine, it’s the release of the space bar which seems to be the problematic thing

Any advice would be much appreciated.

getKeys() is event-based not state-based, if that makes sense. While the key is held down it won’t trigger a new event, so getKeys() will be empty after the first returned press.

You can use the pyglet calls directly to do this sort of thing (see KeyStateHandler at https://pyglet.readthedocs.io/en/pyglet-1.3-maintenance/modules/window_key.html)

BUT I’ve nearly finished a new implementation of keyboard polling using the Psychtoolbox engine ported to Python (Mario’s direct USB polling has better timing) and in that I am providing a duration field for the key events. This will be released in the 3.1.x series as soon as I have time to finish it! :slight_smile:

1 Like

I’m interested to hear about the new thing. In the mean time I’ve tried to check something together. Feel that I’m close, but not quite there. I’m using the same basic approach, but using the pyglet state-based approach you pointed to.

Begin Experiment

from pyglet.window import Window
from pyglet.window import key

window = Window()

Begin Routine

space_pressed = False
space_released = False
space_clock = clock.Clock()

Each frame

win = window.Window
keyboard = key.KeyStateHandler()
win.push_handlers(keyboard)
if not space_pressed and keyboard[key.SPACE]:
    space_pressed = True
    space_clock.reset()
    print('space press detected')
elif space_pressed and not keyboard[key.SPACE]:
    space_duration = space_clock.getTime()
    space_released = True
    print(f'space_duration = {space_duration}')
    continueRoutine = False

Gets me the error…

win = window.Window

AttributeError: ‘CocoaWindow’ object has no attribute ‘Window’

I’ve also got very little intuition on whether all or just some of these should be in the Each Frame block

win = window.Window
keyboard = key.KeyStateHandler()
win.push_handlers(keyboard)

1 Like

You could do this by hacking the pygletbackend, although I think it breaks the keyboard component, so you cannot use Builders keyboard component if you do this, you would have to handle keyboard events yourself. It is also untested, and I would suggest waiting for Jons fix. However, you could add the following to the psychopy.visual.backends.pygletbackend.py:

# To the import section at the beginning of the code
from pyglet.window import key

# After line 194 (after self.winHandle.on_key_press) - although not actually important that its right here

self.winHandle.keyboard = key.KeyStateHandler()
self.winHandle.push_handlers(self.winHandle.keyboard)

Now, in your code component, you can add the following:

# Begin experiment
from pyglet.window import key

# every frame
if win.winHandle.keyboard[key.UP]:  # If you press up on the keyboard
    print(True)  # Or get time

You can set this up for any keypress recognised by pyglets key module.

Thanks for this. I think for the time being I’ll get the experiment pilot working with the solution right up top, measuring duration between 2 keypresses. Once this new code is available, then I’ll swap it out for keypress down duration. That will be better as we will have some relatively short durations to measure - which will be more accurate with a keydown duration measure. Thanks both for the pointers and info.

Sure, the above will do that. It will report the key state on every screen refresh - True if pressed down. Btw, it does not actually break the keyboard. As with using event.getKeys, you cannot use it together with a keyboard component from Builder.

Hello,

I have created a task using the builder so far.
But now I would like to time the duration of the spacebar down press, and have this amount of time recorded.
So far I have compiled my task - created in builder - to python script, and changed one little bit of the code - waitRelease=True (it was originally set as False). Here is a copy of the trial section from the python script:

keyboard checking is just starting
waitOnFlip = True
win.callOnFlip(key_resp.clock.reset) # t=0 on next screen flip
win.callOnFlip(key_resp.clearEvents, eventType=‘keyboard’) # clear events on next screen flip
if key_resp.status == STARTED and not waitOnFlip:
theseKeys = key_resp.getKeys(keyList=[‘space’], waitRelease=True)
_key_resp_allKeys.extend(theseKeys)
if len(_key_resp_allKeys):
key_resp.keys = _key_resp_allKeys[-1].name # just the last key pressed
key_resp.rt = _key_resp_allKeys[-1].rt
# a response ends the routine

But that is as far as I have managed to get. What else should I change or insert (and where) to record the duration of the spacebar press?

Thanks!