154 lines
5.7 KiB
Python
154 lines
5.7 KiB
Python
import tkinter as tk
|
|
from tkinter import ttk
|
|
import threading
|
|
from vista.chat.chat_base import ChatBase
|
|
from logica.red.cliente import conectar_servidor
|
|
from vista.config import *
|
|
|
|
PREFIJO_NOMBRE = "__NOMBRE__:"
|
|
|
|
|
|
class ChatClientePanel(ChatBase):
|
|
"""Vista de chat para el rol de cliente."""
|
|
|
|
def __init__(self, parent, root, ip, puerto, clave, on_auth_error=None, *args, **kwargs):
|
|
super().__init__(parent, root, *args, **kwargs)
|
|
self.ip = ip
|
|
self.puerto = puerto
|
|
self.clave = clave
|
|
self.nombre = None
|
|
self.on_auth_error = on_auth_error
|
|
self.crear_interfaz_chat(
|
|
self, titulo="Chat - Cliente",
|
|
boton_accion_texto="Desconectar",
|
|
boton_accion_callback=self.desconectar_y_volver
|
|
)
|
|
self.conectar()
|
|
|
|
def conectar(self):
|
|
print(f"[DEBUG CLI-GUI] Iniciando conexion a {self.ip}:{self.puerto} con clave={self.clave!r}")
|
|
self.agregar_mensaje_sistema(f"Conectando a {self.ip}:{self.puerto}...")
|
|
|
|
def hilo_conexion():
|
|
cliente, extra = conectar_servidor(self.ip, self.puerto, self.clave)
|
|
if cliente:
|
|
self.socket = cliente
|
|
print(f"[DEBUG CLI-GUI] Conexion exitosa, extra={extra!r}")
|
|
self.root.after(0, self.agregar_mensaje_sistema, f"Conectado a {self.ip}:{self.puerto}")
|
|
self.recibir_mensajes(extra)
|
|
else:
|
|
print(f"[DEBUG CLI-GUI] Conexion fallida")
|
|
if self.on_auth_error:
|
|
self.root.after(0, self.on_auth_error)
|
|
else:
|
|
self.root.after(0, self.agregar_mensaje_sistema, "Error: no se pudo conectar o clave incorrecta")
|
|
|
|
hilo = threading.Thread(target=hilo_conexion, daemon=True)
|
|
hilo.start()
|
|
|
|
def _procesar_mensaje(self, mensaje):
|
|
"""Procesa un mensaje recibido (nombre, chat o remoto)."""
|
|
print(f"[DEBUG CLI-GUI] Procesando mensaje: {mensaje!r}")
|
|
if mensaje.startswith(PREFIJO_NOMBRE):
|
|
self.nombre = mensaje[len(PREFIJO_NOMBRE):]
|
|
print(f"[DEBUG CLI-GUI] Nombre asignado: {self.nombre!r}")
|
|
self.root.after(0, self.agregar_mensaje_sistema, f"Tu nombre: {self.nombre}")
|
|
self.root.after(0, self._actualizar_titulo)
|
|
elif ": " in mensaje:
|
|
remitente, texto = mensaje.split(": ", 1)
|
|
print(f"[DEBUG CLI-GUI] Mensaje de chat: {remitente!r} -> {texto!r}")
|
|
self.root.after(0, self.agregar_mensaje, remitente, texto)
|
|
else:
|
|
print(f"[DEBUG CLI-GUI] Mensaje remoto sin formato: {mensaje!r}")
|
|
self.root.after(0, self.agregar_mensaje, "REMOTO", mensaje)
|
|
|
|
def recibir_mensajes(self, extra=""):
|
|
# Procesar datos que llegaron junto con el "OK" de autenticacion
|
|
if extra:
|
|
print(f"[DEBUG CLI-GUI] Procesando datos extra del handshake: {extra!r}")
|
|
self._procesar_mensaje(extra)
|
|
|
|
print("[DEBUG CLI-GUI] Bucle de recepcion iniciado")
|
|
try:
|
|
while self.socket:
|
|
datos = self.socket.recv(4096)
|
|
if not datos:
|
|
print("[DEBUG CLI-GUI] Datos vacios recibidos (servidor cerro)")
|
|
self.root.after(0, self._servidor_desconectado)
|
|
break
|
|
mensaje = datos.decode("utf-8")
|
|
print(f"[DEBUG CLI-GUI] Datos crudos recibidos: {datos!r}")
|
|
self._procesar_mensaje(mensaje)
|
|
except (ConnectionResetError, OSError) as e:
|
|
print(f"[DEBUG CLI-GUI] Error en recepcion: {e}")
|
|
self.root.after(0, self._servidor_desconectado)
|
|
|
|
def _actualizar_titulo(self):
|
|
if self.nombre:
|
|
self.info_label.config(text=f"Chat - {self.nombre}")
|
|
|
|
def enviar_mensaje(self, event=None):
|
|
mensaje = self.chat_input_entry.get().strip()
|
|
if not mensaje or not self.socket:
|
|
return "break" if event else None
|
|
|
|
self.chat_input_entry.delete(0, "end")
|
|
self.agregar_mensaje("TU", mensaje)
|
|
|
|
try:
|
|
print(f"[DEBUG CLI-GUI] Enviando mensaje: {mensaje!r}")
|
|
self.socket.sendall(mensaje.encode("utf-8"))
|
|
except OSError as e:
|
|
print(f"[DEBUG CLI-GUI] Error al enviar: {e}")
|
|
self.agregar_mensaje_sistema("Error al enviar mensaje")
|
|
|
|
return "break" if event else None
|
|
|
|
def _servidor_desconectado(self):
|
|
"""Bloquea la entrada y muestra popup cuando el servidor cierra la conexion."""
|
|
self.cerrar_conexion()
|
|
self.bloquear_entrada()
|
|
|
|
dialogo = tk.Toplevel(self.root)
|
|
dialogo.title("Servidor desconectado")
|
|
dialogo.geometry("320x140")
|
|
dialogo.resizable(False, False)
|
|
dialogo.transient(self.root)
|
|
dialogo.grab_set()
|
|
|
|
frame = ttk.Frame(dialogo, padding=20)
|
|
frame.pack(expand=True, fill="both")
|
|
|
|
ttk.Label(
|
|
frame, text="El servidor se ha cerrado.",
|
|
font=FUENTE_NEGOCIOS
|
|
).pack(pady=(0, 15))
|
|
|
|
frame_btns = ttk.Frame(frame)
|
|
frame_btns.pack()
|
|
|
|
def volver():
|
|
dialogo.destroy()
|
|
self.desconectar_y_volver()
|
|
|
|
def ver_chat():
|
|
dialogo.destroy()
|
|
|
|
ttk.Button(
|
|
frame_btns, text="Volver al inicio",
|
|
command=volver, style='Action.TButton'
|
|
).pack(side="left", padx=5)
|
|
|
|
ttk.Button(
|
|
frame_btns, text="Ver el chat",
|
|
command=ver_chat
|
|
).pack(side="left", padx=5)
|
|
|
|
def desconectar_y_volver(self):
|
|
"""Desconecta del servidor y vuelve al selector."""
|
|
self.cerrar_conexion()
|
|
parent = self.master
|
|
self.destroy()
|
|
if hasattr(parent, 'volver_al_selector'):
|
|
parent.volver_al_selector()
|