6

I'm having a difficult time with detecting the Backspace key using the Curses module. Whenever I press the Backspace key, the character / string returned is '^?', however I'm not able to detect it with:

if str(key) == '^?':

The code below is set up to run

import curses

def main(win):
    win.nodelay(True)
    key = ''
    record = ''
    win.clear()
    win.addstr("Key:")
    win.addstr('\n\nRecord:')
    win.addstr(record)
    while True:
        try:
            key = win.getkey()

            if str(key) == '^?':
                # Attempt at detecting the Backspace key
                record = record[:-1]

            elif str(key) == '\n':
                # Attempt at detecting the Enter Key
                record = ''

            else:
                record += str(key)
            win.clear()
            win.addstr("Key:")
            win.addstr(str(key))
            win.addstr('\n\nRecord:')
            win.addstr(record)
            if key == os.linesep:
                break
        except Exception as e:
            # No input
            pass

curses.wrapper(main)
# CTRL+C to close the program
Neil Graham
  • 593
  • 1
  • 5
  • 17

4 Answers4

6

According to your clarifying comment, backspace in your terminal is neither 'KEY_BACKSPACE' nor '\b' ('\x08').

Since you're using curses.wrapper(), which calls curses.initscr(), this suggests that something is wrong with either your terminfo database (unlikely) or your terminal configuration (more likely).

getkey() == 'KEY_BACKSPACE' or getch() == curses.KEY_BACKSPACE is supposed to work on any properly-configured system, so you're likely to have problems in other applications too.

The TERM environment variable is how the terminal type is communicated to tools which need the functionality beyond basic teletype, so, to fix that, start by checking what the TERM environment variable contains in the environment where you're running Python.

print(os.environ['TERM'])

As a quick hack, you can check all the values it's been observed to take.

if key in ('KEY_BACKSPACE', '\b', '\x7f'):
ssokolow
  • 14,938
  • 7
  • 52
  • 57
3

On my machine, running Python 3.5.2 or 2.7.12, I get KEY_BACKSPACE shown as the key when I press backspace, and using

if str(key) == 'KEY_BACKSPACE':

in place of

if str(key) == '^?':

results in the backspace key backspacing letters off record.

I think it's possible that, on your system, you're getting the actual backspace character. In that case, you might try '\b' in place of '^?', as according to the docs, that is the string escape for the ASCII backspace character.

andyg0808
  • 1,367
  • 8
  • 18
2

In MacOS, please try this:

    key = w.getch()
    if key == 127:
        print("You've pressed Backspace")
0

Looking at the source code, the textbox Textbox.edit() method will only backspace a character (e.g. curses.delch) when either curses.ascii.BS or curses.ascii.BACKSPACE are pressed. On MacOS, at least, the delete key registers as curses.ascii.DEL.

We can pass a custom validate() to overload this functionality such as backspace on DELETE, exist on ESCAPE, and exist on RESIZE:

import curses
import curses.textpad

def get_input(window):
    curses.curs_set(1)
    curses.set_escdelay(1)
    window.addstr(0, 0, "Enter some text:")
    input_box = curses.textpad.Textbox(curses.newwin(1, 40, 1, 0), insert_mode=True)
    
    def validate(ch):

            # exit input with the escape key
            escape = 27
            if ch == escape:
                ch = curses.ascii.BEL # Control-G
            
            # delete the character to the left of the cursor
            elif ch in (curses.KEY_BACKSPACE, curses.ascii.DEL):
                ch = curses.KEY_BACKSPACE
            
            # exit input to resize windows
            elif ch == curses.KEY_RESIZE:
                ch = curses.ascii.BEL # Control-G

            return ch
    
    input_box.edit(validate)
    curses.curs_set(0)
    curses.set_escdelay(1000)
    return input_box.gather().strip()


def main(window):
    curses.noecho()
    curses.cbreak()
    window.keypad(True)
    curses.curs_set(0)

    input_text = get_input(window)

    window.addstr(2, 0, f"You entered: {input_text}")
    window.refresh()
    window.getch()

if __name__ == "__main__":
    curses.wrapper(main)
aidanmelen
  • 6,194
  • 1
  • 23
  • 24