psychopy.org | Reference | Downloads | Github

Anyone using a lumina box and RatingScale?

Here’s a summary of the problem, hope someone can help…

Aim - to use the psychopy provided RatingScale with a Lumina LSC400 button box.
Problem - the rating scale uses keyboard input to move the scale marker up and down, but the Lumina doesn’t provide keyboard output (unlike fORP etc).

It looks like this has come up before, and the chosen solution then was to hack the ratingscale.py code (but exactly how this worked was not detailed) - and I’d rather not, if I can avoid it. So I wrote some code…

The Lumina generates events, which can be retrieved using buttonBox.get_next_response(), this returns a dict with the keywords ‘pressed’ (can be either True or False) and ‘key’ which can be 0,1,2,3,4 depending on the button pressed or if a trigger was received (=4). Each button press generates two events: similar to a button-down, button-up pair. So you can catch a specific key-press by looking for when ‘pressed’ = True and ‘key’ = chosen_key

What I wanted to happen was that when a subject pressed the right button my code would simulate a key-press, in this case the number “2” to move the rating scale to the right. I stumbled on this (http://stackoverflow.com/questions/13564851/generate-keyboard-events), and have copied the necessary into my code (not shown). Here are the relevant(?) sections:

# Initialize components for "myRatingScale"
ratingClock = core.Clock()
myRatingScale = visual.RatingScale(win, 
                            name='rating', 
                            marker='circle',
                            markerStart = 4,
                            size=1.0, 
                            pos=[0.0, -0.4], 
                            low=1, high=7, 
                            #maxTime = 8.0, 
                            labels=["'President Trump'", " 'Mother Theresa'"], 
                            leftKeys = '1', rightKeys = '2', 
                            scale='Rate trustworthiness', 
                            singleClick=False, 
                            showAccept=False)

At some point later we show the rating scale and start looking for responses from the Lumina (“buttonBox”).

myRatingScale.reset()
logging.data('Rating scale started')
fmriClock.reset()
myRatingScale.setAutoDraw(True)

# start rating scale, show it for 8 seconds
while fmriClock.getTime() <= 8.0:
    buttonBox.poll_for_response()
    if buttonBox.response_queue_size() > 0:
        resp = buttonBox.get_next_response()
        if resp['pressed'] and resp['key'] == 4:
            logging.data('Trigger received')
            trigCounter += 1
        elif resp['pressed'] and resp['key'] == 0:
            PressKey(0x31) # this function comes from stackoverflow link
            print '%s' %('Left button')
        elif resp['pressed'] and resp['key'] == 1:
            PressKey(0x32) # this function comes from stackoverflow link
            print '%s' %('Right button')
        else:
            print '%s' %('wrong button or something else')
    buttonBox.clear_response_queue() # do we really need this?
    if event.getKeys(['escape']):
            core.quit()
    win.flip()
myRatingScale.setAutoDraw(False)

The problem is that when a Lumina button is pressed, the rating scale moves ONCE in the correct direction, and the message is written to the output console (e.g. Right Button), but if I press it again nothing happens, however I see a message in the console for each and every button press. If I press the opposite direction the rating scale moves in the desired direction once more, but again only ONCE, despite the message appearing correctly.

If I use the keys “1” and “2” the ratingscale behaves exactly as expected.

What am I doing wrong? Also, the “solution” to emulate key presses is not great, as it’s Windows only - and I’d like something portable if at all possible.

Cheers, Jon

Stab in the dark, do you need to send a release key message too?

Another stab in the dark, is the problem that you’re using the number 2? Whenever I’ve briefly tinkered with the rating scale, the numbers selected the rating scale numbers directly. What happens if you send ‘right’ and ‘left’?

I suggest using this code for the PressKey function:

def PressKey(key):
    event._onPygletKey(symbol=key, modifiers=None, emulated=True)

I suspect it won’t fix the issue, but will make it more clear that there’s not something lurking in your key handling that is muddying the waters.

So the way it works currently (without the Lumina) is that you repeatedly press 2 and each press moves the slider one hop along the scale to the right.

I think you might be right about the release key, as currently I only use the PressKey function not the ReleaseKey. I’ll try that first thing tomorrow.

I would much prefer using something similar to this as I guess this might work cross platform. What makes you think it won’t work?

The stackoverflow suggested solution does at least generate keypresses, but it seems like a bit of a sledgehammer to crack a nut.

I guess making some alterations to RatingScale might be the way to go if all else fails.

The changes to the rating scale code would be very similar to what you have implemented: detect appropriate events, map them onto keys, and simulate appropriate key-presses. I think you are close to getting it to work.

It might not work because there might be other things going on as well. So it might solve some problems but not all of them (e.g., the ‘2’ mapping onto a specific response – but now you report that that is not happening, good).

The key codes should be strings, like ‘a’, ‘1’, ‘left’, and so on. Best not to use numbers directly (1, 2 i.e., use ‘1’, ‘2’ as string not int).

Bizarrely if I use the numbers as strings ‘1’ etc then the logical test fails. Originally I had the code exactly as you said, but it seems like the dict created by .get_new_response() stores the key as an integer (is that possible?), hence why it now reads response['key'] = 2 etc. Not sure if that’s a problem with the Cedrus provided module (pyxid)?

Cheers, Jon

I don’t know the Cedrus conventions – if it returns int’s then that’s what you have to test against. I just meant that the code you send into the event._onPygletKey() function expects strings

ah - I’ll see how far I get with ReleaseKey before venturing further into this.