Come faccio a far aspettare Python per un tasto premuto?


571

Voglio che il mio script attenda fino a quando l'utente preme un tasto qualsiasi.

Come lo faccio?

Risposte:


543

In Python 3 usare input():

input("Press Enter to continue...")

In Python 2 usa raw_input():

raw_input("Press Enter to continue...")

Ciò attende solo che l'utente prema Invio.

Si consiglia di utilizzare msvcrt ((solo Windows / DOS) Il modulo msvcrt consente di accedere a una serie di funzioni nella libreria Microsoft Visual C / C ++ Runtime (MSVCRT)):

import msvcrt as m
def wait():
    m.getch()

Questo dovrebbe attendere la pressione di un tasto.

Informazioni addizionali:

in Python 3 raw_input()non esiste

In Python 2 input(prompt)è equivalente aeval(raw_input(prompt))


54
Ricevo questo errore quando provo a farlo in Python 2.7: "SintassiError: imprevisto EOF durante l'analisi"
Jon Tirsen,

8
@ Solarsaturn9 e un numero crescente e grande no. Quindi questa risposta non ha funzionato per me e per le molte altre che vengono qui.
ctrl-alt-delor,

5
@richard utilizzando input () dovrebbe funzionare anche su altre piattaforme. È ridicolo attraccare punti per fornire una soluzione alternativa solo per Windows quando la prima soluzione è multipiattaforma.
Cory Buckley

7
@ Solarsaturn9 leggi la domanda e rispondi: inputnon continua se si preme un tasto, solo se si preme invio .
ctrl-alt-delor

13
@JonTirsen è perché Python 2.7 ha una funzione chiamata input che valuta la stringa inserita. Per risolvere, usa raw_input
Samy Bencherif il

316

Un modo per farlo in Python 2 è usare raw_input():

raw_input("Press Enter to continue...")

In python3 è giusto input()


17
Che dire di quando può essere una delle diverse chiavi? Non solo enter?
Noio,

33
Con Python 3+ , questo è cambiato in solo input().
Palswim,

Usando sei per il codice compatibile Py2 e Py3:from six.moves import input; input("Press Enter to continue...")
rcoup

56

Sulla mia scatola di Linux, uso il seguente codice. Questo è simile al codice che ho visto altrove (ad esempio nelle vecchie FAQ di Python) ma quel codice gira in un circuito stretto dove questo codice non lo fa e ci sono molti casi angolari strani che il codice non tiene conto di ciò il codice lo fa.

def read_single_keypress():
    """Waits for a single keypress on stdin.

    This is a silly function to call if you need to do it a lot because it has
    to store stdin's current setup, setup stdin for reading single keystrokes
    then read the single keystroke then revert stdin back after reading the
    keystroke.

    Returns a tuple of characters of the key that was pressed - on Linux, 
    pressing keys like up arrow results in a sequence of characters. Returns 
    ('\x03',) on KeyboardInterrupt which can happen when a signal gets
    handled.

    """
    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    # save old state
    flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
    attrs_save = termios.tcgetattr(fd)
    # make raw - the way to do this comes from the termios(3) man page.
    attrs = list(attrs_save) # copy the stored version to update
    # iflag
    attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                  | termios.ISTRIP | termios.INLCR | termios. IGNCR
                  | termios.ICRNL | termios.IXON )
    # oflag
    attrs[1] &= ~termios.OPOST
    # cflag
    attrs[2] &= ~(termios.CSIZE | termios. PARENB)
    attrs[2] |= termios.CS8
    # lflag
    attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                  | termios.ISIG | termios.IEXTEN)
    termios.tcsetattr(fd, termios.TCSANOW, attrs)
    # turn off non-blocking
    fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
    # read a single keystroke
    ret = []
    try:
        ret.append(sys.stdin.read(1)) # returns a single character
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save | os.O_NONBLOCK)
        c = sys.stdin.read(1) # returns a single character
        while len(c) > 0:
            ret.append(c)
            c = sys.stdin.read(1)
    except KeyboardInterrupt:
        ret.append('\x03')
    finally:
        # restore old state
        termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
    return tuple(ret)

