Ввод ключа без нажатия клавиши ввода? (LINUX) [дубликат]

Конечно, есть много таких подходов, как синхронный запрос, обещание, но из моего опыта я думаю, что вы должны использовать подход обратного вызова. Естественно, что асинхронное поведение Javascript. Итак, ваш фрагмент кода можно переписать немного иначе:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}
208
задан Masked Man 10 May 2015 в 14:56
поделиться

19 ответов

Вот ссылка на сайт, в котором говорится, как вы можете читать один символ в Windows, Linux и OSX: http://code.activestate.com/recipes/134892/

class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()


class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()
150
ответ дан EddieOffermann 25 August 2018 в 15:15
поделиться
4
ответ дан Community 25 August 2018 в 15:15
поделиться

Это НЕ БЛОКИРОВКА, читает ключ и хранит его в keypress.key.

import Tkinter as tk


class Keypress:
    def __init__(self):
        self.root = tk.Tk()
        self.root.geometry('300x200')
        self.root.bind('<KeyPress>', self.onKeyPress)

    def onKeyPress(self, event):
        self.key = event.char

    def __eq__(self, other):
        return self.key == other

    def __str__(self):
        return self.key

в вашей программе

keypress = Keypress()

while something:
   do something
   if keypress == 'c':
        break
   elif keypress == 'i': 
       print('info')
   else:
       print("i dont understand %s" % keypress)
3
ответ дан Davoud Taghawi-Nejad 25 August 2018 в 15:15
поделиться

Попробуйте это с pygame:

import pygame
pygame.init()             // eliminate error, pygame.error: video system not initialized
keys = pygame.key.get_pressed()

if keys[pygame.K_SPACE]:
    d = "space key"

print "You pressed the", d, "."
2
ответ дан Hackaholic 25 August 2018 в 15:15
поделиться

Рецепт ActiveState, похоже, содержит небольшую ошибку для систем «posix», которая предотвращает прерывание Ctrl-C (я использую Mac). Если я поместил следующий код в свой скрипт:

while(True):
    print(getch())

Я никогда не смогу закончить скрипт с помощью Ctrl-C, и мне нужно убить мой терминал, чтобы уйти.

Я полагаю, что причиной является следующая строка, и она слишком жестока:

tty.setraw(sys.stdin.fileno())

Из-за этого пакет tty на самом деле не нужен, termios достаточно для его обработки.

Ниже приведен улучшенный код, который работает для меня (Ctrl-C будет прерывать), с дополнительной функцией getche, которая эхо-символа при вводе:

if sys.platform == 'win32':
    import msvcrt
    getch = msvcrt.getch
    getche = msvcrt.getche
else:
    import sys
    import termios
    def __gen_ch_getter(echo):
        def __fun():
            fd = sys.stdin.fileno()
            oldattr = termios.tcgetattr(fd)
            newattr = oldattr[:]
            try:
                if echo:
                    # disable ctrl character printing, otherwise, backspace will be printed as "^?"
                    lflag = ~(termios.ICANON | termios.ECHOCTL)
                else:
                    lflag = ~(termios.ICANON | termios.ECHO)
                newattr[3] &= lflag
                termios.tcsetattr(fd, termios.TCSADRAIN, newattr)
                ch = sys.stdin.read(1)
                if echo and ord(ch) == 127: # backspace
                    # emulate backspace erasing
                    # https://stackoverflow.com/a/47962872/404271
                    sys.stdout.write('\b \b')
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, oldattr)
            return ch
        return __fun
    getch = __gen_ch_getter(False)
    getche = __gen_ch_getter(True)

Ссылки:

0
ответ дан ibic 25 August 2018 в 15:15
поделиться

Альтернативный метод:

import os
import sys    
import termios
import fcntl

def getch():
  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)
        break
      except IOError: pass
  finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
  return c

Из этого сообщения в блоге .

14
ответ дан jdi 25 August 2018 в 15:15
поделиться

Пакет curses в python может использоваться для входа в «сырой» режим для ввода символов с терминала несколькими инструкциями. Основное использование Curses - это захват экрана для вывода, который может быть не таким, каким вы хотите. Этот фрагмент кода вместо этого использует операторы print(), которые можно использовать, но вы должны знать, как проклятия меняют окончание строк, связанных с выходом.

#!/usr/bin/python3
# Demo of single char terminal input in raw mode with the curses package.
import sys, curses

