Как перехватить нажатие клавиш в (и только в) окне терминала программы Python? - PullRequest
2 голосов
/ 08 июля 2019

Я пишу простой скрипт на Python 3 и хочу иметь возможность перехватывать нажатия клавиш внутри окна терминала, чтобы что-то делать, например, в зависимости от возвращаемого значения.
Я также хочу кроссплатформенное решение.

Я хотел бы воспроизвести что-то вроде этого:

import msvcrt
key = ord(msvcrt.getch()) # Wait for a key to be pressed.
if key == 27: # The ESC key
    print("You have pressed the ESC key!")

Но msvcrt - это модуль для Windows согласно Документам Python (и моим тестам):

Эти функции обеспечивают доступ к некоторым полезным функциям на платформах Windows.

Я нашел модуль keyboard, который довольно прост в использовании (и больше кроссплатформенный), но мне не удалось «поймать» только ключи нажата внутри окна терминала.

Например:

import keyboard as kb
key = kb.read_hotkey()
if key == "esc": # The ESC key
    print("You have pressed the ESC key!")

Приведенный выше код перехватывает нажатия клавиш не только тогда, когда окно терминала, где выполняется скрипт, фокусируется, но и когда это не так.

Итак, в заключение, знаете ли вы Pythonic способ перехвата нажатий клавиш внутри окна терминала (а не снаружи), где выполняется скрипт (что-то вроде input() без необходимости нажимать Enter ) а что кроссплатформенное (хотя бы совместимо с GNU / Linux и Windows)?

Заранее спасибо за ответы,
С уважением,
Алексис.

Ответы [ 2 ]

0 голосов
/ 09 июля 2019

Посмотрите на модуль curses. Он находится в стандартной библиотеке Python, но не поддерживает окна из коробки. Существует регулярно поддерживаемый проект под названием «Windows-Curses», который вы можете посмотреть. Я не проверял его, но он предположительно позволит вам использовать модуль python curses на Windows. https://pypi.org/project/windows-curses/

import curses

def listen(window):
    while True:
        key = window.getch()
        window.addstr(f'You pressed the "{key}" key!\n')
        if key == 'q':
            break
        handle_keypress(key)

curses.wrapper(listen)

Если подход с использованием curses не работает для вас, или вам все еще нужна дополнительная детализация, вы можете довольно легко развернуть свой собственный кроссплатформенный подход. Вы можете попробовать что-то вроде этого:

from sys import platform

class Keyboard:
    def __new__(cls):
        if platform in ['Windows', 'win32', 'cygwin']:
            cls = winKeyboard
        elif platform in ['Mac', 'darwin', 'os2', 'os2emx']:
            cls = MacKeyboard
        else:
            raise Exception(f'Unrecognized platform, {platform}')
        return super(Keyboard, cls).__new__(cls)

    def listen(self):
        while True:
            key = self.getch()
            print(f'You pressed the "{key}" key!')
            if key == 27:
                break
            return self.handle_key(key)

class MacKeyboard(Keyboard):
    def getch(self):
        implement_mac_logic()


class WinKeyboard(Keyboard):
    def getch(self):
        implement_win_logic()

keyboard = Keyboard()
keyboard.listen()

Клавиатура .__ new__ выполняет работу по предоставлению соответствующего решения во время выполнения для текущей операционной системы.

Этот подход по-прежнему регистрирует нажатия клавиш независимо от активного окна.
Для этого вам понадобится доступ к активному окну, что будет другой специфической для ОС процедурой. Посмотрите на это: https://stackoverflow.com/a/36419702/1420455

Вы могли бы реализовать функцию, которая проверяла имя текущего окна

class Keyboard:
    ...
    def listen(self):
        while True:
            if self.get_active_window() != desired_window:
                continue
            key = self.getch()
            print(f'You pressed the "{key}" key!')
            if key == 27:
                break
            return self.handle_key(key)

Тогда вы можете просто реализовать логику согласования в WinKeyboard.get_active_window и MacKeyboard.get_active_window Это не учитывает нахождение в разных вкладках. Это может быть возможно, но я недостаточно знаком с API, чтобы рассказать вам.

Существуют также такие параметры, как pygame, которые потребуют от вас создания и управления собственными окнами, но будут соответствовать вашим требованиям.

Редактировать: Изменены WinKeyboard и MacKeyboard для наследования от клавиатуры.

0 голосов
/ 08 июля 2019

Это частичное решение, которое работает в Windows и будет работать в GNU / Linux.
Я заметил, что в GNU / Linux (по крайней мере, в Debian 9) одинаковое число назначается клавишам со стрелками и клавише ESC.

Для приведенного ниже кода меня вдохновило решение этой темы.

# coding: utf8
import sys


def read() -> int:
    if sys.platform == "win32":
        import msvcrt
        key = ord(msvcrt.getch())  # Wait for a key to be pressed.
    elif sys.platform == "linux":
        import tty
        import termios
        try:
            orig_settings = termios.tcgetattr(sys.stdin)
            tty.setcbreak(sys.stdin)
            key = ord(sys.stdin.read(1)[0])
        except KeyboardInterrupt:
            key = 3 # The code for Ctrl+C got on Windows.
        finally:  # To make sure that the terminal will return to its original state.
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_settings)
    else:
        raise RuntimeError("Your platform is not supported")
    return key

if read() == 27:  # The ESC key (also the UP-DOWN-RIGHT-LEFT on GNU/Linux)
    print("You have pressed the ESC key!")

С уважением,
Алексис.

...