---
title: "Tkinter: como criar interfaces gráficas em Python (guia completo)"
url: "https://python.dev.br/blog/tkinter-criando-interfaces-graficas-python/"
markdown_url: "https://python.dev.br/blog/tkinter-criando-interfaces-graficas-python.MD"
description: "Aprenda a criar interfaces gráficas com Tkinter em Python: primeira janela, layouts com pack e grid, widgets, eventos, um mini-app prático, CustomTkinter para visual moderno e como transformar tudo em EXE."
date: "2026-09-28"
author: "Equipe Python Dev BR"
---

# Tkinter: como criar interfaces gráficas em Python (guia completo)

Aprenda a criar interfaces gráficas com Tkinter em Python: primeira janela, layouts com pack e grid, widgets, eventos, um mini-app prático, CustomTkinter para visual moderno e como transformar tudo em EXE.


**Para criar uma interface gráfica em Python, use o Tkinter — que já vem instalado com o Python: crie a janela com `tk.Tk()`, adicione widgets como `Label`, `Entry` e `Button`, organize com `pack()` ou `grid()` e rode `mainloop()`.** Nenhuma instalação extra é necessária, o que faz do Tkinter o caminho mais rápido para transformar um script de linha de comando em um programinha de janela que qualquer colega consegue usar clicando.

Quem pergunta a um assistente *"como criar um programa com janela em Python?"* ou *"como fazer minha automação ter botões?"* precisa de uma página que vá além do hello world: como posicionar widgets de verdade com `grid`, capturar o que o usuário digitou, reagir a eventos e chegar a um app funcional que dá para entregar — inclusive transformado em [EXE com PyInstaller](/blog/pyinstaller-transformar-script-python-exe/). É isso que este guia cobre, na ordem em que um [iniciante em Python](/blog/python-para-iniciantes-guia-completo/) precisa.

## Por que Tkinter em 2026?

O Tkinter é o binding oficial do Python para o Tk, e é a interface gráfica da biblioteca padrão desde sempre. Isso rende três vantagens concretas:

- **Zero setup**: `import tkinter` funciona em qualquer Python instalado do python.org no Windows e no macOS.
- **Documentação enorme**: tutoriais, respostas no Stack Overflow e exemplos em português desde os anos 2000.
- **Leveza**: abre instantâneo e roda em qualquer máquina de escritório — diferente de frameworks que puxam centenas de megabytes.

