Discussion:
Tools to help with text mode (i.e. non-GUI) input
Add Reply
Chris Green
2025-01-11 14:28:49 UTC
Reply
Permalink
I'm looking for Python packages that can help with text mode input,
i.e. for use with non-GUI programs that one runs from the command
prompt in a terminal window running a bash shell or some such.

What I'm specifically after is a way to provide a default value that
can be accepted or changed easily and also a way to provide a number
of different values to choose from.

I.e. for the default sort of input one might see:-

Colour? red

Hitting return would return 'red' to the program but you could also
backspace over the 'red' and enter something else. Maybe even better
would be that the 'red' disappears as soon as you hit any key other
than return.


For the select a value type of input I want something like the above
but hitting (say) arrow up and arrow down would change the value
displayed by the 'Colour?' prompt and hitting return would accept the
chosen value. In addition I want the ability to narrow down the list
by entering one or more initial characters, so if you enter 'b' at the
Colour? prompt the list of values presented would only include colours
starting with 'b' (beige, blue, black, etc.)


Are there any packages that offer this sort of thing? I'd prefer ones
from the Debian repositories but that's not absolutely necessary.


It might also be possible/useful to use the mouse for this.
--
Chris Green
·
Stefan Ram
2025-01-11 15:13:57 UTC
Reply
Permalink
Post by Chris Green
Are there any packages that offer this sort of thing? I'd prefer ones
from the Debian repositories but that's not absolutely necessary.
So, in my humble opinion, it's a no-brainer to split this into two
camps and draw a line between Linux and Windows.

On Linux, you're going to wanna roll with curses. See [1] below.

For Windows, whip up your own terminal using tkinter (Yeah,
tkinter's a GUI library, no doubt! But in this case, it's just
being used to mimic a terminal.) as your secret sauce. See [2].

If that's too tied to specific systems for your taste,
why not cook up a library that smooths out those wrinkles?

The following drafts still contain bugs, but might convey
the idea.

[1]

import curses

def main(stdscr):
# Clear screen and hide cursor
stdscr.clear()
curses.curs_set(1)

# Display prompt
stdscr.addstr(0, 0, "Color? ")

# Pre-fill with "red"
default_text = "red"
stdscr.addstr(0, 7, default_text)

curses.echo() # Enable echo of characters
curses.cbreak() # React to keys instantly without Enter key

# Initialize cursor position
cursor_x = 7 + len(default_text)
current_text = default_text

while True:
stdscr.move(0, cursor_x)
ch = stdscr.getch()

if ch == ord('\n'): # Enter key
break
elif ch in (curses.KEY_BACKSPACE, 127): # Backspace
if cursor_x > 7:
cursor_x -= 1
current_text = current_text[:cursor_x-7] + current_text[cursor_x-6:]
stdscr.delch(0, cursor_x)
elif ch == curses.KEY_LEFT:
if cursor_x > 7:
cursor_x -= 1
elif ch == curses.KEY_RIGHT:
if cursor_x < 7 + len(current_text):
cursor_x += 1
elif 32 <= ch <= 126: # Printable characters
current_text = current_text[:cursor_x-7] + chr(ch) + current_text[cursor_x-7:]
stdscr.insch(0, cursor_x, ch)
cursor_x += 1

stdscr.refresh()

# Clear screen
stdscr.clear()

# Display result
stdscr.addstr(0, 0, f"You entered: {current_text}")
stdscr.refresh()
stdscr.getch()

if __name__ == "__main__":
curses.wrapper(main)

[2]

import tkinter as tk
from tkinter import simpledialog

class ColorQueryApp:
def __init__(self, master):
self.master = master
master.title("Color Query")

# Create the Text widget
self.text_field = tk.Text(master, height=10, width=40)
self.text_field.pack(padx=10, pady=10)

# Create a button to add new color queries
self.add_button = tk.Button(master, text="Add Color Query", command=self.add_color_query)
self.add_button.pack(pady=5)

# Bind the Return key event
self.text_field.bind('<Return>', self.show_color_dialog)

def add_color_query(self):
# Insert a new line if the Text widget is not empty
if self.text_field.index('end-1c') != '1.0':
self.text_field.insert('end', '\n')

# Insert the new color query line
query_line = "Color? red"
self.text_field.insert('end', query_line)

# Move the cursor to the end of the new line
self.text_field.mark_set('insert', f'{self.text_field.index("end-1c")}')

# Bind the key events
self.text_field.bind('<Key>', self.check_editing)

def check_editing(self, event):
# Allow all keystrokes if cursor is in editable area
if self.text_field.compare('insert', '>=', 'insert linestart+7c'):
return

# Prevent editing in the "Color? " area
if event.keysym in ['BackSpace', 'Delete'] or len(event.char) > 0:
return 'break'

def show_color_dialog(self, event):
# Get the current line
current_line = self.text_field.get("insert linestart", "insert lineend")

# Extract the color
color = current_line[7:].strip()

# Show a dialog with the entered color
simpledialog.messagebox.showinfo("Entered Color", f"You entered: {color}")

# Prevent the default newline behavior
return 'break'

root = tk.Tk()
app = ColorQueryApp(root)
root.mainloop()
Stefan Ram
2025-01-11 19:31:54 UTC
Reply
Permalink
Post by Stefan Ram
If that's too tied to specific systems for your taste,
why not cook up a library that smooths out those wrinkles?
Or, if you want to use a library instead of writing one, check out:

Textual – An async-powered terminal application framework for Python

Textual was built on top of

Rich - a terminal application renderer (not using async) and toolkit

There's also:

py_cui: library for creating CUI/TUI interfaces with pre-built widgets

PyTermGUI: terminal UI library

Python Prompt Toolkit: library for command line applications

Urwid: a console user interface library for Python
r***@nope.com
2025-01-12 02:49:39 UTC
Reply
Permalink
This is what I was going to suggest. Rich is super easy to use.
Chris Green
2025-01-12 12:03:37 UTC
Reply
Permalink
Post by r***@nope.com
This is what I was going to suggest. Rich is super easy to use.
OK, thanks, Rich is on my shortlist then.
--
Chris Green
·
dn
2025-01-12 00:49:03 UTC
Reply
Permalink
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
i.e. for use with non-GUI programs that one runs from the command
prompt in a terminal window running a bash shell or some such.
What I'm specifically after is a way to provide a default value that
can be accepted or changed easily and also a way to provide a number
of different values to choose from.
I.e. for the default sort of input one might see:-
Colour? red
Hitting return would return 'red' to the program but you could also
backspace over the 'red' and enter something else. Maybe even better
would be that the 'red' disappears as soon as you hit any key other
than return.
For the select a value type of input I want something like the above
but hitting (say) arrow up and arrow down would change the value
displayed by the 'Colour?' prompt and hitting return would accept the
chosen value. In addition I want the ability to narrow down the list
by entering one or more initial characters, so if you enter 'b' at the
Colour? prompt the list of values presented would only include colours
starting with 'b' (beige, blue, black, etc.)
Are there any packages that offer this sort of thing? I'd prefer ones
from the Debian repositories but that's not absolutely necessary.
It might also be possible/useful to use the mouse for this.
There must be more choices/combinations of packages to do this. Maybe a
good place to start is the Python Prompt Toolkit
(https://python-prompt-toolkit.readthedocs.io/en/master/)
--
Regards,
=dn
Alan Gauld
2025-01-13 22:46:19 UTC
Reply
Permalink
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
The standard package for this is curses which comes as part
of the standard library on *nix distros.
Post by Chris Green
What I'm specifically after is a way to provide a default value that
can be accepted or changed easily and also a way to provide a number
of different values to choose from.
The default value is easy. The list of values sounds like a pop-up menu.
Ofr you can just offer a prompt with a list in it that the user
picks(maybe by number?) Its really a programmers choice, curses provides
the tools to display it however you choose. (Yu might also find the
dialog third party package useful for displaying text mode dialog boxes)
Post by Chris Green
I.e. for the default sort of input one might see:-
Colour? red
Hitting return would return 'red' to the program but you could also
backspace over the 'red' and enter something else. Maybe even better
would be that the 'red' disappears as soon as you hit any key other
than return.
All of that is possible in curses, you just have to code it.
Post by Chris Green
For the select a value type of input I want something like the above
but hitting (say) arrow up and arrow down would change the value
displayed by the 'Colour?' prompt and hitting return would accept the
chosen value.
Again that's easy enough to do. Or you could pop up a menu and
allow the user to cursor up/dowmn (or use the mouse) to select
an option.
Post by Chris Green
In addition I want the ability to narrow down the list
by entering one or more initial characters, so if you enter 'b' at the
Colour? prompt the list of values presented would only include colours
starting with 'b' (beige, blue, black, etc.)
Again that's more about the programmer's use of the tools. curses
allows you to display the characters wherever/however you want
on screen.
Post by Chris Green
Are there any packages that offer this sort of thing? I'd prefer ones
from the Debian repositories but that's not absolutely necessary.
It might also be possible/useful to use the mouse for this.
curses dores all you want but you need to do all the logic, its not as
simple as using a GUI toolkit. dialog sits on top of curses and provides
a more GUI like experiece.

urwin is another toolkit for text mode but it is more of a pseudo GUI
environment with a menu-bar, dialogs, forms etc etc. Curses is about
mapping areas of the screen(windows) and displaying text within those
areas. You can control colour and strength etc, move things around,
hide/show areas, position the cursor anywhere on screen. You can even
create huge virtual screens and use the visible screen as a viewport
into that(think like a big spreadsheet)

There are a couple of Howto type documents online

Shameless Plug:
---------
I wrote a short kindle book on curses with python:
https://kdp.amazon.com/amazon-dp-action/us/dualbookshelf.marketplacelink/B091B85B77
-------------

HTH
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos
Keith Thompson
2025-01-15 00:41:51 UTC
Reply
Permalink
Post by Alan Gauld
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
The standard package for this is curses which comes as part
of the standard library on *nix distros.
The thing about curses (which may or may not be a problem) is that, by
design, it takes over the whole screen. If you want to do simpler text
manipulations (showing a dismissible message, showing bold text, etc.)
without interfering with existing text, curses can't do it, at least not
easily.

If your terminal supports it, your current screen contents can be
restored after the application finishes (curses enables the "alternate
screen" and then restores the primary screen on exit).

There don't seem to be a lot of good solutions for doing curses-like
text manipulation without taking over the entire screen.

[...]
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Alan Gauld
2025-01-17 18:11:47 UTC
Reply
Permalink
Post by Keith Thompson
Post by Alan Gauld
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
The standard package for this is curses which comes as part
of the standard library on *nix distros.
The thing about curses (which may or may not be a problem) is that, by
design, it takes over the whole screen. If you want to do simpler text
manipulations (showing a dismissible message, showing bold text, etc.)
without interfering with existing text, curses can't do it, at least not
easily.
It's not that difficult to use the terminfo codes directly. But
that won't give access to things like lists of default values, mouse
control etc that the OP wanted. But for simple text characteristics
and moving the cursor around terminfo works ok even if a bit tedious.

Here is "hello world" in bold...

import curses
curses.setupterm()
bold = curses.tigetstr('bold').decode('ascii')
normal = curses.tigetstr('sgr0').decode('ascii')

print(bold, 'Hello world', normal)

Whilst you can position the cursor etc it very quickly
becomes easier to just use full blown curses.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos
Keith Thompson
2025-01-17 19:03:16 UTC
Reply
Permalink
Post by Alan Gauld
Post by Keith Thompson
Post by Alan Gauld
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
The standard package for this is curses which comes as part
of the standard library on *nix distros.
The thing about curses (which may or may not be a problem) is that, by
design, it takes over the whole screen. If you want to do simpler text
manipulations (showing a dismissible message, showing bold text, etc.)
without interfering with existing text, curses can't do it, at least not
easily.
It's not that difficult to use the terminfo codes directly. But
that won't give access to things like lists of default values, mouse
control etc that the OP wanted. But for simple text characteristics
and moving the cursor around terminfo works ok even if a bit tedious.
Here is "hello world" in bold...
import curses
curses.setupterm()
bold = curses.tigetstr('bold').decode('ascii')
normal = curses.tigetstr('sgr0').decode('ascii')
print(bold, 'Hello world', normal)
Cool -- but I think you want:

print(bold, 'Hello world', normal, sep='')
Post by Alan Gauld
Whilst you can position the cursor etc it very quickly
becomes easier to just use full blown curses.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Mats Wichmann
2025-01-17 21:11:57 UTC
Reply
Permalink
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
I haven't followed this whole thread, but rich (low-level) and textual
(higher-level) are designed for this - part of the same project family -
and really worth a look. I know someone else mentioned rich earlier.

There's a little video on the textual homepage that shows some of what
it can do in action:

https://github.com/Textualize/textual
Grant Edwards
2025-01-17 21:09:14 UTC
Reply
Permalink
Post by Alan Gauld
Post by Keith Thompson
Post by Alan Gauld
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
The standard package for this is curses which comes as part
of the standard library on *nix distros.
The thing about curses (which may or may not be a problem) is that, by
design, it takes over the whole screen. If you want to do simpler text
manipulations (showing a dismissible message, showing bold text, etc.)
without interfering with existing text, curses can't do it, at least not
easily.
It's not that difficult to use the terminfo codes directly.
It's easy to do, but it's tricky to do it right.

https://github.com/GrantEdwards/Python-curses-and-terminfo
Post by Alan Gauld
[...]
Here is "hello world" in bold...
import curses
curses.setupterm()
bold = curses.tigetstr('bold').decode('ascii')
normal = curses.tigetstr('sgr0').decode('ascii')
print(bold, 'Hello world', normal)
Don't forget to use tparm() to parameterize strings for things like
"move curser".

Once you've parameterized the string (if needed), you've _might_ need
to worry about the stuff in the string that's meant to be interpreted
by tputs/putp:

Quoting man tparm(3)

All terminfo strings [including the output of tparm] should be
printed with tputs or putp.

That's becuase terminfo strings can contain escape sequences that are
filterd out and interpreted by tputs/putp. The approach above only
works if you only care about certain terminals, and you know that none
of the terminfo strings you're using have those interal terminfo
escape sequences in them [AFAIK, that's true for the linux console,
xterm and the like, but not for many serial terminals.]

--
Grant
Grant Edwards
2025-01-14 00:20:57 UTC
Reply
Permalink
Post by Alan Gauld
All of that is possible in curses, you just have to code it.
All of that is easy with curses in C. Unfortunately, the high level
"panel" and "menu" curses subystems that make it easy aren't included
in the Python curses API, so doing it in Pyhton is a _lot_ more work
than doing it in C.

I always like the Python API for the newt library (yonks ago it was
used for the Redhat installer), but haven't used it for years.
Alan Gauld
2025-01-14 08:56:14 UTC
Reply
Permalink
Post by Grant Edwards
Post by Alan Gauld
All of that is possible in curses, you just have to code it.
All of that is easy with curses in C. Unfortunately, the high level
"panel" and "menu" curses subystems that make it easy aren't included
in the Python curses API,
panel is included. Just import curses.panel.

menu unfortunately isn't but it's not difficult to roll
your own by creating a window with a list of options, provided
you don't try to get too fancy(submenus etc).
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos
Chris Green
2025-01-14 09:15:01 UTC
Reply
Permalink
Post by Alan Gauld
Post by Grant Edwards
Post by Alan Gauld
All of that is possible in curses, you just have to code it.
All of that is easy with curses in C. Unfortunately, the high level
"panel" and "menu" curses subystems that make it easy aren't included
in the Python curses API,
panel is included. Just import curses.panel.
menu unfortunately isn't but it's not difficult to roll
your own by creating a window with a list of options, provided
you don't try to get too fancy(submenus etc).
Yes, thanks all, maybe just straightforward curses is the way to go.
Looking at some of the 'cleverer' ones they end up looking remarkably
like GUI code, in which case I might as well use a GUI. I have written
a (fairly simple) Gtk based python program, I was just trying to avoid
all the GUI overheads for a little new project.
--
Chris Green
·
Stefan Ram
2025-01-14 11:22:15 UTC
Reply
Permalink
Post by Chris Green
Yes, thanks all, maybe just straightforward curses is the way to go.
Looking at some of the 'cleverer' ones they end up looking remarkably
like GUI code, in which case I might as well use a GUI. I have written
a (fairly simple) Gtk based python program, I was just trying to avoid
all the GUI overheads for a little new project.
The Cmd class is your go-to for whipping up those bare-bones
command line interfaces. It's hella useful for cobbling
together test rigs, admin tools, and rough drafts that'll
eventually get a facelift with some fancy UI.

Check out this sample of what Cmd code might look like:

class TurtleShell( cmd.Cmd ):
intro = 'Welcome to the turtle shell. Type help or ?.\n'
prompt = '(turtle) '
file = None

def do_forward(self, arg):
'Move the turtle forward by the specified distance: FORWARD 10'
forward(*parse(arg))
. . .
. . .
. . .

And here's a taste of what a Cmd UI could shape up to be:

Welcome to the turtle shell. Type help or ? to list commands.

(turtle) ?

Documented commands (type help <topic>):
========================================
bye color goto home playback record right
circle forward heading left position reset undo

(turtle) help forward
Move the turtle forward by the specified distance: FORWARD 10
(turtle) record spiral.cmd
(turtle) position
Current position is 0 0
. . .
Grant Edwards
2025-01-15 00:54:09 UTC
Reply
Permalink
Post by Chris Green
Yes, thanks all, maybe just straightforward curses is the way to go.
Looking at some of the 'cleverer' ones they end up looking remarkably
like GUI code, in which case I might as well use a GUI.
The source code to configure and handle a UI with a certain set of
input widgets is going to be pretty much the same regardless of the
low-level screen bashing details involved in rendering the widgets.

You choose a TUI toolkit like curses panel/menu/forms instead of a GUI
toolkit like gtk because you need your app to run on a terminal
instead of on a X11/wayland screen, not because you want your app to
be simpler than the code for a GUI app (as you've seen, it isn't).
Post by Chris Green
I have written a (fairly simple) Gtk based python program, I was
just trying to avoid all the GUI overheads for a little new project.
If you want to avoid the [TG]UI overhead, then command line options
are your friend. If that's not sophisticated enough the gnu "readline"
library with a simple command processor is a common next step.

Or you can use curses to print some help stuff at the top of the
terminal window and then do everything based on single-stroke "command
keys" that print output in the lower part of the terminal window.

--
Grant
Alan Gauld
2025-01-16 01:06:07 UTC
Reply
Permalink
Post by Grant Edwards
are your friend. If that's not sophisticated enough the gnu "readline"
library with a simple command processor is a common next step.
On that front the cmd module in Python is often overlooked
but is useful for structuring a non-GUI-like text UI.

It doesn't support mouse or screen mapping or colours etc.
But if all you want/need is a pdb type interface it works well.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos
Grant Edwards
2025-01-14 14:21:41 UTC
Reply
Permalink
Post by Alan Gauld
Post by Grant Edwards
Post by Alan Gauld
All of that is possible in curses, you just have to code it.
All of that is easy with curses in C. Unfortunately, the high level
"panel" and "menu" curses subystems that make it easy aren't included
in the Python curses API,
panel is included. Just import curses.panel.
Ah my bad, I meant "forms" and "menu" support was missing.
Post by Alan Gauld
menu unfortunately isn't but it's not difficult to roll your own by
creating a window with a list of options, provided you don't try to
get too fancy(submenus etc).
Yea, it was "menu" and "forms" that I really wanted.

--
Grant
Roel Schroeven
2025-01-16 09:09:36 UTC
Reply
Permalink
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
i.e. for use with non-GUI programs that one runs from the command
prompt in a terminal window running a bash shell or some such.
What I'm specifically after is a way to provide a default value that
can be accepted or changed easily and also a way to provide a number
of different values to choose from.
I.e. for the default sort of input one might see:-
Colour? red
Hitting return would return 'red' to the program but you could also
backspace over the 'red' and enter something else. Maybe even better
would be that the 'red' disappears as soon as you hit any key other
than return.
For the select a value type of input I want something like the above
but hitting (say) arrow up and arrow down would change the value
displayed by the 'Colour?' prompt and hitting return would accept the
chosen value. In addition I want the ability to narrow down the list
by entering one or more initial characters, so if you enter 'b' at the
Colour? prompt the list of values presented would only include colours
starting with 'b' (beige, blue, black, etc.)
Are there any packages that offer this sort of thing? I'd prefer ones
from the Debian repositories but that's not absolutely necessary.
Maybe pythondialog could be useful for this. I've never used it so I
can't really tell if it will fit your requirements, but at least it
seems worth a look. It looks like it supports text input with a default
value
(https://pythondialog.sourceforge.io/doc/widgets.html#single-line-input-fields).
The other thing you describe is, if I understand correctly, an input
with predefined values but also the ability to enter a custom value. At
first sight that doesn't seem to be provided.

PyPI page: https://pypi.org/project/pythondialog/
Home page: https://pythondialog.sourceforge.io/
--
"Let everything happen to you
Beauty and terror
Just keep going
No feeling is final"
-- Rainer Maria Rilke
Lele Gaifax
2025-01-18 08:08:04 UTC
Reply
Permalink
Post by Chris Green
I'm looking for Python packages that can help with text mode input,
i.e. for use with non-GUI programs that one runs from the command
prompt in a terminal window running a bash shell or some such.
I'd suggest giving a try to https://pypi.org/project/questionary/,
seems very close to what you are looking for and very simple to drive.

Another package is https://github.com/petereon/beaupy, that seems a good
fit as well.

ciao, lele.
--
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
***@metapensiero.it | -- Fortunato Depero, 1929.
Loading...