Compare commits

...

No commits in common. "168a2a99848319663ea50cf5531f18f0a0157334" and "d3a7ab33969e000d762cf4ae6bb43963dcd9a909" have entirely different histories.

95 changed files with 1233 additions and 1357 deletions

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13 (Final)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

View File

@ -1,7 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11.2 WSL (Debian): (/home/santi/.virtualenvs/Final/bin/python)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (Final)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (ProyectoFinalProcesosServicios)" project-jdk-type="Python SDK" />
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Final.iml" filepath="$PROJECT_DIR$/.idea/Final.iml" />
</modules>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 695 KiB

View File

@ -1,80 +0,0 @@
![Texto alternativo](Imagen/Imagen_Proyecto.png)
![Texto alternativo](Imagen/img.png)
Descripción General
La Aplicación Responsive Window es una aplicación de escritorio multifuncional con las siguientes características principales:
Panel del Clima: Muestra información climática actual de Denia, incluyendo temperatura, condiciones de viento y calidad del aire.
Interfaz con Pestañas:
Descarga de Música: Descarga y gestiona archivos de música.
Lista de Tareas: Organiza tareas y gestiona actividades diarias.
Tres en Raya: Un juego simple para entretenimiento.
Web Scraping: Ingresa una URL para extraer datos de la web y ver los resultados.
Función de Chat: Ventana de chat integrada para la interacción del usuario.
Monitor de Recursos: Muestra información del sistema como uso de CPU, RAM, estado de la batería y actividad de red.
Fecha y Hora: Muestra la fecha y hora actuales en tiempo real.
Características
Panel del Clima
Ubicación: Muestra información climática específica de Denia.
Detalles: Indica temperatura, sensación térmica, velocidad y dirección del viento, ráfagas y índice de calidad del aire.
Interfaz con Pestañas
Descarga de Música
Ingresa y descarga archivos de música desde URLs especificadas.
Gestiona y organiza la música descargada.
Lista de Tareas
Añade, visualiza y gestiona tareas.
Lleva un seguimiento eficiente de las actividades diarias.
Tres en Raya
Juega una partida simple de Tres en Raya.
Web Scraping
Ingresa una URL para extraer contenido de páginas web.
Visualiza y analiza los datos extraídos dentro de la aplicación.
Función de Chat
Sistema de chat integrado para la comunicación del usuario.
Muestra mensajes con usuario y marca de tiempo.
Monitor de Recursos
Uso de CPU: Muestra el porcentaje de uso actual del CPU.
Uso de RAM: Indica el consumo de memoria.
Batería: Monitorea el nivel de batería y el tiempo estimado restante.
Actividad de Red: Rastrea los datos enviados y recibidos a través de la red.
Fecha y Hora
Muestra la fecha y hora actuales en un formato fácil de leer.
LINK PARA VIDEO DE YOUTUBE https://www.youtube.com/watch?v=f5WWiZ_IZv0

View File

@ -1,118 +0,0 @@
import tkinter as tk
from tkinter import ttk
import os
import configparser
class ConfigMgr:
def __init__(self, top_level, config_changed_listener=None):
self.top_level = top_level
self.config_window = None
self.config = configparser.ConfigParser()
self.__load_config()
self.config_changed_listener=config_changed_listener
self.config.read('config.ini')
def __load_config(self):
if os.path.exists('config.ini'):
self.config.read('config.ini')
self.__check_config()
else:
print("Config file not found, creating default config file")
with open('config.ini', 'w') as f:
self.__write_default_config(f)
self.config.read('config.ini')
def __check_config(self):
print("Checking config file")
if "Chat" not in self.config:
self.config["Chat"] = {
"server": "http://localhost:2020",
"name": "User"
}
if "Weather" not in self.config:
self.config["Weather"] = {
"city": "Denia, Alicante"
}
def __write_default_config(self, file):
chat_config = ("[Chat]\n"
"server=http://localhost:2020\n"
"name=User\n")
weather_config = ("[Weather]\n"
"city=Denia, Alicante\n")
file.write(chat_config + weather_config)
def display_config_window(self):
if (self.config_window is None
or not tk.Toplevel.winfo_exists(self.config_window)):
self.config_window = self.__build_config_window()
else:
self.config_window.lift()
def __build_config_window(self):
config_window = tk.Toplevel(self.top_level)
config_window.title("Config")
config_window.geometry("400x300")
notebook = ttk.Notebook(config_window)
notebook.pack(expand=True, fill="both")
# Chat Config Tab
chat_tab = ttk.Frame(notebook)
notebook.add(chat_tab, text="Chat Config")
chat_server_label = tk.Label(chat_tab, text="Chat Server URL")
chat_server_label.pack()
self.chat_server_variable = tk.StringVar()
try:
self.chat_server_variable.set(self.config["Chat"]["server"])
except KeyError:
self.chat_server_variable.set("")
chat_server_input = tk.Entry(chat_tab, textvariable=self.chat_server_variable)
chat_server_input.pack()
chat_name_label = tk.Label(chat_tab, text="Name in the Chat")
chat_name_label.pack()
self.chat_name_variable = tk.StringVar()
try:
self.chat_name_variable.set(self.config["Chat"]["name"])
except KeyError:
self.chat_name_variable.set("")
chat_name_input = tk.Entry(chat_tab, textvariable=self.chat_name_variable)
chat_name_input.pack()
# Weather Config Tab
weather_tab = ttk.Frame(notebook)
notebook.add(weather_tab, text="Weather Config")
weather_city_label = tk.Label(weather_tab, text="City")
weather_city_label.pack()
self.weather_city_variable = tk.StringVar()
try:
self.weather_city_variable.set(self.config["Weather"]["city"])
except KeyError:
self.weather_city_variable.set("")
weather_city_input = tk.Entry(weather_tab, textvariable=self.weather_city_variable)
weather_city_input.pack()
self.save_button = tk.Button(config_window, text="Save", command=self.save_config)
self.save_button.pack(pady=10)
return config_window
def save_config(self):
self.config["Chat"] = {"server": self.chat_server_variable.get(),
"name": self.chat_name_variable.get()}
self.config["Weather"] = {"city": self.weather_city_variable.get()}
with open('config.ini', 'w') as configfile:
self.config.write(configfile)
self.config_changed_listener()
# Close window
self.config_window.destroy()

Binary file not shown.

View File

@ -1,94 +0,0 @@
import threading
import time
import flask
from werkzeug.serving import make_server
import logging
class Server:
"""
This server does NOT use Flask's built-in development server.
Instead, it uses Werkzeug's make_server to create a WSGI server.
This server is thread-safe and can be stopped by setting the stop_event.
This server works as a chat server by polling, not by using websockets (to keep it simple).
"""
def __init__(self, host: str, port: int, stop_event: threading.Event):
self.host = host
self.port = port
self.stop_event = stop_event
# Flask definition
self.flask = flask.Flask(__name__)
self.flask.add_url_rule('/status', methods=['POST'], view_func=self.status)
self.flask.add_url_rule('/send_message', methods=['POST'], view_func=self.send_message)
self.flask.add_url_rule('/get_messages', methods=['POST'], view_func=self.get_messages)
self.server = make_server(self.host, self.port, self.flask)
self.watcher_thread = threading.Thread(target=self.__watcher)
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.start()
self.watcher_thread.start()
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
self.flask.logger.setLevel(logging.ERROR)
# Message initialization
self.message_id = 0
self.messages = self.__init_messages()
def __watcher(self):
while not self.stop_event.is_set():
time.sleep(1)
self.shutdown()
def shutdown(self):
self.__persist_messages()
self.server.shutdown()
def __init_messages(self):
messages = []
try:
with open('chat_server/messages.txt', 'r') as f:
for line in f:
parts = line.strip().split('|')
messages.append({
'id': int(parts[0]),
'sender': parts[1],
'content': parts[2]
})
self.message_id = messages[-1]['id'] if messages else 0
except FileNotFoundError:
with open('chat_server/messages.txt', 'w+') as f:
pass
return messages
def __persist_messages(self):
with open('chat_server/messages.txt', 'w') as f:
for message in self.messages:
f.write(f"{message['id']}|{message['sender']}|{message['content']}\n")
def status(self):
return 'OK'
def send_message(self):
sender = flask.request.json['sender']
content = flask.request.json['content']
self.message_id += 1
message = {
'id': self.message_id,
'sender': sender,
'content': content
}
self.messages.append(message)
return {'id': self.message_id}
def get_messages(self):
try: last_id = flask.request.json.get('last_id')
# last_id is a mandatory parameter
except AttributeError: return flask.Response('Last ID not specified', status=400)
new_messages = [msg for msg in self.messages if msg['id'] > last_id]
return {'messages': new_messages}

View File

@ -1,4 +0,0 @@
1|User|Hola
2|feo|hola
3|feo|uybsdfuhbsd
4|kevion|hola

View File

@ -1,7 +0,0 @@
[Chat]
server = http://localhost:2020
name = kevion
[Weather]
city = Denia

View File

