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 tkinter as tk
import threading import threading
import time
import datetime import datetime
from tkinter import Menu # Importar el widget Menu from tkinter import Menu, ttk
from tkinter import ttk # Importar el widget ttk
from correo_server.EmailTab import MailTab # Importación de widgets personalizados
from correo_server.InboxTab import InboxTab
from correo_server.MailClient import MailClient
from hilos.ChatWidget import ChatWidget from hilos.ChatWidget import ChatWidget
from hilos.MusicPlayer import MusicPlayer from hilos.MusicPlayer import MusicPlayer
from hilos.WeatherWidget import WeatherWidget from hilos.WeatherWidget import WeatherWidget
from hilos.SystemMonitor import SystemMonitor from hilos.SystemMonitor import SystemMonitor
from hilos.ApplicationLauncher import ApplicationLauncher from hilos.ApplicationLauncher import ApplicationLauncher
from hilos.LanguageChart import LanguageChart 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 # Crear la ventana principal
root = tk.Tk() root = tk.Tk()
root.title("Ventana Responsive") root.title("Ventana Responsive")
root.geometry("1000x700") # Tamaño inicial 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 # Crear el menú superior
menu_bar = Menu(root) 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) 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_izquierdo = tk.Frame(root, bg="lightblue", width=150)
frame_central = tk.Frame(root, bg="white") frame_central = tk.Frame(root, bg="white")
frame_derecho = tk.Frame(root, bg="lightgreen", width=150) 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_izquierdo.grid(row=0, column=0, sticky="ns")
frame_central.grid(row=0, column=1, sticky="nsew") frame_central.grid(row=0, column=1, sticky="nsew")
frame_derecho.grid(row=0, column=2, sticky="ns") frame_derecho.grid(row=0, column=2, sticky="ns")
# Configurar los tamaños fijos de los frames laterales # Añadir widgets a los paneles
frame_izquierdo.grid_propagate(False) weather_widget = WeatherWidget(frame_izquierdo, "API_KEY")
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
app_launcher = ApplicationLauncher(frame_izquierdo) app_launcher = ApplicationLauncher(frame_izquierdo)
# Añadir gráfico de lenguajes al panel izquierdo
language_chart = LanguageChart(frame_izquierdo) language_chart = LanguageChart(frame_izquierdo)
# Crear el widget de Chat en el panel derecho con más espacio
chat_widget = ChatWidget(frame_derecho) chat_widget = ChatWidget(frame_derecho)
# Agregar el reproductor de música al panel derecho, en la parte inferior
music_player = MusicPlayer(frame_derecho) music_player = MusicPlayer(frame_derecho)
# Dividir el frame central en dos partes (superior variable e inferior fija) # Barra de estado
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_estado = tk.Label(root, text="Barra de estado", bg="lightgray", anchor="w") barra_estado = tk.Label(root, text="Barra de estado", bg="lightgray", anchor="w")
barra_estado.grid(row=1, column=0, columnspan=3, sticky="ew") barra_estado.grid(row=1, column=0, columnspan=3, sticky="ew")
# Inicializar el monitor del sistema # Inicializar el monitor del sistema
stop_event = threading.Event()
system_monitor = SystemMonitor(barra_estado, stop_event) system_monitor = SystemMonitor(barra_estado, stop_event)
# Notebook para las pestañas # Manejar el cierre de la aplicación
style = ttk.Style() def on_closing():
style.configure("CustomNotebook.TNotebook.Tab", font=("Arial", 12, "bold")) stop_event.set()
notebook = ttk.Notebook(frame_superior, style="CustomNotebook.TNotebook") root.destroy()
notebook.pack(fill="both", expand=True)
# Crear la Solapa 1 y añadir el downloader root.protocol("WM_DELETE_WINDOW", on_closing)
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.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 subprocess
import os import os
class ApplicationLauncher: class ApplicationLauncher:
def __init__(self, parent): def __init__(self, parent):
""" """Crea botones para abrir aplicaciones como VS Code, Eclipse y PyCharm."""
Inicializa los botones para lanzar aplicaciones con detección automática de rutas.
Args:
parent (tk.Frame): Frame donde se colocarán los botones.
"""
self.parent = parent self.parent = parent
# Detectar rutas automáticamente # Detectar rutas de instalación
self.vscode_path = self.detect_path(["C:\\Program Files\\Microsoft VS Code\\Code.exe", 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"])
self.eclipse_path = self.detect_path(["C:\\eclipse\\eclipse.exe", self.pycharm_path = self.detect_path(["C:\\Program Files\\JetBrains\\PyCharm\\bin\\pycharm64.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"])
# Título para el grupo de botones # Crear botones de lanzamiento
title = tk.Label(self.parent, text="Aplicaciones", font=("Helvetica", 14, "bold"), bg="lightblue") self.create_button("Visual Code", self.launch_vscode)
title.pack(pady=10) self.create_button("Eclipse", self.launch_eclipse)
self.create_button("PyCharm", self.launch_pycharm)
# 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)
def detect_path(self, paths): def detect_path(self, paths):
""" """Detecta la primera ruta válida de una lista de rutas posibles."""
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.
"""
for path in paths: for path in paths:
path = os.path.expandvars(path) # Expande variables como %USERNAME%
if os.path.exists(path): if os.path.exists(path):
return path return path
return None 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): 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") 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): def launch_application(self, path, name):
""" """Ejecuta la aplicación si la ruta es válida."""
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).
"""
if path: if path:
threading.Thread(target=self.run_command, args=([path],), daemon=True).start() threading.Thread(target=subprocess.run, args=([path],), daemon=True).start()
else: else:
print(f"No se encontró {name}. Por favor, instálalo o configura la ruta.") print(f"{name} no está instalado.")
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}")

View File

@ -3,71 +3,55 @@ from tkinter import scrolledtext
import socket import socket
import threading import threading
class ChatWidget: class ChatWidget:
def __init__(self, parent): def __init__(self, parent):
"""Widget de chat con conexión a un servidor por sockets."""
self.parent = parent self.parent = parent
self.frame = tk.Frame(self.parent, bg="lightgreen", width=200, height=300) # Ajustar tamaño del frame self.frame = tk.Frame(self.parent, bg="lightgreen")
self.frame.pack(fill="x", expand=False, padx=10, pady=10) self.frame.pack(fill="x", padx=10, pady=10)
# Label superior # Área de texto para mostrar mensajes
self.label = tk.Label(self.frame, text="Chat", font=("Arial", 14, "bold"), fg="red", bg="lightgreen") self.chat_display = scrolledtext.ScrolledText(self.frame, wrap=tk.WORD, state="disabled", width=40, height=10)
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
)
self.chat_display.pack(pady=5) self.chat_display.pack(pady=5)
# Campo de entrada para escribir mensajes # Campo de entrada de mensajes
self.message_entry = tk.Entry(self.frame, width=35) # Reducir ancho self.message_entry = tk.Entry(self.frame, width=35)
self.message_entry.pack(pady=5) self.message_entry.pack(pady=5)
self.message_entry.bind("<Return>", self.send_message) self.message_entry.bind("<Return>", self.send_message)
# Botón para enviar mensajes # Botón de enviar mensaje
self.send_button = tk.Button(self.frame, text="Enviar", command=self.send_message, width=10) # Reducir tamaño self.send_button = tk.Button(self.frame, text="Enviar", command=self.send_message)
self.send_button.pack(pady=5) 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.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: try:
self.client_socket.connect(self.server_address) self.client_socket.connect(self.server_address)
threading.Thread(target=self.receive_messages, daemon=True).start() threading.Thread(target=self.receive_messages, daemon=True).start()
except Exception as e: except:
self.display_message(f"[ERROR] No se pudo conectar al servidor: {e}") self.display_message("[ERROR] No se pudo conectar al servidor.")
def send_message(self, event=None): def send_message(self, event=None):
"""Envía un mensaje al servidor."""
message = self.message_entry.get() message = self.message_entry.get()
if message: if message:
try: self.client_socket.send(message.encode("utf-8"))
self.client_socket.send(message.encode("utf-8")) self.message_entry.delete(0, tk.END)
self.message_entry.delete(0, tk.END)
except Exception as e:
self.display_message(f"[ERROR] No se pudo enviar el mensaje: {e}")
def receive_messages(self): def receive_messages(self):
"""Recibe mensajes del servidor y los muestra en la interfaz."""
while True: while True:
try: try:
message = self.client_socket.recv(1024).decode("utf-8") message = self.client_socket.recv(1024).decode("utf-8")
if message: self.display_message(message)
self.display_message(message)
else:
break
except: except:
self.display_message("[DESCONECTADO] Conexión perdida con el servidor.")
break break
def display_message(self, message): def display_message(self, message):
"""Muestra un mensaje en la ventana de chat."""
self.chat_display.config(state="normal") self.chat_display.config(state="normal")
self.chat_display.insert(tk.END, message + "\n") self.chat_display.insert(tk.END, message + "\n")
self.chat_display.config(state="disabled") self.chat_display.config(state="disabled")
self.chat_display.see(tk.END) 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: class LanguageChart:
def __init__(self, parent): 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: Args:
parent (tk.Frame): Frame donde se colocará el gráfico. parent (tk.Frame): Frame donde se colocará el gráfico.
""" """
self.parent = parent 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.languages = ["Python", "JavaScript", "Java", "C++", "C#"]
self.usage = [30, 25, 20, 15, 10] # Porcentajes de uso 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.figure = Figure(figsize=(4, 3), dpi=100)
self.ax = self.figure.add_subplot(111) self.ax = self.figure.add_subplot(111)
self.ax.bar(self.languages, self.usage, color="skyblue") self.ax.bar(self.languages, self.usage, color="skyblue")
self.ax.set_title("Lenguajes más usados") self.ax.set_title("Lenguajes más usados")
self.ax.set_ylabel("Porcentaje de uso") 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 = FigureCanvasTkAgg(self.figure, master=self.parent)
self.canvas.get_tk_widget().pack(fill="both", expand=True) 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() threading.Thread(target=self.update_chart, daemon=True).start()
def fetch_data(self): 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: Returns:
list: Lista de nuevos porcentajes de uso. 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] 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): 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: while True:
self.fetch_data() self.fetch_data()

View File

@ -4,98 +4,90 @@ from tkinter import filedialog
import threading import threading
import pygame 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" DOWNLOAD_FOLDER = "musicplayer"
class MusicPlayer: class MusicPlayer:
def __init__(self, parent): def __init__(self, parent):
"""Inicializa el reproductor de música con botones de control y lista de canciones."""
self.parent = parent self.parent = parent
self.is_playing = False self.is_playing = False
# Inicializar el reproductor de música # Inicializar el motor de audio
pygame.mixer.init() 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 = tk.Frame(self.parent, bg="lightgreen", width=200, height=100)
self.frame.pack(side="bottom", padx=10, pady=10, fill="both", expand=False) self.frame.pack(side="bottom", padx=10, pady=10, fill="both", expand=False)
# Etiqueta de título # Etiqueta de título
self.title_label = tk.Label( self.title_label = tk.Label(self.frame, text="Reproductor de Música", font=("Arial", 12, "bold"), bg="lightgreen")
self.frame, text="Reproductor de Música", font=("Arial", 12, "bold"), bg="lightgreen"
)
self.title_label.pack(pady=5) self.title_label.pack(pady=5)
# Lista de canciones descargadas # Lista de canciones descargadas
self.song_listbox = tk.Listbox(self.frame, width=50, height=10) self.song_listbox = tk.Listbox(self.frame, width=50, height=10)
self.song_listbox.pack(pady=5) 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 = tk.Frame(self.frame, bg="lightgreen")
self.controls_frame.pack(pady=10) self.controls_frame.pack(pady=10)
# Botón para seleccionar un archivo manualmente # Botón de reproducción
self.select_button = tk.Button( self.play_button = tk.Button(self.controls_frame, text="▶ Reproducir", command=self.play_selected_music, width=12)
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
)
self.play_button.grid(row=0, column=0, padx=5) self.play_button.grid(row=0, column=0, padx=5)
self.stop_button = tk.Button( # Botón para detener la reproducción
self.controls_frame, text="■ Detener", command=self.stop_music, state="disabled", width=12 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) self.stop_button.grid(row=0, column=1, padx=5)
def load_songs(self): 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): 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): 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) self.song_listbox.insert(tk.END, file)
def select_file(self): def select_file(self):
"""Abrir el selector de archivos para elegir un archivo de música manualmente.""" """Permite al usuario seleccionar un archivo de audio manualmente."""
self.music_file = filedialog.askopenfilename( self.music_file = filedialog.askopenfilename(filetypes=[("Archivos de audio", "*.mp3 *.wav"), ("Todos los archivos", "*.*")])
filetypes=[("Archivos de audio", "*.mp3 *.wav"), ("Todos los archivos", "*.*")]
)
if self.music_file: 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): 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() selected_index = self.song_listbox.curselection()
if not selected_index: if not selected_index:
return return # Si no hay selección, no hacer nada
selected_song = self.song_listbox.get(selected_index) selected_song = self.song_listbox.get(selected_index)
self.music_file = os.path.join(DOWNLOAD_FOLDER, selected_song) self.music_file = os.path.join(DOWNLOAD_FOLDER, selected_song)
self.is_playing = True self.is_playing = True
self.play_button.config(state="disabled") self.play_button.config(state="disabled") # Deshabilitar botón de reproducción
self.stop_button.config(state="normal") 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): 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.load(self.music_file)
pygame.mixer.music.play() pygame.mixer.music.play()
while pygame.mixer.music.get_busy(): while pygame.mixer.music.get_busy():
if not self.is_playing: if not self.is_playing:
pygame.mixer.music.stop() pygame.mixer.music.stop()
break break # Salir del bucle si la música se detiene
def stop_music(self): def stop_music(self):
"""Detener la reproducción de música.""" """Detiene la reproducción de música."""
self.is_playing = False self.is_playing = False
self.play_button.config(state="normal") self.play_button.config(state="normal")
self.stop_button.config(state="disabled") self.stop_button.config(state="disabled")

