How to capture keyboard or key combination in python3?

2

Hi, I would like to know how I can capture key combinations in , for example by pressing Ctrl + A execute a method in a class. It's for a console program and this is what I've found so far of information.

#! /usr/bin/env python3
import rlcompleter
import readline

readline.parse_and_bind("tab: complete")
grupo_a = set([1, 2, 3])

cmd = None

while cmd not in ['quit', 'exit']:
    cmd = input('>> ')
    exec(cmd)
    
asked by johni 18.05.2017 в 16:34
source

3 answers

1

There are many ways to approach this and it depends on what you want to do in the end. Adding other possibilities to what was already stated by Patricio in his response:

On Linux you can use curses from the standard library to create enhanced terminal applications using ncurses . It facilitates the presentation of the outputs / entries (by tabulation), colors, window borders, allows to respond to keyboard events (such as key combinations), etc. A simple example using key combinations (Alt + P, Alt + A, Alt + E) to call functions (nothing optimized):

#!/usr/bin/env python

import curses

def get_param(screen, prompt_string):
     curses.echo()
     screen.clear()
     screen.addstr(2, 2, prompt_string)
     screen.refresh()
     input = screen.getstr(2, len(prompt_string)+2, 60)
     return input

def sumar(screen):
    curses.noecho()
    n = int(get_param(screen, 'Ingrese un número: '))
    m = int(get_param(screen, 'Ingrese otro número: '))
    screen.addstr(2, 2, 'La suma de {} y {} es {}.'.format(n, m, n + m))
    screen.addstr(4, 2, 'Pulse Enter para volver al menú')
    while True:
        ch = screen.getch()
        if ch == 10: #Enter
            main(screen)
            break

def restar(screen):
    curses.echo()
    n = int(get_param(screen, 'Ingrese un número: '))
    m = int(get_param(screen, 'Ingrese otro número: '))
    screen.addstr(2, 2, 'La resta de {} y {} es {}.'.format(n, m, n - m))
    screen.addstr(4, 2, 'Pulse Enter para volver al menú')
    while True:
        ch = screen.getch()
        if ch == 10: #Enter
            main(screen)
            break


def main(screen):
    curses.echo()
    while True:
        screen.clear()
        screen.border(0)
        screen.addstr(2, 2, "Alt+A para sumar dos numeros")
        screen.addstr(3, 2, "Alt+P para restar dos numeros")
        screen.addstr(4, 2, "Alt+E para salir de la app")
        ch = screen.getch(5, 2)

        if ch == 27: #Tecla Alt
            screen.nodelay(True)
            ch2 = screen.getch() # obtener tecla pulsada despues de Alt
            if ch2 == -1 or ch2 == ord('e') or ch2 == ord('E'):
                break
            elif ch2 == ord('a') or ch2 == ord('A'):
                screen.nodelay(False)
                sumar(screen)
                break
            elif ch2 == ord('p') or ch2 == ord('P'):
                screen.nodelay(False)
                restar(screen)
                break
            else:
                screen.refresh()
            screen.nodelay(False)


curses.wrapper(main)

Another option may be to use one of the many libraries that there are to capture / control keyboard / mouse actions. They are typically used to implement keyloggers but may have more 'correct' purposes. Among them we have pyxhook or Pynput .

    
answered by 19.05.2017 / 01:20
source
0

In the terminal, what I have seen is using sys.stdin.read(1) , the theme is that in the case of a combination of keys, or the function keys, or the PgUp / Dwn, the arrows and so on, there is to read more than once, for example for a Ctrl + A you should read the Ctrl , detect that it is a "special" key "and then read again A , I also imagine that this can vary a lot from terminal to terminal. There are many "recipes" in the network to handle this, the one I liked the most because it is quite explanatory is the one I found here :

#!/usr/bin/env python

