update t1 finish it's posible change the button for open a navegator with URL
This commit is contained in:
parent
f9cfafe3fe
commit
b1e6f55020
|
|
@ -14,15 +14,15 @@ python -m ProyectoGlobal
|
||||||
|
|
||||||
### T1. Multiprocesos ###
|
### T1. Multiprocesos ###
|
||||||
|
|
||||||
1. Lanzar aplicaciones externas con parámetros (por ejemplo navegadores externos con url)
|
1. ~~Lanzar aplicaciones externas con parámetros (por ejemplo navegadores externos con url)~~
|
||||||
|
|
||||||
2. ~~Copias de seguridad realizadas con scripts powershell (.ps1)~~
|
2. ~~Copias de seguridad realizadas con scripts powershell (.ps1)~~
|
||||||
|
|
||||||
3. ~~Ver los recursos del sistema (memoria, procesador, hilos, etc.) utilizando gráficas (matplotlib) gráficos de barras, de áreas, líneas, etc.~~
|
3. ~~Ver los recursos del sistema (memoria, procesador, hilos, etc.) utilizando gráficas (matplotlib) gráficos de barras, de áreas, líneas, etc.~~
|
||||||
|
|
||||||
4. Editor de texto (estilo notepad).
|
4. ~~Editor de texto (estilo notepad).~~
|
||||||
|
|
||||||
5. Hilo que cuente en kilobytes el tráfico de entrada y de salida de nuestra conexión de red. psutil.net_io_counters()
|
5. ~~Hilo que cuente en kilobytes el tráfico de entrada y de salida de nuestra conexión de red. psutil.net_io_counters()~~
|
||||||
|
|
||||||
6. ~~Abrir VScode desde el programa~~
|
6. ~~Abrir VScode desde el programa~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,138 @@
|
||||||
# Módulo: logica/T1/graficos.py
|
# Módulo: logica/T1/graficos.py
|
||||||
|
|
||||||
|
import psutil
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
import numpy as np
|
||||||
import tkinter as tk
|
|
||||||
|
|
||||||
COLOR_PRINCIPAL = '#0078d4'
|
# --- Datos Históricos ---
|
||||||
COLOR_RAM = '#4CAF50'
|
MAX_PUNTOS = 60 # Mantener los últimos 60 puntos (segundos)
|
||||||
|
historial_cpu = []
|
||||||
|
historial_ram = []
|
||||||
|
historial_net_in = []
|
||||||
|
historial_net_out = []
|
||||||
|
|
||||||
|
|
||||||
def crear_grafico_recursos(parent_frame: tk.Frame, datos: dict):
|
def actualizar_historial_datos(net_in_kb, net_out_kb):
|
||||||
"""
|
"""
|
||||||
Genera un gráfico de matplotlib que muestra el uso de CPU y RAM,
|
Recopila los datos actuales de CPU, RAM y añade los datos de Red
|
||||||
e integra este gráfico en un Frame de Tkinter.
|
pasados como argumento a sus historiales.
|
||||||
"""
|
"""
|
||||||
|
# 1. Obtener datos básicos (CPU y RAM)
|
||||||
|
# interval=None asegura que se use el tiempo transcurrido desde la última llamada
|
||||||
|
# a psutil.cpu_percent (o 0.0 si es la primera vez en este proceso)
|
||||||
|
cpu_percent = psutil.cpu_percent(interval=None)
|
||||||
|
ram_percent = psutil.virtual_memory().percent
|
||||||
|
|
||||||
# Limpiamos el frame padre para redibujar
|
# 2. Añadir CPU y gestionar la longitud
|
||||||
for widget in parent_frame.winfo_children():
|
historial_cpu.append(cpu_percent)
|
||||||
widget.destroy()
|
if len(historial_cpu) > MAX_PUNTOS:
|
||||||
|
historial_cpu.pop(0)
|
||||||
|
|
||||||
# 1. Crear la figura (2 subplots)
|
# 3. Añadir RAM y gestionar la longitud
|
||||||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
|
historial_ram.append(ram_percent)
|
||||||
fig.patch.set_facecolor('white')
|
if len(historial_ram) > MAX_PUNTOS:
|
||||||
|
historial_ram.pop(0)
|
||||||
|
|
||||||
# --- GRÁFICO 1: USO DE CPU (Gráfico de Barras) ---
|
# 4. Añadir Red y gestionar la longitud
|
||||||
core_labels = [f'Núcleo {i + 1}' for i in range(len(datos['cpu_cores']))]
|
historial_net_in.append(net_in_kb)
|
||||||
ax1.bar(core_labels, datos['cpu_cores'], color=COLOR_PRINCIPAL)
|
historial_net_out.append(net_out_kb)
|
||||||
ax1.axhline(datos['cpu_total'], color='red', linestyle='--', linewidth=1, label=f'Total: {datos["cpu_total"]}%')
|
|
||||||
|
|
||||||
ax1.set_title(f'Uso de CPU por Núcleo (Total: {datos["cpu_total"]}%)', fontsize=10)
|
if len(historial_net_in) > MAX_PUNTOS:
|
||||||
ax1.set_ylabel('Uso (%)')
|
historial_net_in.pop(0)
|
||||||
ax1.set_ylim(0, 100)
|
historial_net_out.pop(0)
|
||||||
ax1.tick_params(axis='x', rotation=45)
|
|
||||||
ax1.legend(loc='upper right')
|
|
||||||
|
|
||||||
# --- GRÁFICO 2: USO DE RAM (Gráfico Circular/Pie) ---
|
|
||||||
labels = ['Usada', 'Libre']
|
|
||||||
sizes = [datos['ram_percent'], 100 - datos['ram_percent']]
|
|
||||||
colors = [COLOR_RAM, '#d3d3d3']
|
|
||||||
explode = (0.1, 0)
|
|
||||||
|
|
||||||
ax2.pie(sizes, explode=explode, labels=labels, colors=colors,
|
def crear_grafico_recursos(figure):
|
||||||
autopct='%1.1f%%', shadow=False, startangle=90)
|
"""
|
||||||
ax2.axis('equal')
|
Crea o actualiza un gráfico que muestre la evolución de CPU, RAM y Red.
|
||||||
|
"""
|
||||||
|
# Limpiar la figura antes de dibujar
|
||||||
|
figure.clear()
|
||||||
|
|
||||||
ram_title = f'RAM Total: {datos["ram_total_gb"]} GB\nUso: {datos["ram_uso_gb"]} GB'
|
# Configuramos el fondo de la figura para que coincida con el estilo de la aplicación
|
||||||
ax2.set_title(ram_title, fontsize=10)
|
figure.patch.set_facecolor('#f9f9f9')
|
||||||
|
|
||||||
# 3. Integración en Tkinter
|
# --- Configuración General del Layout ---
|
||||||
canvas = FigureCanvasTkAgg(fig, master=parent_frame)
|
# 3 filas para CPU, RAM, Red con espaciado vertical
|
||||||
canvas_widget = canvas.get_tk_widget()
|
gs = figure.add_gridspec(3, 1, hspace=0.6, top=0.95, bottom=0.05, left=0.1, right=0.95)
|
||||||
fig.tight_layout(pad=3.0)
|
|
||||||
canvas_widget.pack(fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
return canvas_widget
|
# --- Función Helper para el estilo btop ---
|
||||||
|
def configurar_ejes_historial(ax, title, color, data, y_limit=100, y_ticks=None):
|
||||||
|
ax.set_facecolor('#f0f0f0') # Fondo del área de dibujo
|
||||||
|
ax.set_title(title, fontsize=9, loc='left', pad=10)
|
||||||
|
ax.set_ylim(0, y_limit)
|
||||||
|
|
||||||
|
if y_ticks:
|
||||||
|
ax.set_yticks(y_ticks)
|
||||||
|
|
||||||
|
ax.tick_params(axis='x', labelbottom=False, length=0)
|
||||||
|
ax.tick_params(axis='y', labelsize=8)
|
||||||
|
ax.spines['top'].set_visible(False)
|
||||||
|
ax.spines['right'].set_visible(False)
|
||||||
|
ax.spines['left'].set_visible(False)
|
||||||
|
ax.spines['bottom'].set_visible(False)
|
||||||
|
ax.grid(axis='y', linestyle='--', alpha=0.5)
|
||||||
|
|
||||||
|
# Dibujar línea y relleno
|
||||||
|
ax.plot(data, color=color, linewidth=1.5)
|
||||||
|
ax.fill_between(range(len(data)), data, color=color, alpha=0.3)
|
||||||
|
|
||||||
|
# --- 1. Gráfico de CPU ---
|
||||||
|
ax_cpu = figure.add_subplot(gs[0, 0])
|
||||||
|
configurar_ejes_historial(
|
||||||
|
ax_cpu, 'Uso de CPU (%) - Ultimo: {:.1f}%'.format(historial_cpu[-1] if historial_cpu else 0),
|
||||||
|
'red', historial_cpu, 100, [0, 50, 100]
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- 2. Gráfico de RAM ---
|
||||||
|
ax_ram = figure.add_subplot(gs[1, 0])
|
||||||
|
configurar_ejes_historial(
|
||||||
|
ax_ram, 'Uso de RAM (%) - Ultimo: {:.1f}%'.format(historial_ram[-1] if historial_ram else 0),
|
||||||
|
'cyan', historial_ram, 100, [0, 50, 100]
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- 3. Gráfico de Red ---
|
||||||
|
ax_net = figure.add_subplot(gs[2, 0])
|
||||||
|
|
||||||
|
# Calcular el límite Y dinámico para la red (ajusta el gráfico al tráfico real)
|
||||||
|
max_in = max(historial_net_in) if historial_net_in else 0
|
||||||
|
max_out = max(historial_net_out) if historial_net_out else 0
|
||||||
|
y_limit_net = max(max_in, max_out) * 1.2 # 20% de margen
|
||||||
|
y_limit_net = max(y_limit_net, 10) # Mínimo de 10 KB/s
|
||||||
|
|
||||||
|
configurar_ejes_historial(
|
||||||
|
ax_net,
|
||||||
|
'Tráfico de Red (KB/s) - IN: {:.1f} KB/s | OUT: {:.1f} KB/s'.format(
|
||||||
|
historial_net_in[-1] if historial_net_in else 0,
|
||||||
|
historial_net_out[-1] if historial_net_out else 0
|
||||||
|
),
|
||||||
|
'gray', [0] * MAX_PUNTOS, # Usamos un color de base para la configuración
|
||||||
|
y_limit_net,
|
||||||
|
[0, y_limit_net * 0.5, y_limit_net * 0.9]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Sobreescribir las líneas para mostrar IN y OUT
|
||||||
|
ax_net.clear() # Limpiamos para redibujar con las dos líneas
|
||||||
|
ax_net.set_ylim(0, y_limit_net)
|
||||||
|
|
||||||
|
# Dibujar Entrada (Recibido)
|
||||||
|
ax_net.plot(historial_net_in, label='IN (Recibido)', color='green', linewidth=1.5)
|
||||||
|
ax_net.fill_between(range(len(historial_net_in)), historial_net_in, color='green', alpha=0.2)
|
||||||
|
|
||||||
|
# Dibujar Salida (Enviado)
|
||||||
|
ax_net.plot(historial_net_out, label='OUT (Enviado)', color='yellow', linewidth=1.5)
|
||||||
|
ax_net.fill_between(range(len(historial_net_out)), historial_net_out, color='yellow', alpha=0.2)
|
||||||
|
|
||||||
|
# Reconfigurar los títulos y estilos después de limpiar el eje
|
||||||
|
configurar_ejes_historial(
|
||||||
|
ax_net,
|
||||||
|
'Tráfico de Red (KB/s) - IN: {:.1f} KB/s | OUT: {:.1f} KB/s'.format(
|
||||||
|
historial_net_in[-1] if historial_net_in else 0,
|
||||||
|
historial_net_out[-1] if historial_net_out else 0
|
||||||
|
),
|
||||||
|
'gray', [0] * MAX_PUNTOS,
|
||||||
|
y_limit_net,
|
||||||
|
[0, round(y_limit_net * 0.5, 1), round(y_limit_net * 0.9, 1)]
|
||||||
|
)
|
||||||
|
|
||||||
|
figure.tight_layout()
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Módulo: logica/T1/openBrowser.py
|
||||||
|
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
def navegar_a_url(url: str):
|
||||||
|
"""
|
||||||
|
Abre la URL proporcionada en el navegador web predeterminado del sistema.
|
||||||
|
Utiliza el método .open(new=1) para solicitar una nueva ventana.
|
||||||
|
"""
|
||||||
|
url = url.strip()
|
||||||
|
|
||||||
|
if not url:
|
||||||
|
print("Error de Navegación: URL vacía.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Restauramos la verificación de esquema para la estabilidad de la URI
|
||||||
|
if not url.startswith(('http://', 'https://')):
|
||||||
|
url = 'http://' + url
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Se usa new=1 para solicitar una nueva ventana.
|
||||||
|
# El comportamiento final depende del SO y del navegador por defecto del usuario.
|
||||||
|
webbrowser.open(url, new=1)
|
||||||
|
print(f"Lanzando navegador externo (solicitando nueva ventana) con URL: {url}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error crítico al intentar abrir el navegador para {url}: {e}")
|
||||||
|
return False
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Módulo: logica/T1/trafficMeter.py
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
# Constante de conversión: 1 KB = 1024 bytes
|
||||||
|
KB = 1024
|
||||||
|
|
||||||
|
|
||||||
|
class NetIOMonitor(threading.Thread):
|
||||||
|
"""
|
||||||
|
Hilo que monitorea y almacena el tráfico de red (bytes por segundo)
|
||||||
|
convirtiéndolo a Kilobytes por segundo (KB/s).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, intervalo=1):
|
||||||
|
super().__init__()
|
||||||
|
self._stop_event = threading.Event()
|
||||||
|
self.intervalo = intervalo
|
||||||
|
|
||||||
|
# Almacenamiento seguro para los últimos datos de tráfico
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.data_in_kb = 0.0 # Tráfico de entrada en KB/s (Recibido)
|
||||||
|
self.data_out_kb = 0.0 # Tráfico de salida en KB/s (Enviado)
|
||||||
|
|
||||||
|
# Almacena el contador anterior para calcular la diferencia (tasa)
|
||||||
|
self.last_counters = psutil.net_io_counters()
|
||||||
|
# Nota: La primera lectura es solo para inicializar, se requiere una segunda para la tasa.
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Método principal del hilo."""
|
||||||
|
while not self._stop_event.is_set():
|
||||||
|
# Esperar el intervalo antes de la lectura para calcular la tasa
|
||||||
|
time.sleep(self.intervalo)
|
||||||
|
self._actualizar_datos()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""Detiene el hilo de forma segura."""
|
||||||
|
self._stop_event.set()
|
||||||
|
|
||||||
|
def _actualizar_datos(self):
|
||||||
|
"""Calcula el tráfico de red en KB/s."""
|
||||||
|
|
||||||
|
current_counters = psutil.net_io_counters()
|
||||||
|
|
||||||
|
# Calcular la diferencia de bytes recibidos y enviados desde la última lectura
|
||||||
|
bytes_recv_diff = current_counters.bytes_recv - self.last_counters.bytes_recv
|
||||||
|
bytes_sent_diff = current_counters.bytes_sent - self.last_counters.bytes_sent
|
||||||
|
|
||||||
|
# Calcular la tasa (bytes/segundo) y convertir a KB/s
|
||||||
|
# El tiempo transcurrido es igual a self.intervalo
|
||||||
|
rate_in_kb_s = (bytes_recv_diff / self.intervalo) / KB
|
||||||
|
rate_out_kb_s = (bytes_sent_diff / self.intervalo) / KB
|
||||||
|
|
||||||
|
# Actualizar el contador anterior
|
||||||
|
self.last_counters = current_counters
|
||||||
|
|
||||||
|
# Guardar los resultados de forma segura
|
||||||
|
with self.lock:
|
||||||
|
self.data_in_kb = rate_in_kb_s
|
||||||
|
self.data_out_kb = rate_out_kb_s
|
||||||
|
|
||||||
|
def get_io_data_kb(self):
|
||||||
|
"""Devuelve el tráfico de entrada y salida actual en KB/s."""
|
||||||
|
with self.lock:
|
||||||
|
return self.data_in_kb, self.data_out_kb
|
||||||
|
|
||||||
|
|
||||||
|
# --- FUNCIÓN DE INICIO ---
|
||||||
|
def iniciar_monitor_red():
|
||||||
|
"""Inicializa y comienza el monitor de red."""
|
||||||
|
monitor = NetIOMonitor(intervalo=1) # 1 segundo de intervalo
|
||||||
|
monitor.start()
|
||||||
|
return monitor
|
||||||
|
|
@ -2,21 +2,32 @@
|
||||||
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from logica.T1.geterSystemRecource import obtener_datos_cpu_ram
|
|
||||||
from logica.T1.graficos import crear_grafico_recursos
|
|
||||||
from logica.controlador import accion_placeholder
|
from logica.T1.trafficMeter import iniciar_monitor_red
|
||||||
|
from logica.T1.graficos import crear_grafico_recursos, actualizar_historial_datos
|
||||||
|
from matplotlib.figure import Figure
|
||||||
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||||
|
|
||||||
|
|
||||||
class PanelCentral(ttk.Frame):
|
class PanelCentral(ttk.Frame):
|
||||||
"""Contiene el Notebook (subpestañas de T1), el panel de Notas y el panel de Chat."""
|
"""Contiene el Notebook (subpestañas de T1), el panel de Notas y el panel de Chat."""
|
||||||
|
|
||||||
# Definimos el intervalo de actualización en milisegundos (5000 ms = 5 segundos)
|
# Reducimos el intervalo de actualización a 1000ms (1 segundo) para gráficos fluidos
|
||||||
INTERVALO_ACTUALIZACION_MS = 5000
|
INTERVALO_ACTUALIZACION_MS = 1000
|
||||||
|
|
||||||
def __init__(self, parent, *args, **kwargs):
|
def __init__(self, parent, *args, **kwargs):
|
||||||
super().__init__(parent, *args, **kwargs)
|
super().__init__(parent, *args, **kwargs)
|
||||||
self.after_id = None # ID para controlar el timer de tk.after
|
self.after_id = None # ID para controlar el timer de tk.after
|
||||||
|
|
||||||
|
# 1. INICIALIZACIÓN DE VARIABLES
|
||||||
|
self.net_monitor = iniciar_monitor_red() # <-- INICIAR EL HILO DE RED
|
||||||
|
|
||||||
|
# Objeto Figure de Matplotlib (debe crearse antes de crear_sub_pestañas_t1)
|
||||||
|
self.figure = Figure(figsize=(5, 4), dpi=100)
|
||||||
|
self.canvas = None # Se inicializará en crear_sub_pestañas_t1
|
||||||
|
|
||||||
|
# 2. CONFIGURACIÓN DEL LAYOUT
|
||||||
self.grid_columnconfigure(0, weight=3)
|
self.grid_columnconfigure(0, weight=3)
|
||||||
self.grid_columnconfigure(1, weight=1)
|
self.grid_columnconfigure(1, weight=1)
|
||||||
self.grid_rowconfigure(0, weight=1)
|
self.grid_rowconfigure(0, weight=1)
|
||||||
|
|
@ -24,6 +35,7 @@ class PanelCentral(ttk.Frame):
|
||||||
self.crear_area_principal_y_notas()
|
self.crear_area_principal_y_notas()
|
||||||
self.crear_panel_chat_y_alumnos()
|
self.crear_panel_chat_y_alumnos()
|
||||||
|
|
||||||
|
# La actualización se inicia al final de __init__
|
||||||
self.iniciar_actualizacion_automatica()
|
self.iniciar_actualizacion_automatica()
|
||||||
|
|
||||||
def crear_area_principal_y_notas(self):
|
def crear_area_principal_y_notas(self):
|
||||||
|
|
@ -43,7 +55,7 @@ class PanelCentral(ttk.Frame):
|
||||||
panel_notas.grid(row=1, column=0, sticky="nsew", pady=(5, 0))
|
panel_notas.grid(row=1, column=0, sticky="nsew", pady=(5, 0))
|
||||||
|
|
||||||
ttk.Label(panel_notas, text="Panel para notas informativas y mensajes sobre la ejecución de los hilos.",
|
ttk.Label(panel_notas, text="Panel para notas informativas y mensajes sobre la ejecución de los hilos.",
|
||||||
style='Note.TLabel', anchor="nw", justify=tk.LEFT, padding=10, font=('Arial', 9, 'italic')).pack(
|
style='Note.TLabel', anchor="nw", justify=tk.LEFT, padding=10, font=('Arial', 9, 'italic')).pack(
|
||||||
expand=True, fill="both")
|
expand=True, fill="both")
|
||||||
|
|
||||||
def crear_sub_pestañas_t1(self, parent_frame):
|
def crear_sub_pestañas_t1(self, parent_frame):
|
||||||
|
|
@ -64,48 +76,74 @@ class PanelCentral(ttk.Frame):
|
||||||
self.grafico_frame = ttk.Frame(frame, style='TFrame')
|
self.grafico_frame = ttk.Frame(frame, style='TFrame')
|
||||||
self.grafico_frame.pack(expand=True, fill="both", padx=10, pady=10)
|
self.grafico_frame.pack(expand=True, fill="both", padx=10, pady=10)
|
||||||
|
|
||||||
# Llamada inicial, la auto-actualización tomará el control
|
# Inicialización del Canvas de Matplotlib
|
||||||
self.actualizar_grafico_recursos()
|
self.canvas = FigureCanvasTkAgg(self.figure, master=self.grafico_frame)
|
||||||
|
self.canvas_widget = self.canvas.get_tk_widget()
|
||||||
|
self.canvas_widget.pack(expand=True, fill="both")
|
||||||
|
|
||||||
|
# La actualización automática iniciada en __init__ se encarga de esto
|
||||||
sub_notebook.select(i)
|
sub_notebook.select(i)
|
||||||
|
|
||||||
# Contenido de otras pestañas
|
# Contenido de otras pestañas
|
||||||
elif sub_tab_text == "Navegador":
|
elif sub_tab_text == "Navegador":
|
||||||
contenido_area = tk.Text(frame, wrap="word", padx=15, pady=15, bg='white', relief="groove",
|
contenido_area = tk.Text(frame, wrap="word", padx=15, pady=15, bg='white', relief="groove",
|
||||||
borderwidth=1, font=('Consolas', 10), foreground="#555555")
|
borderwidth=1, font=('Consolas', 10), foreground="#555555")
|
||||||
contenido_area.insert(tk.END,
|
contenido_area.insert(tk.END,
|
||||||
">>> ÁREA DE CONTENIDO / VISOR DE NAVEGADOR (Para mostrar resultados o web scraping)\n\n""Este es el espacio dedicado a la visualización de datos o interfaces específicas de cada tarea.")
|
">>> ÁREA DE CONTENIDO / VISOR DE NAVEGADOR (Para mostrar resultados o web scraping)\n\n""Este es el espacio dedicado a la visualización de datos o interfaces específicas de cada tarea.")
|
||||||
contenido_area.pack(expand=True, fill="both", padx=5, pady=5)
|
contenido_area.pack(expand=True, fill="both", padx=5, pady=5)
|
||||||
|
|
||||||
def actualizar_grafico_recursos(self):
|
def actualizar_grafico_recursos(self):
|
||||||
"""Obtiene los datos del sistema y dibuja/redibuja el gráfico."""
|
"""
|
||||||
|
Obtiene los datos del sistema (incluyendo Red) y dibuja/redibuja el gráfico.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
datos_sistema = obtener_datos_cpu_ram()
|
# 1. Obtener datos de red del hilo (en KB/s)
|
||||||
crear_grafico_recursos(self.grafico_frame, datos_sistema)
|
net_in, net_out = self.net_monitor.get_io_data_kb()
|
||||||
|
|
||||||
|
# 2. Actualizar el historial global (CPU y RAM se obtienen dentro de esta función)
|
||||||
|
actualizar_historial_datos(net_in, net_out)
|
||||||
|
|
||||||
|
# 3. Redibujar el gráfico
|
||||||
|
crear_grafico_recursos(self.figure)
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = f"Error al generar el gráfico de recursos: {e}"
|
error_msg = f"Error al generar el gráfico de recursos: {e}"
|
||||||
print(error_msg)
|
print(error_msg)
|
||||||
for widget in self.grafico_frame.winfo_children():
|
# Manejo de errores visual en la pestaña (limpiar y mostrar error)
|
||||||
widget.destroy()
|
if self.canvas_widget.winfo_exists():
|
||||||
ttk.Label(self.grafico_frame, text=error_msg, foreground='red', style='TLabel').pack(pady=20)
|
self.canvas_widget.pack_forget()
|
||||||
|
|
||||||
|
error_label = ttk.Label(self.grafico_frame, text=error_msg, foreground='red', style='TLabel')
|
||||||
|
error_label.pack(pady=20)
|
||||||
|
|
||||||
|
# Detener la actualización para evitar bucle de error
|
||||||
|
self.detener_actualizacion_automatica()
|
||||||
|
|
||||||
|
# 4. Programar la siguiente llamada (solo si no hay error crítico que detenga la actualización)
|
||||||
|
self.after_id = self.after(self.INTERVALO_ACTUALIZACION_MS, self.actualizar_grafico_recursos)
|
||||||
|
|
||||||
|
# --- Control del Ciclo de Vida ---
|
||||||
|
|
||||||
def iniciar_actualizacion_automatica(self):
|
def iniciar_actualizacion_automatica(self):
|
||||||
"""
|
"""Inicia el ciclo de actualización del gráfico de recursos."""
|
||||||
Programa la actualización periódica del gráfico de recursos usando tk.after,
|
print("Iniciando actualización automática de recursos.")
|
||||||
y almacena el ID para poder cancelarlo.
|
# Programar la primera llamada inmediatamente
|
||||||
"""
|
self.after_id = self.after(0, self.actualizar_grafico_recursos)
|
||||||
# 1. Ejecuta la actualización
|
|
||||||
self.actualizar_grafico_recursos()
|
|
||||||
|
|
||||||
# 2. Programa la siguiente llamada y almacena el ID
|
|
||||||
self.after_id = self.after(self.INTERVALO_ACTUALIZACION_MS, self.iniciar_actualizacion_automatica)
|
|
||||||
|
|
||||||
def detener_actualizacion_automatica(self):
|
def detener_actualizacion_automatica(self):
|
||||||
"""Detiene el ciclo de actualización periódica del gráfico."""
|
"""Detiene el ciclo de actualización periódica y el hilo de red."""
|
||||||
if self.after_id:
|
if self.after_id:
|
||||||
self.after_cancel(self.after_id)
|
self.after_cancel(self.after_id)
|
||||||
|
self.after_id = None
|
||||||
print("Ciclo de actualización de gráficos detenido.")
|
print("Ciclo de actualización de gráficos detenido.")
|
||||||
|
|
||||||
|
# Detener el monitor de red al cerrar la aplicación
|
||||||
|
if self.net_monitor:
|
||||||
|
self.net_monitor.stop()
|
||||||
|
self.net_monitor.join() # Esperar a que el hilo termine
|
||||||
|
print("Hilo de TrafficMeter detenido.")
|
||||||
|
|
||||||
def crear_panel_chat_y_alumnos(self, ):
|
def crear_panel_chat_y_alumnos(self, ):
|
||||||
"""Crea el panel de chat, lista de Alumnos y Reproductor de Música (columna derecha)."""
|
"""Crea el panel de chat, lista de Alumnos y Reproductor de Música (columna derecha)."""
|
||||||
panel_chat = ttk.Frame(self, style='TFrame', padding="10")
|
panel_chat = ttk.Frame(self, style='TFrame', padding="10")
|
||||||
|
|
@ -117,17 +155,17 @@ class PanelCentral(ttk.Frame):
|
||||||
|
|
||||||
# 1. Título "Chat"
|
# 1. Título "Chat"
|
||||||
ttk.Label(panel_chat, text="Chat", foreground="#0078d4", font=("Arial", 18, "bold"), style='TLabel').grid(row=0,
|
ttk.Label(panel_chat, text="Chat", foreground="#0078d4", font=("Arial", 18, "bold"), style='TLabel').grid(row=0,
|
||||||
column=0,
|
column=0,
|
||||||
pady=(
|
pady=(
|
||||||
0,
|
0,
|
||||||
10),
|
10),
|
||||||
sticky="w")
|
sticky="w")
|
||||||
|
|
||||||
# 2. Área de Mensaje
|
# 2. Área de Mensaje
|
||||||
ttk.Label(panel_chat, text="Mensaje", style='TLabel').grid(row=1, column=0, sticky="w")
|
ttk.Label(panel_chat, text="Mensaje", style='TLabel').grid(row=1, column=0, sticky="w")
|
||||||
|
|
||||||
chat_text = tk.Text(panel_chat, height=6, width=30, bg='#fff8e1', relief="solid", borderwidth=1,
|
chat_text = tk.Text(panel_chat, height=6, width=30, bg='#fff8e1', relief="solid", borderwidth=1,
|
||||||
font=('Arial', 10))
|
font=('Arial', 10))
|
||||||
chat_text.grid(row=2, column=0, sticky="ew", pady=(0, 5))
|
chat_text.grid(row=2, column=0, sticky="ew", pady=(0, 5))
|
||||||
|
|
||||||
ttk.Button(panel_chat, text="Enviar", style='Action.TButton').grid(row=3, column=0, pady=(0, 15), sticky="e")
|
ttk.Button(panel_chat, text="Enviar", style='Action.TButton').grid(row=3, column=0, pady=(0, 15), sticky="e")
|
||||||
|
|
@ -141,14 +179,15 @@ class PanelCentral(ttk.Frame):
|
||||||
ttk.Label(frame_alumno, text=f"Alumno {i}", font=("Arial", 11, "bold"), style='Alumno.TLabel').grid(row=0,
|
ttk.Label(frame_alumno, text=f"Alumno {i}", font=("Arial", 11, "bold"), style='Alumno.TLabel').grid(row=0,
|
||||||
column=0,
|
column=0,
|
||||||
sticky="w")
|
sticky="w")
|
||||||
|
|
||||||
ttk.Label(frame_alumno, text="Lorem ipsum dolor sit amet, consectetur adipiscing elit.", wraplength=250,
|
ttk.Label(frame_alumno, text="Lorem ipsum dolor sit amet, consectetur adipiscing elit.", wraplength=250,
|
||||||
justify=tk.LEFT, style='Alumno.TLabel').grid(row=1, column=0, sticky="w")
|
justify=tk.LEFT, style='Alumno.TLabel').grid(row=1, column=0, sticky="w")
|
||||||
|
|
||||||
ttk.Button(frame_alumno, text="↻", width=3, style='Action.TButton').grid(row=0, column=1, rowspan=2, padx=5,
|
ttk.Button(frame_alumno, text="↻", width=3, style='Action.TButton').grid(row=0, column=1, rowspan=2, padx=5,
|
||||||
sticky="ne")
|
sticky="ne")
|
||||||
|
|
||||||
# 4. Reproductor de Música (Simulado)
|
# 4. Reproductor de Música (Simulado)
|
||||||
musica_frame = ttk.LabelFrame(panel_chat, text="Reproductor Música", padding=10, style='TFrame')
|
musica_frame = ttk.LabelFrame(panel_chat, text="Reproductor Música", padding=10, style='TFrame')
|
||||||
musica_frame.grid(row=8, column=0, sticky="ew", pady=(15, 0))
|
musica_frame.grid(row=8, column=0, sticky="ew", pady=(15, 0))
|
||||||
ttk.Label(musica_frame, text="[ Botones de Play/Stop y Control de Volumen ]", anchor="center",
|
ttk.Label(musica_frame, text="[ Botones de Play/Stop y Control de Volumen ]", anchor="center",
|
||||||
style='TLabel').pack(fill="x", padx=5, pady=5)
|
style='TLabel').pack(fill="x", padx=5, pady=5)
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from tkinter import messagebox
|
from tkinter import messagebox
|
||||||
from logica.controlador import accion_placeholder, getPlataforma
|
from logica.controlador import accion_placeholder
|
||||||
from logica.T1.backup import accion_backup_t1
|
from logica.T1.backup import accion_backup_t1
|
||||||
from logica.T1.runVScode import abrir_vscode
|
from logica.T1.runVScode import abrir_vscode
|
||||||
from logica.T1.textEditor import cargar_contenido_res_notes, guardar_contenido_res_notes
|
from logica.T1.textEditor import cargar_contenido_res_notes, guardar_contenido_res_notes
|
||||||
import os
|
from logica.T1.openBrowser import navegar_a_url
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PanelLateral(ttk.Frame):
|
class PanelLateral(ttk.Frame):
|
||||||
|
|
@ -17,20 +18,24 @@ class PanelLateral(ttk.Frame):
|
||||||
# no intente expandir el panel lateral más allá de lo deseado.
|
# no intente expandir el panel lateral más allá de lo deseado.
|
||||||
ANCHO_CARACTERES_FIJO = 35
|
ANCHO_CARACTERES_FIJO = 35
|
||||||
|
|
||||||
|
ANCHO_CARACTERES_FIJO = 35
|
||||||
|
|
||||||
def __init__(self, parent, central_panel=None, *args, **kwargs):
|
def __init__(self, parent, central_panel=None, *args, **kwargs):
|
||||||
super().__init__(parent, *args, **kwargs)
|
super().__init__(parent, *args, **kwargs)
|
||||||
self.central_panel = central_panel
|
self.central_panel = central_panel
|
||||||
|
|
||||||
self.configurar_estilos_locales(parent)
|
self.configurar_estilos_locales(parent)
|
||||||
|
|
||||||
# 1. Entrada superior (amarilla) - Aplicamos el ancho fijo
|
# 1. Entrada superior (amarilla) - ¡Guardamos la referencia!
|
||||||
ttk.Entry(self, width=self.ANCHO_CARACTERES_FIJO, style='Yellow.TEntry').pack(fill="x", pady=10, padx=5,
|
self.entrada_superior = ttk.Entry(self, width=self.ANCHO_CARACTERES_FIJO, style='Yellow.TEntry')
|
||||||
ipady=3)
|
self.entrada_superior.pack(fill="x", pady=10, padx=5, ipady=3)
|
||||||
|
self.entrada_superior.bind('<Return>', self.manejar_navegacion) # Opcional: Ejecutar con Enter
|
||||||
|
|
||||||
# 2. Área de Extracción/Navegación
|
# 2. Área de Extracción/Navegación
|
||||||
acciones_extraccion = [
|
acciones_extraccion = [
|
||||||
("Extraer datos", self.manejar_extraccion_datos),
|
("Extraer datos", self.manejar_extraccion_datos),
|
||||||
("Navegar", lambda: accion_placeholder("Navegar")),
|
# 2. Asignamos el nuevo método de manejo a este botón
|
||||||
|
("Navegar", self.manejar_navegacion),
|
||||||
("Buscar API Google", lambda: accion_placeholder("Buscar API Google"))
|
("Buscar API Google", lambda: accion_placeholder("Buscar API Google"))
|
||||||
]
|
]
|
||||||
self.crear_seccion(self, titulo="", acciones=acciones_extraccion)
|
self.crear_seccion(self, titulo="", acciones=acciones_extraccion)
|
||||||
|
|
@ -49,19 +54,28 @@ class PanelLateral(ttk.Frame):
|
||||||
]
|
]
|
||||||
self.crear_seccion(self, titulo="Procesos batch", acciones=acciones_batch)
|
self.crear_seccion(self, titulo="Procesos batch", acciones=acciones_batch)
|
||||||
|
|
||||||
# 5. Espacio expandible (Empuja los elementos superiores hacia arriba)
|
# 5. Espacio expandible
|
||||||
tk.Frame(self, height=1).pack(expand=True, fill="both")
|
tk.Frame(self, height=1).pack(expand=True, fill="both")
|
||||||
|
|
||||||
# 6. Panel de Notas (Editor res/notes, ubicado abajo)
|
# 6. Panel de Notas
|
||||||
self.crear_editor_res_notes()
|
self.crear_editor_res_notes()
|
||||||
|
|
||||||
|
# --- NUEVO MÉTODO PARA MANEJAR LA NAVEGACIÓN ---
|
||||||
|
def manejar_navegacion(self, event=None):
|
||||||
|
"""
|
||||||
|
Obtiene el texto de la entrada superior y llama a la función de navegación.
|
||||||
|
"""
|
||||||
|
url = self.entrada_superior.get()
|
||||||
|
if navegar_a_url(url):
|
||||||
|
# Limpiar la casilla si la navegación fue exitosa
|
||||||
|
self.entrada_superior.delete(0, tk.END)
|
||||||
# --- LÓGICA DEL EDITOR res/notes ---
|
# --- LÓGICA DEL EDITOR res/notes ---
|
||||||
|
|
||||||
def crear_editor_res_notes(self):
|
def crear_editor_res_notes(self):
|
||||||
"""Crea el editor de texto simple para el archivo res/notes."""
|
"""Crea el editor de texto simple para el archivo res/notes."""
|
||||||
|
|
||||||
ttk.Label(self, text="Editor Simple (res/notes)", font=('Arial', 11, 'bold')).pack(fill="x", pady=(10, 0),
|
ttk.Label(self, text="Editor Simple (res/notes)", font=('Arial', 11, 'bold')).pack(fill="x", pady=(10, 0),
|
||||||
padx=5)
|
padx=5)
|
||||||
|
|
||||||
frame_editor = ttk.Frame(self, padding=5)
|
frame_editor = ttk.Frame(self, padding=5)
|
||||||
frame_editor.pack(fill="x", padx=5, pady=(0, 10))
|
frame_editor.pack(fill="x", padx=5, pady=(0, 10))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue