Interfaz¶
import pygame
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import socket
class Aplicacion:
def __init__(self, root):
self.key_pressed = False
self.tecla_pressed = None
self.botones_presionados = set()
self.lista_botones = []
self.contenedor = {}
self.labels = {}
self.imagenes_botones = {
"up": tk.PhotoImage(file="resources2/up.png"),
"left": tk.PhotoImage(file="resources2/left.png"),
"down": tk.PhotoImage(file="resources2/down.png"),
"right": tk.PhotoImage(file="resources2/right.png"),
"up_claw": tk.PhotoImage(file="resources2/up_claw.png"),
"down_claw": tk.PhotoImage(file="resources2/down_claw.png"),
"center1": tk.PhotoImage(file="resources2/center1.png"),
"center2": tk.PhotoImage(file="resources2/center2.png"),
"off": tk.PhotoImage(file="resources2/off1.png")
}
self.botones_config = [
(self.imagenes_botones["up"], 275, 250, self.moveUp),
(self.imagenes_botones["left"], 225, 300, self.moveLeft),
(self.imagenes_botones["down"], 275, 300, self.moveDown),
(self.imagenes_botones["right"], 325, 300, self.moveRight),
(self.imagenes_botones["up_claw"], 475, 250, self.upCraw),
(self.imagenes_botones["down_claw"], 475, 300, self.downCraw),
(self.imagenes_botones["center1"], 555, 275, self.grab),
(self.imagenes_botones["center2"], 625, 275, self.drop)
]
self.teclas_teclado = {
"w": {"coords": (250, 225, 300, 275), "label_pos": (275, 250), "etiqueta": "W", "funcion": self.moveUp},
"a": {"coords": (200, 275, 250, 325), "label_pos": (225, 300), "etiqueta": "A", "funcion": self.moveLeft},
"s": {"coords": (250, 275, 300, 325), "label_pos": (275, 300), "etiqueta": "S", "funcion": self.moveDown},
"d": {"coords": (300, 275, 350, 325), "label_pos": (325, 300), "etiqueta": "D", "funcion": self.moveRight},
"l": {"coords": (450, 225, 500, 275), "label_pos": (475, 250), "etiqueta": "L", "funcion": self.upCraw},
"k": {"coords": (450, 275, 500, 325), "label_pos": (475, 300), "etiqueta": "K", "funcion": self.downCraw},
"x": {"coords": (530, 250, 580, 300), "label_pos": (555, 275), "etiqueta": "X", "funcion": self.grab},
"c": {"coords": (600, 250, 650, 300), "label_pos": (625, 275), "etiqueta": "C", "funcion": self.drop}
}
self.teclas_joystick = {
(0, 1): {"coords": (175, 200, 225, 250), "label_pos": (200, 225), "etiqueta": "w"},
(0, -1): {"coords": (175, 300, 225, 350), "label_pos": (200, 325), "etiqueta": "s"},
(-1, 0): {"coords": (125, 250, 175, 300), "label_pos": (150, 275), "etiqueta": "a"},
(1, 0): {"coords": (225, 250, 275, 300), "label_pos": (250, 275), "etiqueta": "d"},
8: {"coords": (450, 200, 500, 250), "label_pos": (475, 225), "etiqueta": "L2"},
9: {"coords": (450, 250, 500, 300), "label_pos": (475, 275), "etiqueta": "R2"},
0: {"coords": (530, 225, 580, 275), "label_pos": (555, 250), "etiqueta": "A"},
1: {"coords": (600, 225, 650, 275), "label_pos": (625, 250), "etiqueta": "B"},
"C": {"coords": (300, 225, 400, 325), "label_pos": (625, 250), "etiqueta": ""},
"c": {"coords": (340, 265, 360, 285), "label_pos": (625, 250), "etiqueta": ""},
}
self.root = root
self.root.title("Blitz")
self.root.geometry("800x450")
self.root.resizable(0, 0)
self.root.iconbitmap("resources2/logoBlitz.png")
self._configurar_estilos()
self.imagen_fondo = tk.PhotoImage(file="resources2/fondoBlitz.png")
self.canvas = tk.Canvas(self.root, width=800, height=400)
self.canvas.pack(fill="both", expand=True)
self.canvas.create_image(0, 0, image=self.imagen_fondo, anchor="nw")
self.crear_labels()
self.combobox_controles = ttk.Combobox(self.root, values=["Botones", "Teclado", "Joystick"],
state="disabled", font=("Comic Sans MS", 10), width=7,
style="TCombobox")
self.combobox_controles.bind("<<ComboboxSelected>>", self.on_select)
self.canvas.create_window(180, 135, window=self.combobox_controles)
self.combobox_controles.current(0)
self.x0, self.y0, self.x1, self.y1 = 375, 327, 425, 377
self.circle = self.canvas.create_oval(self.x0, self.y0, self.x1, self.y1, fill="blue", outline="black")
self.imagen_conexion = tk.PhotoImage(file="resources2/off1.png")
self.imagen_desconexion = tk.PhotoImage(file="resources2/off2.png")
self.current_image = self.imagen_desconexion
self.canvas_image = self.canvas.create_image((self.x0 + self.x1) / 2, (self.y0 + self.y1) / 2, image=self.current_image)
self.canvas.image = self.current_image
self.current_funcion = self.ventana_conexion_servidor
self.canvas.bind("<Button-1>", self.check_click)
def check_click(self, event):
x, y = event.x, event.y
if self.x0 <= x <= self.x1 and self.y0 <= y <= self.y1:
self.current_funcion()
def crear_labels(self):
self.label1 = tk.Label(self.root, text="Robot", font=("Comic Sans MS", 16, "bold"))
self.canvas.create_window(275, 170, window=self.label1)
self.label2 = tk.Label(self.root, text="Garra", font=("Comic Sans MS", 16, "bold"))
self.canvas.create_window(555, 170, window=self.label2)
self.label_agarrar = tk.Label(self.root, text="Agarrar", font=("Comic Sans MS", 9, "bold"))
self.canvas.create_window(555, 325, window=self.label_agarrar)
self.label_soltar = tk.Label(self.root, text="Soltar", font=("Comic Sans MS", 9, "bold"))
self.canvas.create_window(625, 325, window=self.label_soltar)
def borrar_labels(self):
self.label1.destroy()
self.label2.destroy()
self.label_agarrar.destroy()
self.label_soltar.destroy()
def _configurar_estilos(self):
estilos = {
'*TCombobox*Listbox.font': ("Comic Sans MS", 10),
'*TCombobox*Listbox.background': "#0F2B6A",
'*TCombobox*Listbox.foreground': "#ffffff",
'*TCombobox*Listbox.selectBackground': '#08FBF9'
}
for k, v in estilos.items():
self.root.option_add(k, v)
def crear_botones(self):
for imagen, x, y, funcion in self.botones_config[:4]:
boton = tk.Button(self.root, image=imagen, borderwidth=0, highlightthickness=0, cursor="hand2", bg="#0F2B6A")
boton.bind("<ButtonPress>", lambda event, f=funcion: f())
boton.bind("<ButtonRelease>", lambda event: self.stop())
self.canvas.create_window(x, y, window=boton)
self.lista_botones.append(boton)
for imagen, x, y, funcion in self.botones_config[4:]:
boton = tk.Button(self.root, image=imagen, borderwidth=0, highlightthickness=0, cursor="hand2", bg="#0F2B6A")
boton.bind("<ButtonPress>", lambda event, f=funcion: f())
self.canvas.create_window(x, y, window=boton)
self.lista_botones.append(boton)
def eliminar_botones(self):
for boton in self.lista_botones:
boton.destroy()
self.lista_botones.clear()
def crear_teclas_teclado(self):
for tecla, datos in self.teclas_teclado.items():
coords, label_pos, etiqueta = datos["coords"], datos["label_pos"], datos["etiqueta"]
self.contenedor[tecla] = self.canvas.create_rectangle(*coords, fill="#0F2B6A", outline="black")
label = self.canvas.create_text(*label_pos, text=etiqueta, font=("Comic Sans MS", 20), fill="white")
self.labels[tecla] = label
def crear_teclas_joystick(self):
for tecla, datos in self.teclas_joystick.items():
coords, label_pos, etiqueta = datos["coords"], datos["label_pos"], datos["etiqueta"]
self.contenedor[tecla] = self.canvas.create_oval(*coords, fill="#0F2B6A", outline="black")
label = self.canvas.create_text(*label_pos, text=etiqueta, font=("Comic Sans MS", 20), fill="white")
self.labels[tecla] = label
self.canvas.itemconfig(self.contenedor["C"], fill="")
self.canvas.itemconfig(self.contenedor["c"], fill="white")
def eliminar_teclas_y_botones(self):
if self.contenedor:
for tecla in self.contenedor:
self.canvas.delete(self.contenedor[tecla])
self.contenedor.clear()
for tecla in self.labels:
self.canvas.delete(self.labels[tecla])
for boton in self.lista_botones:
boton.destroy()
self.labels.clear()
self.lista_botones.clear()
def activar_teclas_teclado(self):
for tecla in self.teclas_teclado:
self.root.bind(f"<KeyPress-{tecla}>", self.crear_callback(tecla, self.pressed))
self.root.bind(f"<KeyRelease-{tecla}>", self.crear_callback(tecla, self.released))
self.root.bind(f"<KeyPress-{tecla.upper()}>", self.crear_callback(tecla, self.pressed))
self.root.bind(f"<KeyRelease-{tecla.upper()}>", self.crear_callback(tecla, self.released))
def desactivar_teclas_teclado(self):
for tecla in self.teclas_teclado:
self.root.unbind(f"<KeyPress-{tecla}>")
self.root.unbind(f"<KeyRelease-{tecla}>")
self.root.unbind(f"<KeyPress-{tecla.upper()}>")
self.root.unbind(f"<KeyRelease-{tecla.upper()}>")
def crear_callback(self, tecla, funcion):
return lambda event: funcion(tecla)
def pressed(self, tecla):
if not self.key_pressed and self.tecla_pressed is None:
self.canvas.itemconfig(self.contenedor[tecla], fill="#08FBF9")
self.tecla_pressed = tecla
self.key_pressed = True
self.teclas_teclado[tecla]["funcion"]()
def released(self, tecla):
teclas_sin_stop = ["L", "K", "X", "C"]
if tecla.upper() not in teclas_sin_stop:
self.stop()
if tecla == self.tecla_pressed:
self.canvas.itemconfig(self.contenedor[tecla], fill="#0F2B6A")
self.tecla_pressed = None
self.key_pressed = False
def on_select(self, event):
seleccion = self.combobox_controles.get()
self.eliminar_teclas_y_botones()
if seleccion == "Botones":
self.crear_botones()
elif seleccion == "Teclado":
self.crear_teclas_teclado()
self.activar_teclas_teclado()
else:
self.crear_teclas_joystick()
self.inicializar_pygame()
self.root.focus()
self.tecla_pressed = None
self.key_pressed = False
def validar_ip(self, ip):
try:
socket.inet_aton(ip)
return True
except socket.error:
return False
def validar_puerto(self, port):
try:
port = int(port)
return 1 <= port <= 65535
except ValueError:
return False
def conectar_servidor(self, ip_entry, port_entry, scene):
ip = ip_entry.get()
port = port_entry.get()
if not self.validar_ip(ip):
messagebox.showerror("Error", "Dirección IP no válida.")
return
if not self.validar_puerto(port):
messagebox.showerror("Error", "Puerto no válido. Debe estar entre 1 y 65535.")
return
port = int(port)
try:
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client_socket.connect((ip, port))
self.combobox_controles.config(state="reondly")
self.combobox_controles.set("Botones")
self.crear_botones()
scene.destroy()
self.current_funcion = self.ventana_desconectar_servidor
self.current_image = self.imagen_conexion
self.canvas_image = self.canvas.create_image((self.x0 + self.x1) / 2, (self.y0 + self.y1) / 2, image=self.current_image)
self.canvas.image = self.current_image
messagebox.showinfo("Éxito", "Conexión exitosa al servidor.")
except socket.error as e:
messagebox.showerror("Error", f"Error al conectar al servidor")
except Exception as e:
messagebox.showerror("Error", f"Ha ocurrido un error desconocido")
def ventana_conexion_servidor(self):
self.secondary_window = tk.Toplevel(self.root)
self.secondary_window.title("Ingresar IP y Puerto")
self.secondary_window.geometry("400x225")
self.secondary_window.config(bg="#0F2B6A")
self.secondary_window.resizable(0, 0)
self.secondary_window.iconbitmap("resources2/logoBlitz.png")
tk.Label(self.secondary_window, text="IP del Servidor:", font=("Helvetica ", 9, "bold")).pack(pady=(20,5))
self.ip_entry = tk.Entry(self.secondary_window)
self.ip_entry.pack(pady=5)
tk.Label(self.secondary_window, text="Puerto del Servidor:", font=("Helvetica ", 9, "bold")).pack(pady=5)
self.port_entry = tk.Entry(self.secondary_window)
self.port_entry.pack(pady=5)
confirm_button = tk.Button(self.secondary_window, text="Conectar", bg="#22c8c9", command=lambda: self.conectar_servidor(self.ip_entry, self.port_entry, self.secondary_window))
confirm_button.pack(pady=5)
ip_save_button = tk.Button(self.secondary_window, text="Ingresar IP guardada", bg="#22c8c9", command=lambda: self.ingresar_ip_guardada(self.ip_entry, self.port_entry))
ip_save_button.pack()
def ingresar_ip_guardada(self, ip_entry, port_entry):
ip_entry.delete(0, tk.END)
ip_entry.insert(0, "192.168.144.84")
port_entry.delete(0, tk.END)
port_entry.insert(0, "8080")
def ventana_desconectar_servidor(self):
if messagebox.askyesno("Confirmación", "¿Está seguro que quiere desconectar del servidor?"):
self.client_socket.close()
self.current_funcion = self.ventana_conexion_servidor
self.current_image = self.imagen_desconexion
self.canvas_image = self.canvas.create_image((self.x0 + self.x1) / 2, (self.y0 + self.y1) / 2, image=self.current_image)
self.canvas.image = self.current_image
self.eliminar_teclas_y_botones()
def moveUp(self):
self.client_socket.send(bytes([ord('w')]))
def moveDown(self):
self.client_socket.send(bytes([ord('s')]))
def moveRight(self):
self.client_socket.send(bytes([ord('d')]))
def moveLeft(self):
self.client_socket.send(bytes([ord('a')]))
def upCraw(self):
self.client_socket.send(bytes([ord('l')]))
def downCraw(self):
self.client_socket.send(bytes([ord('k')]))
def grab(self):
self.client_socket.send(bytes([ord('x')]))
def drop(self):
self.client_socket.send(bytes([ord('c')]))
def stop(self):
self.client_socket.send(bytes([ord(' ')]))
if __name__ == "__main__":
root = tk.Tk()
app = Aplicacion(root)
root.mainloop()