12

I have an infinite while loop that I want to break out of when the user presses a key. Usually I use raw_input to get the user's response; however, I need raw_input to not wait for the response. I want something like this:

print 'Press enter to continue.'
while True:
    # Do stuff
    #
    # User pressed enter, break out of loop

This should be a simple, but I can't seem to figure it out. I'm leaning towards a solution using threading, but I would rather not have to do that. How can I accomplish this?

7
  • 1
    This thread might be helpful (contains a working example with curses): ubuntuforums.org/showthread.php?t=1514035 Commented Dec 13, 2013 at 22:20
  • Listening to an event? Commented Dec 13, 2013 at 22:20
  • 1
    There are Windows specific ways to do this in the msvcrt module. Commented Dec 13, 2013 at 22:22
  • @alKid, Not exactly. I am logging data for something that is manually started and ended by the user. It's something that can't be controlled, currently, from my script. Commented Dec 13, 2013 at 22:24
  • The thread also links to a recipe that might be applicable: code.activestate.com/recipes/… Commented Dec 13, 2013 at 22:27

7 Answers 7

14

You can use non-blocking read from stdin:

import sys
import os
import fcntl
import time

fl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK)
while True:
    print("Waiting for user input")
    try:
        stdin = sys.stdin.read()
        if "\n" in stdin or "\r" in stdin:
            break
    except IOError:
        pass
    time.sleep(1)
Sign up to request clarification or add additional context in comments.

1 Comment

It should be noted that your answer is Unix specific. I'm personally on Windows, but this code is still good to have.
8

I think you can do better with msvcrt:

import msvcrt, time
i = 0
while True:
    i = i + 1
    if msvcrt.kbhit():
        if msvcrt.getwche() == '\r':
            break
    time.sleep(0.1)
print(i)

Sadly, still windows-specific.

Comments

6

On python 3.5 you can use the following code. It can be adjusted for a specific keystroke. The while loop will keep running until the user presses a key.

import time
import threading

# set global variable flag
flag = 1

def normal():
    global flag
    while flag==1:
        print('normal stuff')
        time.sleep(2)
        if flag==False:
            print('The while loop is now closing')


def get_input():
    global flag
    keystrk=input('Press a key \n')
    # thread doesn't continue until key is pressed
    print('You pressed: ', keystrk)
    flag=False
    print('flag is now:', flag)

n=threading.Thread(target=normal)
i=threading.Thread(target=get_input)
n.start()
i.start()

Comments

3

I could not get some of the popular answers working. So I came up with another approach using the CTRL + C to plug in user input and imbibe a keyboard interrupt. A simple solution can be using a try-catch block,

i = 0
try:
    while True:
        i+=1
        print(i)
        sleep(1)
except:
    pass
# do what you want to do after it...

I got this idea from running a number of servers like flask and django. This might be slightly different from what the OP asked, but it might help someone else who wanted a similar thing.

Comments

2

Using the msvcrt module as thebjorn recommended I was able to come up with something that works. The following is a basic example that will exit the loop if any key is pressed, not just enter.

import msvcrt, time
i = 0
while True:
    i = i + 1
    if msvcrt.kbhit():
        break
    time.sleep(0.1)
print i

Comments

2

What you need is a non-blocking raw input, if you don't want to use threads there is a simple solution like this one below where he is doing a timeout of 20 ms and then raise and exception if the user doesn't press a key, if he does then the class returns the key pressed.

import signal

class AlarmException(Exception):
    pass

def alarmHandler(signum, frame):
    raise AlarmException

def nonBlockingRawInput(prompt='', timeout=20):
    signal.signal(signal.SIGALRM, alarmHandler)
    signal.alarm(timeout)
    try:
        text = raw_input(prompt)
        signal.alarm(0)
        return text
    except AlarmException:
        print '\nPrompt timeout. Continuing...'
    signal.signal(signal.SIGALRM, signal.SIG_IGN)
    return ''

Source code

Comments

0

I have defined the function which ask number input from the user and returns the factorial of that number. If user wants to stop they have to press 0 and then it will exit from the loop. We can specify any specific key to input 'n' to exit from the loop.

import math

def factorial_func(n):

    return math.factorial(n)

while True:
    n = int(input("Please enter the number to find factorial: "))
    print(factorial_func(n))
    if n == 0:
       exit()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.