detectar pressionamento de tecla em python?

103

Estou fazendo um programa do tipo cronômetro em python e gostaria de saber como detectar se uma tecla é pressionada (como p para pausar es para parar), e não gostaria que fosse algo como raw_input que espera pelo entrada do usuário antes de continuar a execução. Alguém sabe fazer isso em um loop while?

Além disso, gostaria de fazer esta plataforma cruzada, mas se isso não for possível, meu principal alvo de desenvolvimento é o Linux

lobuo
fonte
para OS X stackoverflow.com/a/47197390/5638869 funciona em Python 2 e 3
neoDev

Respostas:

69

Python possui um módulo de teclado com muitos recursos. Instale-o, talvez com este comando:

pip3 install keyboard

Em seguida, use-o em códigos como:

import keyboard  # using module keyboard
while True:  # making a loop
    try:  # used try so that if user pressed other than the given key error will not be shown
        if keyboard.is_pressed('q'):  # if key 'q' is pressed 
            print('You Pressed A Key!')
            break  # finishing the loop
    except:
        break  # if user pressed a key other than the given key the loop will break
Comunidade
fonte
1
Não tenho certeza para Linux, mas funciona no Windows para mim.
71
keyboardaparentemente requer raiz no linux: /
Inaimathi
Tentei esta solução, mas quando tento importar o módulo após instalá-lo, recebo um "ImportError: Nenhum módulo chamado 'teclado'", então não funcionou. Eu verifiquei o repositório do GitHub e encontrei um problema relacionado , mas isso não me resolve o problema. Em seguida, tentei baixar o repo e executar alguns de seus exemplos, mas recebo e "ImportError: Você deve estar root para usar esta biblioteca no linux", como @Inaimathi comentou antes. Aparentemente, parece um módulo completo para gerenciar teclado com Python, mas o requisito de root é uma grande falta :(
Ivanhercaz
3
"Para evitar depender do X, as partes do Linux lêem arquivos de dispositivos brutos (/ dev / input / input *), mas isso requer root."
jrouquie
7
Não vejo por que a tentativa: exceto: seria útil.
TypicalHog
49

Para aqueles que estão no Windows e estavam lutando para encontrar uma resposta que funcionasse, aqui está a minha: pynput

from pynput.keyboard import Key, Listener

def on_press(key):
    print('{0} pressed'.format(
        key))

def on_release(key):
    print('{0} release'.format(
        key))
    if key == Key.esc:
        # Stop listener
        return False

# Collect events until released
with Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()

A função acima imprimirá qualquer tecla que você estiver pressionando e iniciará uma ação quando você soltar a tecla 'esc'. A documentação do teclado está aqui para um uso mais variado.

Markus von Broady destacou um problema potencial que é: Esta resposta não exige que você esteja na janela atual para que este script seja ativado, uma solução para o Windows seria:

from win32gui import GetWindowText, GetForegroundWindow
current_window = (GetWindowText(GetForegroundWindow()))
desired_window_name = "Stopwatch" #Whatever the name of your window should be

#Infinite loops are dangerous.
while True: #Don't rely on this line of code too much and make sure to adapt this to your project.
    if current_window == desired_window_name:

        with Listener(
            on_press=on_press,
            on_release=on_release) as listener:
            listener.join()
Mitrek
fonte
7
@ nimig18 ... e não requer root :)
cz
1
Há um problema com esta solução (não tenho certeza sobre as alternativas): a tecla não precisa ser pressionada dentro de uma janela de console para que tenha efeito. Imagine ter um script que faz algum trabalho até que ESC seja pressionado, mas então você o pressiona em outro programa.
Markus von Broady
1
@MarkusvonBroady Eu acho que win32gui seria o suficiente para resolver isso, eu editei minha resposta de uma forma que potencialmente resolveria para os usuários do Windows, pelo menos.
Mitrek
@Mitrek Eu tentei isso, mas meu código interrompe a execução e está preso aqui. Funciona como input (). Eu tenho o código em execução no selênio, firefox, mas assim que essa sequência é encontrada, não há mais nenhuma ação.
Lakshmi Narayanan
1
Deveria ter sido a resposta aceita, pois funciona tanto no Linux quanto no Windows
Akash Karnatak
31

