This commit is contained in:
Santi 2025-02-18 16:32:28 +01:00
parent 650b20dda5
commit 27d83cc9ad
5 changed files with 70 additions and 96 deletions

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 <a>
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(),