Compare commits
No commits in common. "df0f6059ee7c6a800b2b21cc3d2f544fb7d00771" and "0ee52d27cb0f5f813887bc46329fa7b396c0ae36" have entirely different histories.
df0f6059ee
...
0ee52d27cb
Binary file not shown.
Binary file not shown.
Binary file not shown.
44
config.py
44
config.py
|
|
@ -1,7 +1,6 @@
|
|||
# config.py
|
||||
import os
|
||||
import psutil
|
||||
import uuid # NUEVO: Para identificar cada instancia del chat
|
||||
|
||||
# --- Rutas y Archivos ---
|
||||
SCRIPT_NAME = "backup_script.sh"
|
||||
|
|
@ -13,26 +12,19 @@ PROGRESS_FILE = 'progress.tmp'
|
|||
ALARM_SAVE_FILE = os.path.join(BASE_DIR, "data", "alarmas.json")
|
||||
ALARM_FOLDER = os.path.join(BASE_DIR, "data", "alarmas")
|
||||
ALERTA_SOUND_FILE = None
|
||||
|
||||
SCRAPING_FOLDER = os.path.join(BASE_DIR, "data", "scraping")
|
||||
SCRAPING_CONFIG_FOLDER = os.path.join(BASE_DIR, "data", "tipo_scraping")
|
||||
|
||||
NOTES_FOLDER = os.path.join(BASE_DIR, "data", "notas")
|
||||
|
||||
# --- NUEVO: Configuración de Chat Local (IPC) ---
|
||||
CHAT_FILE = os.path.join(BASE_DIR, "data", "local_chat.txt")
|
||||
INSTANCE_ID = str(uuid.uuid4())[:5] # ID único corto para esta ventana/proceso
|
||||
|
||||
# --- NUEVO: Configuración de Servidor de Correo ---
|
||||
EMAIL_SERVER_IP = "10.10.0.101"
|
||||
EMAIL_SMTP_PORT = 25
|
||||
EMAIL_IMAP_PORT = 143
|
||||
EMAIL_POP_PORT = 110
|
||||
|
||||
# --- Variables de Monitoreo ---
|
||||
MAX_PUNTOS = 30
|
||||
tiempos = list(range(-MAX_PUNTOS + 1, 1))
|
||||
num_cores = psutil.cpu_count(logical=True)
|
||||
datos_cores = [0] * num_cores
|
||||
|
||||
# --- Datos Dinámicos (Inicialización) ---
|
||||
datos_cpu = [0] * MAX_PUNTOS
|
||||
datos_mem = [0] * MAX_PUNTOS
|
||||
datos_net_sent = [0] * MAX_PUNTOS
|
||||
|
|
@ -46,26 +38,32 @@ registro_csv_activo = False
|
|||
system_log = None
|
||||
progress_bar = None
|
||||
editor_texto = None
|
||||
scraping_progress_bar = None
|
||||
scraping_output_text = None
|
||||
scraping_url_input = None
|
||||
scraping_selector_input = None
|
||||
scraping_attr_input = None
|
||||
scraping_config_file_label = None
|
||||
scraping_config_data = {}
|
||||
scraping_running = False
|
||||
scraping_progress_bar = None # Barra de progreso de scraping
|
||||
scraping_output_text = None # Área de texto de salida de scraping
|
||||
scraping_url_input = None # Variable de control de la URL de scraping
|
||||
scraping_selector_input = None # Entrada para el selector CSS
|
||||
scraping_attr_input = None # Entrada para el atributo CSS
|
||||
scraping_config_file_label = None # Label para mostrar el archivo de configuración cargado
|
||||
scraping_config_data = {} # Diccionario para almacenar la configuración JSON de scraping
|
||||
scraping_running = False # Bandera de estado de ejecución de scraping
|
||||
|
||||
# Variables de Alarma
|
||||
alarmas_programadas = {}
|
||||
alarma_counter = 0
|
||||
|
||||
# Control de Sonido
|
||||
alarma_volumen = 0.5
|
||||
alarma_sonando = False
|
||||
|
||||
juego_window = None
|
||||
juego_running = False
|
||||
# Control de Juegos (NUEVO)
|
||||
juego_window = None # Referencia a la ventana Toplevel del juego
|
||||
juego_running = False # Bandera de estado del juego
|
||||
|
||||
# Control de Música Adicional (NUEVO)
|
||||
current_music_file = None
|
||||
music_sonando = False
|
||||
|
||||
# Variables de UI
|
||||
label_hostname = None
|
||||
label_os_info = None
|
||||
label_cpu_model = None
|
||||
|
|
@ -74,6 +72,6 @@ label_disk_total = None
|
|||
label_net_info = None
|
||||
label_uptime = None
|
||||
|
||||
label_1 = None
|
||||
label_2 = None
|
||||
label_1 = None # Estado Backup
|
||||
label_2 = None # Estado Registro CSV
|
||||
label_fecha_hora = None
|
||||
250
system_utils.py
250
system_utils.py
|
|
@ -15,16 +15,6 @@ import pygame
|
|||
import json
|
||||
import random
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import poplib
|
||||
import smtplib
|
||||
import imaplib
|
||||
import email
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.header import decode_header
|
||||
|
||||
# Importaciones directas de módulos (Acceso con el prefijo del módulo)
|
||||
import config
|
||||
import monitor_manager
|
||||
|
|
@ -971,243 +961,3 @@ def simular_juego_camellos(root):
|
|||
|
||||
log_event("Simulación de Carrera de Camellos iniciada.")
|
||||
juego_window.after(100, avanzar_carrera) # Iniciar la simulación
|
||||
|
||||
# ===============================================
|
||||
# Funcionalidades de Chat Local (IPC)
|
||||
# ===============================================
|
||||
def inicializar_chat():
|
||||
"""Usa un socket local para garantizar con 100% de precisión si somos la primera instancia."""
|
||||
try:
|
||||
# Creamos un socket de red local
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# Intentamos ocupar el puerto 54321 de forma exclusiva
|
||||
s.bind(("127.0.0.1", 54321))
|
||||
|
||||
# Si llegamos aquí sin dar error, SOMOS LA ÚNICA VENTANA ABIERTA.
|
||||
# Guardamos el socket en config para mantener la "puerta" bloqueada mientras la app viva.
|
||||
config.instancia_socket = s
|
||||
|
||||
# Como somos la primera instancia, BORRAMOS EL CHAT SIN PIEDAD
|
||||
if os.path.exists(config.CHAT_FILE):
|
||||
os.remove(config.CHAT_FILE)
|
||||
|
||||
log_event("Primera instancia detectada: Historial de chat limpiado al 100%.")
|
||||
|
||||
except socket.error:
|
||||
# Si el puerto ya está ocupado, significa que YA TIENES otra ventana abierta.
|
||||
# Por lo tanto, NO borramos el chat, simplemente nos unimos.
|
||||
log_event("Segunda instancia detectada: Uniéndose al chat existente.")
|
||||
except Exception as e:
|
||||
log_event(f"Error inicializando chat: {e}")
|
||||
|
||||
def enviar_mensaje_chat(entrada_widget, alias_var):
|
||||
"""Escribe un nuevo mensaje en el archivo usando el alias personalizado."""
|
||||
mensaje = entrada_widget.get().strip()
|
||||
if not mensaje:
|
||||
return
|
||||
|
||||
entrada_widget.delete(0, tk.END)
|
||||
os.makedirs(os.path.dirname(config.CHAT_FILE), exist_ok=True)
|
||||
|
||||
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
|
||||
# Usar el alias del usuario o un valor por defecto si lo deja en blanco
|
||||
alias = alias_var.get().strip() or f"User-{config.INSTANCE_ID}"
|
||||
linea = f"[{timestamp}] {alias}: {mensaje}\n"
|
||||
|
||||
try:
|
||||
with open(config.CHAT_FILE, "a", encoding="utf-8") as f:
|
||||
f.write(linea)
|
||||
except Exception as e:
|
||||
log_event(f"ERROR escribiendo en el chat: {e}")
|
||||
|
||||
def monitor_chat_file(text_widget, root):
|
||||
"""Hilo que vigila cambios en el archivo de chat."""
|
||||
last_size = 0
|
||||
if not os.path.exists(config.CHAT_FILE):
|
||||
os.makedirs(os.path.dirname(config.CHAT_FILE), exist_ok=True)
|
||||
open(config.CHAT_FILE, 'a').close()
|
||||
|
||||
while config.monitor_running:
|
||||
if not root.winfo_exists(): break
|
||||
try:
|
||||
current_size = os.path.getsize(config.CHAT_FILE)
|
||||
if current_size > last_size:
|
||||
with open(config.CHAT_FILE, "r", encoding="utf-8") as f:
|
||||
f.seek(last_size)
|
||||
nuevos_mensajes = f.read()
|
||||
last_size = current_size
|
||||
if nuevos_mensajes:
|
||||
root.after(0, text_widget.config, {"state": tk.NORMAL})
|
||||
root.after(0, text_widget.insert, tk.END, nuevos_mensajes)
|
||||
root.after(0, text_widget.see, tk.END)
|
||||
root.after(0, text_widget.config, {"state": tk.DISABLED})
|
||||
elif current_size < last_size:
|
||||
# Si el archivo se redujo (se reinició la app), reseteamos el lector
|
||||
last_size = 0
|
||||
root.after(0, text_widget.config, {"state": tk.NORMAL})
|
||||
root.after(0, lambda: text_widget.delete('1.0', tk.END))
|
||||
root.after(0, text_widget.config, {"state": tk.DISABLED})
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(0.5)
|
||||
|
||||
def enviar_correo(usuario, password, destinatario, asunto, cuerpo, root):
|
||||
"""Envía un correo manejando correctamente los servidores Relay locales."""
|
||||
def tarea():
|
||||
try:
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = usuario
|
||||
msg['To'] = destinatario
|
||||
msg['Subject'] = asunto
|
||||
msg.attach(MIMEText(cuerpo, 'plain'))
|
||||
|
||||
server = smtplib.SMTP(config.EMAIL_SERVER_IP, config.EMAIL_SMTP_PORT, timeout=10)
|
||||
|
||||
try:
|
||||
server.starttls()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# --- EL TRUCO ESTÁ AQUÍ ---
|
||||
try:
|
||||
server.login(usuario, password)
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
# El error 535 salta aquí. Lo silenciamos porque el puerto 25
|
||||
# local seguramente nos deje enviar el mensaje sin login.
|
||||
pass
|
||||
except smtplib.SMTPNotSupportedError:
|
||||
pass
|
||||
|
||||
# Enviamos el mensaje directamente
|
||||
server.send_message(msg)
|
||||
server.quit()
|
||||
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda: messagebox.showinfo("Éxito", "Correo enviado correctamente."))
|
||||
root.after(0, lambda: log_event(f"Correo enviado a {destinatario}"))
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda err=error_msg: messagebox.showerror("Error SMTP", f"No se pudo enviar: {err}"))
|
||||
root.after(0, lambda err=error_msg: log_event(f"Error SMTP: {err}"))
|
||||
|
||||
threading.Thread(target=tarea, daemon=True).start()
|
||||
|
||||
|
||||
def cargar_correos(usuario, password, treeview, root):
|
||||
"""Descarga los correos mediante IMAP en un hilo separado, asegurando el cierre de conexión."""
|
||||
def tarea():
|
||||
mail = None
|
||||
try:
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda: treeview.delete(*treeview.get_children()))
|
||||
|
||||
mail = imaplib.IMAP4(config.EMAIL_SERVER_IP, config.EMAIL_IMAP_PORT)
|
||||
mail.login(usuario, password)
|
||||
mail.select('inbox')
|
||||
|
||||
status, messages = mail.search(None, 'ALL')
|
||||
|
||||
if messages[0]: # Solo procesar si hay correos
|
||||
email_ids = messages[0].split()
|
||||
|
||||
for e_id in email_ids[-10:]:
|
||||
_, msg_data = mail.fetch(e_id, '(RFC822)')
|
||||
for response_part in msg_data:
|
||||
if isinstance(response_part, tuple):
|
||||
msg = email.message_from_bytes(response_part[1])
|
||||
|
||||
asunto_header = msg.get("Subject", "Sin Asunto")
|
||||
asunto, encoding = decode_header(asunto_header)[0]
|
||||
if isinstance(asunto, bytes):
|
||||
asunto = asunto.decode(encoding if encoding else 'utf-8')
|
||||
|
||||
remitente = msg.get("From", "Desconocido")
|
||||
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda eid=e_id, rem=remitente, asu=asunto: treeview.insert('', 0, values=(eid.decode(), rem, asu)))
|
||||
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda: log_event("Bandeja de entrada actualizada vía IMAP."))
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda err=error_msg: messagebox.showerror("Error IMAP", f"Fallo al conectar: {err}"))
|
||||
finally:
|
||||
# ESTO ES LO MÁS IMPORTANTE: Cierra la sesión pase lo que pase para evitar el Errno 111
|
||||
if mail is not None:
|
||||
try:
|
||||
mail.logout()
|
||||
except:
|
||||
pass
|
||||
|
||||
threading.Thread(target=tarea, daemon=True).start()
|
||||
|
||||
|
||||
def comprobar_login_correo(usuario, password, login_frame, mail_content_frame, root):
|
||||
"""Verifica las credenciales por IMAP antes de mostrar la bandeja de correo."""
|
||||
def tarea():
|
||||
try:
|
||||
# Prueba de conexión rápida
|
||||
mail = imaplib.IMAP4(config.EMAIL_SERVER_IP, config.EMAIL_IMAP_PORT)
|
||||
mail.login(usuario, password)
|
||||
mail.logout()
|
||||
|
||||
# Si llega aquí, el usuario es válido. Cambiamos las pantallas.
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda: login_frame.pack_forget())
|
||||
root.after(0, lambda: mail_content_frame.pack(fill=tk.BOTH, expand=True))
|
||||
root.after(0, lambda: log_event(f"Sesión iniciada correctamente como {usuario}"))
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda err=error_msg: messagebox.showerror("Autenticación Fallida", f"Credenciales incorrectas o servidor caído:\n{err}"))
|
||||
|
||||
threading.Thread(target=tarea, daemon=True).start()
|
||||
|
||||
def limpiar_chat_al_cerrar():
|
||||
"""Ya no hace falta borrar al cerrar, se limpiará automáticamente al volver a abrir la primera instancia."""
|
||||
pass
|
||||
|
||||
def cargar_correos(usuario, password, treeview, root):
|
||||
"""Descarga los correos mediante IMAP en un hilo separado."""
|
||||
def tarea():
|
||||
try:
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda: treeview.delete(*treeview.get_children()))
|
||||
|
||||
mail = imaplib.IMAP4(config.EMAIL_SERVER_IP, config.EMAIL_IMAP_PORT)
|
||||
mail.login(usuario, password)
|
||||
mail.select('inbox')
|
||||
|
||||
status, messages = mail.search(None, 'ALL')
|
||||
email_ids = messages[0].split()
|
||||
|
||||
for e_id in email_ids[-10:]:
|
||||
_, msg_data = mail.fetch(e_id, '(RFC822)')
|
||||
for response_part in msg_data:
|
||||
if isinstance(response_part, tuple):
|
||||
msg = email.message_from_bytes(response_part[1])
|
||||
|
||||
asunto, encoding = decode_header(msg["Subject"])[0]
|
||||
if isinstance(asunto, bytes):
|
||||
asunto = asunto.decode(encoding if encoding else 'utf-8')
|
||||
|
||||
remitente = msg.get("From")
|
||||
|
||||
if root.winfo_exists():
|
||||
# CORRECCIÓN: Envolvemos en un lambda para usar el keyword 'values' sin que Tkinter explote
|
||||
root.after(0, lambda eid=e_id, rem=remitente, asu=asunto: treeview.insert('', 0, values=(eid.decode(), rem, asu)))
|
||||
mail.logout()
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda: log_event("Bandeja de entrada actualizada vía IMAP."))
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if root.winfo_exists():
|
||||
root.after(0, lambda err=error_msg: messagebox.showerror("Error IMAP", f"Fallo al conectar: {err}"))
|
||||
|
||||
threading.Thread(target=tarea, daemon=True).start()
|
||||
192
ui_layout.py
192
ui_layout.py
|
|
@ -52,7 +52,6 @@ def crear_ui_completa(root):
|
|||
config.monitor_running = False
|
||||
system_utils.detener_sonido_alarma()
|
||||
system_utils.detener_mp3() # Detener música al cerrar
|
||||
system_utils.limpiar_chat_al_cerrar()
|
||||
root.destroy()
|
||||
|
||||
root.protocol("WM_DELETE_WINDOW", on_closing)
|
||||
|
|
@ -121,14 +120,6 @@ def crear_ui_completa(root):
|
|||
# --- PESTAÑA 6: MÚSICA (NUEVO) ---
|
||||
music_tab = ttk.Frame(notebook)
|
||||
notebook.add(music_tab, text="Música 🎵")
|
||||
|
||||
# --- PESTAÑA 7: CHAT LOCAL (NUEVO) ---
|
||||
chat_tab = ttk.Frame(notebook)
|
||||
notebook.add(chat_tab, text="Chat Local 💬")
|
||||
|
||||
# --- PESTAÑA 8: CORREO (NUEVO) ---
|
||||
email_tab = ttk.Frame(notebook)
|
||||
notebook.add(email_tab, text="Correo 📧")
|
||||
# ---------------------------------------------
|
||||
|
||||
|
||||
|
|
@ -243,9 +234,6 @@ def crear_ui_completa(root):
|
|||
# ===============================================
|
||||
# CONTENIDO DE LA SOLAPA DE ALARMA
|
||||
# ===============================================
|
||||
|
||||
system_utils.inicializar_chat()
|
||||
|
||||
main_alarm_frame = tk.Frame(alarma_tab)
|
||||
main_alarm_frame.pack(fill="both", expand=True)
|
||||
|
||||
|
|
@ -686,186 +674,6 @@ def crear_ui_completa(root):
|
|||
volumen_scale_music.set(config.alarma_volumen * 100)
|
||||
volumen_scale_music.pack(pady=5)
|
||||
|
||||
## ===============================================
|
||||
# CONTENIDO DE LA SOLAPA DE CHAT LOCAL
|
||||
# ===============================================
|
||||
system_utils.inicializar_chat()
|
||||
|
||||
chat_frame = ttk.Frame(chat_tab, padding="25")
|
||||
chat_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Nombre de la aplicación en grande (Marko One)
|
||||
tk.Label(
|
||||
chat_frame,
|
||||
text="Chat Multipípedo",
|
||||
font=('Marko One', 24),
|
||||
fg="#2C3E50"
|
||||
).pack(pady=(0, 10))
|
||||
|
||||
# Marco para la configuración del usuario
|
||||
user_config_frame = ttk.Frame(chat_frame)
|
||||
user_config_frame.pack(fill=tk.X, pady=(0, 15))
|
||||
|
||||
# Variable para guardar el Alias
|
||||
alias_var = tk.StringVar(value=f"Instancia_{config.INSTANCE_ID}")
|
||||
|
||||
# Títulos usando Trirong y texto normal en Nunito
|
||||
tk.Label(user_config_frame, text="Tu Alias:", font=('Trirong', 12, 'bold'), fg="#34495E").pack(side=tk.LEFT, padx=(0, 10))
|
||||
ttk.Entry(user_config_frame, textvariable=alias_var, font=('Nunito', 11), width=20).pack(side=tk.LEFT, ipady=3)
|
||||
|
||||
tk.Label(user_config_frame, text=f"(ID Sistema: {config.INSTANCE_ID})", font=('Nunito', 10, 'italic'), fg="#7F8C8D").pack(side=tk.RIGHT)
|
||||
|
||||
# Área de texto del chat (Texto normal en Nunito)
|
||||
chat_text_area = ScrolledText(
|
||||
chat_frame,
|
||||
wrap='word',
|
||||
state=tk.DISABLED,
|
||||
font=('Nunito', 11),
|
||||
bg='#F8F9FA',
|
||||
fg='#2C3E50',
|
||||
bd=1,
|
||||
relief="solid",
|
||||
padx=15,
|
||||
pady=15
|
||||
)
|
||||
chat_text_area.pack(fill=tk.BOTH, expand=True, pady=(0, 20))
|
||||
|
||||
chat_input_frame = ttk.Frame(chat_frame)
|
||||
chat_input_frame.pack(fill=tk.X)
|
||||
|
||||
# Título secundario en Trirong
|
||||
tk.Label(chat_input_frame, text="Mensaje:", font=('Trirong', 12, 'bold'), fg="#34495E").pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
chat_entry = ttk.Entry(chat_input_frame, font=('Nunito', 12))
|
||||
chat_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10), ipady=5)
|
||||
|
||||
# Vincular Enter y el Botón pasando el alias_var
|
||||
chat_entry.bind("<Return>", lambda event: system_utils.enviar_mensaje_chat(chat_entry, alias_var))
|
||||
|
||||
ttk.Button(
|
||||
chat_input_frame,
|
||||
text="Enviar 🚀",
|
||||
command=lambda: system_utils.enviar_mensaje_chat(chat_entry, alias_var)
|
||||
).pack(side=tk.RIGHT, ipady=4)
|
||||
|
||||
threading.Thread(target=lambda: system_utils.monitor_chat_file(chat_text_area, root), daemon=True).start()
|
||||
|
||||
# ===============================================
|
||||
# CONTENIDO DE LA SOLAPA DE CORREO CLIENTE
|
||||
# ===============================================
|
||||
mail_main_container = ttk.Frame(email_tab)
|
||||
mail_main_container.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Variables de credenciales globales para esta pestaña
|
||||
email_user_var = tk.StringVar()
|
||||
email_pass_var = tk.StringVar()
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# PANTALLA 1: LOGIN (Visible por defecto)
|
||||
# ---------------------------------------------------------
|
||||
login_frame = ttk.Frame(mail_main_container, padding="40")
|
||||
login_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
tk.Label(
|
||||
login_frame,
|
||||
text="Gestor de Correo Interno",
|
||||
font=('Marko One', 22),
|
||||
fg="#2C3E50"
|
||||
).pack(pady=(40, 30))
|
||||
|
||||
form_frame = ttk.Frame(login_frame)
|
||||
form_frame.pack()
|
||||
|
||||
tk.Label(form_frame, text="Email:", font=('Trirong', 12, 'bold')).grid(row=0, column=0, padx=10, pady=15, sticky="e")
|
||||
ttk.Entry(form_frame, textvariable=email_user_var, width=35, font=('Nunito', 11)).grid(row=0, column=1, padx=10, pady=15, ipady=5)
|
||||
|
||||
tk.Label(form_frame, text="Contraseña:", font=('Trirong', 12, 'bold')).grid(row=1, column=0, padx=10, pady=15, sticky="e")
|
||||
ttk.Entry(form_frame, textvariable=email_pass_var, show="•", width=35, font=('Nunito', 11)).grid(row=1, column=1, padx=10, pady=15, ipady=5)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# PANTALLA 2: BANDEJA DE CORREO (Oculta hasta iniciar sesión)
|
||||
# ---------------------------------------------------------
|
||||
mail_content_frame = ttk.Frame(mail_main_container, padding="15")
|
||||
|
||||
# Dividir pantalla (Enviar vs Recibir)
|
||||
paned_window = ttk.PanedWindow(mail_content_frame, orient=tk.HORIZONTAL)
|
||||
paned_window.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# --- Bandeja de Salida (SMTP) ---
|
||||
send_frame = tk.LabelFrame(
|
||||
paned_window,
|
||||
text=" Bandeja de Salida (SMTP) ",
|
||||
font=('Trirong', 11, 'bold'),
|
||||
fg="#2980B9",
|
||||
padx=15,
|
||||
pady=15
|
||||
)
|
||||
paned_window.add(send_frame, weight=1)
|
||||
|
||||
tk.Label(send_frame, text="Destinatario:", font=('Trirong', 10, 'bold')).pack(anchor='w', pady=(0, 2))
|
||||
to_var = tk.StringVar()
|
||||
ttk.Entry(send_frame, textvariable=to_var, font=('Nunito', 11)).pack(fill=tk.X, pady=(0, 10), ipady=3)
|
||||
|
||||
tk.Label(send_frame, text="Asunto:", font=('Trirong', 10, 'bold')).pack(anchor='w', pady=(0, 2))
|
||||
subject_var = tk.StringVar()
|
||||
ttk.Entry(send_frame, textvariable=subject_var, font=('Nunito', 11)).pack(fill=tk.X, pady=(0, 10), ipady=3)
|
||||
|
||||
tk.Label(send_frame, text="Mensaje:", font=('Trirong', 10, 'bold')).pack(anchor='w', pady=(0, 2))
|
||||
body_text = tk.Text(send_frame, height=8, font=('Nunito', 11), bd=1, relief="solid")
|
||||
body_text.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
|
||||
|
||||
ttk.Button(
|
||||
send_frame,
|
||||
text="📨 Enviar Email",
|
||||
command=lambda: system_utils.enviar_correo(
|
||||
email_user_var.get(), email_pass_var.get(), to_var.get(), subject_var.get(), body_text.get("1.0", tk.END), root
|
||||
)
|
||||
).pack(ipady=3)
|
||||
|
||||
# --- Bandeja de Entrada (IMAP) ---
|
||||
recv_frame = tk.LabelFrame(
|
||||
paned_window,
|
||||
text=" Bandeja de Entrada (IMAP) ",
|
||||
font=('Trirong', 11, 'bold'),
|
||||
fg="#27AE60",
|
||||
padx=15,
|
||||
pady=15
|
||||
)
|
||||
paned_window.add(recv_frame, weight=1)
|
||||
|
||||
style = ttk.Style()
|
||||
style.configure("Treeview", font=('Nunito', 10), rowheight=28)
|
||||
style.configure("Treeview.Heading", font=('Trirong', 10, 'bold'))
|
||||
|
||||
cols = ('ID', 'De', 'Asunto')
|
||||
treeview_emails = ttk.Treeview(recv_frame, columns=cols, show='headings')
|
||||
treeview_emails.heading('ID', text='ID')
|
||||
treeview_emails.heading('De', text='Remitente')
|
||||
treeview_emails.heading('Asunto', text='Asunto')
|
||||
treeview_emails.column('ID', width=50, anchor='center')
|
||||
treeview_emails.column('De', width=160)
|
||||
treeview_emails.column('Asunto', width=220)
|
||||
treeview_emails.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
|
||||
|
||||
ttk.Button(
|
||||
recv_frame,
|
||||
text="📥 Actualizar Bandeja",
|
||||
command=lambda: system_utils.cargar_correos(
|
||||
email_user_var.get(), email_pass_var.get(), treeview_emails, root
|
||||
)
|
||||
).pack(ipady=3)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# BOTÓN DE INICIO DE SESIÓN (Llama a la validación)
|
||||
# ---------------------------------------------------------
|
||||
ttk.Button(
|
||||
form_frame,
|
||||
text="🔑 Autenticar e Iniciar",
|
||||
command=lambda: system_utils.comprobar_login_correo(
|
||||
email_user_var.get(), email_pass_var.get(), login_frame, mail_content_frame, root
|
||||
)
|
||||
).grid(row=2, column=0, columnspan=2, pady=25, ipady=5)
|
||||
|
||||
# --- Iniciar Hilos ---
|
||||
system_utils.log_event("Monitor de sistema iniciado. Esperando la primera lectura de métricas...")
|
||||
monitor_manager.iniciar_monitor_sistema(fig, canvas, ax_cpu, ax_mem, ax_net, ax_cores, ax_pie, ax_disk_io, treeview_processes, root)
|
||||
|
|
|
|||
Loading…
Reference in New Issue