3

If I ask for user input, Python will let me use the arrow keys to move around what I typed and change it if necessary. For instance, when running the following program

user_input = input("file name -> ")
print(user_input)

I can use the left arrow to go back, change the 'G' to 'g'

file name -> thisIsAVeryLonGFileName.txt

and when I then hit Enter it prints

thisIsAVeryLongFileName.txt

Is there a way to prompt for user input with a default response already provided which then enables the user to use the arrow keys to modify the default response rather than having to type the whole response in? Basically, things should work just like above but without the user having to type anything in initially (instead it is provided by the program).

rainerpm
  • 343
  • 3
  • 7
  • Does this answer your question? [How can I edit an inputed text in python](https://stackoverflow.com/questions/58673807/how-can-i-edit-an-inputed-text-in-python) – Pranav Hosangadi Aug 10 '21 at 15:12
  • python's `input` uses the terminal's text entry, so on windows it's cmd responsible for that behavior, and on MacOS it's terminal. (bash or other on linux). This means python cannot customize how this works. The closest thing you could do would be using the `curses` library to take more full control over the terminal window – Aaron Aug 10 '21 at 15:13
  • As the answer in @PranavHosangadi mentions, for this sort of more advanced functionality (yes this would be considered advanced compared to typical terminal applications) it is much more common to write a simple GUI. – Aaron Aug 10 '21 at 15:15
  • There are various packages that offer a good user experience, e.g. [PyInputPlus](https://pypi.org/project/PyInputPlus/). – jarmod Aug 10 '21 at 15:17
  • You can also use the `readline` library. – Barmar Aug 10 '21 at 15:19
  • I am on Windows 10 and it errors when I try to install the readline module saying "this module is not meant to work on Window". Bummer. Looks like that would be the way to go. – rainerpm Aug 10 '21 at 15:47
  • Just figured out a pretty good workaround by using pyperclip to provide the default value in the clipboard, so the user can at least paste it in. `wrong_file_name = "aVeryLonGFileName.txt"` `pyperclip.copy(wrong_file_name)` – rainerpm Aug 10 '21 at 15:54

2 Answers2

0

Rather than a pre-filled input (which would probably require some level of GUI/a lot of boilerplate), why not just have a default value built in to your program and give the user the option to choose that default?

For example, users used to command-line interfaces would likely understand that a blank input here would use the default filename:

user_input = input("file name (default: aVeryLongFilename.txt) -> ")
if user_input == "":
    user_input = "aVeryLongFilename.txt"

Or, if your users need more training wheels:

default_choice= input("use default filename, aVeryLongFilename.txt? (y/n) -> ")
if default_choice == "y":
    user_input = "aVeryLongFilename.txt"
else:
    user_input = input("file name -> ")

Obviously you'd want to validate/loop around the y/n input in the second example, but I think this is a nice middle ground between having a default and commiting yourself to using a GUI.

Edit:

In the case where you might not know what a reasonable default is until the user has run the program once, you could try something like this (inspired by hlzl's comment below), using the first suggestion above as a model:

if first_run:
    user_input = input("file name -> ")
else:
    reasonable_default = str(load_previous_run_file())
    user_input = input("file name (previous filename: " + reasonable_default + ") -> ")
    if user_input == "":
        user_input = reasonable_default
Luigi
  • 4,129
  • 6
  • 37
  • 57
  • That would work if the default was a known thing. In my case the user has to enter a filename (that can vary) but may make a typo so it's not quite in the right format. – rainerpm Aug 10 '21 at 15:48
  • Fair point, although in that case I'm not sure how you'd prompt the user with a default response in any context (GUI or command-line) if you don't know it ahead-of-time – Luigi Aug 10 '21 at 15:51
  • 2
    In that case, I would suggest to get the filename from a first user input and then use the option that @Luigi proposed to display the input again and ask for confirmation. – hlzl Aug 10 '21 at 15:53
  • 1
    good idea @hlzl, I incorporated that idea into an edit – Luigi Aug 10 '21 at 16:18
  • The problem is that I am trying to solve is that the user types in some input, but make a mistake. I would like my program to then prompt them to try again, but provide their previous input as a starting point so they don't have to retype everything. – rainerpm Aug 10 '21 at 20:03
0

This works well on Windows (requires pynput):

from pynput.keyboard import Key, Controller
keyboard = Controller()

def inputDefs(*args):
    if not args: args = ('','')
    prompt, default = str(args[0]),str(args[1])
    sys.stdout.write(prompt)
    sys.stdout.flush()
    for char in default: keyboard.press(char)
    return sys.stdin.readline().strip()

The first argument specifies the immutable text to display (like the argument of input), while the second argument specifies the modifiable default text. On Mac/Linux, there's the issue of arrow keys returning escape codes, so you can't use them to navigate to the middle of the default string, but you can still use backspace to delete characters and edit it.

Edit: A solution for Mac:

def editable_input(prompt, prefill=None):
    def hook():
        readline.insert_text(prefill)
        readline.redisplay()
    readline.set_pre_input_hook(hook)
    result = input(prompt + ': ')
    readline.set_pre_input_hook()
    return result 

adapted from this script

In order for this to work, use gnureadline:

import gnureadline as readline