@ -1,180 +1,194 @@
import threading
import tkinter as tk
from tkinter import Menu
from tkinter import ttk
import threading
import time
import datetime
from tkinter import Menu # Importar el widget Menu
from tkinter import ttk # Importar el widget ttk
from app.ConfigMgr import ConfigMgr
from app.chat_server.Server import Server
from app.widgets import ClockLabel
from app.widgets.ChatTab import ChatTab
from app.widgets.MusicDownloadTab import MusicDownloadTab
from app.widgets.MusicPlayerTab import MusicPlayerTab
from app.widgets.TicTacToeTab import TicTacToeTab
from app.widgets.TodoTab import TodoTab
from app.widgets.UsageLabels import CPULabel, RAMLabel, BatteryLabel, NetworkLabel
from app.widgets.WeatherTab import WeatherTab
from app.widgets.WebScrapingTab import WebScrapingTab
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
stop_event = threading.Event()
# Clave de API de OpenWeatherMap
API_KEY = "1fa8fd05b650773bbc3f2130657e808a"
def on_closing():
# Kill all threads that are linked to the stop_event
# This ensures that execution is thread-safe
stop_event.set()
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}"
# Close the main window
root.quit()
root.destroy()
# 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})
def on_config_changed():
chat_frame.change_server_url(config_manager.config["Chat"]["server"])
chat_frame.change_sender_name(config_manager.config["Chat"]["name"])
weather_tab.changeCity(config_manager.config["Weather"]["city"])
# Espera 1 segundo antes de actualizar de nuevo
time.sleep(1)
# Create the main window
# Crear la ventana principal
root = tk.Tk()
root.title("Responsive Window")
root.geometry("1150x700")
root.title("Ventana Responsive")
root.geometry("1000x700") # Tamaño inicial
config_manager = ConfigMgr(root, config_changed_listener=on_config_changed)
server = Server(host="localhost", port=2020, stop_event=stop_event)
# 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
# Configure the main window to be responsive
root.columnconfigure(0, weight=0)
root.columnconfigure(1, weight=1)
root.columnconfigure(2, weight=0)
root.rowconfigure(0, weight=1)
root.rowconfigure(1, weight=0)
# Create the top menu
# Crear el menú superior
menu_bar = Menu(root)
file_menu = Menu(menu_bar, tearoff=0)
file_menu.add_command(label="New")
file_menu.add_command(label="Open")
file_menu.add_command(label="Nuevo")
file_menu.add_command(label="Abrir")
file_menu.add_separator()
file_menu.add_command(label="Config", command=config_manager.display_config_window)
file_menu.add_command(label="Exit", command=on_closing)
file_menu.add_command(label="Salir", command=root.quit)
edit_menu = Menu(menu_bar, tearoff=0)
edit_menu.add_command(label="Copy")
edit_menu.add_command(label="Paste")
edit_menu.add_command(label="Copiar")
edit_menu.add_command(label="Pegar")
menu_bar.add_cascade(label="File", menu=file_menu)
menu_bar.add_cascade(label="Edit", menu=edit_menu)
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)
# Create the side and central frames
frame_left = tk.Frame(root, bg="lightblue", width=300)
frame_center = tk.Frame(root, bg="white")
frame_right = tk.Frame(root, bg="lightgreen", width=300)
# 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)
# Place the side and central frames
frame_left.grid(row=0, column=0, sticky="ns")
frame_center.grid(row=0, column=1, sticky="nsew")
frame_right.grid(row=0, column=2, sticky="ns")
# 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")
# Configure the fixed sizes of the side frames
frame_left.grid_propagate(False)
frame_right.grid_propagate(False)
# Configurar los tamaños fijos de los frames laterales
frame_izquierdo.grid_propagate(False)
frame_derecho.grid_propagate(False)
# Configure the left frame to have three rows
frame_left.rowconfigure(0, weight=1)
frame_left.rowconfigure(1, weight=0)
frame_left.rowconfigure(2, weight=0)
# Integrar el widget del clima en el panel izquierdo
weather_widget = WeatherWidget(frame_izquierdo, API_KEY)
# Add weather tab to the left frame, occupying 1/3 of the height
weather_tab = WeatherTab(frame_left, stop_event=stop_event,
city=config_manager.config["Weather"]["city"], refresh_rate=300)
weather_tab.grid(row=0, column=0, sticky="nsew")
# Añadir el lanzador de aplicaciones al panel izquierdo
app_launcher = ApplicationLauncher(frame_izquierdo)
# Adjust the remaining rows to occupy the rest of the space
frame_left.rowconfigure(1, weight=2)
frame_left.rowconfigure(2, weight=2)
# Añadir gráfico de lenguajes al panel izquierdo
language_chart = LanguageChart(frame_izquierdo)
# Divide the central frame into two parts (top variable and bottom fixed)
frame_center.rowconfigure(0, weight=1)
frame_center.rowconfigure(1, weight=0)
frame_center.columnconfigure(0, weight=1)
# Crear el widget de Chat en el panel derecho con más espacio
chat_widget = ChatWidget(frame_derecho)
# Create subframes within the central frame
frame_top = tk.Frame(frame_center, bg="lightyellow")
frame_bottom = tk.Frame(frame_center, bg="lightgray", height=100)
# Agregar el reproductor de música al panel derecho, en la parte inferior
music_player = MusicPlayer(frame_derecho)
# Place the subframes within the central frame
frame_top.grid(row=0, column=0, sticky="nsew")
frame_bottom.grid(row=1, column=0, sticky="ew")
# 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
# Fix the size of the bottom part
frame_bottom.grid_propagate(False)
# Crear subframes dentro del frame central
frame_superior = tk.Frame(frame_central, bg="lightyellow")
frame_inferior = tk.Frame(frame_central, bg="lightgray", height=100)
# Create the status bar
status_bar = tk.Label(root, text="Status bar", bg="lightgray", anchor="w")
status_bar.grid(row=1, column=0, columnspan=3, sticky="ew")
# 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")
# Notebook for widgets
style = ttk.Style()
style.configure("CustomNotebook.TNotebook.Tab", font=("Arial", 12, "bold"))
notebook = ttk.Notebook(frame_top, style="CustomNotebook.TNotebook")
notebook.pack(fill="both", expand=True)
# Fijar el tamaño de la parte inferior
frame_inferior.grid_propagate(False)
# Add the tabs to the notebook
music_download_tab = MusicDownloadTab(notebook, stop_event=stop_event)
music_download_tab.pack(fill="both", expand=True)
notebook.add(music_download_tab, text="Music Download")
# Crear un evento de parada
stop_event = threading.Event()
# Add the TodoTab to the notebook
todo_tab = TodoTab(notebook, stop_event=stop_event)
todo_tab.pack(fill="both", expand=True)
notebook.add(todo_tab, text="Todo List")
# Add the TodoTab to the notebook
tic_tac_toe_tab = TicTacToeTab(notebook, stop_event=stop_event)
tic_tac_toe_tab.pack(fill="both", expand=True)
notebook.add(tic_tac_toe_tab, text="Tic Tac Toe")
# Add the TodoTab to the notebook
web_scraping_tab = WebScrapingTab(notebook, stop_event=stop_event)
web_scraping_tab.pack(fill="both", expand=True)
notebook.add(web_scraping_tab, text="Web Scraping")
# Create the chat and music player frames within the right frame
frame_chat = tk.Frame(frame_right, bg="lightgreen")
frame_music_player = tk.Frame(frame_right)
# Place the chat and music player frames within the right frame
frame_chat.grid(row=0, column=0, sticky="nsew")
frame_music_player.grid(row=1, column=0, sticky="nsew")
# Configure the right frame to be responsive
frame_right.rowconfigure(0, weight=3)
frame_right.rowconfigure(1, weight=1)
frame_right.columnconfigure(0, weight=1)
# Create and place the chat frame and music player tab update
chat_frame = ChatTab(frame_chat, chat_server_url=config_manager.config["Chat"]["server"],
sender_name=config_manager.config["Chat"]["name"],
stop_event=stop_event, width=200, bg="lightgreen", refresh_rate=1)
chat_frame.pack(fill="both", expand=True)
music_player = MusicPlayerTab(frame_music_player, stop_event=stop_event, refresh_rate=5)
music_player.pack(fill="both", expand=True)
label_cpu = CPULabel(status_bar, bg="lightgreen", font=("Helvetica", 10), relief="groove", anchor="center", width=10, stop_event=stop_event, refresh_rate=1)
label_ram = RAMLabel(status_bar, bg="lightcoral", font=("Helvetica", 10), relief="groove", anchor="center", width=10, stop_event=stop_event, refresh_rate=3)
label_battery = BatteryLabel(status_bar, bg="lightblue", font=("Helvetica", 10), relief="groove", anchor="center", width=20, stop_event=stop_event, refresh_rate=10)
label_net = NetworkLabel(status_bar, text="Network", bg="lightpink", font=("Helvetica", 10), relief="groove", anchor="center", width=20, stop_event=stop_event, refresh_rate=5)
label_time = ClockLabel(status_bar, font=("Helvetica", 12), bd=1, fg="darkblue", relief="sunken", anchor="center", width=20, stop_event=stop_event, refresh_rate=0.5)
label_cpu.pack(side="left", fill="both", expand=True)
label_ram.pack(side="left", fill="both", expand=True)
label_battery.pack(side="left", fill="both", expand=True)
label_net.pack(side="left", fill="both", expand=True)
label_time.pack(side="right", fill="both", expand=True)
# 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)
# Run the application
# 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()