def run_one_char(dummy):
    'Run until a carriage return is entered'
    char = ' '
    print('Welcome to curses', flush=True)
    while ord(char) != 13:
        char = one_char()

def one_char():
    'Read one character from the keyboard'
    print('\r? ', flush= True, end = '')

    ## A blocking single char read in raw mode. 
    char = sys.stdin.read(1)
    print('You entered %s\r' % char)
    return char

## Must init curses before calling any functions
curses.initscr()
## To make sure the terminal returns to its initial settings,
## and to set raw mode and guarantee cleanup on exit. 
curses.wrapper(run_one_char)
print('Curses be gone!')
1
ответ дан John Mark 25 August 2018 в 15:15
поделиться

Этот код, основанный на здесь , будет правильно поднять KeyboardInterrupt и EOFError, если нажаты клавиши Ctrl + C или Ctrl + D.

Должно работать в Windows и Linux. Версия ОС X доступна из исходного источника.

class _Getch:
    """Gets a single character from standard input.  Does not echo to the screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): 
        char = self.impl()
        if char == '\x03':
            raise KeyboardInterrupt
        elif char == '\x04':
            raise EOFError
        return char

class _GetchUnix:
    def __init__(self):
        import tty
        import sys

    def __call__(self):
        import sys
        import tty
        import termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()
8
ответ дан kiri 25 August 2018 в 15:15
поделиться

Рецепт ActiveState , цитируемый дословно в двух ответах, чрезмерно спроектирован. Это можно свести к следующему:

def _find_getch():
    try:
        import termios
    except ImportError:
        # Non-POSIX. Return msvcrt's (Windows') getch.
        import msvcrt
        return msvcrt.getch

    # POSIX system. Create and return a getch that manipulates the tty.
    import sys, tty
    def _getch():
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(fd)
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

    return _getch

getch = _find_getch()
52
ответ дан Louis 25 August 2018 в 15:15
поделиться

Ответ (в настоящее время) верхнего уровня (с кодом ActiveState) слишком сложный. Я не вижу причины использовать классы, когда достаточно простой функции. Ниже приведены две реализации, которые выполняют одно и то же, но с более читаемым кодом.

Обе эти реализации:

  1. работают в Python 2 или Python 3
  2. работают в Windows, OSX и Linux
  3. , читают только один байт (т. е. не ждут новой строки)
  4. не зависят от каких-либо внешних библиотек
  5. являются автономными (нет кода вне определения функции)

Версия 1: читаемая и простая

def getChar():
    try:
        # for Windows-based systems
        import msvcrt # If successful, we are on Windows
        return msvcrt.getch()

    except ImportError:
        # for POSIX-based systems (with termios & tty support)
        import tty, sys, termios  # raises ImportError if unsupported

        fd = sys.stdin.fileno()
        oldSettings = termios.tcgetattr(fd)

        try:
            tty.setcbreak(fd)
            answer = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)

        return answer

Версия 2: избегать повторный импорт и обработка исключений:

[EDIT] Я пропустил одно преимущество кода ActiveState. Если вы планируете читать символы несколько раз, этот код избегает (незначительной) стоимости повторения импорта Windows и обработки исключений ImportError для Unix-подобных систем. Хотя вам, вероятно, следует больше беспокоиться о читаемости кода, чем эта незначительная оптимизация, вот альтернатива (она похожа на ответ Луи, но getChar () является автономной), которая функционирует так же, как и код ActiveState, и более читаема: / g10]

def getChar():
    # figure out which function to use once, and store it in _func
    if "_func" not in getChar.__dict__:
        try:
            # for Windows-based systems
            import msvcrt # If successful, we are on Windows
            getChar._func=msvcrt.getch

        except ImportError:
            # for POSIX-based systems (with termios & tty support)
            import tty, sys, termios # raises ImportError if unsupported

            def _ttyRead():
                fd = sys.stdin.fileno()
                oldSettings = termios.tcgetattr(fd)

                try:
                    tty.setcbreak(fd)
                    answer = sys.stdin.read(1)
                finally:
                    termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)

                return answer

            getChar._func=_ttyRead

    return getChar._func()

Пример кода, который использует одну из вышеперечисленных версий getChar ():

from __future__ import print_function # put at top of file if using Python 2

# Example of a prompt for one character of input
promptStr   = "Please give me a character:"
responseStr = "Thank you for giving me a '{}'."
print(promptStr, end="\n> ")
answer = getChar()
print("\n")
print(responseStr.format(answer))
6
ответ дан lucid_dreamer 25 August 2018 в 15:15
поделиться

Встраивание raw_input должно помочь.

for i in range(3):
    print ("So much work to do!")
k = raw_input("Press any key to continue...")
print ("Ok, back to work.")
0
ответ дан Mabooka 25 August 2018 в 15:15
поделиться
sys.stdin.read(1)

будет в основном читать 1 байт из STDIN.

Если вы должны использовать метод, который не дожидается \n, вы можете использовать этот код, как было предложено в предыдущем ответе:

class _Getch:
    """Gets a single character from standard input.  Does not echo to the screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()


