curses no Python: interfaces no terminal

Publicado em: 26/08/2026
Tempo de leitura: 7 minutos
A male software engineer working on code in a modern office setting.

O módulo curses permite criar interfaces de texto avançadas em terminais de células, com janelas, cores, menus, teclado, mouse, atualização parcial e adaptação ao tamanho da tela. Ele usa a biblioteca curses/ncurses e evita que a aplicação precise escrever sequências ANSI específicas para cada terminal.

É uma boa escolha para dashboards locais, monitores de processos, instaladores, gerenciadores de arquivos, ferramentas administrativas e aplicações executadas por SSH. Para um programa confiável, é essencial restaurar o terminal após erros, respeitar as capacidades informadas pelo terminfo e tratar resize, Unicode e terminais pequenos.

Disponibilidade

curses é um módulo opcional disponível normalmente em Unix. Não é suportado em Android, iOS ou WASI. No Windows, pode ser necessário instalar uma implementação compatível de terceiros.

try:
    import curses
except ImportError:
    curses = None

Uma aplicação deve oferecer mensagem clara ou modo alternativo quando curses não estiver disponível.

Comece com wrapper()

curses.wrapper() inicializa a biblioteca, configura cbreak, desliga echo, ativa o keypad, inicializa cores quando disponíveis e restaura o terminal ao sair, inclusive se a função principal gerar exceção.

import curses


def main(stdscr):
    stdscr.clear()
    stdscr.addstr(0, 0, "Olá, terminal")
    stdscr.refresh()
    stdscr.getch()

curses.wrapper(main)

Prefira wrapper() a chamar manualmente initscr() e endwin(). O traceback aparecerá depois que o terminal já estiver em estado normal.

Coordenadas

Curses usa coordenadas (y, x): linha primeiro, coluna depois. O canto superior esquerdo é (0, 0).

altura, largura = stdscr.getmaxyx()
centro_y = altura // 2
centro_x = largura // 2
stdscr.addstr(centro_y, max(0, centro_x - 5), "Centralizado")

Confundir x e y é uma causa comum de escrita fora da janela.

Evite escrever fora dos limites

addstr() e addch() geram curses.error ao ultrapassar a janela. Escrever exatamente no canto inferior direito também pode gerar exceção depois de desenhar o caractere.

def escrever_seguro(win, y, x, texto, attr=0):
    altura, largura = win.getmaxyx()
    if not (0 <= y < altura and 0 <= x < largura):
        return
    disponivel = max(0, largura - x - 1)
    if disponivel:
        win.addnstr(y, x, texto, disponivel, attr)

Calcule o espaço disponível e trate telas pequenas como estado normal.

Loop de eventos

def main(stdscr):
    stdscr.keypad(True)
    while True:
        stdscr.erase()
        stdscr.addstr(0, 0, "Pressione q para sair")
        stdscr.refresh()

        tecla = stdscr.getch()
        if tecla in (ord("q"), ord("Q")):
            break

Não execute tarefas longas dentro do loop sem permitir atualização e cancelamento.

Teclas especiais

Com keypad(True), sequências do terminal são traduzidas para constantes como KEY_UP, KEY_DOWN, KEY_LEFT e KEY_RIGHT.

if tecla == curses.KEY_UP:
    selecao = max(0, selecao - 1)
elif tecla == curses.KEY_DOWN:
    selecao = min(len(itens) - 1, selecao + 1)

Não presuma que toda tecla existe. has_key() e as capacidades do terminal ajudam a detectar suporte.

getch(), getkey() e get_wch()

getch() retorna inteiro. getkey() retorna string ou nome de tecla. get_wch() é mais adequado a Unicode: retorna um caractere para texto e inteiro para teclas especiais.

entrada = stdscr.get_wch()
if isinstance(entrada, str):
    processar_texto(entrada)
elif entrada == curses.KEY_RESIZE:
    redesenhar = True

Para interfaces internacionais, prefira get_wch() e configure locale corretamente.

Entrada não bloqueante

nodelay(True) faz getch() retornar -1 quando não há entrada. timeout(ms) espera por um período limitado.

stdscr.timeout(100)
while True:
    tecla = stdscr.getch()
    atualizar_metricas()
    if tecla == ord("q"):
        break

Não crie busy loop com timeout zero sem pausa ou trabalho útil.

halfdelay()

halfdelay(tenths) espera entre 0,1 e 25,5 segundos por uma tecla e gera erro se nada chegar. Para aplicações comuns, window.timeout() costuma ser mais simples.