View File

@ -1,28 +0,0 @@
beautifulsoup4==4.12.3
blinker==1.9.0
cairocffi==1.7.1
CairoSVG==2.7.1
certifi==2024.8.30
cffi==1.17.1
charset-normalizer==3.4.0
click==8.1.7
cssselect2==0.7.0
defusedxml==0.7.1
Flask==3.1.0
geographiclib==2.0
geopy==2.4.1
idna==3.10
itsdangerous==2.2.0
Jinja2==3.1.4
MarkupSafe==3.0.2
psutil==6.1.0
pycparser==2.22
pygame==2.6.1
requests==2.32.3
soupsieve==2.6
tinycss2==1.4.0
urllib3==2.2.3
weatherkit==1.1.1
webencodings==0.5.1
Werkzeug==3.1.3
yt-dlp==2024.12.6

View File

@ -1 +0,0 @@
ertdgfjkg

View File

@ -1,110 +0,0 @@
import time
import tkinter as tk
from tkinter import ttk
from tkinter.ttk import Notebook
from tkinter import Tk
import requests
from app.widgets.abc import ThreadedTab
class ChatTab(ThreadedTab):
def __init__(self, root: Notebook | Tk, chat_server_url: str, sender_name: str, **kwargs):
self.chat_server_url = chat_server_url
self.sender_name = sender_name
self.conn = False
self.last_msg = 0
super().__init__(root, **kwargs)
def task(self):
try:
self.get_messages()
if not self.conn:
self.connected()
self.conn = True
except requests.ConnectionError:
self.disconnected()
def build(self):
# Create the main frame for the chat interface
self.chat_frame = tk.Frame(self)
self.chat_frame.pack(fill="both", expand=True)
# Create the status label
self.status_label = tk.Label(self.chat_frame, text="", font=("Helvetica", 16))
self.status_label.pack(fill="x")
# Create the history frame with a scrollbar
self.history_frame = tk.Frame(self.chat_frame)
self.history_frame.pack(fill="both", expand=True)
self.history_canvas = tk.Canvas(self.history_frame)
self.history_canvas.pack(side="left", fill="both", expand=True)
self.scrollbar = ttk.Scrollbar(self.history_frame, orient="vertical", command=self.history_canvas.yview)
self.scrollbar.pack(side="right", fill="y")
self.history_canvas.configure(yscrollcommand=self.scrollbar.set)
self.history_canvas.bind('<Configure>', lambda e: self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")))
self.history_container = tk.Frame(self.history_canvas)
self.history_container.bind("<Configure>", self.on_frame_configure)
self.history_canvas.create_window((0, 0), window=self.history_container, anchor="nw")
# Create the input frame at the bottom
self.input_frame = tk.Frame(self.chat_frame)
self.input_frame.pack(fill="x")
self.message_entry = tk.Entry(self.input_frame)
self.message_entry.pack(side="left", fill="x", expand=True, padx=5, pady=5)
self.message_entry.bind("<Return>", lambda event: self.send_message()) # Bind Enter key to send_message
self.send_button = tk.Button(self.input_frame, text="Send", command=self.send_message)
self.send_button.pack(side="right", padx=5, pady=5)
def on_frame_configure(self, event):
self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all"))
def send_message(self):
message = self.message_entry.get()
if message and self.conn:
response = requests.post(f"{self.chat_server_url}/send_message", json={"content": message, "sender": self.sender_name})
self.last_msg = response.json().get("id")
self.display_message(self.sender_name, message)
self.message_entry.delete(0, tk.END)
def display_message(self, sender, message):
message_frame = tk.Frame(self.history_container, bg="lightgray", pady=5)
message_frame.pack(fill="x", padx=5, pady=5)
message_label = tk.Label(message_frame, text=f"{sender}: {message}", anchor="w", justify="left", wraplength=300)
message_label.pack(fill="x")
self.history_canvas.update_idletasks()
self.history_canvas.yview_moveto(1)
def get_messages(self):
response = requests.post(f"{self.chat_server_url}/get_messages", json={"last_id": self.last_msg})
messages = response.json().get("messages")
for message in messages:
self.display_message(message["sender"], message["content"])
self.last_msg = messages[-1]["id"] if messages else self.last_msg
def connected(self):
self.conn = True
self.status_label.config(text="Connected to chat", fg="green")
self.send_button.config(state="normal")
def disconnected(self):
self.conn = False
self.status_label.config(text="Disconnected from Chat", fg="red")
self.send_button.config(state="disabled")
def change_sender_name(self, new_name: str):
self.sender_name = new_name
def change_server_url(self, new_url: str):
self.chat_server_url = new_url

View File

@ -1,14 +0,0 @@
import time
from datetime import datetime
from .abc import ThreadedLabel
class ClockLabel(ThreadedLabel):
def task(self):
now = datetime.now()
day_of_week = now.strftime("%A")
time_str = now.strftime("%H:%M:%S")
date_str = now.strftime("%Y-%m-%d")
label_text = f"{day_of_week}, {date_str} - {time_str}"
self.config(text=label_text)

View File

@ -1,71 +0,0 @@
import os
import time
from tkinter import Tk, Frame, Entry, Button, Label, StringVar, messagebox
from yt_dlp import YoutubeDL
from urllib.parse import urlparse, parse_qs, urlunparse, urlencode
from app.widgets.abc import ThreadedTab
class MusicDownloadTab(ThreadedTab):
def __init__(self, root: Frame | Tk, **kwargs):
self.download_url = StringVar()
self.status = StringVar()
self.download_queue = []
super().__init__(root, **kwargs)
def build(self):
# Create the main frame for the download interface
self.download_frame = Frame(self)
self.download_frame.pack(fill="both", expand=True)
# Create the input field for the YouTube link
self.url_entry = Entry(self.download_frame, textvariable=self.download_url)
self.url_entry.pack(fill="x", padx=5, pady=5)
# Create the download button
self.download_button = Button(self.download_frame, text="Download", command=self.queue_download)
self.download_button.pack(padx=5, pady=5)
# Create the status label
self.status_label = Label(self.download_frame, textvariable=self.status)
self.status_label.pack(fill="x", padx=5, pady=5)
def queue_download(self):
url = self.download_url.get()
if url:
parsed_url = urlparse(url)
query_params = parse_qs(parsed_url.query)
if 'list' in query_params:
response = messagebox.askyesno("Download Playlist", "Do you want to download the whole playlist?")
if not response:
query_params.pop('list', None)
query_params.pop('index', None)
new_query = urlencode(query_params, doseq=True)
url = urlunparse(parsed_url._replace(query=new_query))
self.download_queue.append(url)
self.status.set("Queued for download")
def task(self):
if self.download_queue:
url = self.download_queue.pop(0)
self.download_music(url)
time.sleep(1)
def download_music(self, url):
try:
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': os.path.join("music", '%(title)s.%(ext)s'),
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
}
with YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
self.status.set("Downloaded successfully")
except Exception as e:
print(e)
self.status.set(f"Error: {str(e)}")
time.sleep(1)

