diff --git a/app/__pycache__/gestor_tareas.cpython-313.pyc b/app/__pycache__/gestor_tareas.cpython-313.pyc new file mode 100644 index 0000000..70349d0 Binary files /dev/null and b/app/__pycache__/gestor_tareas.cpython-313.pyc differ diff --git a/app/__pycache__/graphics.cpython-313.pyc b/app/__pycache__/graphics.cpython-313.pyc new file mode 100644 index 0000000..b92c06b Binary files /dev/null and b/app/__pycache__/graphics.cpython-313.pyc differ diff --git a/app/__pycache__/monitorization.cpython-313.pyc b/app/__pycache__/monitorization.cpython-313.pyc index ccc3c23..e99ef99 100644 Binary files a/app/__pycache__/monitorization.cpython-313.pyc and b/app/__pycache__/monitorization.cpython-313.pyc differ diff --git a/app/__pycache__/panel_izquierdo.cpython-313.pyc b/app/__pycache__/panel_izquierdo.cpython-313.pyc index 82fcb26..0b6c3f1 100644 Binary files a/app/__pycache__/panel_izquierdo.cpython-313.pyc and b/app/__pycache__/panel_izquierdo.cpython-313.pyc differ diff --git a/app/__pycache__/scraping.cpython-313.pyc b/app/__pycache__/scraping.cpython-313.pyc index c784a36..0a019d2 100644 Binary files a/app/__pycache__/scraping.cpython-313.pyc and b/app/__pycache__/scraping.cpython-313.pyc differ diff --git a/app/gestor_tareas.py b/app/gestor_tareas.py new file mode 100644 index 0000000..1a361e1 --- /dev/null +++ b/app/gestor_tareas.py @@ -0,0 +1,54 @@ +import tkinter as tk +from tkinter import ttk +import threading +import psutil +import time + +class GestorTareas: + def __init__(self, frame): + self.frame = frame + + # Configurar estilo para ttk.Frame + style = ttk.Style() + style.configure("Custom.TFrame", background="white") + self.frame.configure(style="Custom.TFrame") + + # Título + self.title_label = ttk.Label( + self.frame, text="Administrador de Tareas", font=("Helvetica", 16, "bold"), background="white" + ) + self.title_label.pack(pady=10) + + # Tabla de procesos + self.processes_frame = ttk.Frame(self.frame) + self.processes_frame.pack(fill="both", expand=True) + + self.processes_list = tk.Listbox(self.processes_frame, width=80, height=20) + self.processes_list.pack(side="left", fill="both", expand=True, padx=10, pady=10) + + scrollbar = tk.Scrollbar(self.processes_frame, orient="vertical", command=self.processes_list.yview) + scrollbar.pack(side="right", fill="y") + self.processes_list.config(yscrollcommand=scrollbar.set) + + # Botón para actualizar procesos + self.update_button = ttk.Button( + self.frame, text="Actualizar", command=self.start_updating_processes + ) + self.update_button.pack(pady=10) + + def start_updating_processes(self): + threading.Thread(target=self.update_processes, daemon=True).start() + + def update_processes(self): + while True: + self.processes_list.delete(0, tk.END) + for proc in psutil.process_iter(attrs=['pid', 'name', 'cpu_percent', 'memory_info']): + try: + process_info = ( + f"PID: {proc.info['pid']} | Nombre: {proc.info['name']} | " + f"CPU: {proc.info['cpu_percent']}% | Memoria: {proc.info['memory_info'].rss / (1024 ** 2):.2f} MB" + ) + self.processes_list.insert(tk.END, process_info) + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + continue + time.sleep(5) diff --git a/app/graphics.py b/app/graphics.py new file mode 100644 index 0000000..b450188 --- /dev/null +++ b/app/graphics.py @@ -0,0 +1,87 @@ +import requests +import yfinance as yf +import threading +import random +import time + +running = True # Variable global para controlar el estado de los hilos + + +def actualizar_grafico_criptomonedas_api(canvas, ax): + """Actualiza los datos del gráfico de criptomonedas utilizando CoinGecko API.""" + while True: + if not running: # Verifica si el programa está en ejecución + break + if not canvas.get_tk_widget().winfo_exists(): # Verifica si el Canvas sigue existiendo + break + try: + # Solicitar datos de precios de criptomonedas + url = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd" + response = requests.get(url) + response.raise_for_status() + data = response.json() + + # Obtener precios actuales + bitcoin_price = data['bitcoin']['usd'] + ethereum_price = data['ethereum']['usd'] + + # Actualizar el gráfico con datos simulados + precios reales + ax.clear() + ax.set_title("Gráfico Criptomonedas") + ax.plot([random.randint(1, 100) for _ in range(10)], label=f"Bitcoin (${bitcoin_price})", color="blue") + ax.plot([random.randint(1, 100) for _ in range(10)], label=f"Ethereum (${ethereum_price})", color="green") + ax.legend() + canvas.draw() + except Exception as e: + print(f"Error al obtener datos de criptomonedas: {e}") + time.sleep(10) # Actualizar cada 10 segundos + + +def actualizar_grafico_ibex_api(canvas, ax): + """Actualiza los datos del gráfico del IBEX utilizando Yahoo Finance API.""" + while True: + if not running: # Verifica si el programa está en ejecución + break + if not canvas.get_tk_widget().winfo_exists(): # Verifica si el Canvas sigue existiendo + break + try: + # Obtener datos históricos de IBEX 35 + data = yf.Ticker("^IBEX").history(period="1d", interval="5m") + + # Extraer los precios de cierre + close_prices = data['Close'].values + + # Actualizar el gráfico con los datos reales + ax.clear() + ax.set_title("Gráfico IBEX") + ax.plot(close_prices, label="IBEX", color="orange") + ax.legend() + canvas.draw() + except Exception as e: + print(f"Error al obtener datos del IBEX: {e}") + time.sleep(300) # Actualizar cada 5 minutos + + +def iniciar_hilos(canvas_cripto, ax_cripto, canvas_ibex, ax_ibex): + """Inicia los hilos de actualización de gráficos.""" + global running + running = True # Asegurar que los hilos pueden ejecutarse + + # Hilo para criptomonedas + thread_criptomonedas = threading.Thread( + target=actualizar_grafico_criptomonedas_api, args=(canvas_cripto, ax_cripto), daemon=True + ) + thread_criptomonedas.start() + + # Hilo para IBEX + thread_ibex = threading.Thread( + target=actualizar_grafico_ibex_api, args=(canvas_ibex, ax_ibex), daemon=True + ) + thread_ibex.start() + + +def detener_hilos(): + """Detiene la ejecución de los hilos.""" + global running + running = False + print("Hilos detenidos.") diff --git a/app/monitorization.py b/app/monitorization.py index 3bd78f6..4231ef3 100644 --- a/app/monitorization.py +++ b/app/monitorization.py @@ -7,6 +7,8 @@ import psutil def monitor_cpu_usage(label): """Monitorea y actualiza el uso de CPU en tiempo real.""" while True: + if not label.winfo_exists(): # Verifica si el Label existe + break cpu_percent = psutil.cpu_percent(interval=1) # Obtiene el uso de CPU cada segundo label.config(text=f"CPU: {cpu_percent}%") time.sleep(1) @@ -14,6 +16,8 @@ def monitor_cpu_usage(label): def monitor_ram_usage(label): """Monitorea y actualiza el uso de RAM en tiempo real.""" while True: + if not label.winfo_exists(): # Verifica si el Label existe + break memory_info = psutil.virtual_memory() ram_percent = memory_info.percent # Porcentaje de uso de RAM label.config(text=f"RAM: {ram_percent}%") @@ -25,6 +29,8 @@ def monitor_battery(label): low_battery_alert_shown = False # Bandera para evitar múltiples alertas while True: + if not label.winfo_exists(): # Verifica si el Label existe + break try: # Obtiene la información de la batería battery = psutil.sensors_battery() @@ -68,6 +74,8 @@ def monitor_network_usage(label): """Monitorea y actualiza el uso de la red en tiempo real.""" prev_net = psutil.net_io_counters() while True: + if not label.winfo_exists(): # Verifica si el Label existe + break time.sleep(1) # Intervalo de actualización current_net = psutil.net_io_counters() sent = (current_net.bytes_sent - prev_net.bytes_sent) / (1024 * 1024) # En MB diff --git a/app/panel_izquierdo.py b/app/panel_izquierdo.py index 97609b9..7a49a70 100644 --- a/app/panel_izquierdo.py +++ b/app/panel_izquierdo.py @@ -9,47 +9,60 @@ class PanelIzquierdo: def __init__(self, frame, text_widget): # Configuración del panel self.frame = frame - self.frame.configure(bg="lightblue", width=300) + self.frame.configure(bg="lightblue", width=200) self.frame.grid_propagate(False) self.frame.columnconfigure(0, weight=1) - # Botón para iniciar scraping (ya existente) + # Sección: Clima + clima_frame = tk.Frame(self.frame, bg="white", bd=2, relief="sunken") + clima_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5) + clima_title = tk.Label(clima_frame, text="Clima Actual", bg="white", font=("Helvetica", 12, "bold"), fg="navy") + clima_title.pack(pady=5) + + self.weather_icon = tk.Label(clima_frame, bg="white") + self.weather_icon.pack() + + self.weather_label = tk.Label(clima_frame, text="Cargando clima...", bg="white", font=("Helvetica", 10), fg="black") + self.weather_label.pack(pady=5) + self.update_weather() + + # Sección: Scrapping + scraping_frame = tk.Frame(self.frame, bg="white", bd=2, relief="sunken") + scraping_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5) + scraping_title = tk.Label(scraping_frame, text="Scraping", bg="white", font=("Helvetica", 12, "bold"), fg="navy") + scraping_title.pack(pady=5) + boton_scrapping = tk.Button( - frame, + scraping_frame, text="Iniciar Scrapping", command=lambda: threading.Thread( - target=scraping.iniciar_scraping_y_insercion, - args=("https://www.amazon.es/", text_widget) - ).start() + target=scraping.iniciar_scraping_y_insercion, + args=("https://www.amazon.es/", text_widget) + ).start(), + bg="lightgreen" ) boton_scrapping.pack(pady=5) - # Clima - Título - self.weather_title = tk.Label( - frame, text="Clima Actual", bg="lightblue", font=("Helvetica", 14, "bold"), fg="navy" + # Sección: Noticias + noticias_frame = tk.Frame(self.frame, bg="white", bd=2, relief="sunken") + noticias_frame.grid(row=2, column=0, sticky="nsew", padx=5, pady=5) + noticias_title = tk.Label(noticias_frame, text="Últimas Noticias", bg="white", font=("Helvetica", 12, "bold"), fg="navy") + noticias_title.pack(pady=5) + + self.news_label = tk.Label( + noticias_frame, text="Cargando noticias...", bg="white", font=("Helvetica", 10), fg="black", wraplength=180, justify="left" ) - self.weather_title.pack(pady=10) + self.news_label.pack(pady=5) + self.update_news() - # Clima - Ícono - self.weather_icon = tk.Label(frame, bg="lightblue") - self.weather_icon.pack() - - # Clima - Información - self.weather_label = tk.Label( - frame, text="Cargando clima...", bg="lightblue", font=("Helvetica", 10, "bold"), fg="black" - ) - self.weather_label.pack(pady=5) - - # Variables para el clima - self.api_key = '2f79c6f35c48e876bceae7fa7f4f4735' - self.city = 'Javea' # Cambia por la ciudad que desees - self.update_weather() + # Configuración para que las filas crezcan dinámicamente + self.frame.rowconfigure(2, weight=1) def update_weather(self): """Actualiza la información del clima utilizando un hilo.""" def fetch_weather(): try: - url = f'http://api.openweathermap.org/data/2.5/weather?q={self.city}&appid={self.api_key}&units=metric&lang=es' + url = f'http://api.openweathermap.org/data/2.5/weather?q=Javea&appid=2f79c6f35c48e876bceae7fa7f4f4735&units=metric&lang=es' response = requests.get(url) response.raise_for_status() data = response.json() @@ -61,7 +74,7 @@ class PanelIzquierdo: # Actualiza el texto del clima self.weather_label.config( - text=f"{self.city}:\n{temp}°C, {weather.capitalize()}" + text=f"Javea:\n{temp}°C, {weather.capitalize()}" ) # Descarga y muestra el ícono del clima @@ -73,7 +86,26 @@ class PanelIzquierdo: self.weather_icon.image = icon_photo # Referencia para evitar que se elimine except Exception as e: - self.weather_label.config(text=f"Error al obtener el clima.") + self.weather_label.config(text="Error al obtener el clima.") print(f"Error al obtener el clima: {e}") threading.Thread(target=fetch_weather, daemon=True).start() + + def update_news(self): + """Consulta y actualiza las últimas noticias en tiempo real.""" + def fetch_news(): + while True: + try: + url = 'https://newsapi.org/v2/top-headlines?country=us&apiKey=b1ea42563bd848499ebad866eeedaf15' + response = requests.get(url) + response.raise_for_status() + data = response.json() + + articles = data['articles'][:7] # Obtiene las 5 primeras noticias + news_text = "\n\n".join([f"- {article['title']}" for article in articles]) + self.news_label.config(text=news_text) + except Exception as e: + self.news_label.config(text="Error al obtener noticias") + print(f"Error al obtener noticias: {e}") + + threading.Thread(target=fetch_news, daemon=True).start() diff --git a/app/scraping.py b/app/scraping.py index 8792a34..f01fa0c 100644 --- a/app/scraping.py +++ b/app/scraping.py @@ -70,8 +70,9 @@ def insertar_enlaces_mysql(cola): # Función para iniciar el scraping y la inserción en hilos def iniciar_scraping_y_insercion(url, text_widget): cola_enlaces = Queue() - hilo_scraping = threading.Thread(target=extraer_enlaces, args=(url, cola_enlaces, text_widget)) - hilo_insercion = threading.Thread(target=insertar_enlaces_mysql, args=(cola_enlaces,)) + # Configurar los hilos como daemon + hilo_scraping = threading.Thread(target=extraer_enlaces, args=(url, cola_enlaces, text_widget), daemon=True) + hilo_insercion = threading.Thread(target=insertar_enlaces_mysql, args=(cola_enlaces,), daemon=True) hilo_scraping.start() hilo_insercion.start() hilo_scraping.join() diff --git a/main.py b/main.py index 8325f8f..b5b8a03 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,11 @@ import datetime import psutil from app import scraping from app import game - +from app import gestor_tareas +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +import matplotlib.pyplot as plt +import random +from app import graphics def update_time(status_bar): while True: @@ -28,20 +32,25 @@ def update_time(status_bar): # Espera 1 segundo antes de actualizar de nuevo time.sleep(1) - - # Crear la ventana principal + +# Detener hilos de los graficos +def detener_aplicacion(): + graphics.detener_hilos() # Detener los hilos de gráficos + root.quit() # Cerrar la ventana principal + +# 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 +# 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) file_menu = Menu(menu_bar, tearoff=0) @@ -63,48 +72,47 @@ menu_bar.add_cascade(label="Ayuda", menu=help_menu) root.config(menu=menu_bar) - # Crear los frames laterales y el central -frame_izquierdo = tk.Frame(root, bg="lightblue", width=300) +# Crear los frames laterales y el central +frame_izquierdo = tk.Frame(root, bg="lightgray", width=200) frame_central = tk.Frame(root, bg="white") -frame_derecho = tk.Frame(root, bg="lightgreen", width=200) +frame_derecho = tk.Frame(root, bg="lightgray", width=200) - # Colocar los frames laterales y el central +# 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 +# Configurar los tamaños fijos de los frames laterales frame_izquierdo.grid_propagate(False) frame_derecho.grid_propagate(False) - # Dividir el frame central en dos partes (superior variable e inferior fija) +# 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 +# Crear subframes dentro del frame central frame_superior = tk.Frame(frame_central, bg="lightyellow") -frame_inferior = tk.Frame(frame_central, bg="lightgray", height=100) +frame_inferior = tk.Frame(frame_central, bg="lightgray", height=250) - # Colocar los subframes dentro del frame central +# 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 +# Fijar el tamaño de la parte inferior frame_inferior.grid_propagate(False) - # Crear la barra de estado +# Crear la 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") - # Notebook para las pestañas - +# Notebook para las pestañas en el frame superior 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) - # Crear cinco solapas +# Crear cinco solapas en el frame superior for i in range(1, 6): tab = ttk.Frame(notebook) if i == 1: @@ -116,6 +124,7 @@ for i in range(1, 6): pomodoro.PomodoroTimer(tab) elif i == 3: notebook.add(tab, text="Gestor de tareas", padding=4) + gestor_tareas.GestorTareas(tab) elif i == 4: notebook.add(tab,text='Juego', padding=4) hilo_juego = game.HiloJuego(tab) @@ -124,13 +133,30 @@ for i in range(1, 6): # Añadir un Label en cada solapa para diferenciarla label = ttk.Label(tab, text=f"Contenido de la Solapa {i}") label.pack(pady=10) + +# Notebook para las pestañas en el frame inferior +notebook_inferior = ttk.Notebook(frame_inferior, style="CustomNotebook.TNotebook") +notebook_inferior.pack(fill="both", expand=True) + +# Pestaña de criptomonedas +tab_criptomonedas = ttk.Frame(notebook_inferior) +notebook_inferior.add(tab_criptomonedas, text="Gráfico Criptomonedas", padding=4) +fig_cripto, ax_cripto = plt.subplots(figsize=(8, 3)) +canvas_cripto = FigureCanvasTkAgg(fig_cripto, tab_criptomonedas) +canvas_cripto.get_tk_widget().pack(fill="both", expand=True) + +# Pestaña de IBEX +tab_ibex = ttk.Frame(notebook_inferior) +notebook_inferior.add(tab_ibex, text="Gráfico IBEX", padding=4) +fig_ibex, ax_ibex = plt.subplots(figsize=(8, 3)) +canvas_ibex = FigureCanvasTkAgg(fig_ibex, tab_ibex) +canvas_ibex.get_tk_widget().pack(fill="both", expand=True) + +# Iniciar los hilos para los gráficos +graphics.iniciar_hilos(canvas_cripto, ax_cripto, canvas_ibex, ax_ibex) - # Barra de estado - # Dividir la barra de estado en 4 labels - - - # Usar pack para alinear los labels horizontalmente +# Barra de estado dividida label_cpu_used = tk.Label(barra_estado, text="CPU: 0%", font=("Helvetica", 11), bd=1, fg="blue", relief="sunken", anchor="w", width=20, padx=10) label_ram_used = tk.Label(barra_estado, text="RAM: 0%", font=("Helvetica", 11), bd=1, fg="blue", relief="sunken", anchor="w", width=20, padx=10) label_pc_battery = tk.Label(barra_estado, text="Temperatura CPU: ", font=("Helvetica", 11), bd=1, fg="blue", relief="sunken", anchor="w", width=20, padx=10) @@ -143,8 +169,7 @@ label_pc_battery.pack(side="left", fill="x", expand=True) label_network_used.pack(side="left", fill="x", expand=True) label_fecha_hora.pack(side="right", fill="x", expand=True) - -#Monitorizacion barra horizontal de abajo +# Monitorización barra horizontal de abajo update_thread = threading.Thread(target=update_time, args=(label_fecha_hora,)) update_thread.daemon = True update_thread.start() @@ -156,7 +181,7 @@ thread_cpu_monitor.start() thread_ram_monitor = threading.Thread(target=monitorization.monitor_ram_usage, args=(label_ram_used,)) thread_ram_monitor.daemon = True thread_ram_monitor.start() - + thread_temperature_battery = threading.Thread(target=monitorization.monitor_battery, args=(label_pc_battery,)) thread_temperature_battery.daemon = True thread_temperature_battery.start() @@ -165,10 +190,11 @@ thread_network_used_monitor = threading.Thread(target=monitorization.monitor_net thread_network_used_monitor.daemon = True thread_network_used_monitor.start() -#Lados verticulas +# Lados verticales panel_d = panel_derecho.PanelDerecho(frame_derecho) panel_i = panel_izquierdo.PanelIzquierdo(frame_izquierdo, text_scrapping) +root.protocol("WM_DELETE_WINDOW", detener_aplicacion) - # Ejecución de la aplicación -root.mainloop() +# Ejecución de la aplicación +root.mainloop() \ No newline at end of file