Como OP menciona sobre raw_input - isso significa que ele quer a solução cli. Linux: curses é o que você quer (windows PDCurses). Curses, é uma API gráfica para software CLI, você pode conseguir mais do que apenas detectar eventos-chave.

Este código detectará as chaves até que uma nova linha seja pressionada.

import curses
import os

def main(win):
    win.nodelay(True)
    key=""
    win.clear()                
    win.addstr("Detected key:")
    while 1:          
        try:                 
           key = win.getkey()         
           win.clear()                
           win.addstr("Detected key:")
           win.addstr(str(key)) 
           if key == os.linesep:
              break           
        except Exception as e:
           # No input   
           pass         

curses.wrapper(main)
Abc Xyz
fonte
Isso é muito bom. Tive que procurar para sempre antes de encontrá-lo. Parece muito mais limpo do que hackear termiose assim por diante ...
Hugh Perkins
5
necessário adicionar import ospara poder sair saia do exemplo.
malte
Se você fizer isso win.nodelay(False), em vez de True, não irá gerar um milhão de exceções por segundo.
Johannes Hoff
24

Existem mais coisas que podem ser feitas com o keyboardmódulo.

Aqui estão alguns dos métodos:


Método 1:

Usando a função read_key():

import keyboard

while True:
    if keyboard.read_key() == "p":
        print("You pressed p")
        break

Isso vai quebrar o loop quando a tecla pfor pressionada.


Método 2:

Usando a função wait:

import keyboard

keyboard.wait("p")
print("You pressed p")

Ele esperará que você pressione pe continue o código conforme é pressionado.


Método # 3:

Usando a função on_press_key:

import keyboard

keyboard.on_press_key("p", lambda _:print("You pressed p"))

Ele precisa de uma função de retorno de chamada. Usei _porque a função do teclado retorna o evento do teclado para essa função.

Uma vez executado, ele executará a função quando a tecla for pressionada. Você pode parar todos os ganchos executando esta linha:

keyboard.unhook_all()

Método # 4:

Este método já foi respondido pelo usuário8167727, mas eu discordo do código que eles fizeram. Ele estará usando a função, is_pressedmas de outra forma:

import keyboard

while True:
    if keyboard.is_pressed("p"):
        print("You pressed p")
        break

Isso interromperá o loop quando pfor pressionado.


Notas:

  • keyboard lerá as teclas pressionadas de todo o sistema operacional.
  • keyboard requer root no linux
Trovão preto
fonte
11
O maior NEGATIVO de usar o módulo de teclado é a exigência de que você execute como usuário ROOT. Isso torna o módulo proibido em meu código. Apenas pesquisar se uma tecla foi pressionada não requer privilégios de root. Eu li o documento e entendo por que a limitação termina no módulo, mas procure outro lugar se tudo o que você precisa é pesquisar uma chave ...
muman
Informação muito útil compartilhada, senhor! Queria saber se posso keyboard.wait()esperar mais de uma tecla e continuar se alguma delas for pressionada
Preetkaran Singh
@PreetkaranSingh wait()não oferece essa funcionalidade. Você terá que usar keyboard.read_key()com uma condição if compactada em um loop while. Veja o método # 1
Black Thunder
Obrigado, senhor !, gostaria de esclarecer o suppressuso de palavras - chave em keyboard.read_key(), quando usá-las e quando não ...
Preetkaran Singh
@PreetkaranSingh eu faria, mas não tenho informações suficientes sobre o argumento de supressão
Black Thunder
13

Para Windows, você pode usar msvcrtassim:

   import msvcrt
   while True:
       if msvcrt.kbhit():
           key = msvcrt.getch()
           print(key)   # just to show the result
