How to break a while loop immediately if the user pressed "q"

Viewed 313

I want to get an integer from the user in each loop and add them. If the user presses q key I want the loop to end immediately without pressing enter and print the total.

!pip install keyboard
import keyboard
total = 0
while True:
  if keyboard.is_pressed('q'):
    break
  x = int(input("Enter a number: "))
  total += x
  print(total)

I tried this but I got an assertion error, detailed as follows:

AssertionError                            
Traceback (most recent call last)
<ipython-input-14-e1e12bbc6dc8> in <module>()
      3 total = 0
      4 while True:
----> 5   if keyboard.is_pressed('q'):
      6     break
      7   x = int(input("Enter a number "))

5 frames
/usr/local/lib/python3.7/dist-packages/keyboard/_nixcommon.py in aggregate_devices(type_name)
    166 
    167     # If no keyboards were found we can only use the fake device to send keys.
--> 168     assert fake_device
    169     return fake_device
    170 

AssertionError: 
4 Answers

It can be done another way:

total = 0

while True:
    x = input("Bir sayı giriniz: ")
    if x == "q":
        break
    total += int(x)

print(total)
total = 0
running =True
while running:
    x = input("Bir sayı giriniz: ")

    if x == "q" or x == "Q":
        running = False
    else:
        total += int(x)

print(total)

You can do this without requiring the user to press enter:

import sys
import termios
import tty

def getchr():
    r"""
    Get a single key from the terminal without printing it.
    Certain special keys return several "characters", all starting with the
    escape character '\x1b'. You could react to that by reading two more
    characters, but the actual escape key will only make this return a single
    escape character, and since it's blocking, that wouldn't be a good solution
    either.
    """
    fd = sys.stdin.fileno()
    old = termios.tcgetattr(fd)
    tty.setraw(sys.stdin.fileno())
    ch = sys.stdin.read(1)
    termios.tcsetattr(fd, termios.TCSADRAIN, old)
    if ord(ch) == 3:  # ^C
        raise KeyboardInterrupt
    return ch


def input_int_except_q(msg):
    print(msg, end="", flush=True)
    chars = []
    while True:
        char = getchr()
        if char == "q":
            return None
        elif char == "\r":
            print()
            break
        print(char, end="", flush=True)
        chars.append(char)
    return int("".join(chars))

total = 0
while True:
    x = input_int_except_q("Bir sayı giriniz: ")
    if not x:
        break
    total += x
    print(total)

getchr will give you a single character that the user enters, without displaying it. input_int_except_q uses that to get an integer that the user entered, or None if they pressed Q. Every character that the user enters is printed back to simulate "normal" line reading, and special care is taken to raise a KeyboardInterrupt as usual when the user presses Ctrl+C.

For a Python3 tty, you could use click:

pip3 install click

in a terminal, then:

from click import getchar

data = ''
while True:
  char = getchar()
  
  if char == 'q':
    break
  data += char
Related