View File

@ -4,66 +4,30 @@ import tkinter as tk
class SystemMonitor: class SystemMonitor:
def __init__(self, parent, stop_event): def __init__(self, parent, stop_event):
"""Muestra el uso del CPU, RAM y red en tiempo real."""
self.parent = parent self.parent = parent
self.stop_event = stop_event self.stop_event = stop_event
# Crear labels para cada métrica # Etiquetas para mostrar métricas
self.cpu_label = tk.Label(parent, text="CPU: 0%", bg="lightgreen", font=("Helvetica", 12), relief="groove") self.cpu_label = tk.Label(parent, text="CPU: 0%", bg="lightgreen")
self.ram_label = tk.Label(parent, text="RAM: 0%", bg="lightcoral", font=("Helvetica", 12), relief="groove") self.ram_label = tk.Label(parent, text="RAM: 0%", bg="lightcoral")
self.battery_label = tk.Label(parent, text="Battery: N/A", bg="lightblue", font=("Helvetica", 12), relief="groove") self.cpu_label.pack(side="left", expand=True)
self.network_label = tk.Label(parent, text="Net: N/A", bg="lightpink", font=("Helvetica", 12), relief="groove") self.ram_label.pack(side="left", expand=True)
# Posicionar los labels # Hilos para actualizar los datos
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
threading.Thread(target=self.update_cpu, daemon=True).start() threading.Thread(target=self.update_cpu, daemon=True).start()
threading.Thread(target=self.update_ram, 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): def update_cpu(self):
"""Actualizar el uso de CPU.""" """Actualiza la etiqueta del uso de CPU."""
while not self.stop_event.is_set(): while not self.stop_event.is_set():
cpu_usage = psutil.cpu_percent() cpu_usage = psutil.cpu_percent()
self.cpu_label.config(text=f"CPU: {cpu_usage}%") self.cpu_label.config(text=f"CPU: {cpu_usage}%")
self.cpu_label.after(1000, lambda: None) # Evitar bloqueo
self.stop_event.wait(1) self.stop_event.wait(1)
def update_ram(self): def update_ram(self):
"""Actualizar el uso de RAM.""" """Actualiza la etiqueta del uso de RAM."""
while not self.stop_event.is_set(): while not self.stop_event.is_set():
ram_usage = psutil.virtual_memory().percent ram_usage = psutil.virtual_memory().percent
self.ram_label.config(text=f"RAM: {ram_usage}%") 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) self.stop_event.wait(1)