View File

@ -1,82 +0,0 @@
import os
import time
import threading
import pygame
from tkinter import Tk, Frame, Button, Label, StringVar
from app.widgets.abc import ThreadedTab
class MusicPlayerTab(ThreadedTab):
def __init__(self, root: Frame | Tk, **kwargs):
self.music_dir = "music"
self.music_files = []
self.current_index = 0
self.is_playing = False
self.current_song = StringVar()
super().__init__(root, **kwargs)
pygame.mixer.init()
def build(self):
# Create the main frame for the music player interface
self.player_frame = Frame(self)
self.player_frame.pack(fill="both", expand=True)
# Create the label to display the current song
self.song_label = Label(self.player_frame, textvariable=self.current_song, anchor="center", wraplength=200)
self.song_label.pack(padx=5, pady=5)
# Create the control buttons frame
self.controls_frame = Frame(self.player_frame)
self.controls_frame.pack(expand=True)
# Create the control buttons
self.prev_button = Button(self.controls_frame, text="<", command=self.previous_song)
self.prev_button.pack(side="left", padx=5)
self.play_pause_button = Button(self.controls_frame, text="|| / >", command=self.play_pause_music)
self.play_pause_button.pack(side="left", padx=5)
self.next_button = Button(self.controls_frame, text=">", command=self.next_song)
self.next_button.pack(side="left", padx=5)
self.load_music()
def load_music(self):
if not os.path.exists(self.music_dir):
os.makedirs(self.music_dir)
self.music_files = [f for f in os.listdir(self.music_dir) if f.endswith('.mp3')]
if self.music_files:
self.current_song.set(self.music_files[self.current_index])
def play_pause_music(self):
if self.is_playing:
pygame.mixer.music.pause()
self.is_playing = False
else:
if pygame.mixer.music.get_busy():
pygame.mixer.music.unpause()
else:
pygame.mixer.music.load(os.path.join(self.music_dir, self.music_files[self.current_index]))
pygame.mixer.music.play()
self.is_playing = True
def next_song(self):
self.current_index = (self.current_index + 1) % len(self.music_files)
self.current_song.set(self.music_files[self.current_index])
pygame.mixer.music.load(os.path.join(self.music_dir, self.music_files[self.current_index]))
pygame.mixer.music.play()
self.is_playing = True
def previous_song(self):
self.current_index = (self.current_index - 1) % len(self.music_files)
self.current_song.set(self.music_files[self.current_index])
pygame.mixer.music.load(os.path.join(self.music_dir, self.music_files[self.current_index]))
pygame.mixer.music.play()
self.is_playing = True
def task(self):
self.load_music()

View File

@ -1,83 +0,0 @@
import tkinter as tk
from tkinter import Frame, Button, Label, StringVar, messagebox
from app.widgets.abc import ThreadedTab
class TicTacToeTab(ThreadedTab):
def __init__(self, root: Frame | tk.Tk, **kwargs):
self.current_player = StringVar(value="X") # Start with player X
self.board = [[None for _ in range(3)] for _ in range(3)] # 3x3 board
super().__init__(root, **kwargs)
def build(self):
# Create the main frame for the Tic Tac Toe interface
self.game_frame = Frame(self)
self.game_frame.pack(fill="both", expand=True)
# Create the label to display the current player
self.player_label = Label(self.game_frame, textvariable=self.current_player, font=("Arial", 16))
self.player_label.pack(padx=5, pady=5)
# Create the grid for the Tic Tac Toe board
self.board_frame = Frame(self.game_frame)
self.board_frame.pack(expand=True)
for row in range(3):
for col in range(3):
button = Button(
self.board_frame,
text="",
font=("Arial", 24),
width=5,
height=2,
command=lambda r=row, c=col: self.make_move(r, c)
)
button.grid(row=row, column=col, padx=5, pady=5)
self.board[row][col] = button
def make_move(self, row, col):
button = self.board[row][col]
# Check if the cell is already occupied
if button["text"] == "":
button["text"] = self.current_player.get()
if self.check_winner():
messagebox.showinfo("Game Over", f"Player {self.current_player.get()} wins!")
self.reset_board()
elif self.is_draw():
messagebox.showinfo("Game Over", "It's a draw!")
self.reset_board()
else:
self.switch_player()
else:
messagebox.showwarning("Invalid Move", "This cell is already taken!")
def switch_player(self):
self.current_player.set("O" if self.current_player.get() == "X" else "X")
def check_winner(self):
# Check rows, columns, and diagonals
for i in range(3):
if self.board[i][0]["text"] == self.board[i][1]["text"] == self.board[i][2]["text"] != "":
return True
if self.board[0][i]["text"] == self.board[1][i]["text"] == self.board[2][i]["text"] != "":
return True
if self.board[0][0]["text"] == self.board[1][1]["text"] == self.board[2][2]["text"] != "":
return True
if self.board[0][2]["text"] == self.board[1][1]["text"] == self.board[2][0]["text"] != "":
return True
return False
def is_draw(self):
# Check if all cells are filled
return all(self.board[row][col]["text"] != "" for row in range(3) for col in range(3))
def reset_board(self):
# Clear the board
for row in range(3):
for col in range(3):
self.board[row][col]["text"] = ""
self.current_player.set("X")
def task(self):
# Placeholder for threaded behavior if needed
pass

