Untitled

 avatar
unknown
plain_text
2 months ago
12 kB
7
Indexable
import tkinter as tk
from tkinter import messagebox
import random

# ======================================================================
# CONFIGURAÇÃO HARDCODED DAS PREFERÊNCIAS POR QUADRANTE
# A matriz é 3 linhas x 5 colunas, totalizando 15 quadrantes.
# A lista abaixo define a probabilidade de cada quadrante ser escolhido.
# A soma deve ser 1.0. Altere os valores conforme sua preferência.
# O mapeamento é feito por linha:
#   indices 0-4   -> primeira linha (topo)
#   indices 5-9   -> segunda linha
#   indices 10-14 -> terceira linha (base)
# ======================================================================
QUADRANT_PROBS = [
    0.10, 0.05, 0.05, 0.05, 0.10,   # linha 0
    0.05, 0.10, 0.10, 0.10, 0.05,   # linha 1
    0.05, 0.05, 0.05, 0.05, 0.05    # linha 2
]
# Verifica se a soma é próxima de 1
if abs(sum(QUADRANT_PROBS) - 1.0) > 1e-6:
    raise ValueError("A soma das probabilidades dos quadrantes deve ser 1.0")

NUM_ROWS = 3
NUM_COLS = 5

class CircleApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Exibição de Círculos")

        # Variáveis de configuração
        self.period = tk.DoubleVar(value=5.0)
        self.quantity = tk.IntVar(value=10)
        self.min_diam = tk.IntVar(value=20)
        self.max_diam = tk.IntVar(value=80)
        self.bg_color = tk.StringVar(value="white")
        self.circle_color = tk.StringVar(value="red")
        self.show_borders = tk.BooleanVar(value=False)
        self.border_color = tk.StringVar(value="black")

        # Controle da animação
        self.animation_running = False
        self.after_id = None
        self.canvas = None
        self.paused = False          # NOVO: estado de pausa

        # Vincula a tecla 'p' (minúscula e maiúscula) ao método de pausa
        root.bind('<KeyPress-p>', self.toggle_pause)
        root.bind('<KeyPress-P>', self.toggle_pause)

        # Construir frames
        self.config_frame = tk.Frame(root)
        self.anim_frame = tk.Frame(root)

        self.build_config_frame()
        self.build_animation_frame()

        # Iniciar com o frame de configuração visível
        self.config_frame.pack(fill=tk.BOTH, expand=True)
        self.anim_frame.pack_forget()

    # ------------------------------------------------------------------
    def build_config_frame(self):
        """Constrói o painel de configuração."""
        tk.Label(self.config_frame, text="CONFIGURAÇÕES", font=("Arial", 14, "bold")).pack(pady=5)

        f1 = tk.Frame(self.config_frame)
        f1.pack(pady=2, fill=tk.X, padx=10)
        tk.Label(f1, text="Período (s):").pack(side=tk.LEFT)
        tk.Entry(f1, textvariable=self.period, width=8).pack(side=tk.LEFT, padx=5)

        f2 = tk.Frame(self.config_frame)
        f2.pack(pady=2, fill=tk.X, padx=10)
        tk.Label(f2, text="Quantidade:").pack(side=tk.LEFT)
        tk.Entry(f2, textvariable=self.quantity, width=8).pack(side=tk.LEFT, padx=5)

        f3 = tk.Frame(self.config_frame)
        f3.pack(pady=2, fill=tk.X, padx=10)
        tk.Label(f3, text="Diâmetro mín:").pack(side=tk.LEFT)
        tk.Entry(f3, textvariable=self.min_diam, width=6).pack(side=tk.LEFT, padx=2)
        tk.Label(f3, text="máx:").pack(side=tk.LEFT)
        tk.Entry(f3, textvariable=self.max_diam, width=6).pack(side=tk.LEFT, padx=5)

        f4 = tk.Frame(self.config_frame)
        f4.pack(pady=2, fill=tk.X, padx=10)
        tk.Label(f4, text="Cor do fundo:").pack(side=tk.LEFT)
        tk.Entry(f4, textvariable=self.bg_color, width=12).pack(side=tk.LEFT, padx=5)

        f5 = tk.Frame(self.config_frame)
        f5.pack(pady=2, fill=tk.X, padx=10)
        tk.Label(f5, text="Cor dos círculos:").pack(side=tk.LEFT)
        tk.Entry(f5, textvariable=self.circle_color, width=12).pack(side=tk.LEFT, padx=5)

        f6 = tk.Frame(self.config_frame)
        f6.pack(pady=2, fill=tk.X, padx=10)
        tk.Checkbutton(f6, text="Mostrar bordas dos quadrantes",
                       variable=self.show_borders).pack(side=tk.LEFT)

        f7 = tk.Frame(self.config_frame)
        f7.pack(pady=2, fill=tk.X, padx=10)
        tk.Label(f7, text="Cor da borda:").pack(side=tk.LEFT)
        tk.Entry(f7, textvariable=self.border_color, width=12).pack(side=tk.LEFT, padx=5)

        tk.Button(self.config_frame, text="START", command=self.start_animation,
                  font=("Arial", 12, "bold"), bg="lightgreen").pack(pady=10)

    # ------------------------------------------------------------------
    def build_animation_frame(self):
        """Constrói o frame da animação."""
        self.canvas = tk.Canvas(self.anim_frame, bg="white")
        self.canvas.pack(fill=tk.BOTH, expand=True)

        self.stop_button = tk.Button(self.anim_frame, text="Stop", command=self.stop_animation)
        self.stop_button.pack(pady=5)

        self.canvas.bind("<Configure>", self.on_canvas_resize)

    # ------------------------------------------------------------------
    def validate_config(self):
        """Valida os valores inseridos."""
        try:
            period = float(self.period.get())
            if period <= 0:
                raise ValueError("Período deve ser positivo.")
            quantity = int(self.quantity.get())
            if quantity <= 0:
                raise ValueError("Quantidade deve ser um inteiro positivo.")
            min_d = int(self.min_diam.get())
            max_d = int(self.max_diam.get())
            if min_d <= 0 or max_d <= 0:
                raise ValueError("Diâmetros devem ser positivos.")
            if min_d > max_d:
                raise ValueError("Diâmetro mínimo não pode ser maior que o máximo.")
            return True
        except ValueError as e:
            messagebox.showerror("Erro de validação", str(e))
            return False

    # ------------------------------------------------------------------
    def start_animation(self):
        """Inicia a sequência de círculos."""
        if not self.validate_config():
            return

        self.config_frame.pack_forget()
        self.anim_frame.pack(fill=tk.BOTH, expand=True)

        self.canvas.configure(bg=self.bg_color.get())
        self.canvas.delete("all")

        self.animation_running = True
        self.paused = False        # reseta a pausa
        self.circle_count = 0
        self.schedule_next_circle()

    # ------------------------------------------------------------------
    def stop_animation(self):
        """Para a animação e retorna ao menu."""
        if self.after_id:
            self.root.after_cancel(self.after_id)
            self.after_id = None
        self.animation_running = False
        self.paused = False

        self.anim_frame.pack_forget()
        self.config_frame.pack(fill=tk.BOTH, expand=True)
        self.stop_button.config(text="Stop", command=self.stop_animation)

    # ------------------------------------------------------------------
    def toggle_pause(self, event=None):
        """Alterna entre pausado e executando (tecla 'p')."""
        if not self.animation_running:
            return  # só funciona durante a animação ativa

        self.paused = not self.paused

        if self.paused:
            # Cancela qualquer evento futuro imediato
            if self.after_id:
                self.root.after_cancel(self.after_id)
                self.after_id = None
            # Mostra indicador de pausa no canvas
            self.show_pause_indicator()
        else:
            # Remove indicador de pausa e retoma a exibição
            self.schedule_next_circle()   # desenha o próximo e agenda o seguinte

    # ------------------------------------------------------------------
    def show_pause_indicator(self):
        """Exibe um texto 'PAUSADO' no centro do canvas."""
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        self.canvas.create_text(w//2, h//2, text="PAUSADO",
                                fill="black", font=("Arial", 24, "bold"),
                                tags="pause_text")

    # ------------------------------------------------------------------
    def schedule_next_circle(self):
        """Agenda o próximo círculo, respeitando pausa e fim da animação."""
        if not self.animation_running:
            return
        if self.paused:
            # Se pausado, não faz nada; aguarda o comando de resume
            return

        total_circles = int(self.quantity.get())
        period = float(self.period.get())

        if self.circle_count >= total_circles:
            self.animation_running = False
            self.stop_button.config(text="Back", command=self.stop_animation)
            self.canvas.create_text(
                self.canvas.winfo_width() // 2,
                self.canvas.winfo_height() // 2,
                text="Fim da animação",
                fill="black",
                font=("Arial", 16, "bold")
            )
            return

        interval_ms = int((period / total_circles) * 1000)

        self.draw_circle()
        self.circle_count += 1

        self.after_id = self.root.after(interval_ms, self.schedule_next_circle)

    # ------------------------------------------------------------------
    def draw_circle(self):
        """Desenha um círculo (substitui o anterior)."""
        self.canvas.delete("all")

        if self.show_borders.get():
            self.draw_quadrant_borders()

        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        if w <= 1 or h <= 1:
            return

        quadrant_idx = random.choices(range(NUM_ROWS * NUM_COLS),
                                      weights=QUADRANT_PROBS, k=1)[0]
        row = quadrant_idx // NUM_COLS
        col = quadrant_idx % NUM_COLS

        col_width = w / NUM_COLS
        row_height = h / NUM_ROWS
        x0 = col * col_width
        y0 = row * row_height
        x1 = x0 + col_width
        y1 = y0 + row_height

        min_d = int(self.min_diam.get())
        max_d = int(self.max_diam.get())
        diameter = random.randint(min_d, max_d)
        radius = diameter / 2.0

        margin = radius
        if col_width < diameter:
            cx = (x0 + x1) / 2
        else:
            cx = random.uniform(x0 + margin, x1 - margin)
        if row_height < diameter:
            cy = (y0 + y1) / 2
        else:
            cy = random.uniform(y0 + margin, y1 - margin)

        self.canvas.create_oval(
            cx - radius, cy - radius,
            cx + radius, cy + radius,
            fill=self.circle_color.get(),
            outline=self.circle_color.get()
        )

    # ------------------------------------------------------------------
    def draw_quadrant_borders(self):
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        if w <= 1 or h <= 1:
            return

        border_col = self.border_color.get()
        for i in range(1, NUM_ROWS):
            y = i * (h / NUM_ROWS)
            self.canvas.create_line(0, y, w, y, fill=border_col)
        for j in range(1, NUM_COLS):
            x = j * (w / NUM_COLS)
            self.canvas.create_line(x, 0, x, h, fill=border_col)

    # ------------------------------------------------------------------
    def on_canvas_resize(self, event):
        pass

# ======================================================================
if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("1920x1080")
    app = CircleApp(root)
    root.mainloop()
Editor is loading...
Leave a Comment