Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python curses: Make enter key terminate Textbox?

I have an application where people are asked to enter their user name and password. I want them to be able to just press enter to send the name and password. To do this, I've made this:

import curses, curses.textpad
def setup_input():
    inp = curses.newwin(8,55, 0,0)
    inp.addstr(1,1, "Please enter your username:")
    sub = inp.subwin(2,1)
    sub.border()
    sub2 = sub.subwin(3,2)
    global tb
    tb = curses.textpad.Textbox(sub2)
    inp.refresh()
    tb.edit(enter_is_terminate)

def enter_is_terminate(x):
    if x == 10:
        tb.do_command(7)
    tb.do_command(x)

setup_input()

Unfortunately this doesn't work as expected. The standard character for termination (Triggered by CTRL+G) is 7 and the enter character is 10, but with the above code, all other keys are still handled correctly but when I press enter, it just gives me a newline, instead of terminating the edit mode of the Textbox. What am I doing wrong?

like image 386
gowner Avatar asked Apr 12 '26 00:04

gowner


2 Answers

Found this on the documentation:

If validator is supplied, it must be a function. It will be called for each keystroke entered with the keystroke as a parameter; command dispatch is done on the result.

So instead of running tb.do_command yourself, just return the key you want to 'input'.

def enter_is_terminate(x):
    if x == 10:
        return 7

Also, now you don't need to define tb as a global variable, which is usually a good thing. :)


If you'd be happy with just a one line input, you wouldn't have to handle the enter key yourself.

On the documentation it sais this:

Control-J -- Terminate if the window is 1 line, otherwise insert newline.

So if you define the textbox's sub window with line count of 1 you don't need to handle the enter key yourself.

def setup_input():
    inp = curses.newwin(8,55, 0,0)
    inp.addstr(1,1, "Please enter your username:")
    sub = inp.subwin(3, 41, 2, 1)
    sub.border()
    sub2 = sub.subwin(1, 40, 3, 2)
    tb = curses.textpad.Textbox(sub2)
    inp.refresh()
    tb.edit()

I also gave the sub a specific line and col count so the border is nicely around the textbox.

like image 117
Sevanteri Avatar answered Apr 14 '26 19:04

Sevanteri


It helps to read the source code. Here is a working validator:

def enter_is_terminate(x):
    if x == 10:
        x = 7
    return x

The validator has to return a character, which the edit function checks with do_command:

def edit(self, validate=None):
    "Edit in the widget window and collect the results."
    while 1:            
        ch = self.win.getch()
        if validate:  
            ch = validate(ch)
        if not ch:      
            continue
        if not self.do_command(ch):
            break             
        self.win.refresh() 
    return self.gather()

and do_command only returns 0 for the two cases (a) ASCII BEL and (b) newline on a one-line window:

    elif ch == curses.ascii.BEL:                           # ^g
        return 0
    elif ch == curses.ascii.NL:                            # ^j
        if self.maxy == 0:
            return 0
        elif y < self.maxy:
            self.win.move(y+1, 0)
like image 32
Thomas Dickey Avatar answered Apr 14 '26 20:04

Thomas Dickey