Benjie
fonte
7
msvcrt é um módulo somente para Windows.
Dunatotatos
1
Na verdade, eu uso o pynput agora, que pode ser uma resposta melhor
Benjie
Observe que o pynput para funcionar no OS X (não sei sobre o Linux) deve ser executado como root para funcionar. Isso pode ser um fracasso para algumas pessoas.
Gabe Weiss
Eu poderia jurar que a pergunta era para 'plataforma cruzada' ou 'linux' ...
Aaron Mann
10

Use este código para encontrar a tecla pressionada

from pynput import keyboard

def on_press(key):
    try:
        print('alphanumeric key {0} pressed'.format(
            key.char))
    except AttributeError:
        print('special key {0} pressed'.format(
            key))

def on_release(key):
    print('{0} released'.format(
        key))
    if key == keyboard.Key.esc:
        # Stop listener
        return False

# Collect events until released
with keyboard.Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()
Manivannan Murugavel
fonte
Mas aqui está a coisa, estou usando macOS e instalei o pynput e o teclado separadamente, e o programa é executado sem erros, mas só pode detectar (no shell do python) chaves especiais. As chaves alfanuméricas não são detectadas e, pelo contrário, são consideradas como se eu estivesse escrevendo um código no shell. Você sabe qual pode ser o problema?
Dario Deniz Ergün
O mesmo código funcionou para mim no shell. Por favor verifique isto. O pacote do teclado não precisa desse código.
Manivannan Murugavel
1
Este é o caminho a seguir no Linux, já que a biblioteca de teclado precisa de root.
David
1
Esta solução detectará todos os pressionamentos de tecla; também aqueles que acontecem em uma janela de terminal diferente. Infelizmente, isso limita severamente seus possíveis casos de uso.
Serge Stroobandt
6

Use PyGame para ter uma janela e então você pode obter os principais eventos.

Para a carta p:

import pygame, sys
import pygame.locals

pygame.init()
BLACK = (0,0,0)
WIDTH = 1280
HEIGHT = 1024
windowSurface = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)

windowSurface.fill(BLACK)

while True:
    for event in pygame.event.get():
        if event.key == pygame.K_p: # replace the 'p' to whatever key you wanted to be pressed
             pass #Do what you want to here
        if event.type == pygame.locals.QUIT:
             pygame.quit()
             sys.exit()
AJ Uppal
fonte
2

Então eu fiz esse .. tipo de jogo .. baseado neste post (usando a biblioteca msvcr e Python 3.7).

A seguir está a "função principal" do jogo, que é detectar as teclas pressionadas:

# Requiered libraries - - - -
import msvcrt
# - - - - - - - - - - - - - -


def _secret_key(self):
    # Get the key pressed by the user and check if he/she wins.

    bk = chr(10) + "-"*25 + chr(10)

    while True:

        print(bk + "Press any key(s)" + bk)
        #asks the user to type any key(s)

        kp = str(msvcrt.getch()).replace("b'", "").replace("'", "")
        # Store key's value.

        if r'\xe0' in kp:
            kp += str(msvcrt.getch()).replace("b'", "").replace("'", "")
            # Refactor the variable in case of multi press.

        if kp == r'\xe0\x8a':
            # If user pressed the secret key, the game ends.
            # \x8a is CTRL+F12, that's the secret key.

            print(bk + "CONGRATULATIONS YOU PRESSED THE SECRET KEYS!\a" + bk)
            print("Press any key to exit the game")
            msvcrt.getch()
            break
        else:
            print("    You pressed:'", kp + "', that's not the secret key(s)\n")
            if self.select_continue() == "n":
                if self.secondary_options():
                    self._main_menu()
                break

Se você quiser o código-fonte completo do porgrama, você pode vê-lo ou baixá-lo aqui:

O jogo da chave secreta (GitHub)

(nota: a tecla secreta é: Ctrl+F12 )

Espero que possam servir de exemplo e ajudar a quem vier consultar esta informação.

Ferd
fonte
1
key = cv2.waitKey(1)

Isso é do pacote openCV. Ele detecta um pressionamento de tecla sem esperar.

Eyllanesc
fonte