обнаружить нажатие клавиш в Python?

106

Я делаю программу типа секундомера на Python, и я хотел бы знать, как определить, нажата ли клавиша (например, p для паузы и s для остановки), и я не хотел бы, чтобы это было что-то вроде raw_input, которое ждет ввод пользователя перед продолжением выполнения. Кто-нибудь знает, как это сделать в цикле while?

Кроме того, я хотел бы сделать этот кроссплатформенный, но если это невозможно, то моя основная цель разработки - Linux.

лобуо
источник
для OS X stackoverflow.com/a/47197390/5638869 работает на Python 2 и 3
neoDev

Ответы:

71

Python имеет модуль клавиатуры с множеством функций. Установите его, возможно, с помощью этой команды:

pip3 install keyboard

Затем используйте его в коде, например:

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
Сообщество
источник
2
Я не уверен в Linux, но у меня он работает в Windows.
75
keyboardпо-видимому, требуется root в linux: /
Inaimathi
Я пробовал это решение, но когда я пытаюсь импортировать модуль после его установки, я получаю сообщение «ImportError: No module named 'keyboard'», поэтому это не сработало. Я заглянул в репозиторий GitHub и обнаружил связанную проблему , но это не решает проблему. Затем я попытался загрузить репо и выполнить некоторые из его примеров, но получаю сообщение «ImportError: вы должны быть пользователем root, чтобы использовать эту библиотеку в Linux», как ранее прокомментировал @Inaimathi. По-видимому, это полный модуль для управления клавиатурой с помощью Python, но требование root - большой недостаток :(
Ivanhercaz
3
«Чтобы избежать зависимости от X, части Linux читают необработанные файлы устройств (/ dev / input / input *), но для этого требуется root».
jrouquie 06
8
Я не понимаю, почему попытка: except: может быть полезной.
TypicalHog
50

Для тех, кто работает с окнами и изо всех сил пытается найти рабочий ответ, вот мой: 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()

Приведенная выше функция будет печатать, какую бы клавишу вы ни нажимали, плюс запускает действие, когда вы отпускаете клавишу «esc». Документация по клавиатуре здесь для более разнообразного использования.

Маркус фон Броуди выделил потенциальную проблему, а именно: для этого ответа не требуется, чтобы вы были в текущем окне, чтобы этот скрипт был активирован, решение для Windows:

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()
Митрек
источник
7
@ nimig18 ... и не требует рута :)
cz
1
С этим решением есть проблема (не уверен в альтернативах): не нужно нажимать клавишу внутри окна консоли, чтобы оно вступило в силу. Представьте, что у вас есть сценарий, который выполняет некоторую работу до тех пор, пока не будет нажата клавиша ESC, а затем вы нажмете ее в другой программе.
Маркус фон Броуди
1
@MarkusvonBroady Я думаю, что win32gui будет достаточно, чтобы решить эту проблему, я отредактировал свой ответ таким образом, чтобы потенциально решить его, по крайней мере, для пользователей Windows.
Митрек
@Mitrek Я пробовал это, но мой код останавливает дальнейшее выполнение и застревает здесь. Он работает как input (). У меня есть код, выполняющийся в selenium, firefox, но как только эта последовательность встречается, дальнейших действий нет.
Лакшми Нараянан
1
Должен был быть принятый ответ, поскольку он работает как в Linux, так и в Windows
Акаш Карнатак,
31

Поскольку OP упоминает raw_input - это означает, что ему нужно решение cli. Linux: curses - это то, что вам нужно (Windows PDCurses). Curses, это графический API для программного обеспечения cli, вы можете добиться большего, чем просто обнаруживать ключевые события.

Этот код будет обнаруживать клавиши до тех пор, пока не будет нажата новая строка.

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
источник
Это действительно здорово. Пришлось искать вечно, прежде чем наткнуться на это. Кажется, намного чище, чем возиться termiosи так далее ...
Хью Перкинс
5
необходимо добавить import os, чтобы иметь возможность выйти из примера.
malte
Если вы win.nodelay(False)вместо того True, он не будет генерировать миллион исключений в секунду.
Йоханнес Хофф
25

С keyboardмодулем можно сделать больше вещей .

Вот некоторые из методов:


Способ №1:

Используя функцию read_key():

import keyboard

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

Это приведет к разрыву цикла при нажатии pклавиши.


Способ №2:

Использование функции wait:

