Entrada de consola sin locking Python

Estoy tratando de hacer un cliente IRC simple en Python (como una clase de proyecto mientras aprendo el idioma).

Tengo un bucle que utilizo para recibir y analizar lo que me envía el servidor IRC, pero si uso raw_input para ingresar cosas, detiene el bucle en seco hasta que ingrese algo (obviamente).

¿Cómo puedo ingresar algo sin que el bucle se detenga?

Gracias por adelantado.

(No creo que deba publicar el código, solo quiero ingresar algo sin detener el ciclo while).

EDIT: estoy en Windows.

Para Windows, solo para consola, use el módulo msvcrt :

 import msvcrt num = 0 done = False while not done: print(num) num += 1 if msvcrt.kbhit(): print "you pressed",msvcrt.getch(),"so now i will quit" done = True 

Para Linux, este artículo describe la siguiente solución, requiere el módulo termios :

 import sys import select import tty import termios def isData(): return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []) old_settings = termios.tcgetattr(sys.stdin) try: tty.setcbreak(sys.stdin.fileno()) i = 0 while 1: print(i) i += 1 if isData(): c = sys.stdin.read(1) if c == '\x1b': # x1b is ESC break finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) 

Para plataformas cruzadas, o en caso de que también desee una GUI, puede usar Pygame:

 import pygame from pygame.locals import * def display(str): text = font.render(str, True, (255, 255, 255), (159, 182, 205)) textRect = text.get_rect() textRect.centerx = screen.get_rect().centerx textRect.centery = screen.get_rect().centery screen.blit(text, textRect) pygame.display.update() pygame.init() screen = pygame.display.set_mode( (640,480) ) pygame.display.set_caption('Python numbers') screen.fill((159, 182, 205)) font = pygame.font.Font(None, 17) num = 0 done = False while not done: display( str(num) ) num += 1 pygame.event.pump() keys = pygame.key.get_pressed() if keys[K_ESCAPE]: done = True 

Esta es la solución más increíble que he visto. Pegado aquí en caso de que el enlace se caiga:

 #!/usr/bin/env python ''' A Python class implementing KBHIT, the standard keyboard-interrupt poller. Works transparently on Windows and Posix (Linux, Mac OS X). Doesn't work with IDLE. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ''' 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 != [] # Test if __name__ == "__main__": kb = KBHit() print('Hit any key, or ESC to exit') while True: if kb.kbhit(): c = kb.getch() if ord(c) == 27: # ESC break print(c) kb.set_normal_term() 

1 Hecho por Simon D. Levy , parte de una comstackción de software que ha escrito y lanzado bajo la Licencia Pública General de Gnu Lesser .

Aquí una solución que se ejecuta en Linux y Windows utilizando un hilo separado:

 import sys import threading import time import Queue def add_input(input_queue): while True: input_queue.put(sys.stdin.read(1)) def foobar(): input_queue = Queue.Queue() input_thread = threading.Thread(target=add_input, args=(input_queue,)) input_thread.daemon = True input_thread.start() last_update = time.time() while True: if time.time()-last_update>0.5: sys.stdout.write(".") last_update = time.time() if not input_queue.empty(): print "\ninput:", input_queue.get() foobar() 

En Linux, aquí hay una refactorización del código de mizipzor que lo hace un poco más fácil, en caso de que tenga que usar este código en múltiples lugares.

 import sys import select import tty import termios class NonBlockingConsole(object): def __enter__(self): self.old_settings = termios.tcgetattr(sys.stdin) tty.setcbreak(sys.stdin.fileno()) return self def __exit__(self, type, value, traceback): termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings) def get_data(self): if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []): return sys.stdin.read(1) return False 

Aquí le indicamos cómo usar esto: este código imprimirá un contador que seguirá creciendo hasta que presione ESC.

 with NonBlockingConsole() as nbc: i = 0 while 1: print i i += 1 if nbc.get_data() == '\x1b': # x1b is ESC break 

Creo que la biblioteca de curses puede ayudar.

 import curses import datetime stdscr = curses.initscr() curses.noecho() stdscr.nodelay(1) # set getch() non-blocking stdscr.addstr(0,0,"Press \"p\" to show count, \"q\" to exit...") line = 1 try: while 1: c = stdscr.getch() if c == ord('p'): stdscr.addstr(line,0,"Some text here") line += 1 elif c == ord('q'): break """ Do more things """ finally: curses.endwin() 

Con python3.3 y superior, puede usar el módulo asyncio como se menciona en esta respuesta. Deberá tener en cuenta su código para trabajar con asyncio . Solicite la entrada del usuario mediante la instancia de python asyncio.create_server

unicurses lo que dijo Mickey Chan, pero usaría unicurses lugar de las maldiciones normales. Unicurses es universal (funciona en todos o al menos en casi todos los sistemas operativos)