Escape e sequências

set_escdelay(ms) controla quanto tempo curses aguarda depois de ESC para distinguir a tecla Escape de uma sequência de função. O valor precisa equilibrar resposta local e conexões SSH lentas.

Janelas

newwin() cria regiões independentes.

altura, largura = stdscr.getmaxyx()
menu = curses.newwin(altura - 2, 30, 1, 0)
conteudo = curses.newwin(altura - 2, largura - 30, 1, 30)
menu.box()
conteudo.box()

Subjanelas compartilham memória com a janela pai. Mudanças e refresh precisam ser coordenados.

Pads

newpad() cria uma área maior que a tela, útil para logs ou documentos extensos. O refresh de um pad recebe coordenadas da região virtual e da tela.

pad = curses.newpad(1000, 200)
for indice in range(1000):
    pad.addstr(indice, 0, f"Linha {indice}")
pad.refresh(offset, 0, 1, 0, altura - 2, largura - 1)

Valide todos os retângulos; dimensões incompatíveis geram curses.error.

Atualizar várias janelas

Chamadas repetidas a refresh() podem aumentar flicker. Use noutrefresh() em cada janela e uma única chamada a doupdate().

menu.noutrefresh()
conteudo.noutrefresh()
curses.doupdate()

Essa técnica sincroniza a tela física depois que todas as janelas atualizaram o estado virtual.

Cores

Chame start_color() e verifique has_colors().

if curses.has_colors():
    curses.start_color()
    curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
    stdscr.addstr(0, 0, "OK", curses.color_pair(1))

O número de cores e pares está em COLORS e COLOR_PAIRS após a inicialização.

Cores padrão no Python 3.14

assume_default_colors(fg, bg), adicionado no Python 3.14, permite usar o foreground e background padrão do terminal e pode preservar transparência.

if hasattr(curses, "assume_default_colors"):
    curses.assume_default_colors(-1, -1)
    curses.init_pair(1, curses.COLOR_CYAN, -1)

Tenha fallback para versões anteriores e terminais sem esse recurso.

Atributos visuais

Constantes como A_BOLD, A_REVERSE, A_UNDERLINE e A_DIM podem ser combinadas com cores.

Nem todo terminal suporta todos os atributos. Não dependa apenas de cor para comunicar estado; use texto, símbolos e posição.

Unicode e largura visual

O número de caracteres Python não é necessariamente o número de células. Ideogramas podem ocupar duas colunas e caracteres combinantes zero.

Para alinhamento internacional robusto, use uma biblioteca de cálculo de largura como wcwidth e teste emojis e caracteres combinados.

Encoding da janela

Cada janela possui atributo encoding, normalmente baseado na locale. Configure a locale antes de inicializar curses.

import locale
locale.setlocale(locale.LC_ALL, "")

Não altere locale global durante o loop; ela afeta todo o processo.

Redimensionamento

O terminal pode retornar KEY_RESIZE. Consulte novamente getmaxyx(), recrie layout e desenhe tudo.

if tecla == curses.KEY_RESIZE:
    curses.update_lines_cols()
    stdscr.erase()
    reconstruir_layout(stdscr)

resizeterm() e resize_term() atualizam estruturas internas. Pads precisam de tratamento próprio.

Tela mínima

Defina dimensões mínimas e mostre mensagem simples quando a janela for pequena.

altura, largura = stdscr.getmaxyx()
if altura < 10 or largura < 40:
    escrever_seguro(stdscr, 0, 0, "Aumente o terminal")
    stdscr.refresh()
    return

Mouse

mousemask() ativa eventos. Depois de KEY_MOUSE, use getmouse().

Suporte varia por terminal, multiplexador e SSH. Sempre ofereça navegação por teclado.

Campo de texto

curses.textpad.Textbox oferece edição de texto com comandos semelhantes ao Emacs. Valide o tamanho e normalize o conteúdo ao terminar.

No Python 3.14, o limite máximo de getstr() e instr() aumentou de 1023 para 2047 caracteres, mas isso não elimina a necessidade de limitar entrada.

Erros de escrita

Algumas operações geram curses.error em terminais pequenos ou coordenadas no limite. Trate erros esperados localmente, mas não ignore toda exceção.

Um except curses.error: pass global pode esconder bugs de layout.

Terminal e terminfo