Mentre questa è la mia preferita delle risposte qui, come le altre non capisce cose come spostamento, controllo, ecc.
Mala

1
@Mala che praticamente non è possibile in Python puro; forse dovresti scrivere un modulo C?
gatto

Ricevo "\ x03" sull'interruzione da tastiera (Ctrl-C) sul mio sistema.
RDT

1
ctrl-c è un ASCII 3 quindi è previsto. Se si desidera generare un segnale su ctrl-c, la soluzione semplice è inserire un if ord (return_value) == 3: os.kill (os.getpid (), signal.SIGINT) ma è anche possibile disattivare l'elaborazione del segnale by attrs [0] | = termios.BRKINT, attrs [3]! = termios.ISIG e elimina l'elaborazione tranne KeyboardInterrupt. Nota: ho modificato il valore restituito per KeyboardInterrupt in '\ x03' in onore della tua query (e poiché ciò rende questo codice restituisce sempre una stringa).
mheyman,

Come si può adattare il codice sopra riportato in modo che restituisca una tupla per una pressione di un tasto complesso come "Pagina su" o "Freccia sinistra"?
Derek,

33

Se si è d'accordo con i comandi di sistema, è possibile utilizzare quanto segue:

Linux:

import os
os.system('read -sn 1 -p "Press any key to continue..."')
print

Finestre:

import os
os.system("pause")

Se vuoi continuare a correre fino a quando non viene generato un segnale (come SIGINT), puoi anche controllare il valore di ritorno da systeme quindi chiamare sys.exit(0).
James Taylor,

29

Semplicemente usando

input("Press Enter to continue...")

causerà un SyntaxError: EOF previsto durante l'analisi.

Utilizzo semplice della correzione:

try:
    input("Press enter to continue")
except SyntaxError:
    pass

5
Non usare inputin Python 2: la funzione corretta è raw_input. In Python 2, inputè equivalente a eval(raw_input()).
Blorgbeard esce il

2
Questo ignora tutti i tasti che l'utente preme, fino a quando non preme invio, il che è abbastanza diverso da quello che l'OP sta chiedendo.
Jonathan Hartley,

1
Inoltre, se avessi intenzione di usare 'input', catturare un SyntaxError non è appropriato. Qualunque sia il tipo di utente che viene valutato, quindi se, ad esempio, digitano "1/0", viene generato un ZeroDivisionError anziché un SyntaxError e il programma verrà chiuso.
Jonathan Hartley,

Come accennato da @Blorgbeard, basterà semplicemente usare raw_input ("Premi Invio per continuare ..."). Lo uso spesso ora durante il debug.
alltrue il

15

Il manuale di Python fornisce quanto segue:

import termios, fcntl, sys, os
fd = sys.stdin.fileno()

oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)

oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

try:
    while 1:
        try:
            c = sys.stdin.read(1)
            print "Got character", repr(c)
        except IOError: pass
finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

che può essere inserito nel tuo caso d'uso.


12
È buona norma copiare la cosa a cui stai collegando in modo che la conoscenza rimanga, anche se il collegamento muore (e lo fanno!).
Richard,

1
Come posso farlo funzionare in Python 3.x? In 3.x, dopo aver modificato l'istruzione print in modo che sia compatibile, questo si sposta all'infinito e non attende l'input. Funziona benissimo in Python 2, però.
gatto

Il collegamento è stato aggiornato per reindirizzare a un'altra pagina. Il nuovo link è qui.
Matthias,

15

Cross Platform, codice Python 2/3:

# import sys, os