class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()

( взято из http://code.activestate.com/recipes/134892/ )

64
ответ дан mgilson 25 August 2018 в 15:15
поделиться

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

Вам лучше использовать что-то вроде pyglet, pygame, cocos2d - если вы делаете что-то более сложное, чем это, и потребуются визуальные эффекты, или проклятия, если вы собираетесь работать с терминалом.

Curses является стандартным: http://docs.python.org/library/curses. HTML

12
ответ дан nachik 25 August 2018 в 15:15
поделиться

Попробуйте использовать это: http://home.wlu.edu/~levys/software/kbhit.py Это неблокирование (это означает, что вы можете иметь цикл while и обнаруживать ключ нажмите, не останавливая его) и кросс-платформенный.

import os

# Windows
if os.name == 'nt':
    import msvcrt

# Posix (Linux, OS X)
else:
    import sys
    import termios
    import atexit
    from select import select


class KBHit:

    def __init__(self):
        '''Creates a KBHit object that you can call to do various keyboard things.'''

        if os.name == 'nt':
            pass

        else:

            # Save the terminal settings
            self.fd = sys.stdin.fileno()
            self.new_term = termios.tcgetattr(self.fd)
            self.old_term = termios.tcgetattr(self.fd)

            # New terminal setting unbuffered
            self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)

            # Support normal-terminal reset at exit
            atexit.register(self.set_normal_term)


    def set_normal_term(self):
        ''' Resets to normal terminal.  On Windows this is a no-op.
        '''

        if os.name == 'nt':
            pass

        else:
            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)


    def getch(self):
        ''' Returns a keyboard character after kbhit() has been called.
            Should not be called in the same program as getarrow().
        '''

        s = ''

        if os.name == 'nt':
            return msvcrt.getch().decode('utf-8')

        else:
            return sys.stdin.read(1)


    def getarrow(self):
        ''' Returns an arrow-key code after kbhit() has been called. Codes are
        0 : up
        1 : right
        2 : down
        3 : left
        Should not be called in the same program as getch().
        '''

        if os.name == 'nt':
            msvcrt.getch() # skip 0xE0
            c = msvcrt.getch()
            vals = [72, 77, 80, 75]

        else:
            c = sys.stdin.read(3)[2]
            vals = [65, 67, 66, 68]

        return vals.index(ord(c.decode('utf-8')))


    def kbhit(self):
        ''' Returns True if keyboard character was hit, False otherwise.
        '''
        if os.name == 'nt':
            return msvcrt.kbhit()

        else:
            dr,dw,de = select([sys.stdin], [], [], 0)
            return dr != []

Пример использования этого:

import kbhit

kb = kbhit.KBHit()

while(True): 
    print("Key not pressed") #Do something
    if kb.kbhit(): #If a key is pressed:
        k_in = kb.getch() #Detect what key was pressed
        print("You pressed ", k_in, "!") #Do something
kb.set_normal_term()

Или вы могли бы использовать модуль g6 getch из PyPi . Но это блокирует цикл while

3
ответ дан NinjaFart 25 August 2018 в 15:15
поделиться

Комментарий в одном из других ответов упоминал режим cbreak, который важен для реализаций Unix, потому что вы, как правило, не хотите, чтобы ^ C (KeyboardError) потреблялся getchar (как и при установке терминала на raw mode, как это сделано большинством других ответов).

Еще одна важная деталь: если вы хотите прочитать один символ , а не один байт , вы должны прочитать 4 байта из входного потока, так как это максимальное количество байтов, которое будет содержать один символ в UTF-8 (Python 3+). Чтение только одного байта приведет к неожиданным результатам для многобайтовых символов, таких как стрелки клавиатуры.

Вот моя измененная реализация для Unix:

import contextlib
import os
import sys
import termios
import tty


_MAX_CHARACTER_BYTE_LENGTH = 4