View File

@ -1,59 +0,0 @@
import os
import time
from tkinter import Tk, Frame, Entry, Button, Label, Listbox, StringVar, messagebox
from app.widgets.abc import ThreadedTab
class TodoListTab(ThreadedTab):
def __init__(self, root: Frame | Tk, **kwargs):
self.task = StringVar()
self.tasks = []
super().__init__(root, **kwargs)
def build(self):
# Create the main frame for the TODO list interface
self.todo_frame = Frame(self)
self.todo_frame.pack(fill="both", expand=True)
# Entry field for adding a task
self.task_entry = Entry(self.todo_frame, textvariable=self.task)
self.task_entry.pack(fill="x", padx=5, pady=5)
# Button to add a task
self.add_task_button = Button(self.todo_frame, text="Add Task", command=self.add_task)
self.add_task_button.pack(padx=5, pady=5)
# Listbox to display tasks
self.task_listbox = Listbox(self.todo_frame)
self.task_listbox.pack(fill="both", expand=True, padx=5, pady=5)
# Button to delete a selected task
self.delete_task_button = Button(self.todo_frame, text="Delete Selected", command=self.delete_task)
self.delete_task_button.pack(padx=5, pady=5)
def add_task(self):
task_text = self.task.get()
if task_text:
self.tasks.append(task_text)
self.update_task_listbox()
self.task.set("") # Clear the entry field
else:
messagebox.showwarning("Warning", "Task cannot be empty!")
def delete_task(self):
selected_indices = self.task_listbox.curselection()
if selected_indices:
for index in selected_indices[::-1]: # Reverse to avoid index shifting issues
del self.tasks[index]
self.update_task_listbox()
else:
messagebox.showwarning("Warning", "No task selected to delete!")
def update_task_listbox(self):
self.task_listbox.delete(0, "end")
for task in self.tasks:
self.task_listbox.insert("end", task)
def task(self):
# Placeholder for threaded behavior if needed in the future
time.sleep(1)

View File

@ -1,62 +0,0 @@
import os
from tkinter import Frame, Entry, Button, Listbox, END, StringVar, Tk
from app.widgets.abc import ThreadedTab
class TodoTab(ThreadedTab):
def __init__(self, root: Frame | Tk, **kwargs):
self.todo_file = 'todo.list'
self.todo_items = []
self.load_todo_list()
super().__init__(root, **kwargs)
def build(self):
self.todo_frame = Frame(self)
self.todo_frame.pack(fill="both", expand=True)
self.todo_listbox = Listbox(self.todo_frame)
self.todo_listbox.pack(fill="both", expand=True, padx=5, pady=5)
self.todo_listbox.bind("<Double-Button-1>", self.remove_todo)
self.new_todo_var = StringVar()
self.new_todo_entry = Entry(self.todo_frame, textvariable=self.new_todo_var)
self.new_todo_entry.pack(fill="x", padx=5, pady=5)
self.new_todo_entry.bind("<Return>", self.add_todo)
self.add_button = Button(self.todo_frame, text="Add", command=self.add_todo)
self.add_button.pack(padx=5, pady=5)
self.update_listbox()
def load_todo_list(self):
if os.path.exists(self.todo_file):
with open(self.todo_file, 'r') as file:
self.todo_items = [line.strip() for line in file.readlines()]
def save_todo_list(self):
with open(self.todo_file, 'w') as file:
for item in self.todo_items:
file.write(f"{item}\n")
def add_todo(self, event=None):
new_todo = self.new_todo_var.get().strip()
if new_todo:
self.todo_items.append(new_todo)
self.new_todo_var.set("")
self.update_listbox()
self.save_todo_list()
def remove_todo(self, event=None):
selected_indices = self.todo_listbox.curselection()
for index in selected_indices[::-1]:
del self.todo_items[index]
self.update_listbox()
self.save_todo_list()
def update_listbox(self):
self.todo_listbox.delete(0, END)
for item in self.todo_items:
self.todo_listbox.insert(END, item)
def task(self, *args):
pass

View File

@ -1,50 +0,0 @@
import time
import psutil
from .abc import ThreadedLabel
class CPULabel(ThreadedLabel):
def task(self, *args):
cpu_percent = psutil.cpu_percent()
self.config(text=f'CPU: {cpu_percent}%')
class RAMLabel(ThreadedLabel):
def task(self, *args):
memory = psutil.virtual_memory()
self.config(text=f'RAM: {memory.percent}%')
class BatteryLabel(ThreadedLabel):
def task(self, *args):
battery = psutil.sensors_battery()
if battery is None:
self.config(text='Battery: N/A')
return
battery_percent = battery.percent
is_charging = battery.power_plugged
time_left = battery.secsleft
text = f'Battery: {battery_percent:.0f}%'
if is_charging:
text += ', Plugged in'
else:
text += f', ({time_left // 3600}h {time_left % 3600 // 60}m left)'
try:
self.config(text=text)
except RuntimeError:
pass # Catch update on closed widget
time.sleep(1)
class NetworkLabel(ThreadedLabel):
def task(self, *args):
network = psutil.net_io_counters()
self.config(text=f'Net: {network.bytes_sent / 1024 / 1024:.2f} MB snt,'
f' {network.bytes_recv / 1024 / 1024:.2f} MB rcv')

View File

@ -1,125 +0,0 @@
import threading
import requests
from bs4 import BeautifulSoup
from tkinter import Frame, Label, PhotoImage, Tk
from app.widgets.abc import ThreadedTab
import cairosvg
from io import BytesIO
class WeatherTab(ThreadedTab):
def __init__(self, root: Frame | Tk, city: str, **kwargs):
self.city = city
self.weather_info = {}
self.weather_image = None
self.weather_frame = None
self.weather_label = None
self.weather_image_label = None
self.city_label = None
self.real_feel_label = None
self.wind_label = None
self.wind_gusts_label = None
self.air_quality_label = None
super().__init__(root, **kwargs)
def build(self):
self.weather_frame = Frame(self)
self.weather_frame.pack(fill="both", expand=True)
self.city_label = Label(self.weather_frame, text=f"Weather in {self.city}", font=("Helvetica", 16))
self.city_label.grid(row=0, column=0, columnspan=2, pady=10, sticky="ew")
self.weather_image_label = Label(self.weather_frame)
self.weather_image_label.grid(row=1, column=0, padx=10, sticky="ew")
self.weather_label = Label(self.weather_frame, text="", font=("Helvetica", 14))
self.weather_label.grid(row=1, column=1, padx=10, sticky="ew")
self.real_feel_label = Label(self.weather_frame, text="", font=("Helvetica", 12))
self.real_feel_label.grid(row=2, column=1, padx=10, sticky="ew")
self.wind_label = Label(self.weather_frame, text="", font=("Helvetica", 12))
self.wind_label.grid(row=3, column=1, padx=10, sticky="ew")
self.wind_gusts_label = Label(self.weather_frame, text="", font=("Helvetica", 12))
self.wind_gusts_label.grid(row=4, column=1, padx=10, sticky="ew")
self.air_quality_label = Label(self.weather_frame, text="", font=("Helvetica", 12))
self.air_quality_label.grid(row=5, column=1, padx=10, sticky="ew")
self.weather_frame.columnconfigure(0, weight=1)
self.weather_frame.columnconfigure(1, weight=1)
# Ensure the frame fills the entire parent space
self.grid(row=0, column=0, sticky="nsew")
self.master.rowconfigure(0, weight=1)
self.master.columnconfigure(0, weight=1)
def task(self, *args):
self.fetch_weather_data()
self.update_ui()
def fetch_weather_data(self):
try:
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
}
search_url = f"https://www.accuweather.com/en/search-locations?query={self.city}"
search_response = requests.get(search_url, headers=headers)
search_soup = BeautifulSoup(search_response.text, 'html.parser')
location_list = search_soup.find('div', class_='locations-list content-module')
if not location_list:
print("Location list not found")
return
location_link = location_list.find('a')['href']
weather_url = f"https://www.accuweather.com{location_link}"
weather_response = requests.get(weather_url, headers=headers)
weather_soup = BeautifulSoup(weather_response.text, 'html.parser')
weather_icon_path = weather_soup.find('svg', class_='weather-icon')['data-src']
weather_icon_url = f"https://www.accuweather.com{weather_icon_path}"
weather_icon_response = requests.get(weather_icon_url, headers=headers)
weather_icon_svg = weather_icon_response.content
# Convert SVG to PNG and resize to 50x50
weather_icon_png = cairosvg.svg2png(bytestring=weather_icon_svg, output_width=50, output_height=50)
weather_icon_image = PhotoImage(data=BytesIO(weather_icon_png).getvalue())
temperature = weather_soup.find('div', class_='temp').text
real_feel = weather_soup.find('div', class_='real-feel').text.strip().replace('RealFeel®', '').strip()
details_container = weather_soup.find('div', class_='details-container')
wind = details_container.find('span', text='Wind').find_next('span', class_='value').text.strip() if details_container else 'N/A'
wind_gusts = details_container.find('span', text='Wind Gusts').find_next('span', class_='value').text.strip() if details_container else 'N/A'
air_quality = details_container.find('span', text='Air Quality').find_next('span', class_='value').text.strip() if details_container else 'N/A'
self.weather_info = {
'icon': weather_icon_image,
'temperature': temperature,
'real_feel': real_feel,
'wind': wind,
'wind_gusts': wind_gusts,
'air_quality': air_quality
}
except Exception as e:
print(f"Error fetching weather data: {e}")
def update_ui(self):
if self.weather_info:
weather_text = f"{self.weather_info['temperature']}"
self.weather_label.config(text=weather_text)
self.weather_image_label.config(image=self.weather_info['icon'])
self.weather_image_label.image = self.weather_info['icon']
self.real_feel_label.config(text=f"RealFeel: {self.weather_info['real_feel']}")
self.wind_label.config(text=f"Wind: {self.weather_info['wind']}")
self.wind_gusts_label.config(text=f"Wind Gusts: {self.weather_info['wind_gusts']}")
self.air_quality_label.config(text=f"Air Quality: {self.weather_info['air_quality']}")
def changeCity(self, city):
self.city = city
self.city_label.config(text=f"Weather in {self.city}")
threading.Thread(target=self.task).start()

View File

