Curso de Python com Tkinter para Criação de Interfaces
O Widget Canvas para Desenho e Gráficos
Aprenda sobre o widget canvas para desenho e gráficos
O Widget Canvas para Desenho e Gráficos 🎨
Bem-vindos à aula sobre o poderoso widget Canvas do Tkinter! Nesta aula, vamos mergulhar no mundo dos gráficos e desenhos personalizados em suas interfaces. O Canvas é como uma tela em branco onde você pode desenhar formas, exibir imagens, inserir texto e até mesmo criar animações interativas. É a ferramenta perfeita para criar jogos simples, editores gráficos ou visualizadores de dados personalizados.
🚀 Introdução ao Widget Canvas
O Canvas é um widget versátil que permite criar gráficos bidimensionais. Pense nele como uma folha de papel digital onde você tem total controle sobre cada pixel. Você pode desenhar linhas, retângulos, círculos, polígonos, arcos e até mesmo exibir imagens e texto. A grande vantagem é que cada item desenhado no Canvas pode ser manipulado individualmente e reagir a eventos, tornando-o extremamente interativo.
Por que usar o Canvas?
- Flexibilidade: Crie qualquer forma ou gráfico que sua imaginação permitir.
- Interatividade: Responda a cliques, arrastos e outros eventos do mouse em itens específicos.
- Animação: Mova itens programaticamente para criar animações.
- Visualização de Dados: Construa gráficos e diagramas personalizados.
- Jogos: Desenvolva jogos simples baseados em gráficos 2D.
Vamos começar a sujar as mãos! 🧑💻
🛠️ Explicação Detalhada com Exemplos
1. Criando um Canvas Básico
Primeiro, precisamos criar uma janela e, em seguida, instanciar o widget Canvas.
import tkinter as tk
# 1. Cria a janela principal
root = tk.Tk()
root.title("Meu Primeiro Canvas")
# 2. Cria o widget Canvas
# width e height definem o tamanho da área de desenho em pixels
# bg (background) define a cor de fundo
canvas = tk.Canvas(root, width=400, height=300, bg="lightblue")
# 3. Empacota o Canvas na janela
canvas.pack()
# 4. Inicia o loop principal da aplicação
root.mainloop()Com este código, você verá uma janela com uma área azul clara, pronta para receber seus desenhos.
2. Desenhando Formas Básicas
O Canvas possui vários métodos create_ para desenhar diferentes tipos de itens. Cada método retorna um ID único para o item criado, que pode ser usado para manipulá-lo posteriormente.
a) Linhas (create_line)
Desenha uma ou mais linhas conectadas.
import tkinter as tk
root = tk.Tk()
root.title("Linhas no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="lightblue")
canvas.pack()
# Desenha uma linha simples (x1, y1, x2, y2)
canvas.create_line(50, 50, 150, 50, fill="red", width=2)
# Desenha uma linha quebrada (múltiplos pontos)
canvas.create_line(200, 50, 250, 100, 300, 50, fill="blue", width=3, dash=(4, 2))
# Desenha uma linha suave (spline)
canvas.create_line(50, 150, 100, 200, 150, 150, 200, 200, smooth=True, fill="green", width=4)
root.mainloop()b) Retângulos (create_rectangle)
Desenha um retângulo usando as coordenadas do canto superior esquerdo e inferior direito.
import tkinter as tk
root = tk.Tk()
root.title("Retângulos no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="lightgray")
canvas.pack()
# Retângulo simples (x1, y1, x2, y2)
canvas.create_rectangle(50, 50, 150, 100, fill="orange", outline="black", width=2)
# Retângulo com cor de contorno diferente
canvas.create_rectangle(200, 150, 350, 250, fill="purple", outline="white", width=3)
root.mainloop()c) Ovals/Círculos (create_oval)
Desenha uma elipse ou um círculo (se a largura e altura forem iguais) dentro de um retângulo delimitador.
import tkinter as tk
root = tk.Tk()
root.title("Ovals e Círculos no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="lightyellow")
canvas.pack()
# Oval (elipse)
canvas.create_oval(50, 50, 150, 100, fill="yellow", outline="brown", width=2)
# Círculo (largura e altura do retângulo delimitador são iguais)
canvas.create_oval(200, 150, 300, 250, fill="pink", outline="red", width=3)
root.mainloop()d) Polígonos (create_polygon)
Desenha uma forma com múltiplos lados, conectando uma série de pontos.
import tkinter as tk
root = tk.Tk()
root.title("Polígonos no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="lightgreen")
canvas.pack()
# Triângulo
canvas.create_polygon(50, 250, 100, 150, 150, 250, fill="lime", outline="darkgreen", width=2)
# Polígono mais complexo (estrelinha simples)
canvas.create_polygon(
200, 50, 220, 100, 270, 110, 230, 140, 240, 190,
200, 160, 160, 190, 170, 140, 130, 110, 180, 100,
fill="gold", outline="darkgoldenrod", width=2
)
root.mainloop()e) Arcos (create_arc)
Desenha uma parte de uma elipse.
import tkinter as tk
root = tk.Tk()
root.title("Arcos no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="wheat")
canvas.pack()
# Arco simples (start=ângulo inicial, extent=extensão do arco)
# style=tk.ARC (somente o arco)
canvas.create_arc(50, 50, 150, 150, start=0, extent=90, style=tk.ARC, outline="blue", width=3)
# Arco como fatia de pizza (style=tk.PIESLICE)
canvas.create_arc(200, 50, 350, 200, start=45, extent=180, style=tk.PIESLICE, fill="red", outline="black", width=2)
# Arco como corda (style=tk.CHORD)
canvas.create_arc(50, 180, 200, 280, start=270, extent=120, style=tk.CHORD, fill="green", outline="purple", width=2)
root.mainloop()3. Desenhando Texto (create_text)
Você pode adicionar texto ao seu Canvas, controlando sua fonte, cor e alinhamento.
import tkinter as tk
root = tk.Tk()
root.title("Texto no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()
# Texto simples
canvas.create_text(200, 50, text="Olá, Canvas!", fill="black", font=("Arial", 24, "bold"))
# Texto com alinhamento
canvas.create_text(100, 150, text="Esquerda", anchor=tk.W, fill="blue", font=("Verdana", 16))
canvas.create_text(300, 150, text="Direita", anchor=tk.E, fill="green", font=("Verdana", 16))
canvas.create_text(200, 250, text="Centro", anchor=tk.CENTER, fill="red", font=("Verdana", 16, "italic"))
root.mainloop()4. Desenhando Imagens (create_image)
Para exibir imagens no Canvas, você precisa primeiro carregar a imagem usando PhotoImage. O Tkinter suporta formatos GIF e PGM/PPM nativamente. Para outros formatos como PNG ou JPG, você precisará da biblioteca Pillow (PIL Fork).
import tkinter as tk
from PIL import Image, ImageTk # Certifique-se de ter 'Pillow' instalado: pip install Pillow
root = tk.Tk()
root.title("Imagens no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="darkgray")
canvas.pack()
# --- Exemplo com GIF (nativo do Tkinter) ---
# Crie um arquivo 'python_logo.gif' na mesma pasta ou forneça o caminho completo
try:
photo = tk.PhotoImage(file="python_logo.gif") # Substitua pelo caminho da sua imagem GIF
canvas.create_image(100, 100, image=photo, anchor=tk.NW)
except tk.TclError:
print("Erro: 'python_logo.gif' não encontrado ou formato inválido. Tentando com PNG...")
# Se o GIF falhar, tenta carregar um PNG com Pillow
# --- Exemplo com PNG (requer Pillow) ---
# Crie um arquivo 'tkinter_logo.png' na mesma pasta ou forneça o caminho completo
try:
# Carrega a imagem usando PIL
pil_image = Image.open("tkinter_logo.png") # Substitua pelo caminho da sua imagem PNG
# Redimensiona se necessário (opcional)
pil_image = pil_image.resize((150, 150), Image.Resampling.LANCZOS)
# Converte para um objeto PhotoImage compatível com Tkinter
tk_image = ImageTk.PhotoImage(pil_image)
canvas.create_image(250, 150, image=tk_image, anchor=tk.CENTER)
# Importante: Mantenha uma referência à imagem para evitar que seja coletada pelo garbage collector
canvas.image = tk_image
except FileNotFoundError:
print("Erro: 'tkinter_logo.png' não encontrado. Crie um ou forneça o caminho correto.")
except Exception as e:
print(f"Erro ao carregar PNG: {e}. Certifique-se de que a imagem existe e Pillow está instalado.")
root.mainloop()Nota: Para o exemplo de imagem funcionar, você precisará ter arquivos .gif e/ou .png válidos e a biblioteca Pillow instalada (pip install Pillow).
5. Manipulando Itens do Canvas
Cada item criado no Canvas recebe um ID único. Você pode usar este ID para mover, redimensionar, alterar propriedades ou excluir itens.
import tkinter as tk
root = tk.Tk()
root.title("Manipulando Itens")
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()
# Cria um retângulo e armazena seu ID
rect_id = canvas.create_rectangle(50, 50, 150, 100, fill="blue", outline="black")
print(f"ID do retângulo: {rect_id}")
# Cria um círculo e armazena seu ID
circle_id = canvas.create_oval(200, 150, 250, 200, fill="red", outline="black")
print(f"ID do círculo: {circle_id}")
# --- Métodos de manipulação ---
# a) Mover item (`move(item_id, dx, dy)`)
# Move o retângulo 50 pixels para a direita e 20 para baixo
canvas.move(rect_id, 50, 20)
# b) Mudar coordenadas (`coords(item_id, x1, y1, x2, y2, ...)`)
# Altera as coordenadas do círculo para uma nova posição e tamanho
canvas.coords(circle_id, 250, 200, 300, 250)
# c) Mudar configurações (`itemconfig(item_id, **options)`)
# Altera a cor de preenchimento do retângulo
canvas.itemconfig(rect_id, fill="green", outline="white", width=3)
# d) Deletar item (`delete(item_id)`)
# Cria um texto que será deletado após um tempo
text_id = canvas.create_text(200, 50, text="Eu vou sumir!", fill="purple", font=("Courier", 18))
def delete_text():
canvas.delete(text_id)
print(f"Item com ID {text_id} deletado.")
# Agenda a função delete_text para ser executada após 2000 milissegundos (2 segundos)
root.after(2000, delete_text)
root.mainloop()6. Interatividade: Eventos no Canvas e Itens
Uma das características mais poderosas do Canvas é a capacidade de responder a eventos do mouse ou teclado, tanto no próprio Canvas quanto em itens específicos dentro dele.
import tkinter as tk
root = tk.Tk()
root.title("Interatividade no Canvas")
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()
# Cria um retângulo e um círculo
rect_id = canvas.create_rectangle(50, 50, 150, 100, fill="blue", tags="minha_forma")
circle_id = canvas.create_oval(200, 150, 250, 200, fill="red", tags="minha_forma")
# Adiciona um texto para feedback
feedback_text = canvas.create_text(200, 20, text="Clique em algo!", fill="black", font=("Arial", 14))
# --- Funções de callback para eventos ---
# Evento no Canvas (clique em qualquer lugar no Canvas)
def on_canvas_click(event):
canvas.itemconfig(feedback_text, text=f"Clique no Canvas em ({event.x}, {event.y})")
print(f"Clique no Canvas em ({event.x}, {event.y})")
# Evento em um item específico (usando o ID)
def on_rect_click(event):
canvas.itemconfig(feedback_text, text=f"Clique no Retângulo! Cor: azul")
canvas.itemconfig(rect_id, fill="cyan") # Muda a cor do retângulo
print("Retângulo clicado!")
# Evento em um item específico (usando tags)
# Tags permitem agrupar itens e aplicar eventos a todos eles
def on_shape_enter(event):
# event.widget é o canvas. event.x, event.y são as coordenadas do mouse.
# find_closest retorna uma tupla, pegamos o primeiro elemento (o ID do item)
item_id = canvas.find_closest(event.x, event.y)[0]
canvas.itemconfig(item_id, outline="green", width=3)
canvas.itemconfig(feedback_text, text=f"Mouse sobre a forma {item_id}!")
print(f"Mouse entrou na forma com ID: {item_id}")
def on_shape_leave(event):
item_id = canvas.find_closest(event.x, event.y)[0]
canvas.itemconfig(item_id, outline="black", width=1) # Volta ao contorno original
canvas.itemconfig(feedback_text, text="Mouse saiu da forma.")
print(f"Mouse saiu da forma com ID: {item_id}")
# --- Vinculando eventos ---
# Vincula um evento ao Canvas inteiro
canvas.bind("<Button-1>", on_canvas_click) # <Button-1> é o clique do botão esquerdo do mouse
# Vincula um evento a um item específico usando seu ID
canvas.tag_bind(rect_id, "<Button-1>", on_rect_click)
# Vincula eventos a itens usando uma tag
# Todos os itens com a tag "minha_forma" responderão a esses eventos
canvas.tag_bind("minha_forma", "<Enter>", on_shape_enter) # Mouse entra no item
canvas.tag_bind("minha_forma", "<Leave>", on_shape_leave) # Mouse sai do item
root.mainloop()Neste exemplo, você aprendeu a:
- Vincular eventos ao Canvas (
canvas.bind). - Vincular eventos a itens específicos usando seu ID (
canvas.tag_bind(item_id, ...)). - Vincular eventos a grupos de itens usando tags (
canvas.tag_bind("minha_tag", ...)). - Usar
event.xeevent.ypara obter as coordenadas do clique/mouse. - Usar
canvas.find_closest()para identificar qual item está sob o cursor do mouse.
📝 Exercícios e Desafios
Hora de colocar a mão na massa e solidificar seu conhecimento! 🚀
Desafio 1: A Casa Simples 🏠
Crie um programa que desenhe uma casa simples no Canvas. A casa deve ter:
- Um corpo retangular.
- Um telhado triangular.
- Uma porta retangular.
- Uma janela quadrada.
Dica
Use create_rectangle para o corpo, porta e janela. Use create_polygon para o telhado. Lembre-se de planejar as coordenadas!
# Seu código para o Desafio 1 aqui
import tkinter as tk
root = tk.Tk()
root.title("Minha Casa no Canvas")
canvas = tk.Canvas(root, width=400, height=400, bg="skyblue")
canvas.pack()
# Corpo da casa
canvas.create_rectangle(100, 200, 300, 350, fill="brown", outline="black", width=2)
# Telhado
canvas.create_polygon(90, 200, 200, 100, 310, 200, fill="darkred", outline="black", width=2)
# Porta
canvas.create_rectangle(175, 280, 225, 350, fill="darkgoldenrod", outline="black", width=1)
# Janela
canvas.create_rectangle(120, 220, 160, 260, fill="white", outline="black", width=1)
canvas.create_line(120, 240, 160, 240, fill="black") # Divisão horizontal
canvas.create_line(140, 220, 140, 260, fill="black") # Divisão vertical
root.mainloop()
Desafio 2: Semáforo Interativo 🚦
Crie um semáforo com três círculos (vermelho, amarelo, verde). Ao clicar em cada círculo, ele deve acender (mudar para uma cor mais brilhante) e os outros devem apagar (voltar para uma cor escura ou cinza).
Dica
Crie os três círculos. Armazene os IDs dos círculos. Use canvas.tag_bind ou canvas.tag_bind(item_id, ...) para cada círculo. Na função de callback, mude a cor de preenchimento do círculo clicado e dos outros.
# Seu código para o Desafio 2 aqui
import tkinter as tk
root = tk.Tk()
root.title("Semáforo Interativo")
canvas = tk.Canvas(root, width=150, height=350, bg="black")
canvas.pack()
# Coordenadas e cores
center_x = 75
radius = 40
spacing = 100
# IDs dos círculos
red_light = canvas.create_oval(center_x - radius, 50 - radius, center_x + radius, 50 + radius, fill="gray", outline="white", width=2, tags="red")
yellow_light = canvas.create_oval(center_x - radius, 50 + spacing - radius, center_x + radius, 50 + spacing + radius, fill="gray", outline="white", width=2, tags="yellow")
green_light = canvas.create_oval(center_x - radius, 50 + 2*spacing - radius, center_x + radius, 50 + 2*spacing + radius, fill="gray", outline="white", width=2, tags="green")
# Cores ativas e inativas
color_map = {
"red": {"active": "red", "inactive": "darkred"},
"yellow": {"active": "yellow", "inactive": "darkgoldenrod"},
"green": {"active": "lime", "inactive": "darkgreen"}
}
# Inicializa o semáforo com vermelho aceso
canvas.itemconfig(red_light, fill=color_map["red"]["active"])
def turn_on_light(light_id, color_name):
# Apaga todas as luzes
for light in [red_light, yellow_light, green_light]:
tag = canvas.gettags(light)[0] # Pega a tag do item
canvas.itemconfig(light, fill=color_map[tag]["inactive"])
# Acende a luz clicada
canvas.itemconfig(light_id, fill=color_map[color_name]["active"])
# Vincula eventos de clique aos círculos
canvas.tag_bind("red", "<Button-1>", lambda event: turn_on_light(red_light, "red"))
canvas.tag_bind("yellow", "<Button-1>", lambda event: turn_on_light(yellow_light, "yellow"))
canvas.tag_bind("green", "<Button-1>", lambda event: turn_on_light(green_light, "green"))
root.mainloop()Desafio 3: Objeto Arrastável 🖐️
Crie um quadrado ou círculo no Canvas. Faça com que ele possa ser arrastado pelo usuário com o mouse.
Dica
Você precisará de três eventos:
<Button-1>: Quando o botão do mouse é pressionado sobre o item. Armazene as coordenadas iniciais do mouse.<B1-Motion>: Enquanto o botão esquerdo do mouse está pressionado e o mouse se move. Calcule o deslocamento (dx, dy) e usecanvas.move(). Atualize as coordenadas iniciais.<ButtonRelease-1>: Quando o botão do mouse é liberado. (Opcional, mas útil para finalizar o arrasto).
# Seu código para o Desafio 3 aqui
import tkinter as tk
root = tk.Tk()
root.title("Objeto Arrastável")
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()
# Cria um quadrado arrastável
square_id = canvas.create_rectangle(50, 50, 100, 100, fill="purple", outline="black", tags="draggable")
# Variáveis para armazenar a posição inicial do mouse
start_x = 0
start_y = 0
def on_drag_start(event):
global start_x, start_y
# Obtém as coordenadas do mouse no momento do clique
start_x = event.x
start_y = event.y
# Manda o item para frente para que ele fique visível ao arrastar
canvas.tag_raise(square_id)
def on_drag_motion(event):
global start_x, start_y
# Calcula o deslocamento do mouse
dx = event.x - start_x
dy = event.y - start_y
# Move o item
canvas.move(square_id, dx, dy)
# Atualiza as coordenadas iniciais para o próximo movimento
start_x = event.x
start_y = event.y
# Vincula os eventos ao item arrastável
canvas.tag_bind("draggable", "<Button-1>", on_drag_start)
canvas.tag_bind("draggable", "<B1-Motion>", on_drag_motion) # <B1-Motion> = Botão 1 (esquerdo) pressionado e movendo
root.mainloop()🎯 Resumo e Próximos Passos
Nesta aula, você explorou o poderoso widget Canvas do Tkinter. Aprendemos a:
- Criar e configurar um
Canvas. - Desenhar diversas formas geométricas como linhas, retângulos, ovais, polígonos e arcos.
- Adicionar texto e imagens ao Canvas.
- Manipular itens do Canvas usando seus IDs ou tags.
- Tornar o Canvas e seus itens interativos, respondendo a eventos do mouse.
O Canvas é a base para muitas aplicações gráficas e interativas em Tkinter. Com ele, você pode construir visualizações de dados complexas, editores simples ou até mesmo pequenos jogos.
⏭️ Próximos Passos:
No próximo módulo, aprofundaremos ainda mais na interatividade, explorando como combinar diferentes widgets e gerenciar eventos de forma mais complexa para criar aplicações robustas. Continuaremos a praticar com projetos que integram o que aprendemos sobre Canvas com outros elementos da interface gráfica.
Parabéns por dominar o Canvas! Continue praticando e explorando as possibilidades. ✨