def wait_key():
    ''' Wait for a key press on the console and return it. '''
    result = None
    if os.name == 'nt':
        import msvcrt
        result = msvcrt.getch()
    else:
        import termios
        fd = sys.stdin.fileno()

        oldterm = termios.tcgetattr(fd)
        newattr = termios.tcgetattr(fd)
        newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
        termios.tcsetattr(fd, termios.TCSANOW, newattr)

        try:
            result = sys.stdin.read(1)
        except IOError:
            pass
        finally:
            termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)

    return result

Ho rimosso la roba fctl / non bloccante perché stava dando IOError se non ne avevo bisogno. Sto usando questo codice specificamente perché voglio che si blocchi. ;)

Addendum:

Ho implementato questo in un pacchetto su PyPI con molti altri gadget chiamati console :

>>> from console.utils import wait_key

>>> wait_key()
'h'

1
Ho ricevuto un errore: ioctl inappropriato per il dispositivo "
Benoit

@Benoit quale sistema operativo?
Gringo Suave,

Linux - Ubuntu 20.04
Benoit

14

Non conosco un modo indipendente dalla piattaforma per farlo, ma in Windows, se usi il modulo msvcrt, puoi usare la sua funzione getch:

import msvcrt
c = msvcrt.getch()
print 'you entered', c

mscvcrt include anche la funzione kbhit () non bloccante per vedere se un tasto è stato premuto senza attendere (non sono sicuro che ci sia una funzione curses corrispondente). Sotto UNIX, c'è il pacchetto curses, ma non sono sicuro se puoi usarlo senza usarlo per tutto l'output dello schermo. Questo codice funziona sotto UNIX:

import curses
stdscr = curses.initscr()
c = stdscr.getch()
print 'you entered', chr(c)
curses.endwin()

Nota che curses.getch () restituisce l'ordinale del tasto premuto in modo da avere lo stesso output che ho dovuto lanciare.


L'uso delle maledizioni è molto più bello degli esempi piuttosto contorti descritti nel manuale, anche se comporta un'enorme dipendenza. +1
Damian,

4

Se si desidera attendere l'invio (quindi l'utente che bussa alla tastiera non provoca un evento indesiderato) utilizzare

sys.stdin.readline()

2
L'intero punto è che l'utente non deve premere solo il tasto Invio, ad esempio per poter semplicemente schiaffeggiare la barra spaziatrice. Se hai bisogno di Enter per evitare che accada qualcosa di indesiderato, allora è un cattivo design.
Synetech,

3

Sono nuovo di Python e stavo già pensando di essere troppo stupido per riprodurre i suggerimenti più semplici fatti qui. Si scopre, c'è una trappola che uno dovrebbe sapere:

Quando uno script Python viene eseguito da IDLE, alcuni comandi IO sembrano comportarsi in modo completamente diverso (poiché in realtà non esiste una finestra terminale).

Per esempio. msvcrt.getch non è bloccante e restituisce sempre $ ff. Questo è già stato segnalato molto tempo fa (vedi ad esempio https://bugs.python.org/issue9290 ) - ed è contrassegnato come risolto, in qualche modo il problema sembra persistere nelle attuali versioni di python / IDLE.

Quindi, se uno qualsiasi dei codici pubblicati sopra non funziona per te, prova a eseguire lo script manualmente e NON da IDLE .


0

Se vuoi vedere se hanno premuto un tasto esatto (come dire 'b') Fai questo:

while True:
    choice = raw_input("> ")

    if choice == 'b' :
        print "You win"
        input("yay")
        break

8
Ciò richiede che l'utente digiti 'b' (o qualcos'altro) quindi premi invio, che è abbastanza diverso da quello che l'OP chiede.
Jonathan Hartley,

0

os.system sembra sempre invocare sh, che non riconosce le opzioni s e n per la lettura. Tuttavia, il comando read può essere passato a bash:

 os.system("""bash -c 'read -s -n 1 -p "Press any key to continue..."'""")

2
La documentazione di lettura mi fa pensare che non andrà in timeout a meno che non specifichi l'opzione -t.
James King,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.