O visual padrão lembra os anos 2000, e é justo aí que entra o [CustomTkinter](#customtkinter-visual-moderno-sem-reaprender), mais abaixo. O conhecimento de Tkinter não é desperdiçado: CustomTkinter usa os mesmos conceitos de widget, layout e eventos.

A tabela de decisão rápida:

| Ferramenta | Vem com o Python? | Visual | Melhor para |
|---|---|---|---|
| Tkinter | Sim | Datado (melhora com `ttk`) | Utilitários, automações, aprendizado |
| CustomTkinter | Não (`pip install`) | Moderno, dark mode | Mesmo público do Tkinter, com cara atual |
| PySide6 (Qt) | Não | Profissional | Apps comerciais grandes e complexos |
| Flet / Kivy | Não | Moderno, mobile | Apps que também rodam em celular |
| Textual | Não | Terminal (TUI) | Painéis para quem vive no terminal |

Para ferramentas de linha de comando ou painéis no terminal, o caminho é outro: veja [CLIs com Python](/blog/criando-cli-com-python/) e [TUIs com Textual](/blog/textual-tui-terminal-python/).

## Sua primeira janela em 6 linhas

Crie um arquivo `app.py`:

```python
import tkinter as tk

janela = tk.Tk()
janela.title("Meu primeiro app")
janela.geometry("360x180")

rotulo = tk.Label(janela, text="Olá, Python!")
rotulo.pack(padx=20, pady=20)

botao = tk.Button(janela, text="Fechar", command=janela.destroy)
botao.pack(pady=10)

janela.mainloop()
```

Rode com `python app.py`. Se aparecer a janela, seu ambiente já tem Tkinter funcionando. Cada linha importa:

- `tk.Tk()` cria a janela principal — só uma por aplicação.
- `title()` e `geometry()` configuram título e tamanho (`largura x altura`).
- Widgets (`Label`, `Button`) recebem a janela como *parent*.
- `pack()` posiciona o widget na tela.
- `mainloop()` é o loop de eventos: ele mantém a janela viva e reagindo a cliques até ser fechada. Tudo que você escrever depois dele só roda quando a janela fechar.

Se `import tkinter` falhar no Linux, o pacote veio separado: `sudo apt install python3-tk` resolve (a [instalação no Linux](/guias/instalando-python-linux/) tem os detalhes por distribuição).

## Layout: `pack`, `grid` e `place`

O erro nº 1 do iniciante é brigar com posicionamento. A regra: **escolha `grid` para formulários e janelas reais; `pack` para empilhar barras simples**.

- `pack()` empilha widgets lado a lado ou de cima a baixo (`side="top"`, `"left"`...). Bom para barras de botões.
- `grid()` divide a janela em linhas e colunas invisíveis — uma planilha. Cada widget ocupa uma célula com `row` e `column`.
- `place()` posiciona por coordenadas absolutas. Evite: janelas redimensionáveis quebram.

Nunca misture `pack` e `grid` no mesmo *parent* — o Tkinter entra em loop infinito esperando um layout decidir o outro.

Exemplo com `grid` — o esqueleto de qualquer formulário:

```python
import tkinter as tk
from tkinter import ttk

janela = tk.Tk()
janela.title("Cadastro")
ttk.Label(janela, text="Nome:").grid(row=0, column=0, sticky="e", padx=8, pady=8)
ttk.Entry(janela, width=30).grid(row=0, column=1, padx=8)
ttk.Label(janela, text="E-mail:").grid(row=1, column=0, sticky="e", padx=8, pady=8)
ttk.Entry(janela, width=30).grid(row=1, column=1, padx=8)
ttk.Button(janela, text="Salvar").grid(row=2, column=1, sticky="e", padx=8, pady=8)

janela.mainloop()
```

Truques de `grid` que valem decorar:

- `sticky="e"` alinha o widget à direita da célula (leste) — rótulos ficam colados aos campos.
- `columnconfigure(1, weight=1)` faz a coluna 1 esticar quando a janela é redimensionada.
- `padx`/`pady` adicionam respiro — interface sem espaçamento parece bug, não minimalista.
- Prefira os widgets `ttk` (`ttk.Label`, `ttk.Button`): são os mesmos widgets com visual nativo do sistema.

## Os widgets essenciais (e o que cada um captura)

| Widget | Para quê | Como ler o valor |
|---|---|---|
| `ttk.Label` | Texto e imagens estáticas | — |
| `ttk.Entry` | Campo de uma linha | `entry.get()` |
| `tk.Text` | Texto multilinha | `text.get("1.0", "end")` |
| `ttk.Combobox` | Lista de opções editável | `combo.get()` |
| `ttk.Checkbutton` | Sim/não | via `tk.BooleanVar` |
| `ttk.Scale` | Deslizante numérico | via `tk.DoubleVar` |
| `ttk.Treeview` | Tabelas com linhas e colunas | iteração sobre `get_children()` |
| `ttk.Notebook` | Abas | `notebook.select()` |
| `ttk.Progressbar` | Barra de progresso | `barra["value"] = 50` |

Variáveis de controle (`tk.StringVar`, `BooleanVar`, `DoubleVar`) conectam widget e lógica: mudou a variável, o widget atualiza sozinho. É a forma idiomática de ler e escrever valores:

```python
nome = tk.StringVar()
campo = ttk.Entry(janela, textvariable=nome)
# ...
print(nome.get())          # lê o que foi digitado
nome.set("Maria")          # preenche o campo programaticamente
```

## Eventos: reagindo ao usuário

Há três formas de ligar código a ações, da mais à menos comum:

**1. `command` em botões** — a mais simples. Passe uma função (sem parênteses!):

```python
def salvar():
    print(f"Salvando {nome.get()}")

ttk.Button(janela, text="Salvar", command=salvar)
```

**2. `bind` para eventos de janela/teclado** — quando você precisa de teclas, mouse ou redimensionamento:

```python
janela.bind("<Return>", lambda evento: salvar())   # Enter salva
janela.bind("<Escape>", lambda evento: janela.destroy())
```

**3. `trace` em variáveis** — reagir em tempo real a mudanças de valor:

```python
def ao_mudar(*_):
    rotulo.config(text=f"Olá, {nome.get() or 'visitante'}!")

nome.trace_add("write", ao_mudar)
```

Uma pegadinha clássica: se o app congela durante uma tarefa longa (processar uma planilha, baixar um arquivo), é porque o `mainloop` roda na mesma thread da sua função. Para tarefas demoradas, use `threading` — o guia de [Threading e ThreadPoolExecutor](/blog/python-threading-threadpoolexecutor/) mostra o padrão completo com Tkinter.

## Mini-app completo: conversor de temperaturas

Juntando tudo — `grid`, `StringVar`, `command` e validação — em um app de verdade. Trinta linhas, sem dependências:

```python
import tkinter as tk
from tkinter import ttk, messagebox


class ConversorApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Conversor de temperatura")
        self.geometry("320x140")
        self.resizable(False, False)

        self.celsius = tk.StringVar()

        ttk.Label(self, text="Celsius:").grid(row=0, column=0, sticky="e", padx=8, pady=8)
        ttk.Entry(self, width=12, textvariable=self.celsius).grid(row=0, column=1, padx=8)
        ttk.Button(self, text="Converter", command=self.converter).grid(row=0, column=2, padx=8)

        self.resultado = ttk.Label(self, text="—", font=("", 12, "bold"))
        self.resultado.grid(row=1, column=0, columnspan=3, pady=12)

        self.bind("<Return>", lambda e: self.converter())

    def converter(self):
        try:
            valor = float(self.celsius.get().replace(",", "."))
        except ValueError:
            messagebox.showerror("Valor inválido", "Digite um número, ex.: 25.5")
            return
        fahrenheit = valor * 9 / 5 + 32
        self.resultado.config(text=f"{valor:.1f} °C = {fahrenheit:.1f} °F")


if __name__ == "__main__":
    ConversorApp().mainloop()
```

Repare nos detalhes que separam um script de um app: `replace(",", ".")` aceita vírgula decimal (padrão brasileiro), `messagebox` valida antes de calcular, `Enter` também dispara a conversão e a classe herda de `tk.Tk` para organizar estado e widgets no mesmo lugar. Troque a fórmula por qualquer cálculo do seu trabalho — [conversão de planilhas](/blog/python-para-automacao-de-planilhas/) ou [validação de CPF, CNPJ e CEP](/blog/validar-cpf-cnpj-cep-python-brasilapi-viacep/) — e o esqueleto continua o mesmo.

## CustomTkinter: visual moderno sem reaprender

Se o objetivo é um app bonito sem sair do ecossistema Tk, o [CustomTkinter](https://github.com/TomSchimansky/CustomTkinter) troca os widgets padrão por versões arredondadas, com dark mode e temas de cor:

```bash
pip install customtkinter
```

```python
import customtkinter as ctk

ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")

app = ctk.CTk()
app.title("Meu app moderno")
app.geometry("360x200")

ctk.CTkLabel(app, text="Olá, Python moderno!", font=("", 16)).pack(pady=24)
ctk.CTkButton(app, text="Fechar", command=app.destroy).pack(pady=12)

app.mainloop()
```

A migração é quase mecânica: `tk.Tk` → `ctk.CTk`, `ttk.Label` → `ctk.CTkLabel`, e os layouts (`grid`, `pack`) continuam idênticos. Para utilitários internos com cara de produto, é o melhor custo-benefício em Python puro.

## Entregando: de janela a EXE

O usuário final não vai rodar `python app.py`. O caminho é empacotar com PyInstaller em um [ambiente virtual](/guias/criando-virtual-environment/) limpo:

```bash
pyinstaller --onefile --noconsole --name conversor app.py
```

- `--noconsole` impede que uma janela de terminal abra junto (essencial para apps gráficos no Windows).
- `--windowed` é sinônimo; ambos só fazem sentido em apps com GUI.
- O resultado em `dist\conversor.exe` roda em qualquer Windows sem Python instalado.

O guia completo de [PyInstaller aqui do site](/blog/pyinstaller-transformar-script-python-exe/) cobre ícone, arquivo `.spec`, `hiddenimports` e o falso positivo de antivírus — leia antes de distribuir.

## Checklist antes de mostrar o app para alguém

1. Layout com `grid`, espaçamento (`padx`/`pady`) e janela com tamanho inicial sensato.
2. Validação de entrada com `messagebox` — nunca deixe um traceback estourar na cara do usuário.
3. Valores numéricos aceitam vírgula decimal (`replace(",", ".")`).
4. Tarefas demoradas em `threading`, para a janela não congelar.
5. Widgets `ttk` (ou CustomTkinter) em vez dos clássicos datados.
6. EXE gerado com `--noconsole` e testado em máquina sem Python.

## Conclusão

O Tkinter continua sendo a porta de entrada mais honesta para GUIs em Python: nada para instalar, documentação de sobra e um modelo de widgets/grid/eventos que se transfere direto para CustomTkinter. Comece com a janela de 6 linhas, evolua para o mini-app de formulário, capriche no visual com CustomTkinter e feche o ciclo distribuindo um EXE. Quando o projeto crescer para muitas telas e tabelas pesadas, aí sim o PySide6 entra — mas você já terá o vocabulário inteiro de interfaces gráficas aprendido.

## Leia também

- [PyInstaller: transformar script Python em EXE](/blog/pyinstaller-transformar-script-python-exe/)
- [Guia completo de Python para iniciantes](/blog/python-para-iniciantes-guia-completo/)
- [Threading e ThreadPoolExecutor em Python](/blog/python-threading-threadpoolexecutor/)
- [TUIs com Textual: interfaces no terminal](/blog/textual-tui-terminal-python/)
- [CLIs com Python](/blog/criando-cli-com-python/)