Curses consulta a base terminfo indicada por TERM. Um valor incorreto, como declarar xterm-256color em terminal incompatível, produz teclas ou cores erradas.

Não defina TERM arbitrariamente. Corrija o ambiente ou instale a entrada terminfo adequada.

Não escreva sequências ANSI manualmente

Misturar escapes diretos com curses pode desalinhar o estado virtual e físico. Use as funções da biblioteca para cursor, atributos e limpeza.

Subprocessos

Antes de iniciar um programa interativo externo, salve e restaure o modo shell com def_prog_mode(), endwin() e reset_prog_mode(), ou encerre a interface e recrie-a.

Para automação isolada, use o guia de pty no Python.

Relação com tty e termios

Curses gerencia cbreak, echo, keypad e detalhes de terminal sobre as APIs explicadas em tty no Python e termios no Python.

Não altere flags termios por fora enquanto curses está ativo sem compreender como restaurar o estado interno.

Threads

Mantenha todas as chamadas curses em uma única thread. Workers podem produzir dados por fila; a thread da interface consome e desenha.

Isso evita concorrência sobre o terminal e simplifica resize e shutdown.

Shutdown cooperativo

Use uma flag ou evento para encerrar o loop e deixe wrapper() restaurar o terminal.

Não faça logging complexo em signal handler. O artigo de signal no Python mostra como acordar o loop principal.

Segurança

Não exiba texto não confiável sem remover caracteres de controle. Dados remotos podem conter escape, newline, tab e sequências que deformam a interface.

def limpar_texto(valor):
    return "".join(ch if ch.isprintable() else "?" for ch in str(valor))

Também limite tamanho para impedir consumo excessivo e operações de desenho muito caras.

Testes

Teste terminais pequenos, resize contínuo, ausência de cor, 8 e 256 cores, Unicode, SSH, tmux, screen, teclado diferente, mouse ausente, exceção durante desenho e encerramento por Ctrl+C.

Use pty para testes de integração, mas faça testes manuais em terminais reais. O rendering exato depende da implementação curses e do terminfo.

Arquitetura recomendada

Separe estado, comandos e renderização. O loop lê uma tecla, converte em ação, atualiza o modelo e redesenha a partir do modelo. Evite lógica de negócio espalhada por chamadas addstr().

Essa estrutura facilita testes sem curses e evita inconsistência visual.

Erros comuns

Os erros mais frequentes são inicializar sem wrapper(), escrever fora da janela, presumir largura igual a len(), depender de cor, ignorar resize, usar busy loop, misturar ANSI direto, chamar curses de várias threads e imprimir texto remoto com controles.

Conclusão

curses permite construir interfaces de terminal portáveis e eficientes sobre as capacidades reais do sistema. Use wrapper(), valide dimensões, coordene refresh, trate Unicode e resize e mantenha a UI em uma única thread.

Teste com terminais e terminfo reais e ofereça fallback quando o módulo não existir. Consulte a documentação oficial de curses e o manual ncurses(3X).

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Detailed close-up of a reticulated python showcasing intricate scales and piercing eyes.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    grp no Python: consulte grupos Unix

    Aprenda grp no Python para consultar grupos Unix, GIDs, membros, ownership e grupos suplementares com NSS e containers.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pwd no Python: consulte usuários Unix

    Aprenda pwd no Python para consultar usuários Unix por UID ou login, obter home, shell e ownership sem usar a

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    Close-up view of freshly cut log slices stacked for wood storage, showing natural texture.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    syslog no Python: envie logs ao sistema

    Aprenda syslog no Python para enviar logs Unix com prioridades, facilities, máscaras, conteúdo estruturado e proteção contra log injection.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    A CPU and RAM sticks displayed on a white surface, showcasing computer hardware components.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    resource no Python: limites de CPU e memória

    Aprenda resource no Python para medir CPU, memória e page faults, definir limites de arquivos, processos e descriptors em Unix.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Close-up of a parking payment terminal in an indoor garage in Almere, Netherlands.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pty no Python: automatize terminais Unix

    Aprenda pty no Python para executar e testar programas interativos, controlar pseudo-terminais, tratar EOF, resize, sinais e timeouts.

    Ler mais

    Tempo de leitura: 6 minutos
    25/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tty no Python: modos raw e cbreak

    Aprenda tty no Python para usar raw e cbreak, ler teclas, tratar sequências, Unicode e restaurar o terminal Unix com

    Ler mais

    Tempo de leitura: 6 minutos
    25/08/2026