import tkinter as tk
from tkinter import ttk, messagebox
import matplotlib
# Configura o backend do Matplotlib para funcionar com Tkinter
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
class AppPessoas(tk.Tk):
"""Classe principal da aplicação Tkinter."""
def __init__(self):
super().__init__()
self.title("Cadastro e Gráfico de Pessoas")
self.geometry("800x600")
# Lista para armazenar os dados de pessoas: [{"idade": 30, "peso": 75}, ...]
self.dados_pessoas = []
# Configura as abas
self.notebook = ttk.Notebook(self)
self.notebook.pack(pady=10, padx=10, expand=True, fill="both")
# Cria as abas
self.aba_cadastro = ttk.Frame(self.notebook)
self.aba_grafico = ttk.Frame(self.notebook)
self.notebook.add(self.aba_cadastro, text="1. Cadastro")
self.notebook.add(self.aba_grafico, text="2. Gráfico")
# Vincula a função de atualização ao evento de mudança de aba
self.notebook.bind("<>", self.on_tab_change)
# Configura o conteúdo de cada aba
self.setup_aba_cadastro()
self.setup_aba_grafico()
def setup_aba_cadastro(self):
"""Configura os widgets na aba de Cadastro."""
# Frame principal para centralizar
frame_cadastro = ttk.Frame(self.aba_cadastro, padding="20")
frame_cadastro.pack(expand=True, anchor="center")
# Rótulo e campo para Idade
ttk.Label(frame_cadastro, text="Idade (anos):").grid(row=0, column=0, pady=10, sticky="w")
self.entry_idade = ttk.Entry(frame_cadastro, width=15)
self.entry_idade.grid(row=0, column=1, pady=10, padx=5, sticky="ew")
# Rótulo e campo para Peso
ttk.Label(frame_cadastro, text="Peso (kg):").grid(row=1, column=0, pady=10, sticky="w")
self.entry_peso = ttk.Entry(frame_cadastro, width=15)
self.entry_peso.grid(row=1, column=1, pady=10, padx=5, sticky="ew")
# Botão para adicionar
botao_adicionar = ttk.Button(frame_cadastro, text="Adicionar Pessoa", command=self.adicionar_pessoa)
botao_adicionar.grid(row=2, column=0, columnspan=2, pady=20)
# Rótulo para mostrar a contagem de pessoas
self.label_contagem = ttk.Label(frame_cadastro, text=f"Pessoas cadastradas: {len(self.dados_pessoas)}")
self.label_contagem.grid(row=3, column=0, columnspan=2, pady=10)
def adicionar_pessoa(self):
"""Coleta, valida e armazena os dados de idade e peso."""
try:
idade = int(self.entry_idade.get())
peso = float(self.entry_peso.get().replace(',', '.'))
if idade <= 0 or peso <= 0:
raise ValueError("Idade e Peso devem ser maiores que zero.")
# Armazena os dados
self.dados_pessoas.append({"idade": idade, "peso": peso})
# Limpa os campos de entrada
self.entry_idade.delete(0, tk.END)
self.entry_peso.delete(0, tk.END)
# Atualiza a contagem
self.label_contagem.config(text=f"Pessoas cadastradas: {len(self.dados_pessoas)}")
messagebox.showinfo("Sucesso", f"Pessoa adicionada: Idade {idade}, Peso {peso}kg.")
except ValueError as e:
messagebox.showerror("Erro de Entrada", f"Por favor, insira valores válidos: {e}")
except Exception as e:
messagebox.showerror("Erro", f"Ocorreu um erro: {e}")
def setup_aba_grafico(self):
"""Configura a área de desenho do gráfico na aba de Gráfico."""
# Cria a Figura (área de desenho) do Matplotlib
self.figura = Figure(figsize=(5, 5), dpi=100)
self.eixo = self.figura.add_subplot(111)
# Cria o Canvas do Tkinter a partir da Figura
self.canvas = FigureCanvasTkAgg(self.figura, master=self.aba_grafico)
# Adiciona o widget do canvas à aba
self.canvas_widget = self.canvas.get_tk_widget()
self.canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
# Desenha o gráfico inicial vazio
self.plotar_grafico()
def plotar_grafico(self):
"""Gera o gráfico de Dispersão (Scatter Plot) com frequência de peso por idade."""
# Limpa o gráfico anterior
self.eixo.clear()
if not self.dados_pessoas:
self.eixo.text(0.5, 0.5, 'Nenhum dado cadastrado para exibir.',
ha='center', va='center', transform=self.eixo.transAxes)
self.figura.canvas.draw()
return
# 1. Preparar os dados para contagem de frequência
# Cria tuplas (idade, peso) e conta a frequência de cada combinação
combinacoes = [(d['idade'], d['peso']) for d in self.dados_pessoas]
from collections import Counter
frequencias = Counter(combinacoes)
# 2. Separar os dados contados
idades = [c[0] for c in frequencias.keys()]
pesos = [c[1] for c in frequencias.keys()]
contagens = np.array(list(frequencias.values()))
# 3. Mapear a contagem para o tamanho do ponto (escala visual)
# O tamanho (s) dos pontos no scatter plot será proporcional à frequência
# Usamos log para suavizar grandes diferenças e adicionamos um offset
tamanhos = (np.log(contagens + 1) * 200) + 50
# 4. Desenhar o gráfico de dispersão (Scatter Plot)
scatter = self.eixo.scatter(idades, pesos, s=tamanhos, alpha=0.6, edgecolors='w', linewidth=1)
# 5. Configurar rótulos e título
self.eixo.set_title("Frequência de Peso por Idade (Tamanho do Ponto = Frequência)")
self.eixo.set_xlabel("Idade (anos)")
self.eixo.set_ylabel("Peso (kg)")
self.eixo.grid(True, linestyle='--', alpha=0.7)
self.eixo.margins(0.1)
# Garante que os valores dos eixos sejam inteiros se forem todos inteiros
if all(isinstance(i, int) for i in idades) and idades:
self.eixo.xaxis.set_major_locator(matplotlib.ticker.MaxNLocator(integer=True))
# 6. Forçar o redesenho do canvas
self.figura.canvas.draw()
def on_tab_change(self, event):
"""Chamado quando o usuário muda de aba. Atualiza o gráfico se for para a aba de Gráfico."""
aba_selecionada = self.notebook.tab(self.notebook.select(), "text")
if aba_selecionada == "2. Gráfico":
self.plotar_grafico()
# Ponto de entrada do programa
if __name__ == "__main__":
app = AppPessoas()
app.mainloop()