Of course! The curses library in Python is a powerful way to create terminal-based user interfaces (TUIs). A core part of any interactive application is handling user input, and curses provides several methods for this.

Let's break down the different ways to get key input, starting with the most common one, getkey().
stdscr.getkey() - The Simplest Method
stdscr.getkey() is the most straightforward way to get a single key press. It blocks execution, meaning your program will pause and wait until the user presses a key. It then returns the key as a string.
Key Characteristics:
- Blocking: Your program waits.
- Single Character: It returns one key press at a time.
- Special Keys: For special keys (like arrow keys, function keys,
Enter,Esc), it returns a multi-character string starting with an escape character (\x1b).
Basic Example
This program will wait for you to press any key and then display what key it was.

import curses
import time
def main(stdscr):
# 1. Clear the screen
stdscr.clear()
# 2. Print instructions
stdscr.addstr(0, 0, "Press any key to see what it is. Press 'q' to quit.")
stdscr.refresh()
# 3. Loop forever until 'q' is pressed
while True:
key = stdscr.getkey() # This line will wait here for a key press
# 4. Check if the key is 'q'
if key == 'q':
break
# 5. Print the pressed key at the current cursor position
# We use f-strings to show the raw key value
stdscr.addstr(1, 0, f"You pressed: {repr(key)}")
stdscr.refresh()
# The wrapper function handles initialization and cleanup
if __name__ == "__main__":
curses.wrapper(main)
How to Run:
- Save the code as a Python file (e.g.,
getkey_example.py). - Run it from your terminal:
python getkey_example.py. - Press various keys (letters, numbers,
Enter,Tab, an arrow key). - Press
qto exit.
What you'll see:
- Pressing
awill showYou pressed: 'a'. - Pressing
Enterwill showYou pressed: '\n'. - Pressing the right arrow key will show
You pressed: '\x1b[C'. This is the escape sequence for the right arrow.
stdscr.getch() - The Integer-Based Method
stdscr.getch() is very similar to getkey(), but it returns the key's integer value instead of a string. This can be useful for performance or when you want to compare keys numerically.
The integer values are often defined in the curses module (e.g., curses.KEY_RIGHT).
Basic Example
This example uses getch() and checks for the integer value of 'q' and the right arrow key.
import curses
def main(stdscr):
stdscr.clear()
stdscr.addstr(0, 0, "Press 'q' or the right arrow to quit.")
stdscr.refresh()
while True:
# getch() returns an integer
key_int = stdscr.getch()
# Check for 'q' (its ASCII value is 113)
if key_int == ord('q'):
break
# Check for the right arrow key (its curses constant is 261)
if key_int == curses.KEY_RIGHT:
stdscr.addstr(1, 0, "You pressed the RIGHT ARROW!")
stdscr.refresh()
if __name__ == "__main__":
curses.wrapper(main)
Handling Special Keys (Arrow Keys, F1, etc.)
As you saw, getkey() returns a string like '\x1b[C' for the right arrow. This string is called an "escape sequence". To handle these keys reliably, you should compare the result to the pre-defined string constants in the curses module.
The curses module provides helpful constants like:
curses.KEY_UPcurses.KEY_DOWNcurses.KEY_LEFTcurses.KEY_RIGHTcurses.KEY_HOME,curses.KEY_ENDcurses.KEY_F1throughcurses.KEY_F12
Crucially, these constants are the string representations of the escape sequences.
Example: Handling Arrow Keys
This program demonstrates how to reliably detect arrow key presses.
import curses
def main(stdscr):
stdscr.clear()
stdscr.addstr(0, 0, "Use arrow keys to move the cursor. Press 'q' to quit.")
y, x = 1, 1 # Initial position of the cursor
while True:
stdscr.addstr(y, x, 'O') # Draw the cursor
stdscr.refresh()
key = stdscr.getkey()
# Erase the old cursor position
stdscr.addstr(y, x, ' ')
# Check for arrow keys using the curses string constants
if key == curses.KEY_UP:
y = max(1, y - 1)
elif key == curses.KEY_DOWN:
y = min(stdscr.getmaxyx()[0] - 2, y + 1)
elif key == curses.KEY_LEFT:
x = max(1, x - 1)
elif key == curses.KEY_RIGHT:
x = min(stdscr.getmaxyx()[1] - 2, x + 1)
elif key == 'q':
break
if __name__ == "__main__":
curses.wrapper(main)
Non-Blocking Input (timeout)
Sometimes you don't want your program to pause. You want it to check for a key and, if none is pressed, continue with other tasks. This is called "non-blocking" input.
You can achieve this with stdscr.timeout().
stdscr.timeout(milliseconds): Sets a timeout for blocking functions.timeout(-1): The default. Waits forever (blocking).timeout(0): Non-blocking. Returns immediately with-1if no key is pressed.timeout(100): Waits for up to 100 milliseconds.
When using timeout(0)), you must check the return value ofgetkey()orgetch()for an error, which is usually-1`.
Example: Non-Blocking Input
This program will print a message every second, but it will also check if you've pressed 'q' to quit in the meantime.
import curses
import time
def main(stdscr):
stdscr.clear()
stdscr.addstr(0, 0, "Press 'q' to quit. The program will keep running.")
stdscr.timeout(0) # Set non-blocking mode
counter = 0
while True:
# Check for a key without blocking
key = stdscr.getch()
if key == ord('q'):
stdscr.addstr(2, 0, "Quitting...")
stdscr.refresh()
break
# If no key was pressed, getch() returns -1
if key == -1:
# Do other work here
counter += 1
stdscr.addstr(1, 0, f"Running... {counter}")
stdscr.clrtoeol() # Clear the rest of the line to avoid artifacts
stdscr.refresh()
# A small delay to prevent the loop from spinning too fast
time.sleep(1)
if __name__ == "__main__":
curses.wrapper(main)
Summary: getkey() vs. getch()
| Feature | stdscr.getkey() |
stdscr.getch() |
|---|---|---|
| Return Type | String | Integer |
| Use Case | General purpose, easy to compare with literal strings ('q'), and curses.KEY_* constants. |
Performance-critical apps, or when you need to compare keys numerically. |
| Special Keys | Returns a string like '\x1b[C'. Compare to curses.KEY_RIGHT. |
Returns an integer like 261. Compare to curses.KEY_RIGHT. |
| Blocking | Yes (by default) | Yes (by default) |
| Non-Blocking | Use stdscr.timeout(0) and check for an exception or a specific return value. |
Use stdscr.timeout(0) and check if the return value is -1. |
Best Practices
- Always use
curses.wrapper(): It handles the necessary initialization (initscr,cbreak,noecho) and cleanup (endwin) automatically, even if your program crashes. noecho()is your friend: In most interactive
