curses
The Python curses module provides an interface to the curses library for building text-based user interfaces in Unix terminals. It controls cursor movement, text rendering, colors, and keyboard input through a grid of character cells, enabling interactive applications that run entirely in the terminal. The module is available on Unix-like systems only.
A minimal application wraps a function with curses.wrapper() to handle initialization and cleanup automatically:
hello.py
import curses
def main(stdscr):
stdscr.addstr(0, 0, "Hello, curses!")
stdscr.refresh()
stdscr.getch()
curses.wrapper(main)
Run it in your terminal:
$ python hello.py
The terminal clears, displays “Hello, curses!”, and waits for a keypress before restoring the normal display.
Key Features
- Creates and manages rectangular windows and panels within the terminal
- Supports foreground and background color pairs for styled text output
- Handles keyboard input including function keys, arrow keys, and special characters
- Draws borders, lines, and box characters using the alternate character set
- Provides mouse event support for click and scroll interactions
- Works portably across Unix-like systems by wrapping the ncurses library
- Available on Unix-like systems only, not on Windows or WebAssembly platforms
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
curses.wrapper() |
Function | Initializes curses, calls a function, and restores the terminal on exit |
curses.initscr() |
Function | Initializes the library and returns a window covering the entire screen |
curses.newwin() |
Function | Creates a new window with specified dimensions and position |
curses.start_color() |
Function | Initializes color support and defines the eight basic colors |
curses.init_pair() |
Function | Defines a foreground and background color pair by number |
curses.color_pair() |
Function | Returns the attribute value for displaying text in a given color pair |
window.addstr() |
Method | Paints a string at the current or specified position in a window |
window.getch() |
Method | Reads a single character or special key code from the user |
window.refresh() |
Method | Updates the physical terminal display to match the window buffer |
window.getmaxyx() |
Method | Returns the height and width of the window as a tuple |
Examples
Drawing a border and rendering colored text centered on the screen:
colored_box.py
import curses
def main(stdscr):
curses.start_color()
curses.init_pair(1, curses.COLOR_YELLOW, curses.COLOR_BLUE)
stdscr.box()
height, width = stdscr.getmaxyx()
label = "Hello, curses!"
stdscr.addstr(
height // 2,
width // 2 - len(label) // 2,
label,
curses.color_pair(1) | curses.A_BOLD,
)
stdscr.refresh()
stdscr.getch()
curses.wrapper(main)
Reading arrow key input to move a label around the screen:
move_text.py
import curses
def main(stdscr):
stdscr.keypad(True)
y, x = 5, 5
label = "Move me!"
while True:
stdscr.clear()
stdscr.addstr(y, x, label)
stdscr.refresh()
key = stdscr.getch()
rows, cols = stdscr.getmaxyx()
if key == curses.KEY_UP:
y = max(0, y - 1)
elif key == curses.KEY_DOWN:
y = min(rows - 1, y + 1)
elif key == curses.KEY_LEFT:
x = max(0, x - 1)
elif key == curses.KEY_RIGHT:
x = min(cols - len(label) - 1, x + 1)
elif key == ord("q"):
break
curses.wrapper(main)
Common Use Cases
The most common tasks for curses include:
- Building interactive text menus and navigation systems
- Creating real-time monitoring dashboards in the terminal
- Implementing full-screen text editors and file browsers
- Displaying progress indicators that update in place without scrolling
- Handling raw keyboard input for game-like or interactive terminal applications
Real-World Example
A live dashboard uses separate subwindows for a fixed header and a scrolling log area. The header stays pinned while the body accumulates status lines on each tick:
dashboard.py
import curses
import time
def main(stdscr):
curses.start_color()
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.curs_set(0)
height, width = stdscr.getmaxyx()
header = stdscr.subwin(3, width, 0, 0)
body = stdscr.subwin(height - 3, width, 3, 0)
messages = []
for tick in range(10):
header.erase()
header.bkgd(" ", curses.color_pair(1))
title = f"Dashboard | Tick: {tick}"
header.addstr(1, 2, title, curses.color_pair(1) | curses.A_BOLD)
header.refresh()
messages.append(f"[{tick:02d}] System OK")
body.erase()
body.box()
for i, msg in enumerate(messages[-(height - 5):]):
body.addstr(i + 1, 2, msg, curses.color_pair(2))
body.refresh()
time.sleep(1)
stdscr.getch()
curses.wrapper(main)
Run it in your terminal:
$ python dashboard.py
The header updates on each tick while the body accumulates log lines, demonstrating how curses composites independent windows into a single cohesive display.
Related Resources
Tutorial
Python Textual: Build Beautiful UIs in the Terminal
Textual is a Python library for building text-based user interfaces (TUIs) that support rich text, advanced layouts, and event-driven interactivity in the terminal. This tutorial showcases some of the ways you can design an appealing and engaging UI using Textual.
For additional information on related topics, take a look at the following resources:
- The Python Rich Package: Unleash the Power of Console Text (Tutorial)
- How to Read User Input From the Keyboard in Python (Tutorial)
- Building UIs in the Terminal With Python Textual (Course)
- Python Textual: Build Beautiful UIs in the Terminal (Quiz)
- Unleashing the Power of the Console With Rich (Course)
- Reading User Input From the Keyboard With Python (Course)
By Leodanis Pozo Ramos • Updated March 25, 2026