0

I am writing a text based game with Python and the library keyboard. At one point of my code I had to gather a number which I got by following code:

if menu == "test":
    try:
        x = int(input("Enter a Number: "))
    except ValueError:
        print("Please input integer only...")

But to set menu to "test" I had to type 2 1 1 by using keyboard. My problem is that although menu was not "test" the input was already gathered. When running the code I get following:

Enter a Number: 211

I could easily delete the 211 in the command prompt and then type some stuff but I want it to delete itself automatically so that I get:

Enter a Number:

What I already tried:

input().clear()

input(None)

x = 0

#import os   --at the beginning of the code

os.system("clear")
martineau
  • 119,623
  • 25
  • 170
  • 301
  • What you want to do is a terminal function, not a keyboard function — so the fact that you're using that third-party `keyboard` module isn't relevant. You might be able to do what needed by using some ANSI escape sequence if the terminal supports them (or you also have something like the [`colorama`](https://pypi.org/project/colorama/) module installed). – martineau Nov 07 '20 at 12:43
  • Do you want to show the numbers at all before they get deleted? – Niko Föhr Nov 07 '20 at 13:11

4 Answers4

0

If you want to avoid having the output shown in the terminal at all, you can use the following on MacOS and Linux:

import sys
import tty
import termios

def hidden_input(*args):
    stdin = sys.stdin.fileno()
    tattr = termios.tcgetattr(stdin)
    try:
        tty.setcbreak(stdin, termios.TCSANOW)
        return input(*args)
    finally:
        termios.tcsetattr(stdin, termios.TCSANOW, tattr)

This works by setting the TTY (99% of the time a terminal emulator) to cbreak mode. Which does the following:

The traditional 'raw' mode is the easier one to explain; it does no in-kernel processing of input or output at all. All characters are returned immediately when typed and all output is produced as-is. The traditional 'cbreak' mode is used for things like password entry; it returns characters immediately as they're typed, doesn't echo characters, and doesn't do any in-kernel line editing (which mostly means that your program can actually see the various editing characters). At a high level, there are two major differences between 'cbreak' and 'raw'. First, cbreak leaves output handling unchanged, which may be relatively 'cooked'. Second, cbreak still allows you to interrupt the program with ^C, suspend it, and so on. You can see this in action with most programs (such as passwd, su, or sudo) that ask for a password; you can immediately interrupt them with ^C in a way that, eg, vi does not respond to.

The low-level settings for cbreak are:

  • disable ECHO; this stops typed characters from being echoed.
  • disable ICANON; this turns off line editing. set VMIN to 1 and VTIME to 0; this makes it so that a read() returns immediately once there's (at least) one character available.

Note that because readline is still in use, you will still be able to edit lines as they are typed like normal.

On Windows, I am not sure how to do this easily.

if you want to clear the input that was typed after the user presses enter, that's a little harder, as it requires partially clearing already-printed output, which needs to use special control codes.

One possible easier solution for Windows: the following will clear the whole terminal on MacOS and Linux, but will also work in Windows Terminal (not the same as the default cmd.exe!)

print('\x1b[2J')

You could also try to get this to work with the the ordinary Windows terminal via the colorama package, as suggested in martineau's comment.

Lytigas
  • 904
  • 1
  • 7
  • 16
  • Ok, thanks I will try it out soon and say wheter it helped or not. – Marmite 64 Nov 07 '20 at 13:51
  • Even if its not solving my problem (or I dont understand what you wanted to say) the syntax: **print('\x1b[2J')** is also very helpful fot other things. – Marmite 64 Nov 07 '20 at 16:17
0

I think my goal was a bit unclear so I also write the code that comes before the already shown part (but simplified).

run = True
while run
   
    menu = "menu1"
    press1 = keyboard.is_pressed("1")
    press2 = keyboard.is_pressed("2")

    if menu == "menu1":
        #stuff
        if press2:                    # 2 is pressed
            menu = "menu2"

    if menu == "menu2":
        #stuff
        if press1:                    # 1 is pressed
            menu = "menu3"

    if menu == "menu3":
        #stuff
        if press1:                    # 1 is pressed
            menu = "menu4"

                                     
                                      # summed up you press 2 1 1 to get menu to "menu4"

    #now the code that was already in the question

    if menu == "menu4":
        try:
            x = int(input("Enter a Number: "))
        except ValueError:
            print("Please input integer only...")

The input in menu4 remembers all the things I typed before (211) BUT I dont want it, the input is already 211 and not nothing, because I typed 211 but I want it to be nothing when it comes to menu 4 it is not my attempt to know what i typed before.

When I run the code that is like I want it to be this should happen:

# menu = "menu1"
# programm does sth and waits for input
# user presses "2"
# menu = "menu2"
# programm does sth and waits for input
# user presses "1"
# menu = "menu3"
# programm does sth and waits for input
# user presses "1"
# menu = "menu4" 

# programm prints the following:

# Enter a Number:   <--here is a EMPTY space where you can write sth.

# Please input integer only...           #(eventually)

this should NOT happen:

# programm prints the following:

# Enter a Number: 211   <--here it is NOT EMPTY

# Please input integer only...           #(eventually)
0

Any documentation I found about the keyboard module doesn't say anything about it or how to do input properly, but I found a workaround (The number of lines depends on how many characters there are, but adding more than you did has no negative effects):

for back_i in range(3):
    keyboard.press_and_release("\b")
x = int(input("Enter a Number: "))

You can only put on character at a time in there, so you have to call it more than once (such as in a loop).

Working example:

PythonHexEditor uses the keyboard module. My fork adds a quit option which caused the same issue as yours so I found your question. The fix worked for me at my fork in the following commit (I only needed one backspace not 3 since my fork of the menu gets to the input after pressing 'q' once): github.com/poikilos/PythonHexEditor commit f223cda

  • The fix is initially in my compatibility-with-shells branch.

@marmite-64 You've put part of your question in an answer and it is confusing. Please remove that and edit your question instead.

Poikilos
  • 1,050
  • 8
  • 11
0

i had the exact same problem but i found a work around its not perfect but it works

keyboard.press_and_release('enter')
input()
os.system('cls')
# print out the menu here
input('what you want to input')
moofie
  • 13
  • 3