Visual analog scale: press and hold key

Description of the problem:
This code creates a visual analog scale which moves left/right via keyboard input. As is, the slider moves one discrete step for every key press. We instead want to press and hold these keys to make the slider move continuously - on key-down the slider begins moving until the key is released.

Thanks for the help!

from psychopy import visual, event
from psychopy.hardware import keyboard

def init_objects(x, y):
    kb = keyboard.Keyboard()
    win = visual.Window(size=(x,y), pos=(10, 50), color='white', monitor='testMonitor', fullscr=False, screen=0)
    title = visual.TextStim(win=win, text=None, color='black', pos=[0,.25])
    outline = visual.Rect(win=win, width=1, height=.2)
    outline.lineColor = 'black'
    outline.lineWidth = 2.3
    rect = visual.Rect(win=win, width=.01, height=.2)
    rect.fillColor = 'red'
    pain0 = visual.TextStim(win=win, text='NO PAIN', color='black', height=.06, pos=[-.6, 0])
    pain10 = visual.TextStim(win=win, text='WORST PAIN\nIMAGINABLE', color='black', height=.06, pos=[.65, 0])
    return kb, win, title, outline, rect, pain0, pain10

def reset(title, rect, pain_type):
    title.text = pain_type
    rect.pos = [-.5,0]
    rect.width = .01
    rect.height = .2

def update_win(win, title, outline, rect, pain0, pain10):
    rect.draw()
    outline.draw()
    title.draw()
    pain0.draw()
    pain10.draw()
    win.flip()

def main():

    x = 600
    y = 400
    kb, win, title, outline, rect, pain0, pain10 = init_objects(x, y)

    for pain_type in ['PAIN INTENSITY', 'PAIN UNPLEASANTNESS']:
        
        reset(title, rect, pain_type)

        pain_rating = 0
        while True:
            update_win(win, title, outline, rect, pain0, pain10)
            keys = kb.getKeys(['1', '2', 'return'], waitRelease=False, clear=True)
            if '1' in keys and pain_rating > 0:
                pain_rating -= .5
                rect.width -= .05
                rect.pos[0] -= .025
            elif '2' in keys and pain_rating < 10:
                pain_rating += .5
                rect.width += .05
                rect.pos[0] += .025
            elif 'return' in keys:
                print(f'{pain_type}: {pain_rating}')
                break

if __name__ == '__main__': main()

Hi There,

Here is a demo I used for something similar that could be helpful here to suit your purposes. A bar will continue to fill whilst a ky (the space key) is held.

fillingBar.psyexp (24.3 KB)

By the sounds of it this could be used for what you need?
Hope this helps,
Becca

2 Likes

There’s also a built-in demo in 2021.1.x called progressBar which is very similar :slight_smile:

Thank you both for these very helpful resources! For those who have a similar issue, it was fixed by changing the while loop to:

while True:
            update_win(win, title, outline, rect, pain0, pain10)
            keys = kb.getKeys(['1', '2', 'return'], waitRelease=False, clear=False)
            if keys and not keys[-1].duration: # key is being held down
                key = keys[-1].name
                if key == '1' and pain_rating > 0:
                    pain_rating -= .05
                    rect.width -= .005
                    rect.pos[0] -= .0025
                elif key == '2' and pain_rating < 10:
                    pain_rating += .05
                    rect.width += .005
                    rect.pos[0] += .0025
                elif key == 'return':
                    print(f'{pain_type}: {pain_rating:.2f}' )
                    win.flip()
                    core.wait(1)
                    break
                del keys
1 Like