class _Getch:
    """
        Gets a single character from standard input.  Does not echo to the
        screen. http://code.activestate.com/recipes/134892/
    """
    def __init__(self):
        self.value = None
        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)
        a=[0,0,0,0,0,0]
        try:
            tty.setraw(sys.stdin.fileno())
            a[0]=ord(sys.stdin.read(1))
            if a[0]==27:
                a[1]=ord(sys.stdin.read(1))
            if a[1]==91:
                a[2]=ord(sys.stdin.read(1))
            if (a[2]>=49 and a[2]<=54) or a[2]==91:
                a[3]=ord(sys.stdin.read(1))
            if a[3]>=48 and a[3]<=57:
                a[4]=ord(sys.stdin.read(1))
            print(a)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

        # Decode keypress
        # https://mail.python.org/pipermail/python-list/2006-June/367344.html
        if   a==[ 10,  0,  0,   0,   0, 0]: k=  13   # Enter
        elif a==[ 27, 27,  0,   0,   0, 0]: k=  27   # Esc (double press)
        elif a==[ 27, 91, 91,  65,   0, 0]: k=1059   # F1
        elif a==[ 27, 91, 91,  66,   0, 0]: k=1060   # F2
        elif a==[ 27, 91, 91,  67,   0, 0]: k=1061   # F3
        elif a==[ 27, 91, 91,  68,   0, 0]: k=1062   # F4
        elif a==[ 27, 91, 91,  69,   0, 0]: k=1063   # F5
        elif a==[ 27, 91, 49,  55, 126, 0]: k=1064   # F6
        elif a==[ 27, 91, 49,  56, 126, 0]: k=1065   # F7
        elif a==[ 27, 91, 49,  57, 126, 0]: k=1066   # F8
        elif a==[ 27, 91, 50,  48, 126, 0]: k=1067   # F9
        elif a==[ 27, 91, 50,  49, 126, 0]: k=1068   # F10
        elif a==[ 27, 91, 50,  51, 126, 0]: k=1133   # F11
        elif a==[ 27, 91, 50,  52, 126, 0]: k=1134   # F12
        elif a==[ 27, 91, 50, 126,   0, 0]: k=1082   # Ins
        elif a==[ 27, 91, 51, 126,   0, 0]: k=1083   # Del
        elif a==[ 27, 91, 49, 126,   0, 0]: k=1071   # Home
        elif a==[ 27, 91, 52, 126,   0, 0]: k=1079   # End
        elif a==[ 27, 91, 53, 126,   0, 0]: k=1073   # Pg Up
        elif a==[ 27, 91, 54, 126,   0, 0]: k=1081   # Pg Dn
        elif a==[ 27, 91, 65,   0,   0, 0]: k=1072   # Up
        elif a==[ 27, 91, 66,   0,   0, 0]: k=1080   # Down
        elif a==[ 27, 91, 68,   0,   0, 0]: k=1075   # Left
        elif a==[ 27, 91, 67,   0,   0, 0]: k=1077   # Right
        elif a==[127,  0,  0,   0,   0, 0]: k=   8   # Backspace
        else:                               k=a[0]   # Ascii code

        return k


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        k=ord(msvcrt.getch())
        if k == 0 or k == 224:                    #Special keys
            return 1000+ord(msvcrt.getch())   #return 1000+ 2nd code
        else:
            return k


getkey = _Getch()

c = ""
#Salir con Ctrl-c
while c != 3:
    c = getkey()
    print(c)

This I've tried on a Linux Mint 17, it runs fine, but some of the combinations already defined in the script are not exactly the same. There is also a library that starts from the same work concept that can also answer your question: readchar

    
answered by 18.05.2017 в 22:43
0

To read the keyboard you can use Pynput , I'll give you an example:

from pynput.keyboard import Key, Listener # Importamos Pynput

def teclaPulsada(key): # Función para imprimir la tecla pulsada, puede ser reemplazada por un logger.

print("Pulsada: ", key) # Imprimimos tecla pulsada

with Listener (on_press = keyPulsed) as listener: # We say Pynput that the keyPulsed function must be called when a key is pressed

listener.join() # Iniciamos el listener
    
answered by 02.09.2018 в 23:09