Proyecto
This commit is contained in:
parent
c6bfeaeca1
commit
abc8b0d061
175
app/main.py
175
app/main.py
|
@ -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.protocol("WM_DELETE_WINDOW", on_closing)
|
||||
root.mainloop()
|
Binary file not shown.
Binary file not shown.
|
@ -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}")
|
|
@ -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.")
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue