psychopy.org | Reference | Downloads | Github

How Do I Detect If A Button On The Keyboard Is Being Pressed?


#1

I’m writing this input text box with psychopy, basically redrawing the screen every time when there is a keypress. The problem is that right now people are unable to type in capital letters or to type a question mark, both of which requires being able to detect if “shift” is being pressed at the moment. How can I do that?

class InputTextBox(object):
    def __init__(self, window, pos=(0, -0.5), size=(1.4, 0.8), character_limit=None, valid_characters='[a-zA-Z0-9]',
                 resp_type=None, validation_func=lambda x: True, illegal_input_err_message='illegal input!'):
        self.window = window
        self.text_buffer = ''
        self.box_params = {'win': window,
                           'name': 'box',
                           'fillColor': 'white',
                           'pos': pos,
                           'width': size[0],
                           'height': size[1]}
        self.text_params = {'win': window,
                            'name': 'text',
                            'font': u'Arial',
                            'alignHoriz': 'left',
                            'alignVert': 'top',
                            'pos': (pos[0] - size[0] / 2 + 0.01, pos[1] + size[1] / 2 - 0.01),
                            'height': 0.05,
                            'color': 'black',
                            'wrapWidth': size[0] - 0.02}
        self.box = Rect(**self.box_params)
        self.text_box = TextStim(text='|', **self.text_params)
        self.character_limit = character_limit
        self.valid_characters = valid_characters
        self.illegal_input_err_message = illegal_input_err_message
        self.resp_type = resp_type
        self.validation_func = validation_func
        if resp_type == 'num' and validation_func is None:
            self.validation_func = isnumber
        elif resp_type == 'int' and validation_func is None:
            self.validation_func = isinteger

    def set_auto_draw(self, auto_draw=True):
        self.box.setAutoDraw(auto_draw)
        self.text_box.setAutoDraw(auto_draw)

    def get_input_result(self):
        if self.resp_type == 'num':
            return float(self.text_buffer)
        elif self.resp_type == 'int':
            return int(self.text_buffer)
        return self.text_buffer

    def update_text(self, key):
        special_char_list = {'space': ' ',
                             'period': '.',
                             'comma': ',',
                             'apostrophe': '\'',
                             'backslash': '\\',
                             'minus': '-',
                             'equal': '=',
                             'slash': '/'}
        if key == 'backspace':
            self.text_buffer = self.text_buffer[:-1]
            key = ''
        elif key in special_char_list:
            key = special_char_list[key]
        elif not re.match(''.join(['^', self.valid_characters, '$']), key):
            key = ''
        if self.character_limit and type(self.character_limit) is int:
            if len(self.text_buffer) >= self.character_limit:
                key = ''
        self.text_buffer = self.text_buffer + key
        self.text_box.setText(text=self.text_buffer + '|')

    def get_input(self, time_limit=None, exit_key='return'):
        self.box.draw()
        self.text_box.draw()
        self.window.flip()
        event.clearEvents(eventType='keyboard')
        timer = core.Clock()
        while True:
            keyboard_input = event.getKeys()
            for key in keyboard_input:
                self.update_text(key)
            if exit_key in keyboard_input:
                if not self.validation_func(self.text_buffer):
                    self.text_box.setText(self.illegal_input_err_message)
                    self.box.draw()
                    self.text_box.draw()
                    self.window.flip()
                    time.sleep(1)
                    self.update_text('')
                else:
                    break
            elif time_limit and timer.getTime() > time_limit:
                break
            elif 'escape' in keyboard_input:
                core.quit()
            self.box.draw()
            self.text_box.draw()
            self.window.flip()
        return self.get_input_result()

#2

Don’t know if you’ve figured out a solution, but there’s a way to bypass PsychoPy’s handling of keyboard inputs altogether that should give you what you need. You can basically just listen to the pyglet window directly. See here: Tracking Key release