@ -1,123 +0,0 @@
import tkinter as tk
from tkinter import Frame, Button, Label, Entry, Listbox, StringVar, messagebox
import mysql.connector
import requests
from bs4 import BeautifulSoup
from app.widgets.abc import ThreadedTab
class WebScrapingTab(ThreadedTab):
def __init__(self, root: Frame | tk.Tk, stop_event, **kwargs):
# Inicializa los atributos necesarios antes de llamar a la clase base
self.url = StringVar()
self.data = []
self.conn = None # La conexión se inicializa después
super().__init__(root, stop_event, **kwargs) # Llama al constructor de ThreadedTab
self.conn = self.create_database() # Crea o conecta a la base de datos
def build(self):
# Main frame
self.scraping_frame = Frame(self)
self.scraping_frame.pack(fill="both", expand=True)
# Input field for URL
Label(self.scraping_frame, text="Enter URL:", font=("Arial", 12)).pack(pady=5)
Entry(self.scraping_frame, textvariable=self.url, font=("Arial", 12), width=50).pack(pady=5)
# Buttons for actions
Button(self.scraping_frame, text="Scrape", command=self.scrape_website).pack(pady=5)
Button(self.scraping_frame, text="View Data", command=self.view_data).pack(pady=5)
# Listbox to display scraped data
self.data_listbox = Listbox(self.scraping_frame, font=("Arial", 10), width=80, height=20)
self.data_listbox.pack(pady=10)
def create_database(self):
try:
# Conectar sin especificar la base de datos
conn = mysql.connector.connect(
host="127.0.0.1",
user="santipy",
password="1234"
)
cursor = conn.cursor()
# Crear la base de datos si no existe
cursor.execute("CREATE DATABASE IF NOT EXISTS scraping_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
conn.commit()
# Conectar a la base de datos
conn = mysql.connector.connect(
host="127.0.0.1",
user="santipy",
password="1234",
database="scraping_db"
)
# Crear la tabla si no existe
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS scraped_data (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
link TEXT
)
""")
conn.commit()
return conn
except mysql.connector.Error as err:
print(f"Error al conectar o crear la base de datos: {err}")
messagebox.showerror("Database Error", f"Error al conectar o crear la base de datos: {err}")
return None
def save_to_database(self):
cursor = self.conn.cursor()
query = "INSERT INTO scraped_data (title, link) VALUES (%s, %s)"
cursor.executemany(query, self.data)
self.conn.commit()
def scrape_website(self):
url = self.url.get()
if not url:
messagebox.showwarning("Warning", "Please enter a URL.")
return
try:
response = requests.get(url)
response.raise_for_status()
except requests.RequestException as e:
messagebox.showerror("Error", f"Failed to fetch URL: {e}")
return
soup = BeautifulSoup(response.text, "html.parser")
items = soup.select("h2 a") # Modify selector based on website structure
self.data = [(item.get_text(strip=True), item.get("href")) for item in items]
if self.data:
self.save_to_database()
messagebox.showinfo("Success", f"Scraped {len(self.data)} items and saved to database.")
else:
messagebox.showinfo("No Data", "No data found on the page.")
self.update_listbox()
def update_listbox(self):
self.data_listbox.delete(0, "end")
for title, link in self.data:
self.data_listbox.insert("end", f"Title: {title} | Link: {link}")
def view_data(self):
cursor = self.conn.cursor()
cursor.execute("SELECT title, link FROM scraped_data")
rows = cursor.fetchall()
self.data_listbox.delete(0, "end")
for title, link in rows:
self.data_listbox.insert("end", f"Title: {title} | Link: {link}")
def task(self):
# Placeholder for any background task
pass

View File

@ -1,5 +0,0 @@
from .ClockLabel import ClockLabel
from .UsageLabels import CPULabel, RAMLabel
from .WebScrapingTab import WebScrapingTab
__all__ = ['ClockLabel', 'CPULabel', 'RAMLabel', 'WebScrapingTab']

View File

@ -1,28 +0,0 @@
import threading
import time
from abc import ABC, abstractmethod
from threading import Thread
from tkinter import Label
class ThreadedLabel(ABC, Label):
def __init__(self, root: Label, stop_event: threading.Event, refresh_rate = None, **kwargs):
super().__init__(root, **kwargs)
self.stop_event = stop_event
self.refresh_rate = refresh_rate
self.declared_thread: Thread = threading.Thread(target=self.__loop)
self.declared_thread.start()
def __loop(self):
while not self.stop_event.is_set():
self.task()
start = time.time()
while time.time() - start < self.refresh_rate:
if self.stop_event.is_set():
break
time.sleep(0.2)
@abstractmethod
def task(self, *args):
pass

View File

@ -1,33 +0,0 @@
import threading
import time
from abc import ABC, abstractmethod
from tkinter import Tk
from tkinter.ttk import Notebook
from tkinter import Frame
class ThreadedTab(ABC, Frame):
def __init__(self, root: Notebook | Tk, stop_event: threading.Event, refresh_rate=1, **kwargs):
super().__init__(root, **kwargs)
self.stop_event = stop_event
self.refresh_rate = refresh_rate
self._thread = threading.Thread(target=self.__loop)
self.build()
self._thread.start()
def __loop(self):
while not self.stop_event.is_set():
self.task()
start = time.time()
while time.time() - start < self.refresh_rate:
if self.stop_event.is_set():
break
time.sleep(0.2)
@abstractmethod
def build(self):
pass
@abstractmethod
def task(self, *args):
raise NotImplementedError("Method not implemented")

View File

@ -1,4 +0,0 @@
from .ThreadedLabel import ThreadedLabel
from .ThreadedTab import ThreadedTab
__all__ = ['ThreadedLabel', 'ThreadedTab']

View File

@ -0,0 +1,95 @@
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}")

73
hilos/ChatWidget.py Normal file
View File

@ -0,0 +1,73 @@
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("<Return>", 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):
while True:
try:
message = self.client_socket.recv(1024).decode("utf-8")
if message:
self.display_message(message)
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

57
hilos/LanguageChart.py Normal file
View File

@ -0,0 +1,57 @@
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

77
hilos/MusicPlayer.py Normal file
View File

@ -0,0 +1,77 @@
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()

69
hilos/SystemMonitor.py Normal file
View File

@ -0,0 +1,69 @@
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)

129
hilos/WeatherWidget.py Normal file
View File

@ -0,0 +1,129 @@
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()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,69 @@
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)

View File

@ -0,0 +1,68 @@
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

113
solapas/SQLQueryExecutor.py Normal file
View File

@ -0,0 +1,113 @@
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))

123
solapas/TicTacToe.py Normal file
View File

@ -0,0 +1,123 @@
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

195
solapas/WebScraperToDB.py Normal file
View File

@ -0,0 +1,195 @@
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 <a>
# 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))

0
solapas/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.