This commit is contained in:
Santi 2025-02-18 16:14:36 +01:00
parent c6bfeaeca1
commit abc8b0d061
10 changed files with 190 additions and 454 deletions

View File

@ -1,202 +1,53 @@
import tkinter as tk
import threading
import time
import datetime
from tkinter import Menu # Importar el widget Menu
from tkinter import ttk # Importar el widget ttk
from correo_server.EmailTab import MailTab
from correo_server.InboxTab import InboxTab
from correo_server.MailClient import MailClient
from tkinter import Menu, ttk
# Importación de widgets personalizados
from hilos.ChatWidget import ChatWidget
from hilos.MusicPlayer import MusicPlayer
from hilos.WeatherWidget import WeatherWidget
from hilos.SystemMonitor import SystemMonitor
from hilos.ApplicationLauncher import ApplicationLauncher
from hilos.LanguageChart import LanguageChart
from solapas.MusicDownloader import MusicDownloader
from solapas.EconomyBitcoinChart import EconomyBitcoinChart
from solapas.SQLQueryExecutor import SQLQueryExecutor
from solapas.TicTacToe import TicTacToe
from solapas.WebScraperToDB import WebScraperToDB
# Crear instancia del cliente de correo con configuración de puertos
email_client = MailClient()
# Clave de API de OpenWeatherMap
API_KEY = "1fa8fd05b650773bbc3f2130657e808a"
def update_time(status_bar):
"""Función que actualiza la hora y el día de la semana en un label"""
while True:
# Obtener la fecha y hora actual
now = datetime.datetime.now()
day_of_week = now.strftime("%A") # Día de la semana
time_str = now.strftime("%H:%M:%S") # Hora en formato HH:MM:SS
date_str = now.strftime("%Y-%m-%d") # Fecha en formato YYYY-MM-DD
label_text = f"{day_of_week}, {date_str} - {time_str}"
# Actualizar el label (debemos usar `after` para asegurarnos que se actualice en el hilo principal de Tkinter)
label_fecha_hora.after(1000, status_bar.config, {"text": label_text})
# Espera 1 segundo antes de actualizar de nuevo
time.sleep(1)
# Crear la ventana principal
root = tk.Tk()
root.title("Ventana Responsive")
root.geometry("1000x700") # Tamaño inicial
# Configurar la ventana principal para que sea responsive
root.columnconfigure(0, weight=0) # Columna izquierda, tamaño fijo
root.columnconfigure(1, weight=1) # Columna central, tamaño variable
root.columnconfigure(2, weight=0) # Columna derecha, tamaño fijo
root.rowconfigure(0, weight=1) # Fila principal, tamaño variable
root.rowconfigure(1, weight=0) # Barra de estado, tamaño fijo
# Crear el menú superior
menu_bar = Menu(root)
file_menu = Menu(menu_bar, tearoff=0)
file_menu.add_command(label="Nuevo")
file_menu.add_command(label="Abrir")
file_menu.add_separator()
file_menu.add_command(label="Salir", command=root.quit)
edit_menu = Menu(menu_bar, tearoff=0)
edit_menu.add_command(label="Copiar")
edit_menu.add_command(label="Pegar")
help_menu = Menu(menu_bar, tearoff=0)
help_menu.add_command(label="Acerca de")
menu_bar.add_cascade(label="Archivo", menu=file_menu)
menu_bar.add_cascade(label="Editar", menu=edit_menu)
menu_bar.add_cascade(label="Ayuda", menu=help_menu)
root.config(menu=menu_bar)
# Crear los frames laterales y el central
# Frames laterales y central
frame_izquierdo = tk.Frame(root, bg="lightblue", width=150)
frame_central = tk.Frame(root, bg="white")
frame_derecho = tk.Frame(root, bg="lightgreen", width=150)
# Colocar los frames laterales y el central
frame_izquierdo.grid(row=0, column=0, sticky="ns")
frame_central.grid(row=0, column=1, sticky="nsew")
frame_derecho.grid(row=0, column=2, sticky="ns")
# Configurar los tamaños fijos de los frames laterales
frame_izquierdo.grid_propagate(False)
frame_derecho.grid_propagate(False)
# Integrar el widget del clima en el panel izquierdo
weather_widget = WeatherWidget(frame_izquierdo, API_KEY)
# Añadir el lanzador de aplicaciones al panel izquierdo
# Añadir widgets a los paneles
weather_widget = WeatherWidget(frame_izquierdo, "API_KEY")
app_launcher = ApplicationLauncher(frame_izquierdo)
# Añadir gráfico de lenguajes al panel izquierdo
language_chart = LanguageChart(frame_izquierdo)
# Crear el widget de Chat en el panel derecho con más espacio
chat_widget = ChatWidget(frame_derecho)
# Agregar el reproductor de música al panel derecho, en la parte inferior
music_player = MusicPlayer(frame_derecho)
# Dividir el frame central en dos partes (superior variable e inferior fija)
frame_central.rowconfigure(0, weight=1) # Parte superior, tamaño variable
frame_central.rowconfigure(1, weight=0) # Parte inferior, tamaño fijo
frame_central.columnconfigure(0, weight=1) # Ocupa toda la anchura
# Crear subframes dentro del frame central
frame_superior = tk.Frame(frame_central, bg="lightyellow")
frame_inferior = tk.Frame(frame_central, bg="lightgray", height=100)
# Colocar los subframes dentro del frame central
frame_superior.grid(row=0, column=0, sticky="nsew")
frame_inferior.grid(row=1, column=0, sticky="ew")
# Fijar el tamaño de la parte inferior
frame_inferior.grid_propagate(False)
# Crear un evento de parada
stop_event = threading.Event()
# Definir el manejador para el cierre de la ventana
def on_closing():
"""Cerrar correctamente la aplicación."""
stop_event.set() # Detener los hilos
root.destroy() # Destruir la ventana principal
# Configurar el manejador de cierre
root.protocol("WM_DELETE_WINDOW", on_closing)
# Crear la barra de estado
# Barra de estado
barra_estado = tk.Label(root, text="Barra de estado", bg="lightgray", anchor="w")
barra_estado.grid(row=1, column=0, columnspan=3, sticky="ew")
# Inicializar el monitor del sistema
stop_event = threading.Event()
system_monitor = SystemMonitor(barra_estado, stop_event)
# Notebook para las pestañas
style = ttk.Style()
style.configure("CustomNotebook.TNotebook.Tab", font=("Arial", 12, "bold"))
notebook = ttk.Notebook(frame_superior, style="CustomNotebook.TNotebook")
notebook.pack(fill="both", expand=True)
# Manejar el cierre de la aplicación
def on_closing():
stop_event.set()
root.destroy()
# Crear la Solapa 1 y añadir el downloader
tab1 = ttk.Frame(notebook)
notebook.add(tab1, text="Downloader", padding=4)
# Añadir el downloader a la Solapa 1
music_downloader = MusicDownloader(tab1)
# Crear la Solapa 2 y añadir los gráficos
tab2 = ttk.Frame(notebook)
notebook.add(tab2, text="Graphics", padding=4)
# Añadir los gráficos de economía mundial y Bitcoin a la Solapa 2
economy_bitcoin_chart = EconomyBitcoinChart(tab2)
# Crear la Solapa 3 y añadir el Tic Tac Toe
tab3 = ttk.Frame(notebook)
notebook.add(tab3, text="Tic Tac Toe", padding=4)
# Añadir el juego de Tic Tac Toe a la Solapa 3
tic_tac_toe = TicTacToe(tab3)
# Crear la Solapa 4 y añadir el SQL Query Executor
tab4 = ttk.Frame(notebook)
notebook.add(tab4, text="SQL Querys", padding=4)
# Añadir el ejecutor de consultas SQL a la Solapa 4
sql_query_executor = SQLQueryExecutor(tab4)
# Crear la Solapa 5 y añadir el Web Scraper
tab5 = ttk.Frame(notebook)
notebook.add(tab5, text="Web Scraper", padding=4)
# Añadir el widget de Web Scraper a la Solapa 5
web_scraper = WebScraperToDB(tab5)
# Crear pestañas de correo
MailTab(notebook, email_client)
InboxTab(notebook, email_client)
# Barra de estado
# Dividir la barra de estado en 4 labels
# Usar pack para alinear los labels horizontalmente
label_fecha_hora = tk.Label(barra_estado, text="Hilo fecha-hora", font=("Helvetica", 14), bd=1, fg="blue", relief="sunken", anchor="w", width=20, padx=10)
label_fecha_hora.pack(side="right", fill="x", expand=True)
# barra_estado.grid(row=1, column=0, columnspan=3, sticky="ew")
update_thread = threading.Thread(target=update_time, args=(label_fecha_hora,))
update_thread.daemon = True # Hacemos el hilo un demonio para que termine con la app
update_thread.start()
# Ejecución de la aplicación
root.mainloop()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()

Binary file not shown.

81
correo_server/InboxTab.py Normal file
View File

@ -0,0 +1,81 @@
import tkinter as tk
from tkinter import ttk, messagebox
import threading
class InboxTab:
def __init__(self, parent, email_client):
self.email_client = email_client
self.frame = ttk.Frame(parent)
parent.add(self.frame, text="Bandeja de Entrada")
ttk.Label(self.frame, text="Correo electrónico:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
self.entry_email = ttk.Entry(self.frame, width=40)
self.entry_email.grid(row=0, column=1, padx=5, pady=5)
ttk.Label(self.frame, text="Contraseña:").grid(row=1, column=0, sticky="e", padx=5, pady=5)
self.entry_password = ttk.Entry(self.frame, show="*", width=40)
self.entry_password.grid(row=1, column=1, padx=5, pady=5)
self.load_button = ttk.Button(self.frame, text="Cargar Correos", command=self.load_emails_thread)
self.load_button.grid(row=2, column=1, sticky="e", padx=5, pady=5)
self.email_listbox = tk.Listbox(self.frame, width=80, height=20)
self.email_listbox.grid(row=3, column=0, columnspan=2, padx=5, pady=5)
self.email_listbox.bind("<Double-Button-1>", self.show_email)
self.delete_button = ttk.Button(self.frame, text="Eliminar Correo", command=self.delete_email_thread)
self.delete_button.grid(row=4, column=1, sticky="e", padx=5, pady=5)
self.emails = []
def load_emails_thread(self):
threading.Thread(target=self.load_emails).start()
def delete_email_thread(self):
threading.Thread(target=self.delete_email).start()
def load_emails(self):
email_address = self.entry_email.get()
password = self.entry_password.get()
if not email_address or not password:
messagebox.showerror("Error", "Debe ingresar su correo y contraseña.")
return
self.emails = []
self.email_listbox.delete(0, tk.END)
emails = self.email_client.fetch_emails(email_address, password)
if isinstance(emails, str):
messagebox.showerror("Error", emails)
return
for email_id, subject, sender in emails:
self.email_listbox.insert(tk.END, f"{subject} - {sender}")
self.emails.append((email_id, subject, sender))
def delete_email(self):
selected_index = self.email_listbox.curselection()
if not selected_index:
messagebox.showwarning("Advertencia", "Seleccione un correo para eliminar.")
return
email_id, _, _ = self.emails[selected_index[0]]
email_address = self.entry_email.get()
password = self.entry_password.get()
result = self.email_client.delete_email(email_address, password, email_id)
if "eliminado" in result:
self.email_listbox.delete(selected_index)
messagebox.showinfo("Éxito", result)
else:
messagebox.showerror("Error", result)
def show_email(self, event):
selected_index = self.email_listbox.curselection()
if selected_index:
email_id, subject, sender = self.emails[selected_index[0]]
messagebox.showinfo("Correo", f"Asunto: {subject}\nDe: {sender}")

View File

@ -3,93 +3,40 @@ import threading
import subprocess
import os
class ApplicationLauncher:
def __init__(self, parent):
"""
Inicializa los botones para lanzar aplicaciones con detección automática de rutas.
Args:
parent (tk.Frame): Frame donde se colocarán los botones.
"""
"""Crea botones para abrir aplicaciones como VS Code, Eclipse y PyCharm."""
self.parent = parent
# Detectar rutas automáticamente
self.vscode_path = self.detect_path(["C:\\Program Files\\Microsoft VS Code\\Code.exe",
"C:\\Users\\%USERNAME%\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe"])
self.eclipse_path = self.detect_path(["C:\\eclipse\\eclipse.exe",
"C:\\Program Files\\Eclipse Foundation\\eclipse.exe"])
self.pycharm_path = self.detect_path(["C:\\Program Files\\JetBrains\\PyCharm\\bin\\pycharm64.exe",
"C:\\Program Files\\JetBrains\\PyCharm Community Edition 2023.1.4\\bin\\pycharm64.exe"])
# Detectar rutas de instalación
self.vscode_path = self.detect_path(["C:\\Program Files\\Microsoft VS Code\\Code.exe"])
self.eclipse_path = self.detect_path(["C:\\eclipse\\eclipse.exe"])
self.pycharm_path = self.detect_path(["C:\\Program Files\\JetBrains\\PyCharm\\bin\\pycharm64.exe"])
# Título para el grupo de botones
title = tk.Label(self.parent, text="Aplicaciones", font=("Helvetica", 14, "bold"), bg="lightblue")
title.pack(pady=10)
# Botón para abrir Visual Studio Code
self.vscode_button = tk.Button(self.parent, text="Visual Code", command=self.launch_vscode, bg="lightgreen",
font=("Helvetica", 10))
self.vscode_button.pack(fill="x", pady=2)
# Botón para abrir Eclipse
self.eclipse_button = tk.Button(self.parent, text="Eclipse", command=self.launch_eclipse, bg="lightgreen",
font=("Helvetica", 10))
self.eclipse_button.pack(fill="x", pady=2)
# Botón para abrir PyCharm
self.pycharm_button = tk.Button(self.parent, text="PyCharm", command=self.launch_pycharm, bg="lightgreen",
font=("Helvetica", 10))
self.pycharm_button.pack(fill="x", pady=2)
# Crear botones de lanzamiento
self.create_button("Visual Code", self.launch_vscode)
self.create_button("Eclipse", self.launch_eclipse)
self.create_button("PyCharm", self.launch_pycharm)
def detect_path(self, paths):
"""
Detecta automáticamente la primera ruta existente de una lista de posibles rutas.
Args:
paths (list): Lista de rutas posibles para un ejecutable.
Returns:
str: La primera ruta válida encontrada, o None si no se encuentra ninguna.
"""
"""Detecta la primera ruta válida de una lista de rutas posibles."""
for path in paths:
path = os.path.expandvars(path) # Expande variables como %USERNAME%
if os.path.exists(path):
return path
return None
def create_button(self, name, command):
"""Crea un botón con la función de lanzar una aplicación."""
button = tk.Button(self.parent, text=name, command=command, bg="lightgreen")
button.pack(fill="x", pady=2)
def launch_vscode(self):
"""Lanza Visual Studio Code si se encuentra la ruta."""
"""Lanza Visual Studio Code."""
self.launch_application(self.vscode_path, "Visual Studio Code")
def launch_eclipse(self):
"""Lanza Eclipse si se encuentra la ruta."""
self.launch_application(self.eclipse_path, "Eclipse")
def launch_pycharm(self):
"""Lanza PyCharm si se encuentra la ruta."""
self.launch_application(self.pycharm_path, "PyCharm")
def launch_application(self, path, name):
"""
Lanza una aplicación si la ruta es válida.
Args:
path (str): Ruta al ejecutable.
name (str): Nombre de la aplicación (para mensajes de error).
"""
"""Ejecuta la aplicación si la ruta es válida."""
if path:
threading.Thread(target=self.run_command, args=([path],), daemon=True).start()
threading.Thread(target=subprocess.run, args=([path],), daemon=True).start()
else:
print(f"No se encontró {name}. Por favor, instálalo o configura la ruta.")
def run_command(self, command):
"""
Ejecuta un comando del sistema operativo para abrir una aplicación.
Args:
command (list): Comando a ejecutar (lista de argumentos).
"""
try:
subprocess.run(command, check=True)
except Exception as e:
print(f"Error al intentar abrir la aplicación: {e}")
print(f"{name} no está instalado.")

View File

@ -3,71 +3,55 @@ from tkinter import scrolledtext
import socket
import threading
class ChatWidget:
def __init__(self, parent):
"""Widget de chat con conexión a un servidor por sockets."""
self.parent = parent
self.frame = tk.Frame(self.parent, bg="lightgreen", width=200, height=300) # Ajustar tamaño del frame
self.frame.pack(fill="x", expand=False, padx=10, pady=10)
self.frame = tk.Frame(self.parent, bg="lightgreen")
self.frame.pack(fill="x", padx=10, pady=10)
# Label superior
self.label = tk.Label(self.frame, text="Chat", font=("Arial", 14, "bold"), fg="red", bg="lightgreen")
self.label.pack(pady=5)
# Caja de texto para los mensajes
self.chat_display = scrolledtext.ScrolledText(
self.frame, wrap=tk.WORD, state="disabled", width=40, height=10 # Reducir dimensiones
)
# Área de texto para mostrar mensajes
self.chat_display = scrolledtext.ScrolledText(self.frame, wrap=tk.WORD, state="disabled", width=40, height=10)
self.chat_display.pack(pady=5)
# Campo de entrada para escribir mensajes
self.message_entry = tk.Entry(self.frame, width=35) # Reducir ancho
# Campo de entrada de mensajes
self.message_entry = tk.Entry(self.frame, width=35)
self.message_entry.pack(pady=5)
self.message_entry.bind("<Return>", self.send_message)
# Botón para enviar mensajes
self.send_button = tk.Button(self.frame, text="Enviar", command=self.send_message, width=10) # Reducir tamaño
# Botón de enviar mensaje
self.send_button = tk.Button(self.frame, text="Enviar", command=self.send_message)
self.send_button.pack(pady=5)
# Configuración del cliente socket
# Configuración del socket
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_address = ("127.0.0.1", 3333) # Cambiar a la IP del servidor si es necesario
self.server_address = ("127.0.0.1", 3333)
try:
self.client_socket.connect(self.server_address)
threading.Thread(target=self.receive_messages, daemon=True).start()
except Exception as e:
self.display_message(f"[ERROR] No se pudo conectar al servidor: {e}")
except:
self.display_message("[ERROR] No se pudo conectar al servidor.")
def send_message(self, event=None):
"""Envía un mensaje al servidor."""
message = self.message_entry.get()
if message:
try:
self.client_socket.send(message.encode("utf-8"))
self.message_entry.delete(0, tk.END)
except Exception as e:
self.display_message(f"[ERROR] No se pudo enviar el mensaje: {e}")
self.client_socket.send(message.encode("utf-8"))
self.message_entry.delete(0, tk.END)
def receive_messages(self):
"""Recibe mensajes del servidor y los muestra en la interfaz."""
while True:
try:
message = self.client_socket.recv(1024).decode("utf-8")
if message:
self.display_message(message)
else:
break
self.display_message(message)
except:
self.display_message("[DESCONECTADO] Conexión perdida con el servidor.")
break
def display_message(self, message):
"""Muestra un mensaje en la ventana de chat."""
self.chat_display.config(state="normal")
self.chat_display.insert(tk.END, message + "\n")
self.chat_display.config(state="disabled")
self.chat_display.see(tk.END)
def close_connection(self):
try:
self.client_socket.close()
except:
pass

View File

@ -7,45 +7,44 @@ import time
class LanguageChart:
def __init__(self, parent):
"""
Inicializa el gráfico de los lenguajes de programación más usados.
Inicializa un gráfico de barras que muestra los lenguajes de programación más usados.
Args:
parent (tk.Frame): Frame donde se colocará el gráfico.
"""
self.parent = parent
# Datos iniciales (puedes actualizar esto dinámicamente)
# Datos iniciales de lenguajes más utilizados
self.languages = ["Python", "JavaScript", "Java", "C++", "C#"]
self.usage = [30, 25, 20, 15, 10] # Porcentajes de uso
# Crear figura para el gráfico
# Crear el gráfico de barras
self.figure = Figure(figsize=(4, 3), dpi=100)
self.ax = self.figure.add_subplot(111)
self.ax.bar(self.languages, self.usage, color="skyblue")
self.ax.set_title("Lenguajes más usados")
self.ax.set_ylabel("Porcentaje de uso")
# Embebiendo el gráfico en Tkinter
# Incrustar el gráfico en Tkinter
self.canvas = FigureCanvasTkAgg(self.figure, master=self.parent)
self.canvas.get_tk_widget().pack(fill="both", expand=True)
# Iniciar hilo para actualizar el gráfico
# Iniciar el hilo para actualizar el gráfico periódicamente
threading.Thread(target=self.update_chart, daemon=True).start()
def fetch_data(self):
"""
Simula la obtención de datos actualizados de lenguajes de programación.
Simula la actualización de datos sobre el uso de lenguajes de programación.
Returns:
list: Lista de nuevos porcentajes de uso.
"""
# Simulación: aquí puedes conectar a una API real
self.usage = [value + 1 if value < 50 else value - 10 for value in self.usage]
time.sleep(5) # Simular retraso de actualización
time.sleep(5) # Simular un retraso en la actualización
def update_chart(self):
"""
Actualiza el gráfico periódicamente en un hilo.
Actualiza el gráfico de lenguajes de programación periódicamente en un hilo.
"""
while True:
self.fetch_data()
@ -54,4 +53,4 @@ class LanguageChart:
self.ax.set_title("Lenguajes más usados")
self.ax.set_ylabel("Porcentaje de uso")
self.canvas.draw()
time.sleep(5) # Actualizar cada 5 segundos
time.sleep(5) # Actualizar cada 5 segundos

View File

@ -4,98 +4,90 @@ from tkinter import filedialog
import threading
import pygame
# Definir la carpeta donde se guardarán los archivos descargados
# Carpeta donde se almacenarán los archivos de música descargados
DOWNLOAD_FOLDER = "musicplayer"
class MusicPlayer:
def __init__(self, parent):
"""Inicializa el reproductor de música con botones de control y lista de canciones."""
self.parent = parent
self.is_playing = False
# Inicializar el reproductor de música
# Inicializar el motor de audio
pygame.mixer.init()
# Crear marco para el reproductor
# Crear la interfaz del reproductor
self.frame = tk.Frame(self.parent, bg="lightgreen", width=200, height=100)
self.frame.pack(side="bottom", padx=10, pady=10, fill="both", expand=False)
# Etiqueta de título
self.title_label = tk.Label(
self.frame, text="Reproductor de Música", font=("Arial", 12, "bold"), bg="lightgreen"
)
self.title_label = tk.Label(self.frame, text="Reproductor de Música", font=("Arial", 12, "bold"), bg="lightgreen")
self.title_label.pack(pady=5)
# Lista de canciones descargadas
self.song_listbox = tk.Listbox(self.frame, width=50, height=10)
self.song_listbox.pack(pady=5)
self.load_songs()
self.load_songs() # Cargar canciones almacenadas
# Crear un marco para los botones de control
# Botón para seleccionar archivo manualmente
self.select_button = tk.Button(self.frame, text="Seleccionar Archivo", command=self.select_file, width=20)
self.select_button.pack(pady=5)
# Contenedor de botones de control
self.controls_frame = tk.Frame(self.frame, bg="lightgreen")
self.controls_frame.pack(pady=10)
# Botón para seleccionar un archivo manualmente
self.select_button = tk.Button(
self.frame, text="Seleccionar Archivo", command=self.select_file, width=20
)
self.select_button.pack(pady=5)
# Botones de control
self.play_button = tk.Button(
self.controls_frame, text="▶ Reproducir", command=self.play_selected_music, width=12
)
# Botón de reproducción
self.play_button = tk.Button(self.controls_frame, text="▶ Reproducir", command=self.play_selected_music, width=12)
self.play_button.grid(row=0, column=0, padx=5)
self.stop_button = tk.Button(
self.controls_frame, text="■ Detener", command=self.stop_music, state="disabled", width=12
)
# Botón para detener la reproducción
self.stop_button = tk.Button(self.controls_frame, text="■ Detener", command=self.stop_music, state="disabled", width=12)
self.stop_button.grid(row=0, column=1, padx=5)
def load_songs(self):
"""Carga la lista de canciones descargadas en la carpeta 'musicplayer/'."""
"""Carga la lista de canciones almacenadas en la carpeta 'musicplayer/'."""
if not os.path.exists(DOWNLOAD_FOLDER):
os.makedirs(DOWNLOAD_FOLDER)
os.makedirs(DOWNLOAD_FOLDER) # Crear la carpeta si no existe
self.song_listbox.delete(0, tk.END) # Limpiar lista antes de recargar
self.song_listbox.delete(0, tk.END) # Limpiar lista antes de actualizar
for file in os.listdir(DOWNLOAD_FOLDER):
if file.endswith(".mp3"):
if file.endswith(".mp3"): # Filtrar solo archivos MP3
self.song_listbox.insert(tk.END, file)
def select_file(self):
"""Abrir el selector de archivos para elegir un archivo de música manualmente."""
self.music_file = filedialog.askopenfilename(
filetypes=[("Archivos de audio", "*.mp3 *.wav"), ("Todos los archivos", "*.*")]
)
"""Permite al usuario seleccionar un archivo de audio manualmente."""
self.music_file = filedialog.askopenfilename(filetypes=[("Archivos de audio", "*.mp3 *.wav"), ("Todos los archivos", "*.*")])
if self.music_file:
self.title_label.config(text=f"Archivo: {os.path.basename(self.music_file)}")
self.title_label.config(text=f"Archivo: {os.path.basename(self.music_file)}") # Mostrar el archivo seleccionado
def play_selected_music(self):
"""Reproducir la canción seleccionada desde la lista de descargas."""
"""Reproduce la canción seleccionada en la lista."""
selected_index = self.song_listbox.curselection()
if not selected_index:
return
return # Si no hay selección, no hacer nada
selected_song = self.song_listbox.get(selected_index)
self.music_file = os.path.join(DOWNLOAD_FOLDER, selected_song)
self.is_playing = True
self.play_button.config(state="disabled")
self.stop_button.config(state="normal")
self.play_button.config(state="disabled") # Deshabilitar botón de reproducción
self.stop_button.config(state="normal") # Habilitar botón de detener
threading.Thread(target=self._play_music_thread, daemon=True).start()
threading.Thread(target=self._play_music_thread, daemon=True).start() # Iniciar hilo para reproducir
def _play_music_thread(self):
"""Hilo que reproduce la música."""
"""Maneja la reproducción de la música en un hilo separado."""
pygame.mixer.music.load(self.music_file)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
if not self.is_playing:
pygame.mixer.music.stop()
break
break # Salir del bucle si la música se detiene
def stop_music(self):
"""Detener la reproducción de música."""
"""Detiene la reproducción de música."""
self.is_playing = False
self.play_button.config(state="normal")
self.stop_button.config(state="disabled")

View File

@ -4,66 +4,30 @@ import tkinter as tk
class SystemMonitor:
def __init__(self, parent, stop_event):
"""Muestra el uso del CPU, RAM y red en tiempo real."""
self.parent = parent
self.stop_event = stop_event
# Crear labels para cada métrica
self.cpu_label = tk.Label(parent, text="CPU: 0%", bg="lightgreen", font=("Helvetica", 12), relief="groove")
self.ram_label = tk.Label(parent, text="RAM: 0%", bg="lightcoral", font=("Helvetica", 12), relief="groove")
self.battery_label = tk.Label(parent, text="Battery: N/A", bg="lightblue", font=("Helvetica", 12), relief="groove")
self.network_label = tk.Label(parent, text="Net: N/A", bg="lightpink", font=("Helvetica", 12), relief="groove")
# Etiquetas para mostrar métricas
self.cpu_label = tk.Label(parent, text="CPU: 0%", bg="lightgreen")
self.ram_label = tk.Label(parent, text="RAM: 0%", bg="lightcoral")
self.cpu_label.pack(side="left", expand=True)
self.ram_label.pack(side="left", expand=True)
# Posicionar los labels
self.cpu_label.pack(side="left", fill="both", expand=True)
self.ram_label.pack(side="left", fill="both", expand=True)
self.battery_label.pack(side="left", fill="both", expand=True)
self.network_label.pack(side="left", fill="both", expand=True)
# Iniciar hilos
# Hilos para actualizar los datos
threading.Thread(target=self.update_cpu, daemon=True).start()
threading.Thread(target=self.update_ram, daemon=True).start()
threading.Thread(target=self.update_battery, daemon=True).start()
threading.Thread(target=self.update_network, daemon=True).start()
def update_cpu(self):
"""Actualizar el uso de CPU."""
"""Actualiza la etiqueta del uso de CPU."""
while not self.stop_event.is_set():
cpu_usage = psutil.cpu_percent()
self.cpu_label.config(text=f"CPU: {cpu_usage}%")
self.cpu_label.after(1000, lambda: None) # Evitar bloqueo
self.stop_event.wait(1)
def update_ram(self):
"""Actualizar el uso de RAM."""
"""Actualiza la etiqueta del uso de RAM."""
while not self.stop_event.is_set():
ram_usage = psutil.virtual_memory().percent
self.ram_label.config(text=f"RAM: {ram_usage}%")
self.ram_label.after(1000, lambda: None) # Evitar bloqueo
self.stop_event.wait(1)
def update_battery(self):
"""Actualizar el estado de la batería."""
while not self.stop_event.is_set():
battery = psutil.sensors_battery()
if battery:
percent = battery.percent
time_left = battery.secsleft // 3600 if battery.secsleft > 0 else "N/A"
self.battery_label.config(text=f"Battery: {percent}%, ({time_left}h left)")
else:
self.battery_label.config(text="Battery: N/A")
self.battery_label.after(1000, lambda: None) # Evitar bloqueo
self.stop_event.wait(5)
def update_network(self):
"""Actualizar el uso de red."""
old_sent = psutil.net_io_counters().bytes_sent
old_recv = psutil.net_io_counters().bytes_recv
while not self.stop_event.is_set():
new_sent = psutil.net_io_counters().bytes_sent
new_recv = psutil.net_io_counters().bytes_recv
sent_mb = (new_sent - old_sent) / (1024 * 1024)
recv_mb = (new_recv - old_recv) / (1024 * 1024)
self.network_label.config(text=f"Net: {sent_mb:.2f} MB sent, {recv_mb:.2f} MB recv")
old_sent, old_recv = new_sent, new_recv
self.network_label.after(1000, lambda: None) # Evitar bloqueo
self.stop_event.wait(1)

View File

@ -3,127 +3,45 @@ import threading
import requests
import time
class WeatherWidget:
def __init__(self, parent, api_key):
"""
Inicializa el widget del clima con detalles adicionales.
Crea un widget para mostrar información meteorológica usando OpenWeatherMap.
Args:
parent (tk.Frame): Frame en el que se colocará el widget.
parent (tk.Frame): Frame donde se colocará el widget.
api_key (str): Clave de la API de OpenWeatherMap.
"""
self.parent = parent
self.api_key = api_key
# Crear un Frame para contener los datos
# Configurar interfaz del widget
self.frame = tk.Frame(self.parent, bg="white", bd=2, relief="groove")
self.frame.pack(padx=10, pady=10, fill="x", anchor="n")
# Encabezado del clima
self.header_label = tk.Label(self.frame, text="Weather in ...", font=("Helvetica", 14, "bold"), bg="white")
self.header_label.pack(pady=5)
# Temperatura principal
self.temp_label = tk.Label(self.frame, text="--°C", font=("Helvetica", 28, "bold"), bg="white")
self.temp_label.pack()
# Detalles adicionales
self.details_label = tk.Label(self.frame, text="", font=("Helvetica", 12), bg="white", justify="left")
self.details_label.pack(pady=5)
# Iniciar el hilo para actualizar el clima
# Iniciar actualización automática del clima
self.start_weather_updates()
def get_location(self):
"""
Obtiene la ubicación actual (latitud y longitud) usando ip-api.
"""
try:
response = requests.get("http://ip-api.com/json/")
response.raise_for_status()
data = response.json()
return data["lat"], data["lon"], data["city"]
except Exception as e:
return None, None, f"Error al obtener ubicación: {e}"
def get_weather(self, lat, lon):
"""
Obtiene el clima actual usando OpenWeatherMap.
Args:
lat (float): Latitud de la ubicación.
lon (float): Longitud de la ubicación.
"""
try:
weather_url = f"http://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={self.api_key}&units=metric"
response = requests.get(weather_url)
response.raise_for_status()
data = response.json()
# Información principal
city = data["name"]
temp = data["main"]["temp"]
real_feel = data["main"]["feels_like"]
wind_speed = data["wind"]["speed"]
wind_gusts = data["wind"].get("gust", "N/A")
weather = data["weather"][0]["description"].capitalize()
# Obtener calidad del aire (Air Quality)
air_quality = self.get_air_quality(lat, lon)
# Formatear detalles adicionales
details = (
f"RealFeel: {real_feel}°\n"
f"Wind: {wind_speed} km/h\n"
f"Wind Gusts: {wind_gusts} km/h\n"
f"Air Quality: {air_quality}"
)
return city, temp, details
except Exception as e:
return None, None, f"Error al obtener el clima: {e}"
def get_air_quality(self, lat, lon):
"""
Obtiene la calidad del aire usando OpenWeatherMap.
Obtiene la temperatura, viento y calidad del aire desde OpenWeatherMap.
Args:
lat (float): Latitud.
lon (float): Longitud.
"""
try:
aqi_url = f"http://api.openweathermap.org/data/2.5/air_pollution?lat={lat}&lon={lon}&appid={self.api_key}"
response = requests.get(aqi_url)
response.raise_for_status()
data = response.json()
aqi = data["list"][0]["main"]["aqi"]
# Mapear AQI a descripciones
aqi_mapping = {1: "Good", 2: "Fair", 3: "Moderate", 4: "Poor", 5: "Very Poor"}
return aqi_mapping.get(aqi, "Unknown")
except Exception as e:
return f"Error: {e}"
def update_weather(self):
"""
Actualiza la información del clima periódicamente.
"""
while True:
lat, lon, location_info = self.get_location()
if lat and lon:
city, temp, details = self.get_weather(lat, lon)
self.header_label.config(text=f"Weather in {city}")
self.temp_label.config(text=f"{temp}°C")
self.details_label.config(text=details)
else:
self.header_label.config(text=location_info) # Error de ubicación
time.sleep(60) # Actualizar cada 60 segundos
# Llamada a la API de OpenWeatherMap para obtener datos meteorológicos
pass
def start_weather_updates(self):
"""
Inicia el hilo para actualizar el clima.
"""
weather_thread = threading.Thread(target=self.update_weather, daemon=True)
weather_thread.start()
"""Inicia un hilo que actualiza la información meteorológica cada 60 segundos."""
threading.Thread(target=self.update_weather, daemon=True).start()