class Cliente(): def __init__(self): print "init Cliente..." self.nombre = ""; self.ip = ""; self.puerto = 0; self.ipServer = "127.0.0.1"; #IP fija del servidor self.puertoServer = 4040; #Puerto fijo del servidor self.usuariosConectados = []; #Lista local de usuarios conectados self.conversacionesActivas = []; #Lista de conversaciones en las que este usuario participa actualmente self.qtGuiApp = QtGui.QApplication(sys.argv) #Para crear interfaces gráficas con PyQt4 print "Inicializando Interfaces..." self.loginUI = LoginUI(); #inicializa la interfaz para solicitar datos del usuario self.loginUI.botonLogin.clicked.connect(self.login); self.clienteUI = ClienteUI(); #Inicializa la interfaz del cliente del chat self.clienteUI.botonActualizarUsuarios.clicked.connect(self.actualizarUsuarios); self.clienteUI.botonLogout.clicked.connect(self.desconectar); self.mostarLoginUI(); #muestra la interfaz para pedir datos del usuario """ Función para mostrar la interfaz LoginUI """ def mostarLoginUI(self): self.loginUI.show(); os._exit(self.qtGuiApp.exec_()); """ Función para conectar el cliente con el servidor - Obtiene los datos de la interfaz de Login - Valida los datos - Arranca el servidor del cliente con los datos obtenidos - Manda sus datos al servidor """ def login(self): self.nombre = str(self.loginUI.lineEditNombre.text()); self.ip = str(self.loginUI.lineEditIP.text()); self.puerto = self.loginUI.lineEditPuerto.text(); ipValores = self.ip.split("."); #parto la IP donde haya puntos if(len(ipValores) != 4): #Si la IP está separada con tres puntos self.loginUI.mostrarError(u"La dirección IP es inválida"); else: for valor in ipValores: try: int(valor); #Si cada valor entre puntos de la IP es numérico except ValueError as ve: self.loginUI.mostrarError(u"La dirección IP solo puede contener valores numéricos"); return; try: self.puerto = int(self.puerto); #Valido que el puerto sea numérico except ValueError as ve: self.loginUI.mostrarError(u"El puerto es inválido"); return; print "Inicializando threadServidorCliente..." #inicializa el servidor del cliente con estos datos self.threadServidorCliente = clientServer(self.nombre,self.ip,self.puerto); #threadServidorCliente = threading.Thread(target=self.iniciarServidor); #threadServidorCliente.start(); #inicializo un hilo para el servidor del cliente print "Servidor Escuchando..." self.clienteUI.setWindowTitle(self.nombre); #La ventana del cliente tendrá mi nombre """ Conecto las señales emitidas por el servidor del cliente a la interfaz principal del cliente """ self.clienteUI.connect(self.threadServidorCliente,QtCore.SIGNAL("crearChat(QString,QString,QString)"),self.crearChat); self.clienteUI.connect(self.threadServidorCliente,QtCore.SIGNAL("recibirMensaje(QString,QString)"),self.recibirMensaje); self.clienteUI.connect(self.threadServidorCliente,QtCore.SIGNAL("iniciarLlamada(QString)"),self.iniciarLlamada); self.clienteUI.connect(self.threadServidorCliente,QtCore.SIGNAL("reproducirLlamada(QString,PyQt_PyObject)"),self.reproducirLlamada); self.clienteUI.connect(self.threadServidorCliente,QtCore.SIGNAL("actualizarUsuarios()"),self.actualizarUsuarios); #inicializo el servidor del cliente self.threadServidorCliente.start(); #Me conecto al servidor con la IP y el puerto fijos que tengo try: proxy = xmlrpclib.ServerProxy("http://" + str(self.ipServer) + ":" + str(self.puertoServer) + "/",allow_none=True); proxy.addCliente(self.nombre,self.ip,self.puerto); #Mando mi información al servidor self.clienteUI.show(); #Muestro la pantalla del Cliente self.actualizarUsuarios(); #Actualizo mi lista de usuarios except Exception as e: self.clienteUI.mostrarError(u"No se pudo conectar con el servidor. Intente de nuevo más tarde"); self.loginUI.close(); #cierro la pantalla de Login """ Función que solicita del servidor la lista actual de usuarios y la guarda en la lista local """ def actualizarUsuarios(self): print "Actualizando usuarios..." #abro un proxy al servidor con la IP y el puerto fijos try: proxy = xmlrpclib.ServerProxy("http://" + str(self.ipServer) + ":" + str(self.puertoServer) + "/",allow_none=True); self.usuariosConectados = proxy.getUsuarios(); #sustituyo mi lista por la lista de usuarios del servidor print "Usuarios conectados: " + str(self.usuariosConectados); self.clienteUI.clearUsuarios(); #borro la lista de usuarios de la interfaz de Cliente #por cada usuario que recibí for i,usuario in enumerate(self.usuariosConectados): labelUsuario = QtGui.QLabel(); labelUsuario.setText(usuario['nombre']); #creo una etiqueta con su nombre self.clienteUI.grid.addWidget(labelUsuario,3+i,0,1,3); #lo añado hasta abajo de ClienteUI if(not (usuario['ip'] == self.ip and usuario['puerto'] == self.puerto)): #si el usuario no soy yo botonLlamar = QtGui.QPushButton('Llamar'); #creo el botón para llamar botonLlamar.clicked.connect(self.crearFuncionLlamar(usuario['nombre'],usuario['ip'],usuario['puerto'])); #le asigno su función self.clienteUI.grid.addWidget(botonLlamar,3+i,3,1,1); #Lo añado a lado de la etiqueta except Exception as e: self.clienteUI.mostrarError(u"Error al actualizar los usuarios. No se pudo comunicar con el server. Por favor reinicie la aplicación"); """ Función para eliminarme de la lista de usuarios conectados del server """ def desconectar(self): try: proxy = xmlrpclib.ServerProxy("http://" + str(self.ipServer) + ":" + str(self.puertoServer) + "/",allow_none=True); proxy.desconectarCliente(self.nombre,self.ip,self.puerto); os._exit(1); except Exception as e: self.clienteUI.mostarError(u"Error al desconectarme"); """ Función que recibe una ip y un puerto y crea una función para contactar a esa IP y a ese puerto """ def crearFuncionLlamar(self,nombre,ip,puerto): def llamar(): self.iniciarChat(nombre,ip,puerto); return llamar; """ Función que abre una interfaz para mandar mensajes e iniciar llamadas con otro usuario dentro de la lista. También abre una interfaz en el equipo de la otra persona @param str El nombre de la persona con la que voy a empezar a hablar @param str El ip en donde se encuentra su equipo @param int El puerto por donde está escuchando """ def iniciarChat(self,nombre,ipRemoto,puertoRemoto): self.crearChat(nombre,ipRemoto,puertoRemoto); try: proxy = xmlrpclib.ServerProxy("http://" + str(ipRemoto) + ":" + str(puertoRemoto) + "/",allow_none=True); proxy.crearChat(self.nombre,self.ip,self.puerto); except Exception as e: self.clienteUI.mostarError(nombre + " es inalcanzable"); self.cerrarChat(nombre); """ Función que inicializa un chat directamente con otro usuario. También agrega esta conversación a la lista de conversacionesActivas. @param str nombre El nombre del usuario con el que voy a hablar @param str ipRemoto La dirección IP en donde se encuentra el otro usuario @param int puertoRemoto El puerto por donde el otro usuario está escuchando """ def crearChat(self,nombre,ipRemoto,puertoRemoto): print "Creando chat con " + nombre + " en la ip " + ipRemoto + " en el puerto " + str(puertoRemoto); #si se llama remotamente, el parámetro puertoRemoto es un QString if isinstance(puertoRemoto,QtCore.QString): #por lo tanto se parsea a entero puertoRemoto,ok = puertoRemoto.toInt(); #inicializo un nuevo chat con mis datos y los datos del otro usuario nuevoChat = chatUserUI(self.nombre,nombre,self.ip,ipRemoto,self.puerto,puertoRemoto); """ conecto la interfaz principal del chat con una señal que mandará la interfaz del chat directo al cerrarse """ self.clienteUI.connect(nuevoChat,QtCore.SIGNAL("cerrarChat(QString)"),self.cerrarChat); nuevoChat.show(); self.conversacionesActivas.append(nuevoChat); #agrego el chat a conversacionesActivas """ Función para eliminar un chat de la lista conversacionesActivas. @param str nombre El nombre del usuario con el que estoy hablando a través de ese chat """ def cerrarChat(self,nombre): for chat in self.conversacionesActivas: if(chat.nombreRemoto == nombre): self.conversacionesActivas.remove(chat); print "Removi a " + nombre + " de la lista"; print str(self.conversacionesActivas); """ Función que se ejecuta cuando el servidor del cliente manda una señal a la interfaz principal del chat de que se recibió un mensaje de texto. @param str nombre El remitente del mensaje @param str mensaje """ def recibirMensaje(self,nombre,mensaje): print "Nombre del remitente " + nombre; #busco al remitente en mis conversacionesActivas for chat in self.conversacionesActivas: #si lo encuentro, agrego el mensaje solo a esa vista if(chat.nombreRemoto == nombre): chat.agregarMensaje(nombre,mensaje); """ Función que se ejecuta cuando el servidor del cliente manda una señal de que alguien quiere iniciar una llamada conmigo. @param str nombreRemoto El nombre del usuario que me está llamando """ def iniciarLlamada(self,nombreRemoto): #busco el nombre del usuario en mis conversacionesActivas for chat in self.conversacionesActivas: if(chat.nombreRemoto == nombreRemoto): #si lo encuentro, inicio la llamada con el manejador de ese chat chat.manejadorLlamadas.iniciarLlamada(); """ Función que se ejecuta cuando el servidor del cliente manda una señal de que llegó un paquete de audio de una llamada @param str nombreRemoto El nombre del usuario que me envió el audio @param str audio El paquete de audio recibido. Creado con la bilbioteca PyAudio """ def reproducirLlamada(self,nombreRemoto,audio): for chat in self.conversacionesActivas: if(chat.nombreRemoto == nombreRemoto): chat.manejadorLlamadas.reproducirLlamada(audio);
class Cliente(): def __init__(self): print "init Cliente..." self.nombre = ""; self.ip = ""; self.puertoTCP = 0; self.puertoUDP = 0; self.ipServer = "127.0.0.1"; #IP fija del servidor self.puertoServer = 4040; #Puerto fijo del servidor self.usuariosConectados = []; #Lista local de usuarios conectados self.conversacionesActivas = []; #Lista de conversaciones en las que este usuario participa actualmente self.qtGuiApp = QtGui.QApplication(sys.argv) #Para crear interfaces gráficas con PyQt4 print "Inicializando Interfaces..." self.loginUI = LoginUI(); #inicializa la interfaz para solicitar datos del usuario self.loginUI.botonLogin.clicked.connect(self.login); self.clienteUI = ClienteUI(); #Inicializa la interfaz del cliente del chat self.mostarLoginUI(); #muestra la interfaz para pedir datos del usuario """ Función para mostrar la interfaz LoginUI """ def mostarLoginUI(self): self.loginUI.show(); os._exit(self.qtGuiApp.exec_()); """ Función para conectar el cliente con el servidor - Obtiene los datos de la interfaz de Login - Valida los datos - Arranca el servidor del cliente con los datos obtenidos - Manda sus datos al servidor """ def login(self): self.nombre = str(self.loginUI.lineEditNombre.text()); self.ip = str(self.loginUI.lineEditIP.text()); self.puerto = self.loginUI.lineEditPuerto.text(); ipValores = self.ip.split("."); #parto la IP donde haya puntos if(len(ipValores) != 4): #Si la IP está separada con tres puntos self.loginUI.mostrarError(u"La dirección IP es inválida"); else: for valor in ipValores: try: int(valor); #Si cada valor entre puntos de la IP es numérico except ValueError as ve: self.loginUI.mostrarError(u"La dirección IP solo puede contener valores numéricos"); return; try: self.puerto = int(self.puerto); #Valido que el puerto sea numérico self.puertoTCP = self.puerto; self.puertoUDP = self.puerto + 1; #hay dos para tener dos tipos de servidores: uno TCP y otro UDP except ValueError as ve: self.loginUI.mostrarError(u"El puerto es inválido"); return; print "Inicializando threadServidorClienteSocket..." self.threadServidorClienteSocket = clientServerSocket(self.nombre,self.ip,self.puertoTCP); print "Inicializando threadClientServerUPD..." self.threadServidorClienteUDP = clientServerUDP(self.nombre,self.ip,self.puertoUDP); print "Servidor Escuchando..." self.clienteUI.setWindowTitle(self.nombre); #La ventana del cliente tendrá mi nombre """ Conecto las señales emitidas por el servidor del cliente a la interfaz principal del cliente """ self.clienteUI.connect(self.threadServidorClienteUDP,QtCore.SIGNAL("crearChat(QString,QString,QString)"),self.crearChat); self.clienteUI.connect(self.threadServidorClienteUDP,QtCore.SIGNAL("recibirMensaje(QString,QString)"),self.recibirMensaje); self.clienteUI.connect(self.threadServidorClienteUDP,QtCore.SIGNAL("iniciarLlamada(QString)"),self.iniciarLlamada); self.clienteUI.connect(self.threadServidorClienteUDP,QtCore.SIGNAL("reproducirLlamada(QString,PyQt_PyObject)"),self.reproducirLlamada); self.clienteUI.connect(self.threadServidorClienteSocket,QtCore.SIGNAL("actualizarUsuarios(PyQt_PyObject)"),self.actualizarUsuarios); self.clienteUI.connect(self.threadServidorClienteSocket,QtCore.SIGNAL("errorInicioServidorSocket(QString)"),self.loginUI.mostrarError); self.threadServidorClienteSocket.start(); self.threadServidorClienteUDP.start(); #Me conecto al servidor con la IP y el puerto fijos que tengo try: #empaqueto mis datos con la operacion que quiero realizar en un JSON datos_json = json.dumps({"peticion":"addCliente","nombre":self.nombre,"ip":self.ip,"puerto":self.puertoTCP}).encode("utf-8"); #creo un socket serverSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM); #conecto ese socket al socket del servidor serverSocket.connect((self.ipServer,self.puertoServer)); print "Mandando mis datos al servidor... " #mando el JSON con mis datos serverSocket.send(datos_json); self.clienteUI.show(); #Muestro la pantalla del Cliente except socket.error as socket_error: self.clienteUI.mostrarError(u"No se pudo conectar con el servidor. Intente de nuevo más tarde"); self.loginUI.close(); #cierro la pantalla de Login """ Función que solicita del servidor la lista actual de usuarios y la guarda en la lista local """ def actualizarUsuarios(self,usuarios): print "Actualizando usuarios..." self.usuariosConectados = usuarios; #print "Usuarios conectados: " + str(self.usuariosConectados); self.clienteUI.clearUsuarios(); #borro la lista de usuarios de la interfaz de Cliente #por cada usuario que recibí for i,usuario in enumerate(self.usuariosConectados): labelUsuario = QtGui.QLabel(); labelUsuario.setText(usuario['nombre']); #creo una etiqueta con su nombre self.clienteUI.grid.addWidget(labelUsuario,3+i,0,1,3); #lo añado hasta abajo de ClienteUI if(not (usuario['ip'] == self.ip and usuario['puerto'] == self.puerto)): #si el usuario no soy yo botonLlamar = QtGui.QPushButton('Llamar'); #creo el botón para llamar botonLlamar.clicked.connect(self.crearFuncionLlamar(usuario['nombre'],usuario['ip'],usuario['puerto']+1)); #le asigno su función. Es +1 porque así calculo el puerto UDP del otro usuario self.clienteUI.grid.addWidget(botonLlamar,3+i,3,1,1); #Lo añado a lado de la etiqueta """ Función que recibe una ip y un puerto y crea una función para contactar a esa IP y a ese puerto """ def crearFuncionLlamar(self,nombre,ip,puerto): def llamar(): self.iniciarChat(nombre,ip,puerto); return llamar; """ Función que abre una interfaz para mandar mensajes e iniciar llamadas con otro usuario dentro de la lista. También abre una interfaz en el equipo de la otra persona @param str El nombre de la persona con la que voy a empezar a hablar @param str El ip en donde se encuentra su equipo @param int El puerto por donde está escuchando """ def iniciarChat(self,nombre,ipRemoto,puertoRemoto): self.crearChat(nombre,ipRemoto,puertoRemoto); sock = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP datos_json = json.dumps({"peticion":"crearChat","nombreRemoto":self.nombre,"ipRemoto":self.ip,"puertoRemoto":self.puertoUDP}); sock.sendto(datos_json, (ipRemoto, puertoRemoto)) """ Función que inicializa un chat directamente con otro usuario. También agrega esta conversación a la lista de conversacionesActivas. @param str nombre El nombre del usuario con el que voy a hablar @param str ipRemoto La dirección IP en donde se encuentra el otro usuario @param int puertoRemoto El puerto por donde el otro usuario está escuchando """ def crearChat(self,nombre,ipRemoto,puertoRemoto): print "Creando chat con " + nombre + " en la ip " + ipRemoto + " en el puerto " + str(puertoRemoto); #si se llama remotamente, el parámetro puertoRemoto es un QString if isinstance(puertoRemoto,QtCore.QString): #por lo tanto se parsea a entero puertoRemoto,ok = puertoRemoto.toInt(); #inicializo un nuevo chat con mis datos y los datos del otro usuario nuevoChat = chatUserUI(self.nombre,nombre,self.ip,ipRemoto,self.puertoUDP,puertoRemoto); """ conecto la interfaz principal del chat con una señal que mandará la interfaz del chat directo al cerrarse """ self.clienteUI.connect(nuevoChat,QtCore.SIGNAL("cerrarChat(QString)"),self.cerrarChat); nuevoChat.show(); self.conversacionesActivas.append(nuevoChat); #agrego el chat a conversacionesActivas """ Función para eliminar un chat de la lista conversacionesActivas. @param str nombre El nombre del usuario con el que estoy hablando a través de ese chat """ def cerrarChat(self,nombre): for chat in self.conversacionesActivas: if(chat.nombreRemoto == nombre): self.conversacionesActivas.remove(chat); print "Removi a " + nombre + " de la lista"; print str(self.conversacionesActivas); """ Función que se ejecuta cuando el servidor del cliente manda una señal a la interfaz principal del chat de que se recibió un mensaje de texto. @param str nombre El remitente del mensaje @param str mensaje """ def recibirMensaje(self,nombre,mensaje): print "Nombre del remitente " + nombre; #busco al remitente en mis conversacionesActivas for chat in self.conversacionesActivas: #si lo encuentro, agrego el mensaje solo a esa vista if(chat.nombreRemoto == nombre): chat.agregarMensaje(nombre,mensaje); """ Función que se ejecuta cuando el servidor del cliente manda una señal de que alguien quiere iniciar una llamada conmigo. @param str nombreRemoto El nombre del usuario que me está llamando """ def iniciarLlamada(self,nombreRemoto): #busco el nombre del usuario en mis conversacionesActivas for chat in self.conversacionesActivas: if(chat.nombreRemoto == nombreRemoto): #si lo encuentro, inicio la llamada con el manejador de ese chat chat.manejadorLlamadas.iniciarLlamada(); """ Función que se ejecuta cuando el servidor del cliente manda una señal de que llegó un paquete de audio de una llamada @param str nombreRemoto El nombre del usuario que me envió el audio @param str audio El paquete de audio recibido. Creado con la bilbioteca PyAudio """ def reproducirLlamada(self,nombreRemoto,audio): for chat in self.conversacionesActivas: if(chat.nombreRemoto == nombreRemoto): chat.manejadorLlamadas.reproducirLlamada(audio);