@contextlib.contextmanager
def _tty_reset(file_descriptor):
    """
    A context manager that saves the tty flags of a file descriptor upon
    entering and restores them upon exiting.
    """
    old_settings = termios.tcgetattr(file_descriptor)
    try:
        yield
    finally:
        termios.tcsetattr(file_descriptor, termios.TCSADRAIN, old_settings)


def get_character(file=sys.stdin):
    """
    Read a single character from the given input stream (defaults to sys.stdin).
    """
    file_descriptor = file.fileno()
    with _tty_reset(file_descriptor):
        tty.setcbreak(file_descriptor)
        return os.read(file_descriptor, _MAX_CHARACTER_BYTE_LENGTH)
2
ответ дан Noah 25 August 2018 в 15:15
поделиться

Также стоит попробовать библиотеку readchar , которая частично основана на рецепте ActiveState, упомянутом в других ответах.

Установка:

pip install readchar

Использование:

import readchar
print("Reading a char:")
print(repr(readchar.readchar()))
print("Reading a key:")
print(repr(readchar.readkey()))

Протестировано в Windows и Linux с помощью Python 2.7.

В Windows поддерживаются только клавиши, которые сопоставляются буквам или управляющим кодам ASCII (Backspace, Enter, Esc , Tab, Ctrl + письмо ). В GNU / Linux (в зависимости от точного терминала, возможно?) Вы также получаете клавиши Insert, Delete, Pg Up, Pg Dn, Home, End и F n ... но тогда есть проблемы, разделяющие эти специальные клавиши из Esc.

Предостережение. Как и в большинстве (все?) ответы здесь, сигнальные клавиши, такие как Ctrl + C, Ctrl + D и Ctrl + Z, захватываются и возвращаются (как '\x03', '\x04' и '\x1a' соответственно); вашей программе может быть трудно отменить.

37
ответ дан Søren Løvborg 25 August 2018 в 15:15
поделиться

Я считаю, что это одно из самых элегантных решений.

import os

if os.name == 'nt':
    import msvcrt
    def getch():
        return msvcrt.getch().decode()
else:
    import sys, tty, termios
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    def getch():
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

, а затем использовать его в коде:

if getch() == chr(ESC_ASCII_VALUE):
    print("ESC!")
0
ответ дан theAlse 25 August 2018 в 15:15
поделиться

Это может быть прецедентом для менеджера контекста. Оставляя в стороне ограничения для ОС Windows, вот мое предложение:

#!/usr/bin/env python3
# file: 'readchar.py'
"""
Implementation of a way to get a single character of input
without waiting for the user to hit <Enter>.
(OS is Linux, Ubuntu 14.04)
"""

import tty, sys, termios

class ReadChar():
    def __enter__(self):
        self.fd = sys.stdin.fileno()
        self.old_settings = termios.tcgetattr(self.fd)
        tty.setraw(sys.stdin.fileno())
        return sys.stdin.read(1)
    def __exit__(self, type, value, traceback):
        termios.tcsetattr(self.fd, termios.TCSADRAIN, self.old_settings)

def test():
    while True:
        with ReadChar() as rc:
            char = rc
        if ord(char) <= 32:
            print("You entered character with ordinal {}."\
                        .format(ord(char)))
        else:
            print("You entered character '{}'."\
                        .format(char))
        if char in "^C^D":
            sys.exit()

if __name__ == "__main__":
    test()
4
ответ дан ThP 25 August 2018 в 15:15
поделиться

Мое решение для python3, не зависящее от пакетов pip.

# precondition: import tty, sys
def query_yes_no(question, default=True):
    """
    Ask the user a yes/no question.
    Returns immediately upon reading one-char answer.
    Accepts multiple language characters for yes/no.
    """
    if not sys.stdin.isatty():
        return default
    if default:
        prompt = "[Y/n]?"
        other_answers = "n"
    else:
        prompt = "[y/N]?"
        other_answers = "yjosiá"

    print(question,prompt,flush= True,end=" ")
    oldttysettings = tty.tcgetattr(sys.stdin.fileno())
    try:
        tty.setraw(sys.stdin.fileno())
        return not sys.stdin.read(1).lower() in other_answers
    except:
        return default
    finally:
        tty.tcsetattr(sys.stdin.fileno(), tty.TCSADRAIN , oldttysettings)
        sys.stdout.write("\r\n")
        tty.tcdrain(sys.stdin.fileno())
0
ответ дан xro 25 August 2018 в 15:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: