From 27d83cc9ad2211d640d61d46f86ab2f1476669c0 Mon Sep 17 00:00:00 2001 From: Santi Date: Tue, 18 Feb 2025 16:32:28 +0100 Subject: [PATCH] Proyecto --- solapas/EconomyBitcoinChart.py | 25 ++++++++-------- solapas/MusicDownloader.py | 14 +++++---- solapas/SQLQueryExecutor.py | 25 ++++++++-------- solapas/TicTacToe.py | 52 +++++++--------------------------- solapas/WebScraperToDB.py | 50 +++++++++++++++++--------------- 5 files changed, 70 insertions(+), 96 deletions(-) diff --git a/solapas/EconomyBitcoinChart.py b/solapas/EconomyBitcoinChart.py index 41cd3b9..ba4e583 100644 --- a/solapas/EconomyBitcoinChart.py +++ b/solapas/EconomyBitcoinChart.py @@ -23,21 +23,22 @@ class EconomyBitcoinChart: 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.economy_data = [random.randint(50, 100) for _ in range(10)] # Índice económico en meses + self.bitcoin_data = [random.randint(20000, 60000) for _ in range(10)] # Precio del Bitcoin en días + # Dibujar los gráficos iniciales self.update_economy_chart() self.update_bitcoin_chart() - # Embebiendo los gráficos en Tkinter + # Embebiendo los gráficos en la interfaz de 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 + # Iniciar hilos para actualizar los gráficos periódicamente threading.Thread(target=self.update_charts, daemon=True).start() def update_economy_chart(self): - """Actualiza el gráfico de economía mundial.""" + """Actualiza el gráfico de economía mundial con nuevos datos.""" self.ax_economy.clear() self.ax_economy.plot(self.economy_data, marker="o", color="blue") self.ax_economy.set_title("Economía Mundial") @@ -45,22 +46,22 @@ class EconomyBitcoinChart: self.ax_economy.grid(True) def update_bitcoin_chart(self): - """Actualiza el gráfico de Bitcoin.""" + """Actualiza el gráfico del precio 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.set_xlabel("Días") # Etiqueta del eje X self.ax_bitcoin.grid(True) def update_charts(self): - """Actualiza ambos gráficos periódicamente.""" + """Actualiza ambos gráficos con nuevos datos 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 + # Generar nuevos datos simulados + self.economy_data = self.economy_data[1:] + [random.randint(50, 100)] + self.bitcoin_data = self.bitcoin_data[1:] + [random.randint(20000, 60000)] - # Actualizar gráficos + # Redibujar gráficos con los nuevos datos self.update_economy_chart() self.update_bitcoin_chart() self.canvas.draw() diff --git a/solapas/MusicDownloader.py b/solapas/MusicDownloader.py index 3dfe309..f332260 100644 --- a/solapas/MusicDownloader.py +++ b/solapas/MusicDownloader.py @@ -10,7 +10,7 @@ DOWNLOAD_FOLDER = "musicplayer" class MusicDownloader: def __init__(self, parent): """ - Inicializa la interfaz para descargar música de YouTube en MP3. + Inicializa la interfaz gráfica para descargar música de YouTube en formato MP3. """ self.parent = parent @@ -22,7 +22,7 @@ class MusicDownloader: title = tk.Label(self.parent, text="Descargar Música MP3", font=("Helvetica", 14, "bold")) title.pack(pady=10) - # Entrada para la URL + # Campo de entrada para la URL de YouTube 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) @@ -32,16 +32,16 @@ class MusicDownloader: 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 + # Barra de progreso de la descarga self.progress = ttk.Progressbar(self.parent, orient="horizontal", length=300, mode="determinate") self.progress.pack(pady=10) - # Etiqueta de estado + # Etiqueta de estado para mostrar mensajes 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.""" + """Inicia la descarga en un hilo separado para no bloquear la interfaz.""" url = self.url_entry.get() if not url: self.status_label.config(text="Por favor, ingrese una URL válida.", fg="red") @@ -50,11 +50,12 @@ class MusicDownloader: threading.Thread(target=self.download_music, args=(url,), daemon=True).start() def download_music(self, url): - """Descarga el audio de YouTube en MP3 usando yt-dlp.""" + """Descarga el audio de YouTube en formato MP3 usando `yt-dlp`.""" try: self.status_label.config(text="Descargando...", fg="blue") output_template = os.path.join(DOWNLOAD_FOLDER, "%(title)s.%(ext)s") + # Opciones para descargar solo el audio y convertirlo a MP3 ydl_opts = { 'format': 'bestaudio/best', 'outtmpl': output_template, @@ -71,6 +72,7 @@ class MusicDownloader: 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 diff --git a/solapas/SQLQueryExecutor.py b/solapas/SQLQueryExecutor.py index a7f37a5..e849464 100644 --- a/solapas/SQLQueryExecutor.py +++ b/solapas/SQLQueryExecutor.py @@ -4,33 +4,32 @@ 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. + Clase que permite ejecutar consultas SQL en una base de datos MySQL. Args: - parent (tk.Frame): Frame donde se colocarán los widgets. + parent (tk.Frame): Frame donde se colocarán los widgets de la interfaz gráfica. """ self.parent = parent - # Campos para ingresar información de conexión + # Frame para ingresar los datos de conexión a la base de datos 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.insert(0, "localhost") # Valor por defecto 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.insert(0, "root") # Usuario por defecto 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 = tk.Entry(self.db_info_frame, show="*") # Campo oculto 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") @@ -41,7 +40,7 @@ class SQLQueryExecutor: 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 + # Área para escribir la consulta SQL self.query_frame = tk.Frame(self.parent) self.query_frame.pack(pady=10, padx=10, fill="both", expand=True) @@ -49,11 +48,11 @@ class SQLQueryExecutor: self.query_text = tk.Text(self.query_frame, height=10) self.query_text.pack(fill="both", expand=True) - # Botón para ejecutar consultas + # Botón para ejecutar la consulta self.execute_button = tk.Button(self.query_frame, text="Ejecutar", command=self.execute_query) self.execute_button.pack(pady=5) - # Área para mostrar resultados + # Área para mostrar los resultados self.result_frame = tk.Frame(self.parent) self.result_frame.pack(pady=10, padx=10, fill="both", expand=True) @@ -62,7 +61,7 @@ class SQLQueryExecutor: self.result_text.pack(fill="both", expand=True) def connect_to_database(self): - """Conecta a la base de datos utilizando los datos proporcionados.""" + """Conecta a la base de datos MySQL utilizando los datos ingresados por el usuario.""" self.host = self.host_entry.get() self.user = self.user_entry.get() self.password = self.password_entry.get() @@ -81,7 +80,7 @@ class SQLQueryExecutor: messagebox.showerror("Error de Conexión", str(e)) def execute_query(self): - """Ejecuta la consulta SQL en un hilo separado.""" + """Ejecuta la consulta SQL en un hilo separado para evitar bloquear la interfaz.""" query = self.query_text.get("1.0", tk.END).strip() if not query: messagebox.showwarning("Consulta Vacía", "Por favor, ingrese una consulta SQL.") @@ -90,7 +89,7 @@ class SQLQueryExecutor: threading.Thread(target=self.run_query, args=(query,), daemon=True).start() def run_query(self, query): - """Ejecuta la consulta y muestra los resultados.""" + """Ejecuta la consulta en la base de datos y muestra los resultados.""" try: cursor = self.connection.cursor() cursor.execute(query) diff --git a/solapas/TicTacToe.py b/solapas/TicTacToe.py index 7504754..eb21fdc 100644 --- a/solapas/TicTacToe.py +++ b/solapas/TicTacToe.py @@ -4,7 +4,6 @@ from tkinter import messagebox import threading import random - class TicTacToe: def __init__(self, parent): """ @@ -14,19 +13,19 @@ class TicTacToe: 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.board = [""] * 9 # Representación del tablero 3x3 + self.current_player = "X" # Jugador que inicia la partida self.vs_computer = False # Modo jugador vs máquina - # Etiqueta para el título + # Título del juego title = tk.Label(self.parent, text="Tic Tac Toe", font=("Helvetica", 16, "bold")) title.pack(pady=10) - # Botón para alternar entre modos + # Botón para alternar entre modos de juego 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 + # Crear la cuadrícula del tablero self.buttons = [] self.board_frame = tk.Frame(self.parent) self.board_frame.pack() @@ -38,12 +37,12 @@ class TicTacToe: font=("Helvetica", 20), width=5, height=2, - command=self.create_button_command(i) # Aquí usamos la función auxiliar + command=self.create_button_command(i) ) button.grid(row=i // 3, column=i % 3) self.buttons.append(button) - # Etiqueta para el estado del juego + # Mensaje que indica el turno del jugador self.status_label = tk.Label(self.parent, text="Turno: X", font=("Helvetica", 12)) self.status_label.pack(pady=5) @@ -55,7 +54,7 @@ class TicTacToe: self.reset_game() def reset_game(self): - """Reinicia el tablero y el estado del juego.""" + """Reinicia el juego a su estado inicial.""" self.board = [""] * 9 self.current_player = "X" for button in self.buttons: @@ -63,12 +62,11 @@ class TicTacToe: self.status_label.config(text="Turno: X") def make_move(self, index): - """Realiza un movimiento en el tablero.""" + """Registra un movimiento y actualiza 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}!") @@ -77,28 +75,11 @@ class TicTacToe: 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.""" + """Verifica si hay un ganador en el juego.""" winning_combinations = [ (0, 1, 2), (3, 4, 5), (6, 7, 8), # Filas (0, 3, 6), (1, 4, 7), (2, 5, 8), # Columnas @@ -108,16 +89,3 @@ class TicTacToe: 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 index 248deab..3f14068 100644 --- a/solapas/WebScraperToDB.py +++ b/solapas/WebScraperToDB.py @@ -9,52 +9,59 @@ from tkinter import messagebox class WebScraperToDB: def __init__(self, parent): """ - Inicializa el widget de scraping con integración a base de datos. + Inicializa la interfaz gráfica para scraping web con integración a base de datos. + + Args: + parent (tk.Frame): Contenedor en el que se mostrará la interfaz gráfica. """ self.parent = parent - self.scraping_thread = None - self.stop_event = threading.Event() + self.scraping_thread = None # Hilo que ejecutará el scraping + self.stop_event = threading.Event() # Evento para detener el scraping - # Crear campos de conexión para la base de datos + # Crear la sección de conexión a la base de datos db_frame = tk.Frame(self.parent) db_frame.pack(pady=5) + # Campos de entrada para la conexión a MySQL 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.insert(0, "localhost") # Valor predeterminado 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.insert(0, "root") # Usuario predeterminado 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 = tk.Entry(db_frame, show="*") # Campo oculto para seguridad 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.insert(0, "scraping_db") # Base de datos predeterminada self.database_entry.grid(row=3, column=1) + # Botón para crear la base de datos 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 + # Sección de controles para scraping control_frame = tk.Frame(self.parent) control_frame.pack(pady=5) + # Campo de entrada para la URL a scrape 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.insert(0, "https://quotes.toscrape.com/") # URL de prueba self.url_entry.grid(row=0, column=1) - # Campo para Selector HTML + # Campo para ingresar el 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.insert(0, "h1") # Selector predeterminado self.selector_entry.grid(row=2, column=1) + # Botones de control self.start_button = tk.Button(control_frame, text="Iniciar Scraping", command=self.start_scraping) self.start_button.grid(row=1, column=0, pady=5) @@ -64,7 +71,7 @@ class WebScraperToDB: 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 + # Etiqueta para mostrar el estado del scraping self.status_label = tk.Label(self.parent, text="Estado: Inactivo", fg="red") self.status_label.pack(pady=5) @@ -78,7 +85,7 @@ class WebScraperToDB: 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.""" + """Crea la base de datos y la tabla para almacenar los datos de scraping.""" try: connection = mysql.connector.connect( host=self.host_entry.get(), @@ -119,9 +126,7 @@ class WebScraperToDB: 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" - } + headers = {"User-Agent": "Mozilla/5.0"} connection = mysql.connector.connect( host=self.host_entry.get(), user=self.user_entry.get(), @@ -134,7 +139,7 @@ class WebScraperToDB: response = requests.get(url, headers=headers) soup = BeautifulSoup(response.text, "html.parser") - # Busca elementos según el selector ingresado por el usuario + # Busca elementos en la página usando el selector proporcionado elements = soup.select(selector) if not elements: self.status_label.config(text="Estado: Sin datos encontrados.", fg="orange") @@ -143,9 +148,9 @@ class WebScraperToDB: 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 + link = element.get("href", "Sin enlace") # Extrae el enlace si está disponible - # Insertar en la base de datos + # Insertar datos en la base de datos cursor.execute("INSERT INTO scraped_data (title, link) VALUES (%s, %s)", (title_text, link)) connection.commit() @@ -157,8 +162,7 @@ class WebScraperToDB: self.status_label.config(text=f"Estado: Scrapeando {title_text}...", fg="green") - # Pausa entre iteraciones - time.sleep(5) + time.sleep(5) # Pausa entre iteraciones connection.close() self.status_label.config(text="Estado: Inactivo", fg="red") @@ -173,7 +177,7 @@ class WebScraperToDB: self.stop_button.config(state="disabled") def reset_database(self): - """Elimina todos los datos de la tabla.""" + """Elimina todos los datos de la tabla scraped_data.""" try: connection = mysql.connector.connect( host=self.host_entry.get(),