View File

@ -3,127 +3,45 @@ import threading
import requests import requests
import time import time
class WeatherWidget: class WeatherWidget:
def __init__(self, parent, api_key): 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: 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. api_key (str): Clave de la API de OpenWeatherMap.
""" """
self.parent = parent self.parent = parent
self.api_key = api_key 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 = tk.Frame(self.parent, bg="white", bd=2, relief="groove")
self.frame.pack(padx=10, pady=10, fill="x", anchor="n") 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 = tk.Label(self.frame, text="Weather in ...", font=("Helvetica", 14, "bold"), bg="white")
self.header_label.pack(pady=5) 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 = tk.Label(self.frame, text="--°C", font=("Helvetica", 28, "bold"), bg="white")
self.temp_label.pack() self.temp_label.pack()
# Detalles adicionales
self.details_label = tk.Label(self.frame, text="", font=("Helvetica", 12), bg="white", justify="left") self.details_label = tk.Label(self.frame, text="", font=("Helvetica", 12), bg="white", justify="left")
self.details_label.pack(pady=5) self.details_label.pack(pady=5)
# Iniciar el hilo para actualizar el clima # Iniciar actualización automática del clima
self.start_weather_updates() 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): def get_weather(self, lat, lon):
""" """
Obtiene el clima actual usando OpenWeatherMap. Obtiene la temperatura, viento y calidad del aire desde 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.
Args: Args:
lat (float): Latitud. lat (float): Latitud.
lon (float): Longitud. lon (float): Longitud.
""" """
try: # Llamada a la API de OpenWeatherMap para obtener datos meteorológicos
aqi_url = f"http://api.openweathermap.org/data/2.5/air_pollution?lat={lat}&lon={lon}&appid={self.api_key}" pass
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
def start_weather_updates(self): def start_weather_updates(self):
""" """Inicia un hilo que actualiza la información meteorológica cada 60 segundos."""
Inicia el hilo para actualizar el clima. threading.Thread(target=self.update_weather, daemon=True).start()
"""
weather_thread = threading.Thread(target=self.update_weather, daemon=True)
weather_thread.start()