from __future__ import unicode_literals # -*- coding: utf-8 -*- import os from sys import exit, argv from time import time, sleep from threading import Thread from socket import socket, AF_INET, SOCK_STREAM from subprocess import Popen, PIPE import logging import cPickle as pickle #modules importes from psutil import process_iter #import de QT et des interfaces from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4 import QtOpenGL #from PyQt4 import QtCore, QtGui from ecran_connexion import Ui_ecranConnexion from ecran_principal import Ui_principal from ecran_chatPrive import Ui_chatPrive from visionneuse import Ui_visionneuse #modules persos from serveur import Serveur, ServeurVoc from lancer import jet from clientvoc import ClientVoc #fonctions potentiellement utiles: #print(psutil.connections()) #print(psutil.net_io_counters(pernic=True)) #gestion des erreurs et log logging.basicConfig(level=logging.DEBUG) logProg = logging.getLogger(__name__) handlerProg = logging.FileHandler('prog.log') handlerProg.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s- %(message)s')) #'%(name)s' nom du thread logProg.addHandler(handlerProg) logProg.debug(" ---------------------- \n") def ipValide(addr): """controle la validite d'une adresse ip""" #if addr == "localhost": # retour = addr #else: # try: # socket.inet_aton(addr) # retour = True #except socket.error: # except: # retour = False retour = True return retour class EcranConnexion(QGroupBox): """fenetre de connexion a l'application et module d'envoi/reception""" def __init__(self, parent=None): """initialisation de la connexion et creation de la fenetre""" super (EcranConnexion, self).__init__(parent) self.client_connecte = False self.serveur_connecte = False self.idServeur = "" self.idClient = "00" self.ip = "" self.pseudo = "" self.autresCo = {} #liste des chat prives ouverts avec les autres clients self.chatPrive = {} #echanges fichiers self.eFichier = {"id": "00", "chemin": "", "fichier": None, "dest": "", "envoi" : False, "annule" : False, "invisible": False} #fichier en cours d'envoi : id fichier, chemin du fichier, fichier (objet), dest, envoi autorise, envoi annule self.destFichier = {} #liste des clients dont on attend confirmation pour l'envoi de fichier self.rFichier = {} #liste des fichiers recus ou en cours de reception #(id fichier: dictionnaire des infos du fichier [nom, taille (en o), recu (en o), ligne du tableau d'affichage]) self.repReceptionFichiers = os.getcwd()+"\\FichiersRecus\\" #chat voc self.ipServeurVoc = "" self.port = 6660 #recup des param et affichage: try: logProg.info(self.recupParamCo()) except: logProg.info("pas de parametres a recuperer") self.createWidgets() def createWidgets(self): """creation de l'interface de connexion""" self.ui = Ui_ecranConnexion() self.ui.setupUi(self) if len(self.ip) > 0: self.ui.in_ip.setText(QString.fromUtf8(self.ip)) if self.port > 0: self.ui.in_port.setText(QString.fromUtf8(str(self.port))) if len(self.pseudo) > 0: self.ui.in_pseudo.setText(QString.fromUtf8(self.pseudo)) self.connect(self.ui.fermer, SIGNAL("clicked()"), self.fermer) self.connect(self.ui.creerServeur, SIGNAL("clicked()"), self.creerServeur) self.connect(self.ui.seConnecter, SIGNAL("clicked()"), self.seConnecter) def msg(self, txt): """affichage d'un message informatif sous forme de label""" self.ui.txt_msg.setText(QString.fromUtf8(txt)) QApplication.processEvents() def creerServeur(self): """instancie un serveur local""" self.pseudo = self.ui.in_pseudo.text() self.port = self.ui.in_port.text() if len(self.pseudo) == 0 or len(self.port) == 0: self.msg("Parametres incorrects") else: self.ui.in_ip.setText(QString.fromUtf8("localhost")) self.s = Serveur(int(self.ui.in_port.text())) self.serveur_connecte, txt = self.s.creer() self.msg(txt) if self.serveur_connecte: self.seConnecter() def seConnecter(self): """se connecte au serveur correspondant a l'ip et au port saisis""" self.pseudo = self.ui.in_pseudo.text() self.port = self.ui.in_port.text() self.ip = self.ui.in_ip.text() essais = 0 if len(self.pseudo) == 0 or not ipValide(self.ip) or len(self.port) == 0: self.msg("Parametres incorrects") else: self.msg("En attente du serveur...") while self.client_connecte == False: #en attente de la connexion try: self.cnn = socket(AF_INET, SOCK_STREAM) self.cnn.connect((self.ip, int(self.port))) self.client_connecte = True txt = "Connexion etablie avec le serveur sur le port {}\n".format(self.port) logProg.info(txt) except KeyboardInterrupt: break except: essais += 1 if essais > 3: txt = "Delai depasse" self.msg(txt) logProg.warning(txt) break txt = "Connexion : essai {}".format(essais) self.msg(txt) logProg.info(txt) if self.client_connecte: #demarre le fil de reception Thread(None, self.filReception, None, (), {}).start() #previent le serveur et envoie le pseudo self.envoi("ci", "sa", self.pseudo) self.msg(txt) sleep(0.01) self.close() else: txt = "Erreur: impossible de se connecter" self.msg(txt) logProg.error(txt) def filReception(self): """thread de reception des donnees du serveur, reste actif tant que l'application est active""" while self.client_connecte: recu = self.cnn.recv(1024) if len(recu) > 0: self.traitement(recu) def traitement(self, msg): """determine le traitement a apporter a un message recu, selon sa nature""" if len(msg) >= 6: emet = msg[2:4] dest = msg[4:6] categorie = msg[:1] nature = msg[:2] try: contenu = msg[6:] except: contenu = "" if nature != "fd" and nature != "ff": #on decode le message, sauf s'il contient des donnees binaires issues d'un fichier contenu = contenu.decode('utf-8') if nature == "ci": #recoit l'identifiant du client fourni par le serveur self.idClient = dest logProg.info("mon id est: {}\n".format(self.idClient)) elif nature == "cc": #un autre client se connecte if contenu[0:2] != self.idClient: self.autresCo[str(contenu[0:2])] = str(contenu[2:]) #on lui envoie notre id+x+pseudo (x=c si on est client simple, s si on est serveur) sleep(0.02) if self.serveur_connecte: code = "s" else: code = "c" self.envoi("cp", contenu[0:2], "{}{}{}".format(self.idClient, code, self.pseudo)) logProg.info("{} s'est connecte ({})".format(contenu[2:], contenu[0:2])) self.recuInfo("cc", "{} s'est connecte ({})".format(contenu[2:], contenu[0:2])) self.emit(SIGNAL("majAffichageLstClient()")) elif nature == "cp": #ce client est deja present idClient = str(contenu[0:2]) self.autresCo[idClient] = str(contenu[3:]) if str(contenu[2:3]) == "s": self.idServeur = idClient sleep(0.001) logProg.info("{} est deja present [{}]".format(str(contenu[3:]), idClient)) self.emit(SIGNAL("majAffichageLstClient()")) elif nature == "cd": #un client se deconnecte if contenu != self.idClient and len(contenu) == 2: logProg.info("{} s'est deconnecte ({})".format(self.autresCo[contenu], contenu)) self.recuInfo("cd", "{} s'est deconnecte ({})".format(self.autresCo[contenu], contenu)) if contenu in self.chatPrive: self.chatPrive[contenu].close() del self.chatPrive[contenu] del self.autresCo[contenu] sleep(0.001) self.emit(SIGNAL("majAffichageLstClient()")) elif categorie == "m": if nature == "m ": #afficher dans le chat publique logProg.info("chat: {} -> {}\n".format(emet, contenu)) self.recuMsg(emet, contenu) elif nature == "mp": #message pour le chat prive self.emit(SIGNAL("chatPrive(QString, QString,QString)"), QString.fromUtf8(emet), QString.fromUtf8(contenu), QString.fromUtf8("")) elif nature == "md": #jet de des pour le chat prive self.emit(SIGNAL("chatPrive(QString, QString,QString)"), QString.fromUtf8(emet), QString.fromUtf8(contenu), QString.fromUtf8("red")) elif categorie == "i": #afficher dans la fenetre évènement self.recuInfo(nature, contenu) if nature == "id": #jet de dés: afficher en rouge logProg.info("jet de dé: {} -> {}\n".format(emet, contenu)) elif nature == "ic": #nouveau client connecte: afficher en bleu logProg.info("info connexion: {} -> {}\n".format(emet, contenu)) elif categorie == "f": #envoi ou reception de fichier if nature == "f0": #le serveur nous renvoie l'identifiant du fichier que l'on veut envoyer if self.eFichier["id"] == "00": self.eFichier["id"] = contenu else: logProg.warning("Un fichier est deja en cours d'envoi\n") elif nature == "fp": #un clients est pret a recevoir le fichier self.pretEnvoiFichier(emet) elif nature == "fi": #quelqu'un nous envoie un fichier try: if len(contenu)>2: #les 2 premiers car sont l'identifiant, puis le nom/taille du fichier (taille.nom) try: taille = int(contenu[2:].split(".",1)[0]) #on coupe seulement suivant le premier point nom_recu = contenu[2:].split(".",1)[1] except: logProg.error("impossible de lire la taille du nouveau fichier: {}".format(contenu[2:])) taille = 0 nom_recu = contenu[2:] #on determine le nom qu'aura le fichier recu (au cas ou un fichier portant ce nom existe deja) essai = self.nouveauFichier(contenu[0:2], nom_recu, taille) logProg.info("Creation d'un nouveau fichier: {}".format(essai)) if len(essai) > 0: if dest == self.idClient: #si le fichier n'est destine qu'a nous, on affiche aussi le msg dans la fenetre de chat prive txt = "{} vous envoie {} ({} ko)".format(emet, nom_recu, int(taille/1000)) self.emit(SIGNAL("chatPrive(QString, QString, QString)"), QString.fromUtf8(emet), QString.fromUtf8(txt), QString.fromUtf8("blue")) self.recuInfo(nature, "Fichier {} en cours de reception ({} ko)".format(nom_recu, int(taille/1000))) #on envoie confirmation de la reception: sleep(0.001) self.envoi("fp", emet, contenu[0:2]) self.emit(SIGNAL("majAffichageRecFichier(QString)"), QString.fromUtf8(contenu[0:2])) else: self.recuInfo(nature, "Impossible de créer le fichier à recevoir") logProg.error("Impossible de créer le fichier à recevoir") else: logProg.error("erreur reception fichier: id ou nom du fichier incorrect\n") except: #erreur de reception logProg.error("erreur de reception des donnees du fichier\n") self.envoi("fe", emet, contenu[0:2]) elif nature == "fd": #on recoit les donnees a ecrire dans le fichier try: if len(contenu) > 2: if not self.rFichier[contenu[0:2]]["annule"]: self.rFichier[contenu[0:2]]["fichier"].write(contenu[2:]) self.rFichier[contenu[0:2]]["recu"] += len(contenu[2:]) try: taux = int(100*self.rFichier[contenu[0:2]]["recu"] / self.rFichier[contenu[0:2]]["taille"]) except: taux = "." self.emit(SIGNAL("majAffichageRecFichier(QString)"), QString.fromUtf8(contenu[0:2])) #le client renvoie la longueur de la donnee recue a l'emetteur pour confirmation sleep(0.001) self.envoi("fp", emet, contenu[0:2]) elif len(contenu) == 2: #l'emetteur redemande confirmation, on lui envoie if not self.rFichier[contenu[0:2]]["annule"]: self.envoi("fp", emet, contenu[0:2]) else: #erreur de reception, on annule la reception et on le signale logProg.error("erreur de reception des donnees du fichier\n") self.rFichier[contenu[0:2]]["annule"] = True self.envoi("fe", emet, contenu[0:2]) except: #erreur de reception, on annule la reception et on le signale logProg.error("erreur de reception des donnees du fichier\n") self.rFichier[contenu[0:2]]["annule"] = True self.envoi("fe", emet, contenu[0:2]) elif nature == "ff": #fin de reception du fichier try: self.rFichier[contenu[0:2]]["fichier"].write(contenu[2:]) sleep(0.001) self.envoi("fp", emet, contenu[0:2]) sleep(0.01) self.rFichier[contenu[0:2]]["fichier"].close() self.rFichier[contenu[0:2]]["termine"] = True except: #erreur de reception, on annule la reception et on le signale logProg.error("erreur de reception des donnees du fichier\n") self.rFichier[contenu[0:2]]["annule"] = True self.envoi("fe", emet, contenu[0:2]) if self.rFichier[contenu[0:2]]["termine"]: self.emit(SIGNAL("majAffichageRecFichier(QString)"), QString.fromUtf8(contenu[0:2])) self.emit(SIGNAL("imageRecue(QString)"), QString.fromUtf8(contenu[0:2])) self.recuInfo("ff", "Fichier {} recu\n".format(self.rFichier[contenu[0:2]]["nomOriginal"])) if dest == self.idClient: #si le fichier n'est destine qu'a nous, on affiche aussi le msg dans la fenetre de chat prive txt = "Fichier {} bien recu".format(self.rFichier[contenu[0:2]]["nomOriginal"]) self.emit(SIGNAL("chatPrive(QString, QString, QString)"), QString.fromUtf8(emet), QString.fromUtf8(txt), QString.fromUtf8("blue")) logProg.info("Fichier recu") elif nature == "fa": #envoi annule logProg.info("Annulation de la reception du fichier") if self.rFichier[contenu[0:2]]: self.rFichier[contenu[0:2]]["annule"] = True self.recuInfo("ff", "Reception {} annule \n".format(self.rFichier[contenu[0:2]]["nom"])) if dest == self.idClient: #si le fichier n'est destine qu'a nous, on affiche aussi le msg dans la fenetre de chat prive txt = "Transfert du fichier {} annule".format(self.rFichier[contenu[0:2]]["nomOriginal"]) self.emit(SIGNAL("chatPrive(QString, QString, QString)"), QString.fromUtf8(emet), QString.fromUtf8(txt), QString.fromUtf8("blue")) self.emit(SIGNAL("majAffichageRecFichier(QString)"), QString.fromUtf8(contenu[0:2])) sleep(0.01) self.rFichier[contenu[0:2]]["fichier"].close() #on supprime le fichier incomplet: essais = 0 while essais < 100: try: os.remove(self.rFichier[contenu[0:2]]["fichier"].name) break except: essais += 1 if essais == 100: logProg.error("Possible erreur de suppression du fichier temporaire") del self.rFichier[contenu[0:2]] elif nature == "fe": #le destinataire du fichier signale une erreur de reception self.recuInfo("fe", "{}: erreur de reception du fichier, l'envoi est annule \n".format(emet)) self.annuleEnvoiFichier() elif categorie == "s": #infos sur le fonctionnement du serveur principal if nature == "sd": #le serveur a ete ferme self.serveurDeco() elif categorie == "v": #infos liees au chat vocal if nature == "vs": #un serveur vocal a ete cree self.recuInfo("vs", "{} a cree un serveur vocal ({})".format(self.autresCo[emet], contenu)) self.ipServeurVoc = contenu if nature == "vi": #un client rejoint le chat vocal self.recuInfo("vi", "{} a rejoint le chat vocal".format(self.autresCo[emet])) if nature == "vq": #un client quitte le chat vocal self.recuInfo("vq", "{} a quitte le chat vocal".format(self.autresCo[emet])) if nature == "vf": #fermeture du serveur vocal self.recuInfo("vf", "{} a ferme le serveur vocal".format(self.autresCo[emet])) elif categorie == "p": if nature == "pi": #nouveau plateau créé logProg.info("nouveau plateau: {}\n".format(emet, contenu)) else: logProg.warning("Erreur: message illisible -> {}\n".format(msg)) def recuInfo(self, nature, contenu): """signale une nouvelle information""" self.emit(SIGNAL("nouvelleInfo(QString, QString)"), QString.fromUtf8(nature), QString.fromUtf8(contenu)) def recuMsg(self, emetteur, contenu): """signale un nouveau message texte pour le chat ecrit""" self.emit(SIGNAL("msgChat(QString, QString)"), QString.fromUtf8(emetteur), QString.fromUtf8(contenu)) def envoi(self, nature, dest, msg, idFichier = ""): """envoie un message au serveur TCP - longueur du message (3 car) - nature du message (2 car) - exp: id de l'expediteur (2 car) - dest: id du destinataire (2 car) - msg: contenu du message (999 car max) - un identifiant pour le fichier le cas echeant""" exp = self.idClient if self.client_connecte: try: if len(msg) <= 999: if len(idFichier) == 0: msg = unicode(msg) lg = "%003.f"%len(msg.encode('utf-8')) #la longueur apres encodage peut changer txt = "{}{}{}{}{}".format(lg, nature, exp, dest, msg) txt = txt.encode('utf-8') else: lg = "%003.f"%(len(msg)+2) txt = "{}{}{}{}{}".format(lg, nature, exp, dest, idFichier) txt = txt.encode('utf-8') + msg retour = len(txt) self.cnn.sendall(txt) else: self.recuInfo(" ","999 caracteres au max.") retour = 0 except: retour = 0 logProg.warning("Envoi impossible") else: retour = "" logProg.warning("Le client n'est pas connecte au serveur") self.recuInfo(" ","Vous n'etes pas connecte a un serveur") return retour def envoiFichier(self, chemin, dest = "ac"): """intialise l'envoi d'un fichier""" if self.eFichier["id"] == "00": # on verifie si le fichier existe: fichier = None try: fichier = open(chemin, "rb") except: logProg.error("Le fichier '{}' est introuvable.".format(fichier)) if not os.path.getsize(chemin) > 0: logProg.error("Envoi impossible - fichier vide") fichier.close() fichier = None if fichier: #on demande un identifiant au serveur self.eFichier["fichier"] = fichier self.eFichier["chemin"] = chemin self.eFichier["dest"] = dest self.eFichier["annule"] = False self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(os.path.basename(chemin)), QString.fromUtf8("0")) logProg.debug(self.eFichier) if dest == "ac": for idC in self.autresCo: self.destFichier[idC] = False else: self.destFichier[dest] = False #on demande un identifiant au serveur self.envoi("f0","sa","") Thread(None, self.envoiFichier_fil, None, (), {}).start() else: self.recuInfo("fi", "Impossible d'envoyer le fichier, un fichier est peut-etre deja en cours d'envoi") def envoiFichier_fil(self): """le fichier est pret a etre envoye, on attend confirmation des destinataires""" #on attend que l'id du fichier soit mis a jour essais = 0 while self.eFichier["id"]=="00" and essais < 3000: sleep(0.001) essais += 1 if len(self.eFichier["id"]) == 2 and self.eFichier["id"] != "00": idFichier = self.eFichier["id"] dest = self.eFichier["dest"] nomFichier = os.path.basename(self.eFichier["chemin"]) taille = os.path.getsize(self.eFichier["chemin"]) logProg.debug("{} a pour id {}\n".format(nomFichier, idFichier)) #on previent les destinataires, et on leur transmet l'identifiant et taille.nom du fichier (taille au format numerique, en octets) logProg.info("En attente des destinataires...") self.recuInfo("fi", "{} - En attente des destinataires...".format(nomFichier)) self.envoi("fi", dest, "{}{}.{}".format(idFichier, taille, nomFichier)) essais = 0 while not self.eFichier["envoi"] and not self.eFichier["annule"]: sleep(0.001) essais += 1 if essais >= 3000: logProg.error("Pas de reponse des destinataires - envoi avorte") self.eFichier["annule"] = True if not self.eFichier["annule"]: #on a recu confirmation de la part des destinataires envoye = 0 essais = 0 #on lit un premier paquet de donnees data = self.eFichier["fichier"].read(512) while len(data) == 512 and not self.eFichier["annule"]: #on reinitialise le controle de reception pour le prochain paquet: self.eFichier["envoi"] = False if dest == "ac": for idC in self.autresCo: self.destFichier[idC] = False else: self.destFichier[dest] = False #on envoie le paquet precedent, que les destinataires ont confirme pouvoir recevoir self.envoi("fd", dest, data, idFichier) envoye += len(data) taux = str((100*envoye)/taille) self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8(taux)) #logProg.info("{} / {}".format(int(envoye/1000), int(taille/1000))) #attention: ralentit enormement l'envoi #on attend que les clients confirment reception pour envoyer le prochain essais = 0 while not self.eFichier["envoi"]: if self.eFichier["annule"]: break sleep(0.001) essais += 1 if essais >= 500: #on renvoie un message toutes les demi-secondes pour redemander confirmation self.envoi("fd", dest, b'', idFichier) essais = 0 #on lit le paquet suivant data = self.eFichier["fichier"].read(512) #FIN DE LA BOUCLE, on a envoye tous les paquets de 512o, il reste donc un dernier paquet a envoyer #pour dernier paquet on ajoute au drapeau: "%003.f"%len(paquet) #on signale que c'est le dernier paquet et on ajoute 3 caracteres pour en specifier la taille: #ENVOI DU DERNIER PAQUET: if not self.eFichier["annule"]: #on reinitialise le controle de reception pour le prochain paquet: self.eFichier["envoi"] = False if dest == "ac": for idC in self.autresCo: self.destFichier[idC] = False else: self.destFichier[dest] = False #on envoie le paquet precedent, que les destinataires ont confirme pouvoir recevoir self.envoi("ff", dest, data, idFichier) envoye += len(data) taux = str((100*envoye)/taille) self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8(taux)) #on attend que les clients confirment reception pour envoyer le signal de fin essais = 0 while not self.eFichier["envoi"]: if self.eFichier["annule"]: break sleep(0.001) essais += 1 if essais >= 500: #on renvoie un message pour redemander confirmation logProg.warning("envoi fichier - on redemande confirmation") self.envoi("fd", dest, b'', idFichier) essais = 0 #on signale au serveur qu'il peut liberer l'id du fichier self.envoi("f1", "sa", "{}".format(idFichier)) self.recuInfo("ff", "- {} a bien ete envoye -".format(nomFichier)) logProg.info("\n- Fichier envoye -") else: logProg.error("Erreur envoi fichier: signal serveur non-recu -> {}".format(self.eFichier["id"])) else: logProg.error("Erreur envoi fichier: identifiant incorrect -> {}".format(idFichier)) self.eFichier["fichier"].close() self.eFichier = {"id": "00", "chemin": "", "fichier": None, "dest": "", "envoi" : False, "annule": False} def pretEnvoiFichier(self, idClient): """signale un destinataire comme etant pret a recevoir le fichier ou le paquet de donnees, et renvoie Vrai si tous les destinataires sont prets""" pretEnvoi = False if self.eFichier["dest"] == "ac": self.destFichier[idClient] = True pretEnvoi = True for idC in self.destFichier: if self.destFichier[idC] == False: pretEnvoi = False else: if idClient == self.eFichier["dest"]: pretEnvoi = True #print("{} -> {}".format(idClient, pretEnvoi)) self.eFichier["envoi"] = pretEnvoi def annuleEnvoiFichier(self): """annule l'envoi d'un fichier""" logProg.warning("Annulation de l'envoi") self.envoi("fa", self.eFichier["dest"], "{}".format(self.eFichier["id"])) self.eFichier["annule"] = True essais = 0 while not self.eFichier["id"] == "00": sleep(0.01) essais += 1 if essais > 500: logProg.error("Erreur: impossible d'annuler l'envoi") if self.eFichier["id"] == "00": logProg.info("-> Envoi annulé") self.recuInfo("fa", "- Envoi du fichier annule -") self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("x")) def nouveauFichier(self, idFichier, nomFichier, taille): """cree le fichier a recevoir - le renomme si un fichier portant ce nom existe deja - retourne le chemin complet""" k = 1 tmp = nomFichier retour = "" try: while os.path.isfile(self.repReceptionFichiers+tmp): k += 1 tmp = nomFichier.split(".")[0]+str(k)+"."+nomFichier.split(".")[1] if k == 100: tmp = "" break if len(tmp) > 0: fichier = open((self.repReceptionFichiers + tmp), "wb") self.rFichier[idFichier] = {"fichier": fichier, "nom": tmp, "nomOriginal": nomFichier, "taille": taille, "recu": 0, "ligneAffichage": None, "termine": False, "annule": False} retour = tmp except: logProg.error("Impossible de creer le fichier") return retour def serveurDeco(self): """le serveur a ferme - on affiche a nouveau l'ecran de connexion""" #on annule les envois de fichier en cours if self.eFichier["fichier"] != None: self.eFichier["annule"] = True self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("x")) #on annule les receptions de fichiers en cours for idFichier in self.rFichier: if not self.rFichier[idFichier]["termine"]: logProg.warning("{} - reception annulee".format(self.rFichier[idFichier]["nom"])) self.rFichier[idFichier]["fichier"].close() os.remove(self.rFichier[idFichier]["fichier"].name) self.emit(SIGNAL("majAffichageRecFichier(QString)"), QString.fromUtf8(idFichier)) self.rFichier = {} self.recuInfo("sd", "(!) Le serveur a mis la clef sous la porte (!)") self.idServeur = "" logProg.warning("Serveur deconnecte") self.cnn.close() self.client_connecte = False self.serveur_lance = False sleep(0.01) self.emit(SIGNAL("majAffichage()")) #on affiche l'ecran de connexion self.show() self.raise_() self.activateWindow() self.msg("Le serveur a ete deconnecte") def recupParamCo(self): """recupere les derniers parametres de connexion enregistres s'il existent""" try: with open("parametresCo", 'rb') as input: dico = pickle.load(input) self.pseudo = dico["pseudo"] self.port = dico["port"] self.ip = dico["ip"] if os.path.isdir(dico["repReceptionFichiers"]): self.repReceptionFichiers = dico["repReceptionFichiers"] retour = dico input.close() except IOError: retour = ("Erreur: parametresCo introuvable") return retour def sauverParamCo(self): """sauvegarde les parametres de connexion pour une prochaine utilisation""" with open("parametresCo", 'wb') as output: dico = {"pseudo": str(self.pseudo), "port" : int(self.port), "ip": str(self.ip), "repReceptionFichiers" : str(self.repReceptionFichiers)} pickle.dump(dico, output, -1) output.close() chaine = "parametres sauvegarde." return chaine def fermer(self): """fermeture de la connexion, et du serveur le cas echeant""" if self.client_connecte: try: self.envoi("cd", "sa", "") except: logProg.warning("impossible de prévenir le serveur de la deco") for fenetre in self.chatPrive: self.chatPrive[fenetre].close() self.chatPrive = {} self.client_connecte = False self.cnn.close() if self.eFichier["fichier"] != None: self.eFichier["annule"] = True sleep(0.001) logging.info(self.sauverParamCo()) if self.serveur_connecte: self.s.stop() self.serveur_connecte = False sleep(0.001) self.close() def closeEvent(self, event): """sur fermeture de la fenetre""" if self.client_connecte: self.emit(SIGNAL("majAffichage()")) else: self.fermer() class AfficherImage(QGroupBox): """fenetre pop-up permettant l'affichage d'une image""" def __init__(self, chemin, parent=None): super (AfficherImage, self).__init__(parent) self.ui = Ui_visionneuse() self.ui.setupUi(self) self.setWindowTitle(QString.fromUtf8(chemin)) myPixmap = QPixmap(chemin) myScaledPixmap = myPixmap.scaled(self.ui.label.size(), Qt.KeepAspectRatio) self.ui.label.setPixmap(myScaledPixmap) self.resize(myScaledPixmap.width(),myScaledPixmap.height()) class ChatPrive(QMainWindow): """interface de chat prive entre 2 joueurs""" def __init__(self, connexion, interloc, parent=None): """initialisation de la fenetre""" super (ChatPrive, self).__init__(parent) self.co = connexion self.idInterloc = interloc self.createWidgets() def createWidgets(self): """construction de l'interface""" #construction de l'interface self.ui = Ui_chatPrive() self.ui.setupUi(self) self.ui.lblInterlocChatPrive.setText(QString.fromUtf8("Chat prive avec {}".format(self.co.autresCo[self.idInterloc]))) self.connect(self.ui.inSaisieChatPrive, SIGNAL("returnPressed()"), self.envoiMsgPrive) self.connect(self.ui.envoiFichierChatPrive, SIGNAL("clicked()"), self.envoyerfichierPrive) self.connect(self.ui.inDesChatPrive, SIGNAL("returnPressed()"), self.jetDesPrive) def envoiMsgPrive(self): """ajout d'une ligne au chat prive""" msg = self.ui.inSaisieChatPrive.text() self.afficherMsgPrive(self.co.idClient, msg) self.co.envoi("mp", "{}".format(self.idInterloc), msg) self.ui.inSaisieChatPrive.clear() QApplication.processEvents() def envoyerfichierPrive(self): """envoi d'un fichier a l'interlocuteur actuel""" if self.co.eFichier["id"] == "00": fichier = QFileDialog.getOpenFileName( self, "Selectionnez un fichier a envoyer", "c:\\", "") if len(str(fichier)) > 0: self.co.envoiFichier(str(fichier), self.idInterloc) else: self.co.annuleEnvoiFichier() def jetDesPrive(self): """ajout d'une ligne de jet de des au chat prive""" expr = str(self.ui.inDesChatPrive.text()) self.ui.inDesChatPrive.clear() res, detail = jet(expr) if res > 0: txt = "{} ({}) [{}]".format(res, detail, expr) self.afficherMsgPrive(self.co.idClient, txt, "red") self.co.envoi("md", self.idInterloc, txt) else: self.afficherMsgPrive(self.co.idClient, "mmmhh, pas bon le jet", "red") return res def afficherMsgPrive(self, emetteur, msg, couleur="black"): """ajoute un message au chat prive""" emetteur = str(emetteur) if len(couleur)>0 and couleur != "black": if emetteur != self.co.idClient: pseudo = self.co.autresCo["{}".format(emetteur)] else: pseudo = self.co.pseudo txt = "{} - {}".format(couleur, pseudo, msg) else: if emetteur == self.co.idClient: txt = "{} : {}".format(self.co.pseudo, msg) elif emetteur in self.co.autresCo: pseudo = self.co.autresCo["{}".format(emetteur)] txt = "{} : {}".format(pseudo, msg) else: txt = str(msg) txt = QString.fromUtf8(txt) item = QListWidgetItem(self.ui.lstChatPrive) self.ui.lstChatPrive.addItem(item) label = QLabel() label.setWordWrap(True) label.setText(txt) item.setSizeHint(QSize(1, label.heightForWidth (self.ui.lstChatPrive.width()) + 5)) self.ui.lstChatPrive.setItemWidget(item, label) self.ui.lstChatPrive.scrollToItem(item) self.show() self.raise_() self.activateWindow() QApplication.processEvents() class EcranPrincipal(QMainWindow): """interface comprenant: chat ecrit, fenetre d'infos, lancer de des, echange de fichiers, lancement du chat vocal""" def __init__(self, connexion, parent=None): """initialisation de la fenetre""" super (EcranPrincipal, self).__init__(parent) self.co = connexion self.connecte = True self.estServeurVoc = False self.estClientVoc = False self.cases = {} self.createWidgets() def createWidgets(self): """construction de l'interface""" #construction de l'interface self.ui = Ui_principal() self.ui.setupUi(self) #connexion des commandes self.connect(self.ui.inChat, SIGNAL("returnPressed()"), self.envoiMsg) self.connect(self.ui.d20, SIGNAL("clicked()"), self.d20) self.connect(self.ui.d100, SIGNAL("clicked()"), self.d100) self.connect(self.ui.inJetDes, SIGNAL("returnPressed()"), self.autreJet) self.connect(self.ui.envoiFichier, SIGNAL("clicked()"), self.envoyerfichier) self.connect(self.ui.repReceptionFichiers, SIGNAL("clicked()"), self.repReception) self.connect(self.ui.listFichiers, SIGNAL("itemDoubleClicked(QTreeWidgetItem*, int)"), self.ouvrirFichier) self.connect(self.ui.lstStatutJoueurs, SIGNAL("itemDoubleClicked (QListWidgetItem *)"), self.ouvrirChatPrive) self.connect(self.ui.chatVoc, SIGNAL("clicked()"), self.chatVoc) #reception des signaux self.emit(SIGNAL("majAffichage()")) self.connect(self, SIGNAL("msgChat(QString, QString)"), self.ajoutChat) self.connect(self.co, SIGNAL("msgChat(QString, QString)"), self.ajoutChat) self.connect(self, SIGNAL("nouvelleInfo(QString, QString)"), self.ajoutInfo) self.connect(self.co, SIGNAL("nouvelleInfo(QString, QString)"), self.ajoutInfo) self.connect(self, SIGNAL("majAffichageRecFichier(QString)"), self.afficheReception) self.connect(self.co, SIGNAL("majAffichageRecFichier(QString)"), self.afficheReception) self.connect(self, SIGNAL("imageRecue(QString)"), self.imageRecue) self.connect(self.co, SIGNAL("imageRecue(QString)"), self.imageRecue) self.connect(self, SIGNAL("initEnvFichier(QString, QString)"), self.afficheEnvoi) self.connect(self.co, SIGNAL("initEnvFichier(QString, QString)"), self.afficheEnvoi) self.connect(self, SIGNAL("majAffichage()"), self.majStatut) self.connect(self.co, SIGNAL("majAffichage()"), self.majStatut) self.connect(self, SIGNAL("majAffichageLstClient()"), self.majStatutLstClients) self.connect(self.co, SIGNAL("majAffichageLstClient()"), self.majStatutLstClients) self.connect(self, SIGNAL("chatPrive(QString, QString, QString)"), self.chatPrive) self.connect(self.co, SIGNAL("chatPrive(QString, QString, QString)"), self.chatPrive) #plateau de jeu self.creerPlateau(20,20) self.nouveauPion() #mise a jour de l'affichage self.majStatut() self.majStatutLstClients() """def msg(self, txt): self.ui.txt_msg.setText(QString.fromUtf8(txt)) QApplication.processEvents()""" def ajoutChat(self, emetteur, msg): """ajoute une nouvelle ligne au chat ecrit""" emetteur = str(emetteur) if emetteur == self.co.idClient: txt = "{} : {}".format(self.co.pseudo, msg) else: pseudo = self.co.autresCo["{}".format(emetteur)] txt = "{} : {}".format(pseudo, msg) txt = QString.fromUtf8(txt) item = QListWidgetItem(self.ui.listAffichage) self.ui.listAffichage.addItem(item) label = QLabel() label.setWordWrap(True) label.setText(txt) item.setSizeHint(QSize(1, label.heightForWidth (self.ui.listAffichage.width()) + 5)) self.ui.listAffichage.setItemWidget(item, label) self.ui.listAffichage.scrollToItem(item) QApplication.processEvents() def ajoutInfo(self, nature, msg): """ajoute une nouvelle ligne a la liste des evenements""" #couleur selon nature de l'info: if nature[:1] == "c": txt = "{}".format(msg) elif nature == "id": txt = "{}".format(msg) elif nature[:1] == "f": txt = "{}".format(msg) elif nature[:1] == "v": txt = "{}".format(msg) else: txt = msg txt = QString.fromUtf8(txt) item = QListWidgetItem(self.ui.listEvenement) self.ui.listEvenement.addItem(item) label = QLabel() label.setWordWrap(True) label.setText(txt) item.setSizeHint(QSize(110, label.heightForWidth (self.ui.listEvenement.width()) + 6)) self.ui.listEvenement.setItemWidget(item, label) self.ui.listEvenement.scrollToItem(item) QApplication.processEvents() def envoiMsg(self): """envoie un message pour le chat ecrit""" msg = self.ui.inChat.text() self.co.envoi("m ", "tc", msg) self.ui.inChat.clear() QApplication.processEvents() def lancerD(self, expr): """fonction de lancer de des""" res, detail = jet(expr) if res > 0: txt = "{}: {} ({}) [{}]".format(self.co.pseudo, res, detail, expr) self.co.envoi("id", "tc", txt) else: self.ajoutInfo("id", "mmmhh, pas bon le jet") return res def d20(self): """lance un D20""" self.lancerD("1d20") def d100(self): """lance un D100""" self.lancerD("1d100") def autreJet(self): """lance un jet personnalise""" expr = str(self.ui.inJetDes.text()) retour = self.lancerD(expr) self.ui.inJetDes.clear() def envoyerfichier(self): """selectionne et envoie un fichier ou annule l'envoi en cours s'il existe""" if self.co.eFichier["id"] == "00": fichier = QFileDialog.getOpenFileName( self, "Selectionnez un fichier a envoyer", "c:\\", "") if len(str(fichier)) > 0: self.co.envoiFichier(str(fichier)) else: self.co.annuleEnvoiFichier() def ouvrirFichier(self): """ouvre un fichier depuis la liste des fichiers recus/envoyes""" item = self.ui.listFichiers.currentItem() eR = item.text(0) taux = item.text(1) nom = item.text(2) if eR == "R": if taux == "ok": try: chemin = "{}\\{}".format(self.co.repReceptionFichiers, nom) Popen(chemin, shell=True, stdout=PIPE) #from subprocess except: print("impossible d'ouvrir le fichier") def repReception(self): """permet de choisir son repertoire de reception des fichiers""" dossier = QFileDialog.getExistingDirectory(self) if len(dossier) > 0: if dossier[len(dossier)-1:] != "\\": dossier += "\\" self.co.repReceptionFichiers = str(dossier) self.majStatut() def afficheReception(self, idFichier): """ajoute/maj une ligne dans la liste des fichiers en cours de reception""" idFichier = str(idFichier) #try: if self.co.rFichier[idFichier]["ligneAffichage"] == None: self.co.rFichier[idFichier]["ligneAffichage"] = QTreeWidgetItem(self.ui.listFichiers, ["R", "0", self.co.rFichier[idFichier]["nom"]]) self.ui.listFichiers.scrollToItem(self.co.rFichier[idFichier]["ligneAffichage"]) else: if self.co.rFichier[idFichier]["termine"]: taux = "ok" elif self.co.rFichier[idFichier]["annule"]: taux = "x" else: taux = str(int(100*self.co.rFichier[idFichier]["recu"]/self.co.rFichier[idFichier]["taille"])) self.co.rFichier[idFichier]["ligneAffichage"].setText(1, taux) #except: # logProg.error("erreur d'affichage de la progression: " + idFichier) def imageRecue(self, idFichier): """verifie si le fichier recu est une image, et l'ouvre le cas echeant""" idFichier = str(idFichier) if self.co.rFichier[idFichier]["termine"]: try: typeFichier = self.co.rFichier[idFichier]["nom"].split(".")[len(self.co.rFichier[idFichier]["nom"].split("."))-1] except: typeFichier = "" if typeFichier.lower() in ["png", "jpeg", "jpg"]: #si c'est une image, on l'ouvre dans une nouvelle fenetre logProg.debug("ouverture d'une image : " + self.co.rFichier[idFichier]["nom"]) chemin = self.co.repReceptionFichiers + self.co.rFichier[idFichier]["nom"] #print(chemin) self.affiche = AfficherImage("{}".format(chemin)) self.affiche.show() self.affiche.raise_() QApplication.processEvents() def afficheEnvoi(self, fichier, taux): """ajoute/maj une ligne a la liste des fichiers en cours d'envoi""" fichier = str(fichier) taux = str(taux) if len(fichier) > 0: self.ligneEFichier = QTreeWidgetItem(self.ui.listFichiers, ["E", taux, fichier]) self.ui.listFichiers.scrollToItem(self.ligneEFichier) elif len(taux) > 0: if taux != self.ligneEFichier.text(1): self.ligneEFichier.setText(1, taux) def majStatut(self): """met a jour l'onglet de statut de connexion""" self.ui.txtStatutCoPseudo.setText(QString.fromUtf8("Pseudo : {}".format(self.co.pseudo))) if self.co.serveur_connecte: self.ui.txtStatutCoServeur.setText(QString.fromUtf8("Serveur : {} [{}]".format(self.co.pseudo, self.co.ip))) elif len(self.co.idServeur) > 0 and self.co.idServeur in self.co.autresCo: self.ui.txtStatutCoServeur.setText(QString.fromUtf8("Serveur : {} [{}]".format(self.co.autresCo[self.co.idServeur], self.co.ip))) else: self.ui.txtStatutCoServeur.setText(QString.fromUtf8("Serveur : Pas de serveur")) if self.estServeurVoc: self.ui.txtStatutCoServeur.setText(QString.fromUtf8("Serveur vocal : {} [{}]".format(self.co.pseudo, self.co.ip))) QApplication.processEvents() def majStatutLstClients(self): """met a jour la liste des joueurs""" self.ui.lstStatutJoueurs.clear() txt = "{} - {}".format(self.co.idClient, self.co.pseudo) self.ui.lstStatutJoueurs.addItem(QString.fromUtf8(txt)) for idClient in self.co.autresCo: txt = "{} - {}".format(idClient, self.co.autresCo[idClient]) self.ui.lstStatutJoueurs.addItem(QString.fromUtf8(txt)) self.majStatut() QApplication.processEvents() def ouvrirChatPrive(self): """ouvre un chat prive avec le joueur sur lequel on a clique""" item = self.ui.lstStatutJoueurs.currentItem() idInterloc = str(item.text())[0:2] if idInterloc != self.co.idClient: self.chatPrive(idInterloc) def chatPrive(self, idInterloc, txt="", couleur=""): """ouvre la fenetre de chat prive si elle ne l'est pas deja, puis affiche le message""" idInterloc = str(idInterloc) if not idInterloc in self.co.chatPrive: self.co.chatPrive[idInterloc] = ChatPrive(self.co, idInterloc) self.co.chatPrive[idInterloc].show() if len(txt)>0: if len(couleur)>0: self.co.chatPrive[idInterloc].afficherMsgPrive(idInterloc, txt, couleur) else: self.co.chatPrive[idInterloc].afficherMsgPrive(idInterloc, txt) def creerServeurVoc(self): """cree un serveur vocal""" if not self.estServeurVoc: self.sVoc = ServeurVoc(6660) #on ouvre une boite de dialogue pour demander l'ip distante #de celui qui veut ouvrir le serveur voc ipS, ok = QInputDialog.getText(self, 'Creation du serveur vocal', 'Saisissez votre adresse IP distante :') if ok: if ipValide(ipS): #self.co.ipServeurVoc = self.ui.ipServeurVoc.text() self.co.ipServeurVoc = ipS txt = self.sVoc.creer() self.ajoutInfo("vs", txt) if self.sVoc.serveur_lance: self.co.envoi("vs","ac","{}".format(self.co.ipServeurVoc)) self.estServeurVoc = True self.majStatut() else: self.ajoutInfo("vs", "Erreur - Creation du serveur vocal annulee") else: self.ajoutInfo("vs", "Veuillez entrer une adresse ip valide") else: self.fermerServeurVoc() def fermerServeurVoc(self): """ferme le serveur vocal""" txt = self.sVoc.stop() self.ajoutInfo("vs", txt) if not self.sVoc.serveur_lance: self.co.envoi("vf","ac","") self.estServeurVoc = False self.majStatut() def chatVoc(self): """connexion au chat vocal""" if not self.estClientVoc: if len(self.co.ipServeurVoc) == 0: if QMessageBox.question(self.parent(), "Chat Vocal", "Aucun serveur vocal ne semble avoir ete cree, /nVoulez-vous en creer un?", QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: self.creerServeurVoc() sleep(0.01) if len(self.co.ipServeurVoc) > 0: self.cVoc = ClientVoc(self.co.idClient, self.co.ipServeurVoc, 6660) txt = self.cVoc.creer() if self.cVoc.connecte: self.ajoutInfo("vi", txt) self.co.envoi("vi","ac","") self.estClientVoc = True self.majStatut() else: self.quitterChatVoc() def quitterChatVoc(self): """deconnexion du chat vocal""" if self.estClientVoc: txt = self.cVoc.stop() self.ajoutInfo("vq", txt) self.co.envoi("vq","ac","") self.estClientVoc = False self.majStatut() if self.sVoc.serveur_lance: self.fermerServeurVoc() def creerPlateau(self, x, y): """Creation du Plateau de combat""" if x > 0 and y > 0 and x == int(x) and y == int(y): self.plateau = QGraphicsScene(self) self.cCase = 30 self.plateau.setSceneRect(0, 0, self.cCase*x, self.cCase*y) pinceau = QPen() pinceau.setColor(QColor(85, 85, 85, 85)) pinceau.setWidth(1) i = 0 j = 0 for i in range(x): for j in range(y): if 1 == (i % 2): j += 0.5 polygon = QPolygonF() polygon << QPointF(i*self.cCase,(j+0.5)*self.cCase) << QPointF((i+0.34)*self.cCase,j*self.cCase)<< QPointF((i+1)*self.cCase,(j)*self.cCase) << QPointF((i+1.34)*self.cCase,(j+0.5)*self.cCase) << QPointF((i+1)*self.cCase,(j+1)*self.cCase) << QPointF((i+0.34)*self.cCase,(j+1)*self.cCase) self.plateau.addPolygon(polygon) self.ui.vuePlateau.setScene(self.plateau) def nouveauPion(self): """essai de creation graphique: ajout d'un pion sur le plateau de jeu""" #creation d'un pion pinceau = QPen() pinceau.setColor(QColor(0, 0, 0, 120)) pinceau.setWidth(2) self.pion = QGraphicsEllipseItem(0, 0, self.cCase, self.cCase) self.pion.setPen(pinceau) self.pion.setFlag(QGraphicsItem.ItemIsMovable) self.pion.setFlag(QGraphicsItem.ItemIsFocusable) self.pion.setBrush(QColor(255, 0, 0, 150)) #ombre self.shadow = QGraphicsDropShadowEffect() self.shadow.setColor(QColor(50, 50, 50, 200)) self.shadow.setXOffset(1) self.shadow.setYOffset(1) self.shadow.setBlurRadius(3) self.pion.setGraphicsEffect(self.shadow) self.shadow.setEnabled(True) self.plateau.addItem(self.pion) def closeEvent(self, event): """sur fermeture de la fenetre""" if self.estClientVoc: self.cVoc.stop() if self.estServeurVoc: self.sVoc.stop() self.co.fermer() sleep(0.01) self.connecte = False self.close() if __name__ == "__main__": demarrageOk = True #repertoire de travail et creation des dossiers necessaires: try: repCourant = os.getcwd() except: repCourant = "" if not len(repCourant) > 0: logProg.error("Impossible de determiner le repertoire courant") demarrageOk = False else: try: #repertoire media (musiques+images) if not os.path.exists(repCourant+"\\media"): os.mkdir(repCourant+"\\media") #repertoire utilisateur (sauvegardes) if not os.path.exists(repCourant+"\\svg"): os.mkdir(repCourant+"\\svg") #repertoire reception des fichiers persos if not os.path.exists(repCourant+"\\FichiersRecus"): os.mkdir(repCourant+"\\FichiersRecus") except: logProg.error("Erreur de creation des repertoires de l'application") demarrageOk = False #verif si l'appli est deja lancee: nomAppli = "DMonde.exe" compte = 0 for proc in process_iter(): #from psutil try: nomProc = proc.name() if nomProc == nomAppli: compte += 1 if compte > 1: demarrageOk = False logProg.error("Une instance de l'application est deja en cours d'execution") break except: pass if demarrageOk: #lancement de l'appli app = QApplication(argv) #'argv' vient de 'sys' connexion = EcranConnexion() connexion.show() r = app.exec_() if r == 0 and connexion.client_connecte: #si pas d'erreur et client connecte, on ouvre l'interface principale ecranPrincipal = EcranPrincipal(connexion) ecranPrincipal.show() r = app.exec_() exit(r) #'exit' vient de 'sys'