import keyboard

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

Он будет ждать, пока вы нажмете, pи продолжит вводить код по мере его нажатия.


Способ №3:

Используя функцию on_press_key:

import keyboard

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

Ему нужна функция обратного вызова. Я использовал, _потому что функция клавиатуры возвращает этой функции событие клавиатуры.

После выполнения он запустит функцию при нажатии клавиши. Вы можете остановить все перехватчики, запустив эту строку:

keyboard.unhook_all()

Метод №4:

На этот метод уже ответил user8167727, но я не согласен с кодом, который они сделали. Он будет использовать функцию, is_pressedно по-другому:

import keyboard

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

При pнажатии он разрывает петлю .


Ноты:

  • keyboard будет читать нажатия клавиш со всей ОС.
  • keyboard требует рут в Linux
Черный гром
источник
11
Самый большой НЕГАТИВ при использовании модуля клавиатуры - это требование, чтобы вы запускали его как ROOT-пользователь. Это делает модуль запрещенным в моем коде. Просто для того, чтобы узнать, была ли нажата клавиша, не требуются привилегии root. Я прочитал документ и понимаю, почему ограничение выходит в модуле, но посмотрите в другом месте, если все, что вам нужно, это опросить ключ ...
muman
Очень полезная информация, сэр! Я хотел знать, могу ли я использовать keyboard.wait()для ожидания более одной клавиши и продолжения, если одна из них будет нажата
Приткаран Сингх
@PreetkaranSingh wait()не предоставляет эту функцию. Вам придется использовать keyboard.read_key()с условием if, упакованным в цикл while. См. Метод №1
Black Thunder
Спасибо, сэр !, не хотели бы вы пролить свет на suppressиспользование ключевых слов keyboard.read_key(), когда его использовать, а когда нет ....
Приткаран Сингх
@PreetkaranSingh Я бы хотел, но у меня недостаточно информации об аргументе подавления
Black Thunder
14

Для Windows вы можете использовать msvcrtтак:

   import msvcrt
   while True:
       if msvcrt.kbhit():
           key = msvcrt.getch()
           print(key)   # just to show the result
Бенджи
источник
7
msvcrt - это модуль только для Windows.
Dunatotatos
1
На самом деле сейчас я использую pynput, это может быть лучшим ответом
Бенджи
Обратите внимание, что pynput для работы в OS X (не знаю о Linux) должен запускаться как root, чтобы работать. Для некоторых это может быть неудачным решением.
Гейб Вайс
Могу поклясться, что вопрос касался «кроссплатформенности» или «Linux» ...
Аарон Манн
10

Используйте этот код, чтобы узнать, какая клавиша нажата

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()
Маниваннан Муругавел
источник
Но вот в чем дело: я использую macOS и устанавливаю как pynput, так и клавиатуру отдельно, и программа работает без каких-либо ошибок, но может только обнаруживать (в оболочке python) специальные клавиши. Буквенно-цифровые ключи не обнаруживаются и, наоборот, считаются так, как если бы я писал код в оболочке. Вы знаете, в чем может быть проблема?
Дарио Дениз Эргюн
Тот же код работал у меня в оболочке. Пожалуйста, проверь это. В пакете клавиатуры этот код не требуется.
Manivannan Murugavel
1
Это способ пойти в Linux, поскольку библиотека клавиатуры требует root.
Дэвид
1
Это решение обнаружит все нажатия клавиш; также те, которые происходят в другом окне терминала. К сожалению, это сильно ограничивает возможные варианты использования.
Serge
6

Используйте PyGame, чтобы получить окно, а затем вы можете получить ключевые события.

Для письма 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()
Эй Джей Уппал
источник
2

Итак, я сделал эту ... своего рода игру ... на основе этого сообщения (используя библиотеку msvcr и Python 3.7).

Ниже приводится «основная функция» игры - обнаружение нажатых клавиш:

# 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

Если вам нужен полный исходный код программы, вы можете увидеть его или скачать отсюда:

Игра с секретным ключом (GitHub)

(примечание: секретное нажатие клавиши: Ctrl+ F12)

Я надеюсь, что вы можете послужить примером и помочь тем, кто пришел ознакомиться с этой информацией.

Ферд
источник
1
key = cv2.waitKey(1)

Это из пакета openCV. Он обнаруживает нажатие клавиши без ожидания.

Eyllanesc
источник