diff --git a/.idea/MutiFunctionProgramProject.iml b/.idea/MutiFunctionProgramProject.iml index d8b3f6c..f571432 100644 --- a/.idea/MutiFunctionProgramProject.iml +++ b/.idea/MutiFunctionProgramProject.iml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1d3ce46..153e9c5 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/src/services/__pycache__/email_client_pop.cpython-312.pyc b/src/services/__pycache__/email_client_pop.cpython-312.pyc new file mode 100644 index 0000000..61845b6 Binary files /dev/null and b/src/services/__pycache__/email_client_pop.cpython-312.pyc differ diff --git a/src/services/__pycache__/threads_manager.cpython-312.pyc b/src/services/__pycache__/threads_manager.cpython-312.pyc index 08978d3..78e9a4f 100644 Binary files a/src/services/__pycache__/threads_manager.cpython-312.pyc and b/src/services/__pycache__/threads_manager.cpython-312.pyc differ diff --git a/src/services/email_client_pop.py b/src/services/email_client_pop.py index 11cc23d..3689b66 100644 --- a/src/services/email_client_pop.py +++ b/src/services/email_client_pop.py @@ -19,6 +19,7 @@ class EmailClientPOP: self.smtp_port = smtp_port self.pop_conn = None self.smtp_conn = None + self.running = True # Ruta del archivo SQLite: self.db_file = os.path.join("resources/db_email", "emails.db") @@ -59,7 +60,7 @@ class EmailClientPOP: def connect_pop(self): """Conexión al servidor POP""" try: - self.pop_conn = poplib.POP3(self.pop_server, self.pop_port) + self.pop_conn = poplib.POP3(self.pop_server, self.pop_port, timeout=10) self.pop_conn.user(self.email) self.pop_conn.pass_(self.password) print("Conexión POP exitosa") @@ -70,7 +71,7 @@ class EmailClientPOP: def connect_smtp(self): """Conexión al servidor SMTP""" try: - self.smtp_conn = smtplib.SMTP(self.smtp_server, self.smtp_port) + self.smtp_conn = smtplib.SMTP(self.smtp_server, self.smtp_port, timeout=10) self.smtp_conn.login(self.email, self.password) print("Conexión SMTP exitosa") except Exception as e: @@ -84,8 +85,12 @@ class EmailClientPOP: def reconnect(self): """Intenta reconectar a los servidores POP y SMTP""" print("Intentando reconectar al servidor de correo...") - self.connect_pop() - self.connect_smtp() + if not self.running: + return + if self.pop_conn is None: + self.connect_pop() + if self.smtp_conn is None: + self.connect_smtp() def fetch_unread_count(self): """Obtener el número de correos (POP3 no distingue entre leídos y no leídos)""" @@ -203,7 +208,18 @@ class EmailClientPOP: def close_connections(self): """Cierra las conexiones POP y SMTP""" - if self.pop_conn: - self.pop_conn.quit() - if self.smtp_conn: - self.smtp_conn.quit() + try: + if self.pop_conn: + self.pop_conn.quit() + self.pop_conn = None + print("Conexión POP cerrada.") + except Exception as e: + print(f"Error al cerrar conexión POP: {e}") + + try: + if self.smtp_conn: + self.smtp_conn.quit() + self.smtp_conn = None + print("Conexión SMTP cerrada.") + except Exception as e: + print(f"Error al cerrar conexión SMTP: {e}") diff --git a/src/services/threads_manager.py b/src/services/threads_manager.py index 77b5605..caae0a1 100644 --- a/src/services/threads_manager.py +++ b/src/services/threads_manager.py @@ -9,6 +9,7 @@ from src.services.threaden_task import ThreadenTask class ThreadsManager: """Constructor""" + def __init__(self, ui_instance, email_client): self.ui_instance = ui_instance self.email_client = email_client @@ -16,23 +17,23 @@ class ThreadsManager: self.radio_player = RadioPlayer() self.tasks = { "time": ThreadenTask(), - "temperature": ThreadenTask(), - "emails":ThreadenTask(), - "email_client":ThreadenTask(), - "tetris_game":ThreadenTask(), - "scrapper":ThreadenTask(), + "temperature": ThreadenTask(), + "emails": ThreadenTask(), + "email_client": ThreadenTask(), + "tetris_game": ThreadenTask(), + "scrapper": ThreadenTask(), "radio_player": ThreadenTask(), } self.system_monitor_tasks = {} self.scrapper = Scrapper(ui_instance) - - def play_radio(self, url): - """Inicia la reproducción de radio en un hilo.""" - if not self.tasks["radio_player"].running: - self.tasks["radio_player"].start(self.radio_player.play, url) - def stop_radio(self): - """Detiene la reproducción de radio.""" + def play_radio(self, url): + """Inicia la reproducción de radio en un hilo.""" + if not self.tasks["radio_player"].running: + self.tasks["radio_player"].start(self.radio_player.play, url) + + def stop_radio(self): + """Detiene la reproducción de radio.""" self.radio_player.stop() def set_system_monitor(self, system_monitor): @@ -58,13 +59,13 @@ class ThreadsManager: if hasattr(self.ui_instance, "tetris_game"): self.tasks["tetris_game"].start(self.update_tetris_game) - - def stop_threads(self): - """Recorre tasks y para los hilos""" + """Detiene todos los hilos y cierra las conexiones.""" for name, task in self.tasks.items(): task.stop() print(f"Hilo '{name}' detenido") + if name == "email_client" and hasattr(self.email_client, "close_connections"): + self.email_client.close_connections() for name, task in self.system_monitor_tasks.items(): task.stop() @@ -73,7 +74,6 @@ class ThreadsManager: if self.system_monitor: self.system_monitor.running = False - def update_tetris_game(self): """Ciclo de actualizacion del tetris game""" while self.tasks["tetris_game"].running: @@ -84,25 +84,24 @@ class ThreadsManager: except Exception as e: print(f"Error en update_tetris_game: {e}") break - - def update_system_metric(self, metric): - """Actualiza una métrica específica del monitor del sistema.""" - while self.system_monitor_tasks[metric].running: - try: - self.system_monitor.update_metric(metric) - time.sleep(self.system_monitor.metrics[metric]["interval"]) - except Exception as e: - print(f"Error updating metric {metric}: {e}") + def update_system_metric(self, metric): + """Actualiza una métrica específica del monitor del sistema.""" + while self.system_monitor_tasks[metric].running: + try: + self.system_monitor.update_metric(metric) + time.sleep(self.system_monitor.metrics[metric]["interval"]) + except Exception as e: + print(f"Error updating metric {metric}: {e}") - - def update_time(self): while self.tasks["time"].running: current_time = datetime.datetime.now().strftime('%H:%M:%S') current_date = datetime.datetime.now().strftime('%d/%m/%Y') - self.ui_instance.after(0, lambda: self.ui_instance.info_labels["hora"].configure(text=f"Hora: {current_time}")) - self.ui_instance.after(0, lambda: self.ui_instance.info_labels["fecha"].configure(text=f"Fecha: {current_date}")) + self.ui_instance.after(0, + lambda: self.ui_instance.info_labels["hora"].configure(text=f"Hora: {current_time}")) + self.ui_instance.after(0, lambda: self.ui_instance.info_labels["fecha"].configure( + text=f"Fecha: {current_date}")) time.sleep(1) def update_temperature(self): @@ -114,7 +113,8 @@ class ThreadsManager: if temperature is not None: self.ui_instance.after( 0, - lambda: self.ui_instance.info_labels["temperatura"].configure(text=f"Temperatura local: {temperature}°C") + lambda: self.ui_instance.info_labels["temperatura"].configure( + text=f"Temperatura local: {temperature}°C") ) except Exception as e: print(f"Error al obtener la temperatura: {e}") @@ -139,9 +139,12 @@ class ThreadsManager: except Exception as e: print(f"Error en el EmailClient: {e}") time.sleep(10) + finally: + self.email_client.close_connections() + print("Cliente de correo detenido y conexiones cerradas") def update_emails(self): - """Actualiza la cantidad de correos no leidos en tiempo real""" + """Actualiza la cantidad de correos no leídos en tiempo real.""" while self.tasks["emails"].running: try: if not self.email_client.is_connected(): @@ -149,24 +152,16 @@ class ThreadsManager: if self.email_client.is_connected(): unread_count = self.email_client.fetch_unread_count() - self.ui_instance.after( - 0, - lambda: self.ui_instance.info_labels["emails"].configure( - text=f"Correos sin leer: {unread_count}" + if self.ui_instance.winfo_exists(): # Verifica si la ventana aún existe + self.ui_instance.after( + 0, + lambda: self.ui_instance.info_labels["emails"].configure( + text=f"Correos sin leer: {unread_count}" + ) ) - ) else: print("No hay conexión al servidor de correo") - self.ui_instance.after( - 0, - lambda: self.ui_instance.info_labels["emails"].configure( - text="Servidor no disponible" - ) - ) except Exception as e: print(f"Error en el hilo de correos: {e}") time.sleep(60) - - - diff --git a/src/ui/__pycache__/centered_window.cpython-312.pyc b/src/ui/__pycache__/centered_window.cpython-312.pyc index 30dbfa1..5f3a0c8 100644 Binary files a/src/ui/__pycache__/centered_window.cpython-312.pyc and b/src/ui/__pycache__/centered_window.cpython-312.pyc differ diff --git a/src/ui/centered_window.py b/src/ui/centered_window.py index 555ff3d..6cde708 100644 --- a/src/ui/centered_window.py +++ b/src/ui/centered_window.py @@ -64,24 +64,37 @@ class CenteredWindow(ctk.CTk): def on_close(self): """Maneja el cierre de la ventana""" - self.thread_manager.stop_threads() + try: + # Establecer bandera de cierre para el cliente de correo + if hasattr(self, "email_client") and self.email_client: + self.email_client.running = False - if hasattr(self, "tetris_game") and self.tetris_game.running: - self.tetris_game.stop_game() + # Detener todos los hilos + self.thread_manager.stop_threads() - if "tetris_game" in self.thread_manager.tasks: - self.thread_manager.tasks["tetris_game"].stop() + # Cancelar tareas programadas en Tkinter + for task in self.after_tasks: + try: + self.after_cancel(task) + except Exception as e: + print(f"Error al cancelar tarea programada: {e}") - if hasattr(self.thread_manager, "scrapper"): - self.thread_manager.scrapper.stop_scraping() + # Cerrar conexiones del cliente POP/SMTP + if hasattr(self, "email_client") and self.email_client: + self.email_client.close_connections() - if self.system_monitor: - self.system_monitor.running = False + # Detener tareas adicionales (Tetris, scraping) + if hasattr(self, "tetris_game") and self.tetris_game.running: + self.tetris_game.stop_game() - for task in self.after_tasks: - self.after_cancel(task) + if hasattr(self.thread_manager, "scrapper") and self.thread_manager.scrapper: + self.thread_manager.scrapper.stop_scraping() - self.destroy() + # Destruir la ventana principal + self.destroy() + print("Aplicación cerrada correctamente.") + except Exception as e: + print(f"Error al cerrar la aplicación: {e}") def create_left_panel(self): # Panel izquierdo @@ -335,7 +348,7 @@ class CenteredWindow(ctk.CTk): listbox_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) # Crear una lista de correos con un scrollbar - self.email_listbox = ctk.CTkTextbox(listbox_frame, width=800, height=800) + self.email_listbox = ctk.CTkTextbox(listbox_frame, width=600, height=600) self.email_listbox.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) self.email_listbox.configure(state="disabled") # Inicialmente deshabilitada @@ -375,9 +388,11 @@ class CenteredWindow(ctk.CTk): if self.email_client.is_connected(): emails = self.email_client.fetch_emails() - self.email_listbox.delete(0, tk.END) + self.email_listbox.configure(state="normal") + self.email_listbox.delete("1.0", tk.END) # Borra todo el contenido actual for email in emails: - self.email_listbox.insert(tk.END, f"{email['subject']} - {email['from']}") + self.email_listbox.insert(tk.END, f"De: {email['sender']}\nAsunto: {email['subject']}\n\n") + self.email_listbox.configure(state="disabled") else: print("No hay conexión al servidor de correo.") except Exception as e: