diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/ProyectoFinalProcesosServicios.iml b/.idea/ProyectoFinalProcesosServicios.iml
deleted file mode 100644
index eccdbc3..0000000
--- a/.idea/ProyectoFinalProcesosServicios.iml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 105ce2d..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index fba7da9..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 7bd9d81..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/__init__.py b/app/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/app/main.py b/app/main.py
deleted file mode 100644
index 60f65c5..0000000
--- a/app/main.py
+++ /dev/null
@@ -1,194 +0,0 @@
-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 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
-
-# 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
-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
-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_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
-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)
-
-# Crear la Solapa 1 y añadir el downloader
-tab1 = ttk.Frame(notebook)
-notebook.add(tab1, text="Solapa 1", 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="Solapa 2", 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="Solapa 3", 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="Solapa 4", 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="Solapa 5", padding=4)
-
-# Añadir el widget de Web Scraper a la Solapa 5
-web_scraper = WebScraperToDB(tab5)
-
-# Barra de estado
-# Dividir la barra de estado en 4 labels
-
-# Usar pack para alinear los labels horizontalmente
-label_fecha_hora = tk.Label(barra_estado, text="Hilo fecha-hora", font=("Helvetica", 14), bd=1, fg="blue", relief="sunken", anchor="w", width=20, padx=10)
-
-
-label_fecha_hora.pack(side="right", fill="x", expand=True)
-# barra_estado.grid(row=1, column=0, columnspan=3, sticky="ew")
-
-
-update_thread = threading.Thread(target=update_time, args=(label_fecha_hora,))
-update_thread.daemon = True # Hacemos el hilo un demonio para que termine con la app
-update_thread.start()
-
-# Ejecución de la aplicación
-root.mainloop()
\ No newline at end of file
diff --git a/hilos/ApplicationLauncher.py b/hilos/ApplicationLauncher.py
deleted file mode 100644
index d831832..0000000
--- a/hilos/ApplicationLauncher.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import tkinter as tk
-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.
- """
- 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"])
-
- # 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)
-
- 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.
- """
- for path in paths:
- path = os.path.expandvars(path) # Expande variables como %USERNAME%
- if os.path.exists(path):
- return path
- return None
-
- def launch_vscode(self):
- """Lanza Visual Studio Code si se encuentra la ruta."""
- 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).
- """
- if path:
- threading.Thread(target=self.run_command, 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}")
diff --git a/hilos/ChatWidget.py b/hilos/ChatWidget.py
deleted file mode 100644
index 308fa37..0000000
--- a/hilos/ChatWidget.py
+++ /dev/null
@@ -1,74 +0,0 @@
-import tkinter as tk
-from tkinter import scrolledtext
-import socket
-import threading
-
-
-class ChatWidget:
- def __init__(self, parent):
- 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)
-
- # 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
- )
- self.chat_display.pack(pady=5)
-
- # Campo de entrada para escribir mensajes
- self.message_entry = tk.Entry(self.frame, width=35) # Reducir ancho
- self.message_entry.pack(pady=5)
- self.message_entry.bind("", 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
- self.send_button.pack(pady=5)
-
- # Configuración del cliente 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
-
- 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}")
-
- def send_message(self, event=None):
- 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}")
-
- def receive_messages(self):
- """Recibe mensajes del servidor y los muestra en el chat."""
- while True:
- try:
- message = self.client_socket.recv(1024).decode("utf-8")
- if message:
- self.display_message(message) # Mostrar mensaje en la caja de chat
- else:
- break
- except:
- self.display_message("[DESCONECTADO] Conexión perdida con el servidor.")
- break
-
- def display_message(self, message):
- 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
\ No newline at end of file
diff --git a/hilos/LanguageChart.py b/hilos/LanguageChart.py
deleted file mode 100644
index 91fb6d0..0000000
--- a/hilos/LanguageChart.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import tkinter as tk
-from matplotlib.figure import Figure
-from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
-import threading
-import time
-
-class LanguageChart:
- def __init__(self, parent):
- """
- Inicializa el gráfico de 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)
- self.languages = ["Python", "JavaScript", "Java", "C++", "C#"]
- self.usage = [30, 25, 20, 15, 10] # Porcentajes de uso
-
- # Crear figura para el gráfico
- 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
- 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
- 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.
-
- 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
-
- def update_chart(self):
- """
- Actualiza el gráfico periódicamente en un hilo.
- """
- while True:
- self.fetch_data()
- self.ax.clear()
- self.ax.bar(self.languages, self.usage, color="skyblue")
- self.ax.set_title("Lenguajes más usados")
- self.ax.set_ylabel("Porcentaje de uso")
- self.canvas.draw()
- time.sleep(5) # Actualizar cada 5 segundos
\ No newline at end of file
diff --git a/hilos/MusicPlayer.py b/hilos/MusicPlayer.py
deleted file mode 100644
index 5402a8a..0000000
--- a/hilos/MusicPlayer.py
+++ /dev/null
@@ -1,77 +0,0 @@
-import tkinter as tk
-from tkinter import filedialog
-import threading
-import pygame # Necesitas instalar pygame: pip install pygame
-
-
-class MusicPlayer:
- def __init__(self, parent):
- self.parent = parent
- self.is_playing = False
-
- # Inicializar el reproductor de música
- pygame.mixer.init()
-
- # Crear marco para el 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.pack(pady=5)
-
- # Botón para seleccionar archivo
- self.select_button = tk.Button(
- self.frame, text="Seleccionar Archivo", command=self.select_file, width=20
- )
- self.select_button.pack(pady=5)
-
- # Crear un marco para los botones de control
- self.controls_frame = tk.Frame(self.frame, bg="lightgreen")
- self.controls_frame.pack(pady=10)
-
- # Botones de control (centrados)
- self.play_button = tk.Button(
- self.controls_frame, text="▶ Reproducir", command=self.play_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
- )
- self.stop_button.grid(row=0, column=1, padx=5)
-
- def select_file(self):
- """Abrir el selector de archivos para elegir un archivo de música."""
- 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: {self.music_file.split('/')[-1]}")
-
- def play_music(self):
- """Iniciar la reproducción de música."""
- if hasattr(self, "music_file"):
- self.is_playing = True
- self.play_button.config(state="disabled")
- self.stop_button.config(state="normal")
-
- threading.Thread(target=self._play_music_thread, daemon=True).start()
-
- def _play_music_thread(self):
- """Hilo que reproduce la música."""
- 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
-
- def stop_music(self):
- """Detener la reproducción de música."""
- self.is_playing = False
- self.play_button.config(state="normal")
- self.stop_button.config(state="disabled")
- pygame.mixer.music.stop()
diff --git a/hilos/SystemMonitor.py b/hilos/SystemMonitor.py
deleted file mode 100644
index 3ba1e35..0000000
--- a/hilos/SystemMonitor.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import psutil
-import threading
-import tkinter as tk
-
-class SystemMonitor:
- def __init__(self, parent, stop_event):
- 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")
-
- # 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
- 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."""
- 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."""
- 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)
diff --git a/hilos/WeatherWidget.py b/hilos/WeatherWidget.py
deleted file mode 100644
index 4a3f306..0000000
--- a/hilos/WeatherWidget.py
+++ /dev/null
@@ -1,129 +0,0 @@
-import tkinter as tk
-import threading
-import requests
-import time
-
-
-class WeatherWidget:
- def __init__(self, parent, api_key):
- """
- Inicializa el widget del clima con detalles adicionales.
-
- Args:
- parent (tk.Frame): Frame en el que 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
- 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
- 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.
-
- 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
-
- 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()
diff --git a/hilos/__init__.py b/hilos/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/solapas/EconomyBitcoinChart.py b/solapas/EconomyBitcoinChart.py
deleted file mode 100644
index 41cd3b9..0000000
--- a/solapas/EconomyBitcoinChart.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import tkinter as tk
-from matplotlib.figure import Figure
-from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
-import threading
-import time
-import random
-
-class EconomyBitcoinChart:
- def __init__(self, parent):
- """
- Inicializa los gráficos de economía mundial y Bitcoin en disposición vertical.
-
- Args:
- parent (tk.Frame): Frame donde se colocarán los gráficos.
- """
- self.parent = parent
-
- # Crear la figura para los gráficos
- self.figure = Figure(figsize=(8, 6), dpi=100)
-
- # Subgráficos: Economía mundial y Bitcoin
- self.ax_economy = self.figure.add_subplot(211) # Gráfico superior
- self.ax_bitcoin = self.figure.add_subplot(212) # Gráfico inferior
-
- # Inicializar datos simulados
- self.economy_data = [random.randint(50, 100) for _ in range(10)] # Economía en meses
- self.bitcoin_data = [random.randint(20000, 60000) for _ in range(10)] # Bitcoin en días
-
- self.update_economy_chart()
- self.update_bitcoin_chart()
-
- # Embebiendo los gráficos en Tkinter
- self.canvas = FigureCanvasTkAgg(self.figure, master=self.parent)
- self.canvas.get_tk_widget().pack(fill="both", expand=True, padx=10, pady=10)
-
- # Iniciar hilos para actualizar los gráficos
- threading.Thread(target=self.update_charts, daemon=True).start()
-
- def update_economy_chart(self):
- """Actualiza el gráfico de economía mundial."""
- self.ax_economy.clear()
- self.ax_economy.plot(self.economy_data, marker="o", color="blue")
- self.ax_economy.set_title("Economía Mundial")
- self.ax_economy.set_ylabel("Índice económico")
- self.ax_economy.grid(True)
-
- def update_bitcoin_chart(self):
- """Actualiza el gráfico de Bitcoin."""
- self.ax_bitcoin.clear()
- self.ax_bitcoin.plot(self.bitcoin_data, marker="o", color="green")
- self.ax_bitcoin.set_title("Precio de Bitcoin")
- self.ax_bitcoin.set_ylabel("Precio en USD")
- self.ax_bitcoin.set_xlabel("Días") # Etiqueta para los días
- self.ax_bitcoin.grid(True)
-
- def update_charts(self):
- """Actualiza ambos gráficos periódicamente."""
- while True:
- # Actualizar datos simulados
- self.economy_data = self.economy_data[1:] + [random.randint(50, 100)] # Economía en meses
- self.bitcoin_data = self.bitcoin_data[1:] + [random.randint(20000, 60000)] # Bitcoin en días
-
- # Actualizar gráficos
- self.update_economy_chart()
- self.update_bitcoin_chart()
- self.canvas.draw()
-
- # Esperar 5 segundos antes de la próxima actualización
- time.sleep(5)
diff --git a/solapas/MusicDownloader.py b/solapas/MusicDownloader.py
deleted file mode 100644
index 5d7859c..0000000
--- a/solapas/MusicDownloader.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import tkinter as tk
-from tkinter import ttk
-import threading
-from pytube import YouTube
-
-
-class MusicDownloader:
- def __init__(self, parent):
- """
- Inicializa la interfaz para descargar música de YouTube en MP3.
-
- Args:
- parent (tk.Frame): Frame donde se colocará el downloader.
- """
- self.parent = parent
-
- # Etiqueta de título
- title = tk.Label(self.parent, text="Descargar Música MP3", font=("Helvetica", 14, "bold"))
- title.pack(pady=10)
-
- # Entrada para la URL
- self.url_label = tk.Label(self.parent, text="URL de YouTube:")
- self.url_label.pack(pady=5)
- self.url_entry = tk.Entry(self.parent, width=50)
- self.url_entry.pack(pady=5)
-
- # Botón para iniciar la descarga
- self.download_button = tk.Button(self.parent, text="Descargar MP3", command=self.start_download, bg="lightblue")
- self.download_button.pack(pady=10)
-
- # Barra de progreso
- self.progress = ttk.Progressbar(self.parent, orient="horizontal", length=300, mode="determinate")
- self.progress.pack(pady=10)
-
- # Etiqueta de estado
- self.status_label = tk.Label(self.parent, text="", font=("Helvetica", 10))
- self.status_label.pack(pady=5)
-
- def start_download(self):
- """Inicia la descarga en un hilo separado."""
- url = self.url_entry.get()
- if not url:
- self.status_label.config(text="Por favor, ingrese una URL válida.", fg="red")
- return
- self.status_label.config(text="Iniciando descarga...", fg="blue")
- threading.Thread(target=self.download_music, args=(url,), daemon=True).start()
-
- def download_music(self, url):
- """Descarga el audio de YouTube como MP3."""
- try:
- # Inicializa la descarga
- yt = YouTube(url, on_progress_callback=self.update_progress)
- stream = yt.streams.filter(only_audio=True).first()
-
- # Descargar archivo
- self.status_label.config(text="Descargando...")
- self.progress["value"] = 0
- stream.download(filename=f"{yt.title}.mp3")
- self.status_label.config(text="¡Descarga completada!", fg="green")
- except Exception as e:
- self.status_label.config(text=f"Error: {str(e)}", fg="red")
-
- def update_progress(self, stream, chunk, bytes_remaining):
- """Actualiza la barra de progreso durante la descarga."""
- total_size = stream.filesize
- bytes_downloaded = total_size - bytes_remaining
- percentage = (bytes_downloaded / total_size) * 100
- self.progress["value"] = percentage
diff --git a/solapas/SQLQueryExecutor.py b/solapas/SQLQueryExecutor.py
deleted file mode 100644
index a7f37a5..0000000
--- a/solapas/SQLQueryExecutor.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import tkinter as tk
-from tkinter import ttk, messagebox
-import threading
-import mysql.connector
-from mysql.connector import Error
-
-
-class SQLQueryExecutor:
- def __init__(self, parent):
- """
- Clase para ejecutar consultas SQL en una base de datos MySQL.
-
- Args:
- parent (tk.Frame): Frame donde se colocarán los widgets.
- """
- self.parent = parent
-
- # Campos para ingresar información de conexión
- self.db_info_frame = tk.Frame(self.parent)
- self.db_info_frame.pack(pady=10, padx=10, fill="x")
-
- tk.Label(self.db_info_frame, text="Host:").grid(row=0, column=0, sticky="w")
- self.host_entry = tk.Entry(self.db_info_frame)
- self.host_entry.insert(0, "localhost")
- self.host_entry.grid(row=0, column=1)
-
- tk.Label(self.db_info_frame, text="Usuario:").grid(row=1, column=0, sticky="w")
- self.user_entry = tk.Entry(self.db_info_frame)
- self.user_entry.insert(0, "root")
- self.user_entry.grid(row=1, column=1)
-
- tk.Label(self.db_info_frame, text="Contraseña:").grid(row=2, column=0, sticky="w")
- self.password_entry = tk.Entry(self.db_info_frame, show="*")
- self.password_entry.grid(row=2, column=1)
-
- tk.Label(self.db_info_frame, text="Base de datos:").grid(row=3, column=0, sticky="w")
- self.database_entry = tk.Entry(self.db_info_frame)
- self.database_entry.grid(row=3, column=1)
-
- # Botón para conectar a la base de datos
- self.connect_button = tk.Button(self.db_info_frame, text="Conectar", command=self.connect_to_database)
- self.connect_button.grid(row=4, column=0, columnspan=2, pady=5)
-
- # Área para ingresar consultas SQL
- self.query_frame = tk.Frame(self.parent)
- self.query_frame.pack(pady=10, padx=10, fill="both", expand=True)
-
- tk.Label(self.query_frame, text="Consulta SQL:").pack(anchor="w")
- self.query_text = tk.Text(self.query_frame, height=10)
- self.query_text.pack(fill="both", expand=True)
-
- # Botón para ejecutar consultas
- self.execute_button = tk.Button(self.query_frame, text="Ejecutar", command=self.execute_query)
- self.execute_button.pack(pady=5)
-
- # Área para mostrar resultados
- self.result_frame = tk.Frame(self.parent)
- self.result_frame.pack(pady=10, padx=10, fill="both", expand=True)
-
- tk.Label(self.result_frame, text="Resultados:").pack(anchor="w")
- self.result_text = tk.Text(self.result_frame, height=10, state="disabled")
- self.result_text.pack(fill="both", expand=True)
-
- def connect_to_database(self):
- """Conecta a la base de datos utilizando los datos proporcionados."""
- self.host = self.host_entry.get()
- self.user = self.user_entry.get()
- self.password = self.password_entry.get()
- self.database = self.database_entry.get()
-
- try:
- self.connection = mysql.connector.connect(
- host=self.host,
- user=self.user,
- password=self.password,
- database=self.database
- )
- if self.connection.is_connected():
- messagebox.showinfo("Conexión Exitosa", "Conectado a la base de datos")
- except Error as e:
- messagebox.showerror("Error de Conexión", str(e))
-
- def execute_query(self):
- """Ejecuta la consulta SQL en un hilo separado."""
- query = self.query_text.get("1.0", tk.END).strip()
- if not query:
- messagebox.showwarning("Consulta Vacía", "Por favor, ingrese una consulta SQL.")
- return
-
- threading.Thread(target=self.run_query, args=(query,), daemon=True).start()
-
- def run_query(self, query):
- """Ejecuta la consulta y muestra los resultados."""
- try:
- cursor = self.connection.cursor()
- cursor.execute(query)
-
- if query.strip().lower().startswith("select"):
- rows = cursor.fetchall()
- column_names = [desc[0] for desc in cursor.description]
-
- # Mostrar los resultados
- self.result_text.config(state="normal")
- self.result_text.delete("1.0", tk.END)
- self.result_text.insert(tk.END, "\t".join(column_names) + "\n")
- for row in rows:
- self.result_text.insert(tk.END, "\t".join(map(str, row)) + "\n")
- self.result_text.config(state="disabled")
- else:
- self.connection.commit()
- messagebox.showinfo("Éxito", "Consulta ejecutada correctamente.")
- except Error as e:
- messagebox.showerror("Error de Consulta", str(e))
diff --git a/solapas/TicTacToe.py b/solapas/TicTacToe.py
deleted file mode 100644
index 7504754..0000000
--- a/solapas/TicTacToe.py
+++ /dev/null
@@ -1,123 +0,0 @@
-import time
-import tkinter as tk
-from tkinter import messagebox
-import threading
-import random
-
-
-class TicTacToe:
- def __init__(self, parent):
- """
- Inicializa el juego de Tic Tac Toe.
-
- Args:
- parent (tk.Frame): Frame donde se colocará el juego.
- """
- self.parent = parent
- self.board = [""] * 9 # Tablero de 3x3 representado como una lista
- self.current_player = "X" # Jugador inicial
- self.vs_computer = False # Modo jugador vs máquina
-
- # Etiqueta para el título
- title = tk.Label(self.parent, text="Tic Tac Toe", font=("Helvetica", 16, "bold"))
- title.pack(pady=10)
-
- # Botón para alternar entre modos
- self.mode_button = tk.Button(self.parent, text="Modo: Jugador vs Jugador", command=self.toggle_mode)
- self.mode_button.pack(pady=5)
-
- # Crear el tablero
- self.buttons = []
- self.board_frame = tk.Frame(self.parent)
- self.board_frame.pack()
-
- for i in range(9):
- button = tk.Button(
- self.board_frame,
- text="",
- font=("Helvetica", 20),
- width=5,
- height=2,
- command=self.create_button_command(i) # Aquí usamos la función auxiliar
- )
- button.grid(row=i // 3, column=i % 3)
- self.buttons.append(button)
-
- # Etiqueta para el estado del juego
- self.status_label = tk.Label(self.parent, text="Turno: X", font=("Helvetica", 12))
- self.status_label.pack(pady=5)
-
- def toggle_mode(self):
- """Alterna entre los modos Jugador vs Jugador y Jugador vs Máquina."""
- self.vs_computer = not self.vs_computer
- mode_text = "Modo: Jugador vs Máquina" if self.vs_computer else "Modo: Jugador vs Jugador"
- self.mode_button.config(text=mode_text)
- self.reset_game()
-
- def reset_game(self):
- """Reinicia el tablero y el estado del juego."""
- self.board = [""] * 9
- self.current_player = "X"
- for button in self.buttons:
- button.config(text="", state=tk.NORMAL)
- self.status_label.config(text="Turno: X")
-
- def make_move(self, index):
- """Realiza un movimiento en el tablero."""
- if self.board[index] == "":
- self.board[index] = self.current_player
- self.buttons[index].config(text=self.current_player)
-
- # Verificar si hay un ganador
- winner = self.check_winner()
- if winner:
- self.end_game(f"¡Ganador: {winner}!")
- return
- elif "" not in self.board:
- self.end_game("¡Empate!")
- return
-
- # Cambiar de jugador
- self.current_player = "O" if self.current_player == "X" else "X"
- self.status_label.config(text=f"Turno: {self.current_player}")
-
- # Si está en modo Jugador vs Máquina y es el turno de la máquina
- if self.vs_computer and self.current_player == "O":
- threading.Thread(target=self.computer_move).start()
-
- def computer_move(self):
- """Simula el movimiento de la máquina."""
- self.status_label.config(text="Turno: Máquina (O)")
- available_moves = [i for i, v in enumerate(self.board) if v == ""]
- move = random.choice(available_moves)
-
- def delayed_move():
- time.sleep(1) # Simular el tiempo de "pensar"
- self.make_move(move)
-
- threading.Thread(target=delayed_move).start()
-
- def check_winner(self):
- """Verifica si hay un ganador."""
- winning_combinations = [
- (0, 1, 2), (3, 4, 5), (6, 7, 8), # Filas
- (0, 3, 6), (1, 4, 7), (2, 5, 8), # Columnas
- (0, 4, 8), (2, 4, 6) # Diagonales
- ]
- for a, b, c in winning_combinations:
- if self.board[a] == self.board[b] == self.board[c] and self.board[a] != "":
- return self.board[a]
- return None
-
- def end_game(self, message):
- """Finaliza el juego mostrando un mensaje."""
- messagebox.showinfo("Fin del Juego", message)
- self.reset_game()
-
- def create_button_command(self, index):
- """Crea un comando para un botón con un índice específico."""
-
- def command():
- self.make_move(index)
-
- return command
\ No newline at end of file
diff --git a/solapas/WebScraperToDB.py b/solapas/WebScraperToDB.py
deleted file mode 100644
index 248deab..0000000
--- a/solapas/WebScraperToDB.py
+++ /dev/null
@@ -1,195 +0,0 @@
-import tkinter as tk
-import threading
-import time
-import mysql.connector
-import requests
-from bs4 import BeautifulSoup
-from tkinter import messagebox
-
-class WebScraperToDB:
- def __init__(self, parent):
- """
- Inicializa el widget de scraping con integración a base de datos.
- """
- self.parent = parent
- self.scraping_thread = None
- self.stop_event = threading.Event()
-
- # Crear campos de conexión para la base de datos
- db_frame = tk.Frame(self.parent)
- db_frame.pack(pady=5)
-
- tk.Label(db_frame, text="Host:").grid(row=0, column=0)
- self.host_entry = tk.Entry(db_frame)
- self.host_entry.insert(0, "localhost")
- self.host_entry.grid(row=0, column=1)
-
- tk.Label(db_frame, text="Usuario:").grid(row=1, column=0)
- self.user_entry = tk.Entry(db_frame)
- self.user_entry.insert(0, "root")
- self.user_entry.grid(row=1, column=1)
-
- tk.Label(db_frame, text="Contraseña:").grid(row=2, column=0)
- self.password_entry = tk.Entry(db_frame, show="*")
- self.password_entry.grid(row=2, column=1)
-
- tk.Label(db_frame, text="Nombre BD:").grid(row=3, column=0)
- self.database_entry = tk.Entry(db_frame)
- self.database_entry.insert(0, "scraping_db")
- self.database_entry.grid(row=3, column=1)
-
- tk.Button(db_frame, text="Crear Base de Datos", command=self.create_database).grid(row=4, column=0, columnspan=2, pady=5)
-
- # Área para URL y botones de control
- control_frame = tk.Frame(self.parent)
- control_frame.pack(pady=5)
-
- tk.Label(control_frame, text="URL para Scraping:").grid(row=0, column=0)
- self.url_entry = tk.Entry(control_frame, width=50)
- self.url_entry.insert(0, "https://quotes.toscrape.com/")
- self.url_entry.grid(row=0, column=1)
-
- # Campo para Selector HTML
- tk.Label(control_frame, text="Selector HTML:").grid(row=2, column=0)
- self.selector_entry = tk.Entry(control_frame, width=50)
- self.selector_entry.insert(0, "h1") # Valor predeterminado
- self.selector_entry.grid(row=2, column=1)
-
- self.start_button = tk.Button(control_frame, text="Iniciar Scraping", command=self.start_scraping)
- self.start_button.grid(row=1, column=0, pady=5)
-
- self.stop_button = tk.Button(control_frame, text="Parar Scraping", command=self.stop_scraping, state="disabled")
- self.stop_button.grid(row=1, column=1, pady=5)
-
- self.reset_button = tk.Button(control_frame, text="Resetear Scraping", command=self.reset_database)
- self.reset_button.grid(row=1, column=2, pady=5)
-
- # Área para mostrar el estado
- self.status_label = tk.Label(self.parent, text="Estado: Inactivo", fg="red")
- self.status_label.pack(pady=5)
-
- # Área para mostrar los datos scrapeados
- self.scraped_data_frame = tk.Frame(self.parent)
- self.scraped_data_frame.pack(pady=5, fill="both", expand=True)
-
- tk.Label(self.scraped_data_frame, text="Datos Scrapeados:").pack(anchor="w")
-
- self.scraped_data_text = tk.Text(self.scraped_data_frame, height=10, state="disabled")
- self.scraped_data_text.pack(fill="both", expand=True)
-
- def create_database(self):
- """Crea la base de datos y la tabla para almacenar datos de scraping."""
- try:
- connection = mysql.connector.connect(
- host=self.host_entry.get(),
- user=self.user_entry.get(),
- password=self.password_entry.get()
- )
- cursor = connection.cursor()
- cursor.execute(f"CREATE DATABASE IF NOT EXISTS {self.database_entry.get()}")
- connection.database = self.database_entry.get()
- cursor.execute("""
- CREATE TABLE IF NOT EXISTS scraped_data (
- id INT AUTO_INCREMENT PRIMARY KEY,
- title TEXT NOT NULL,
- link TEXT NOT NULL,
- scraped_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
- )
- """)
- connection.close()
- messagebox.showinfo("Éxito", "Base de datos y tabla creadas correctamente.")
- except mysql.connector.Error as e:
- messagebox.showerror("Error", str(e))
-
- def start_scraping(self):
- """Inicia el scraping en un hilo separado."""
- if self.scraping_thread and self.scraping_thread.is_alive():
- messagebox.showwarning("Aviso", "El scraping ya está en ejecución.")
- return
- self.stop_event.clear()
- self.scraping_thread = threading.Thread(target=self.scrape_data, daemon=True)
- self.scraping_thread.start()
- self.status_label.config(text="Estado: Ejecutando...", fg="green")
- self.start_button.config(state="disabled")
- self.stop_button.config(state="normal")
-
- def scrape_data(self):
- """Realiza el scraping de manera continua y guarda los datos en la base de datos."""
- url = self.url_entry.get()
- selector = self.selector_entry.get()
-
- try:
- headers = {
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
- }
- connection = mysql.connector.connect(
- host=self.host_entry.get(),
- user=self.user_entry.get(),
- password=self.password_entry.get(),
- database=self.database_entry.get()
- )
- cursor = connection.cursor()
-
- while not self.stop_event.is_set():
- response = requests.get(url, headers=headers)
- soup = BeautifulSoup(response.text, "html.parser")
-
- # Busca elementos según el selector ingresado por el usuario
- elements = soup.select(selector)
- if not elements:
- self.status_label.config(text="Estado: Sin datos encontrados.", fg="orange")
- time.sleep(5) # Pausa antes de intentar de nuevo
- continue
-
- for element in elements:
- title_text = element.get_text(strip=True)
- link = element.get("href", "Sin enlace") # Asegúrate de que el selector apunte a elementos
-
- # Insertar en la base de datos
- cursor.execute("INSERT INTO scraped_data (title, link) VALUES (%s, %s)", (title_text, link))
- connection.commit()
-
- # Mostrar en la interfaz
- self.scraped_data_text.config(state="normal")
- self.scraped_data_text.insert("end", f"{title_text} - {link}\n")
- self.scraped_data_text.see("end")
- self.scraped_data_text.config(state="disabled")
-
- self.status_label.config(text=f"Estado: Scrapeando {title_text}...", fg="green")
-
- # Pausa entre iteraciones
- time.sleep(5)
-
- connection.close()
- self.status_label.config(text="Estado: Inactivo", fg="red")
- except Exception as e:
- messagebox.showerror("Error", str(e))
- self.status_label.config(text="Estado: Error", fg="red")
-
- def stop_scraping(self):
- """Detiene el proceso de scraping."""
- self.stop_event.set()
- self.start_button.config(state="normal")
- self.stop_button.config(state="disabled")
-
- def reset_database(self):
- """Elimina todos los datos de la tabla."""
- try:
- connection = mysql.connector.connect(
- host=self.host_entry.get(),
- user=self.user_entry.get(),
- password=self.password_entry.get(),
- database=self.database_entry.get()
- )
- cursor = connection.cursor()
- cursor.execute("TRUNCATE TABLE scraped_data")
- connection.commit()
- connection.close()
- messagebox.showinfo("Éxito", "Datos reseteados correctamente.")
-
- # Limpiar el cuadro de texto
- self.scraped_data_text.config(state="normal")
- self.scraped_data_text.delete("1.0", "end")
- self.scraped_data_text.config(state="disabled")
- except Exception as e:
- messagebox.showerror("Error", str(e))
diff --git a/solapas/__init__.py b/solapas/__init__.py
deleted file mode 100644
index e69de29..0000000