From c480f9a926b1bd87e1aabdd1bf5c55d6661aa6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moran?= Date: Wed, 11 Dec 2024 23:35:50 +0100 Subject: [PATCH] Panel izquierdo y graficos crypto e ibex --- app/__pycache__/gestor_tareas.cpython-313.pyc | Bin 0 -> 3960 bytes app/__pycache__/graphics.cpython-313.pyc | Bin 0 -> 4127 bytes .../monitorization.cpython-313.pyc | Bin 4355 -> 4635 bytes .../panel_izquierdo.cpython-313.pyc | Bin 4620 -> 7079 bytes app/__pycache__/scraping.cpython-313.pyc | Bin 3920 -> 3939 bytes app/gestor_tareas.py | 54 +++++++++++ app/graphics.py | 87 ++++++++++++++++++ app/monitorization.py | 8 ++ app/panel_izquierdo.py | 86 +++++++++++------ app/scraping.py | 5 +- main.py | 86 +++++++++++------ 11 files changed, 267 insertions(+), 59 deletions(-) create mode 100644 app/__pycache__/gestor_tareas.cpython-313.pyc create mode 100644 app/__pycache__/graphics.cpython-313.pyc create mode 100644 app/gestor_tareas.py create mode 100644 app/graphics.py diff --git a/app/__pycache__/gestor_tareas.cpython-313.pyc b/app/__pycache__/gestor_tareas.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70349d0a1c2a3023b2f7867c435f8833cdddbfa9 GIT binary patch literal 3960 zcmaJETTmO<^{!Sctw2Ixge}VmE!f6Y6O+amH#oLy0x_`-lxV^6gULonE3vTJmG7>w zMKja%Cxbhgahi@N9{-qr)t>Q;=~vyaI@8RwpIXxp}be^fS19Zw~(oHW+Gur*cHXN!XyAr}5N=KH~r9tuuq})>kFe)!igE zi+*aYE4!Q>iI6j^jkfkWGR1({A_fOp(BABDgZACdyf)U=-YQc4Jp8l`axa}BT-)i) z8lGWS1C0@9XKfVQ2U(4sX6v3NBzB0qtDaNu2GN=vX9kkGoOx{&_w0C2p+<9NjowoX z!)wXMAMO2BoXMs;R&?^X4$EQ&`amIA{Ch|FBy)ES)2$%MN$r$Hm-Y;$bBd-ICQhbxETm8d4fMh;DleJ5L1P6s0(3dX~UXs9>Oi3ag z+si|rvrO}sp+P?T1g&*MK5P2UgW5u0`o8e}x_Oyf;{!K?H-fi=tE0<7i$Af(`)@Yg zXe#ra7T>w@lM)^KVMk}|?%iVWykPOdN@n%g;{$(5lrCH>(S=LjhIW5B z_QlvrsN6kZbq_q6FC{LN6S9?%%ZUXGenaZAZ;fxe*>a=h_UOI+%Pkh)w@qk|#rLen ze}DLQhf6%nPs7=}y6;}TbGdr&+cT?2%l(s9|0G+XPYL*)R1wzdTEVk7>BZx zCruL@OWfqyk>dg$fxS!3!pFjdo}0xoth`HCcvu;poCfp^MFR%`AE#oJB&?0Xnu1U( zT4MW-WR_TA{GE0BVDT1w7zPMGS(Qy0zeW-E-H8$6$r}YzQHj4AbP{w6n7HYY2#XDa zcoYo^he-kxx>!ifK{24kj`)XCbR3a2MNX5zWjLfFJ4Cms(AL9HWjP;b?cjxa4m-E0 z0R;vP8z@hekUmY%6n!bAcjkj10*moI04N|WyC4^q{V~fQEBkvbfA7lG2R#pa@Ap3S zzxhqLs~mpq3H%-&xG-a$+FQ7QK3>b^%)f4uaEOOF?= zlM|&AKixpgBqOW|2W+Opmhe_7Huxy|*!*+xt77T>53NzDJesmbQ>AR)8ogS=h0>YD z(qeIgW%mAz*~ha^Fcg+@%Gj;cTd0xgepAK7WM z@`lvcFa`7%{CFno&27^7f{ z0$U`lQ#Cb&KT)~DDf|%-82B@2F9#N4m}e;T547hQYJG+R&rtJ!+;1?E7YKm8|9_ke BKQ;gW literal 0 HcmV?d00001 diff --git a/app/__pycache__/graphics.cpython-313.pyc b/app/__pycache__/graphics.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b92c06bf88b4a7221e0f67807fdfec7022581072 GIT binary patch literal 4127 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 F{cmHH983TJ delta 1159 zcmZ8gO=weD6uvj_H!sP%Njgca)!6jM&a17ow6+#AQ^(q27FIE_AQX~j?6X!)Qto?I zl#0RWEF74j<&8wBEUqjB+lR~d1reRH(snLX5}ZgqMO8HE2+hl71%6SI;jxX-oB^J)9UiH5VBlI%>Yz&v`M-zN zPAZP~&sHZo5 zpmx&D%W{K|axn5yAE79r_iw+2sA=u_X{hgDH@QQT0=tdjZPJ~6so|7#H>IF|LNsXI z#8gPQRy&|#c8A9{^ zG#rWj(S*828OEdAbpMSNBt94I&iXTvSf^F7-Sbs`=JQ;9gI5^B<@}6LBQIIvF>C#o zwf@K2YTrFUj#BI#CibV~a%{%|QNzwVCC><06Ciq?o-J1= gX3pAQ^I>=%YpPdh@{Oq?#M^0d^*9aEqtp-o1QOc(%m4rY diff --git a/app/__pycache__/panel_izquierdo.cpython-313.pyc b/app/__pycache__/panel_izquierdo.cpython-313.pyc index 82fcb264bdd95196d7067a864d300726f067c5cb..0b6c3f17f3f3ceadd6baec821e863155c510d12a 100644 GIT binary patch delta 3720 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 diff --git a/app/__pycache__/scraping.cpython-313.pyc b/app/__pycache__/scraping.cpython-313.pyc index c784a368dc36d486b956fa80e0392a9e79a02be3..0a019d2d3c098f18343fe47dd8374f2757b61f57 100644 GIT binary patch delta 218 zcmca0_gIeiGcPX}0}#~kM5SNd$m`6*s4+Q^$D1XX$&z{UJRT!15F?KzlsSld@;P>? z$)9*48JQ>h@ajsl_=RXP-(o9CEJ{x;xy1scif^%{B&O!(=S}YBUF7ZzG}Rr5i>rV{ z1H&C*iOJ>-K2JnsuZyT$5>Z*>f57-cSmFhzqzl>k7exvheD8?LT^CioB&vEv*ZG9a z3H=L6X%|J)C+G2nGuli(#%C%6GI1qCkqeM25(5&qIBatBQ%ZAE?TS1ni}M?Da574P FWdR1?LGAzm delta 199 zcmaDXcR`N#GcPX}0}y=p9g#kHBd;?Lqsrt!9&aW~rpXIoKdgSaQh@ybm8 z!4uBNG&z7*SBTk9lj#;)Nn%lYYRN4YAXPkh2Ja$AC!j&@KwMk`BpMj*2un;hZ}7Pz zB70p#<&ucX8vg^v7s3)RI3-;aNpA4HBPw@YRP~am>J?q*6E-LGFN#J@ZsZGRw4QvE p&r}9v)=GvVXCPH11|)8A*yQG?l;)(`6?sh7;y2{rV3Y#O0swLAI~D){ 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