DMonde (1).py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. from __future__ import unicode_literals
  2. # -*- coding: utf-8 -*-
  3. import os
  4. from sys import exit, argv
  5. from time import sleep
  6. from threading import Thread
  7. from socket import socket, AF_INET, SOCK_STREAM
  8. from subprocess import Popen, PIPE
  9. import logging
  10. import cPickle as pickle
  11. #modules importes
  12. from psutil import process_iter
  13. #import de QT et des interfaces
  14. from PyQt4.QtCore import *
  15. from PyQt4.QtGui import *
  16. from ecran_connexion import Ui_ecranConnexion
  17. from ecran_chat import Ui_chat
  18. #modules persos
  19. from serveur import Serveur, ServeurVoc
  20. from lancer import jet
  21. from clientvoc import ClientVoc
  22. #fonctions potetiellement utiles:
  23. #print(psutil.connections())
  24. #print(psutil.net_io_counters(pernic=True))
  25. #gestion des erreurs et log
  26. logging.basicConfig(level=logging.DEBUG)
  27. logProg = logging.getLogger(__name__)
  28. handlerProg = logging.FileHandler('prog.log')
  29. handlerProg.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s- %(message)s')) #'%(name)s' nom du thread
  30. logProg.addHandler(handlerProg)
  31. logProg.debug(" ---------------------- \n")
  32. class EcranConnexion(QGroupBox):
  33. """fenetre de connexion a l'application et module d'envoi/reception"""
  34. def __init__(self, parent=None):
  35. """initialisation de la connexion et creation de la fenetre"""
  36. super (EcranConnexion, self).__init__(parent)
  37. self.client_connecte = False
  38. self.serveur_connecte = False
  39. self.idClient = "00"
  40. self.ip = ""
  41. self.pseudo = ""
  42. self.autresCo = {}
  43. #echanges fichiers
  44. self.eFichier = {"id": "00", "chemin": "", "fichier": None, "dest": "", "envoi" : False, "annule" : False}
  45. #fichier en cours d'envoi : id fichier, chemin du fichier, fichier (objet), dest, envoi autorise, envoi annule
  46. self.destFichier = {} #liste des clients dont on attend confirmation pour l'envoi de fichier
  47. self.receptionFichier = {} #id du fichier: nom du fichier
  48. self.repReceptionFichiers = os.getcwd()+"\\FichiersRecus\\"
  49. #chat voc
  50. self.ipServeurVoc = ""
  51. self.port = 6660
  52. #recup des param et affichage:
  53. try:
  54. logProg.info(self.recupParamCo())
  55. except:
  56. logProg.info("pas de parametres a recuperer")
  57. self.createWidgets()
  58. def createWidgets(self):
  59. """creation de l'interface de connexion"""
  60. self.ui = Ui_ecranConnexion()
  61. self.ui.setupUi(self)
  62. if len(self.ip) > 0:
  63. self.ui.in_ip.setText(QString.fromUtf8(self.ip))
  64. if self.port > 0:
  65. self.ui.in_port.setText(QString.fromUtf8(str(self.port)))
  66. if len(self.pseudo) > 0:
  67. self.ui.in_pseudo.setText(QString.fromUtf8(self.pseudo))
  68. self.connect(self.ui.fermer, SIGNAL("clicked()"), self.fermer)
  69. self.connect(self.ui.creerServeur, SIGNAL("clicked()"), self.creerServeur)
  70. self.connect(self.ui.seConnecter, SIGNAL("clicked()"), self.seConnecter)
  71. def msg(self, txt):
  72. """affichage d'un message informatif sous forme de label"""
  73. self.ui.txt_msg.setText(QString.fromUtf8(txt))
  74. QApplication.processEvents()
  75. def creerServeur(self):
  76. """instancie un serveur local"""
  77. self.pseudo = self.ui.in_pseudo.text()
  78. self.port = self.ui.in_port.text()
  79. if len(self.pseudo) == 0 or len(self.port) == 0:
  80. self.msg("Parametres incorrects")
  81. else:
  82. self.ui.in_ip.setText(QString.fromUtf8("localhost"))
  83. self.s = Serveur(int(self.ui.in_port.text()))
  84. self.serveur_connecte, txt = self.s.creer()
  85. self.msg(txt)
  86. if self.serveur_connecte:
  87. self.seConnecter()
  88. def seConnecter(self):
  89. """se connecte au serveur correspondant a l'ip et au port saisis"""
  90. self.pseudo = self.ui.in_pseudo.text()
  91. self.port = self.ui.in_port.text()
  92. self.ip = self.ui.in_ip.text()
  93. essais = 0
  94. if len(self.pseudo) == 0 or len(self.ip) == 0 or len(self.port) == 0:
  95. self.msg("Parametres incorrects")
  96. else:
  97. self.msg("En attente du serveur...")
  98. while self.client_connecte == False:
  99. #en attente de la connexion
  100. try:
  101. self.cnn = socket(AF_INET, SOCK_STREAM)
  102. self.cnn.connect((self.ip, int(self.port)))
  103. self.client_connecte = True
  104. txt = "Connexion etablie avec le serveur sur le port {}\n".format(self.port)
  105. logProg.info(txt)
  106. except KeyboardInterrupt:
  107. break
  108. except:
  109. essais += 1
  110. if essais > 3:
  111. txt = "Delai depasse"
  112. self.msg(txt)
  113. logProg.warning(txt)
  114. break
  115. txt = "Connexion : essai {}".format(essais)
  116. self.msg(txt)
  117. logProg.info(txt)
  118. if self.client_connecte:
  119. #demarre le fil de reception
  120. Thread(None, self.filReception, None, (), {}).start()
  121. #previent le serveur et envoie le pseudo
  122. self.envoi("ci", "sa", self.pseudo)
  123. self.msg(txt)
  124. sleep(0.01)
  125. self.close()
  126. self.emit(SIGNAL("majAffichage()"))
  127. else:
  128. txt = "Erreur: impossible de se connecter"
  129. self.msg(txt)
  130. logProg.error(txt)
  131. def filReception(self):
  132. """thread de reception des donnees du serveur, reste actif tant que l'application est active"""
  133. while self.client_connecte:
  134. recu = self.cnn.recv(1024)
  135. if len(recu) > 0:
  136. self.traitement(recu)
  137. def traitement(self, msg):
  138. """determine le traitement a apporter a un message recu, selon sa nature"""
  139. if len(msg) >= 6:
  140. emet = msg[2:4]
  141. dest = msg[4:6]
  142. categorie = msg[:1]
  143. nature = msg[:2]
  144. try:
  145. contenu = msg[6:]
  146. except:
  147. contenu = ""
  148. if nature != "fd" and nature != "ff":
  149. #on decode le message, sauf s'il contient des donnees binaires issues d'un fichier
  150. contenu = contenu.decode('utf-8')
  151. if nature == "ci":
  152. #recoit l'identifiant du client fourni par le serveur
  153. self.idClient = dest
  154. logProg.info("mon id est: {}\n".format(self.idClient))
  155. elif nature == "cc":
  156. if contenu[0:2] != self.idClient:
  157. self.autresCo[str(contenu[0:2])] = str(contenu[2:])
  158. #on lui envoie id+pseudo
  159. sleep(0.02)
  160. self.envoi("cp", contenu[0:2], "{}{}".format(self.idClient, self.pseudo))
  161. logProg.info("{} s'est connecte ({})".format(contenu[2:], contenu[0:2]))
  162. self.recuInfo("cc", "{} s'est connecte ({})".format(contenu[2:], contenu[0:2]))
  163. elif nature == "cp":
  164. self.autresCo[str(contenu[0:2])] = str(contenu[2:])
  165. sleep(0.01)
  166. logProg.info("{} est deja present ({})".format(contenu[2:], contenu[0:2]))
  167. self.recuInfo("cc", "{} est deja la ({})".format(contenu[2:], contenu[0:2]))
  168. elif nature == "cd":
  169. if contenu != self.idClient and len(contenu) == 2:
  170. logProg.info("{} s'est deconnecte ({})".format(self.autresCo[contenu], contenu))
  171. self.recuInfo("cd", "{} s'est deconnecte ({})".format(self.autresCo[contenu], contenu))
  172. del self.autresCo[contenu]
  173. elif categorie == "m":
  174. #afficher dans le chat
  175. logProg.info("chat: {} -> {}\n".format(emet, contenu))
  176. self.recuMsg(emet, contenu)
  177. elif categorie == "i":
  178. #afficher dans la fenetre évènement
  179. self.recuInfo(nature, contenu)
  180. if nature == "id":
  181. #jet de dés: afficher en rouge
  182. logProg.info("jet de dé: {} -> {}\n".format(emet, contenu))
  183. if nature == "ic":
  184. #nouveau client connecte: afficher en bleu
  185. logProg.info("info connexion: {} -> {}\n".format(emet, contenu))
  186. elif categorie == "f":
  187. #envoi ou reception de fichier
  188. if nature == "f0":
  189. #le serveur nous renvoie l'identifiant du fichier que l'on veut envoyer
  190. if self.eFichier["id"] == "00":
  191. if len(contenu) == 2:
  192. Thread(None, self.envoiFichier_1, None, (contenu,), {}).start()
  193. else:
  194. logProg.error("erreur: id du fichier\n")
  195. else:
  196. logProg.warning("Un fichier est en cours d'envoi\n")
  197. elif nature == "fp":
  198. #un clients est pret a recevoir le fichier
  199. self.pretEnvoiFichier(emet)
  200. elif nature == "fi":
  201. #quelqu'un nous envoie un fichier
  202. if len(contenu)>2:
  203. #les 2 premiers car sont l'identifiant, puis le nom du fichier (* la taille)
  204. essai = self.nouveauFichier(contenu[0:2], contenu[2:])
  205. logProg.info("nouveau fichier: {}".format(essai))
  206. if len(essai) > 0:
  207. self.recuInfo(nature, "Fichier {} en cours de reception".format(contenu[2:]))
  208. #on envoie confirmation de la reception:
  209. sleep(0.001)
  210. self.envoi("fp", emet, contenu[0:2])
  211. self.emit(SIGNAL("initRecFichier(QString, QString)"), QString.fromUtf8(essai), QString.fromUtf8(""))
  212. else:
  213. self.recuInfo(nature, "Impossible de créer le fichier à recevoir")
  214. logProg.error("Impossible de créer le fichier à recevoir")
  215. else:
  216. logProg.error("erreur reception fichier: id ou nom du fichier incorrect\n")
  217. elif nature == "fd":
  218. #on recoit les donnees a ecrire dans le fichier
  219. #print("reception: {}".format(contenu))
  220. if len(contenu) > 2:
  221. self.receptionFichier[contenu[0:2]].write(contenu[2:])
  222. #print("{} -> paquet recu".format(contenu[0:2]))
  223. self.emit(SIGNAL("initRecFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("."))
  224. #le client renvoie la longueur de la donnee recue a l'emetteur pour confirmation
  225. sleep(0.001)
  226. self.envoi("fp", emet, contenu[0:2])
  227. elif len(contenu) == 2:
  228. #l'emetteur redemande confirmation, on lui envoie
  229. self.envoi("fp", emet, contenu[0:2])
  230. else:
  231. logProg.error("erreur de reception des donnees du fichier\n")
  232. #pass
  233. elif nature == "ff":
  234. #fin de reception du fichier
  235. self.receptionFichier[contenu[0:2]].write(contenu[2:])
  236. sleep(0.001)
  237. self.envoi("fp", emet, contenu[0:2])
  238. self.recuInfo("ff", "Fichier {} recu\n".format(os.path.basename(self.receptionFichier[contenu[0:2]].name)))
  239. self.emit(SIGNAL("initRecFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("ok"))
  240. self.emit(SIGNAL("inserImg(QString, QString)"), QString.fromUtf8(emet), QString.fromUtf8(self.receptionFichier[contenu[0:2]].name))
  241. sleep(0.01)
  242. self.receptionFichier[contenu[0:2]].close()
  243. del self.receptionFichier[contenu[0:2]]
  244. logProg.info("Fichier recu")
  245. elif nature == "fa":
  246. #envoi annule
  247. logProg.info("Annulation de la reception du fichier")
  248. if self.receptionFichier[contenu[0:2]]:
  249. self.recuInfo("ff", "Reception {} annule \n".format(os.path.basename(self.receptionFichier[contenu[0:2]].name)))
  250. self.emit(SIGNAL("initRecFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("x"))
  251. self.receptionFichier[contenu[0:2]].close()
  252. #on supprime le fichier incomplet:
  253. os.remove(self.receptionFichier[contenu[0:2]].name)
  254. del self.receptionFichier[contenu[0:2]]
  255. elif categorie == "s":
  256. #infos sur le fonctionnement du serveur principal
  257. if nature == "sd":
  258. #le serveur a ete ferme
  259. self.serveurDeco()
  260. elif categorie == "v":
  261. #infos liees au chat vocal
  262. if nature == "vs":
  263. #un serveur vocal a ete cree
  264. self.recuInfo("vs", "{} a cree un serveur vocal ({})".format(self.autresCo[emet], contenu))
  265. self.ipServeurVoc = contenu
  266. if nature == "vi":
  267. #un client rejoint le chat vocal
  268. self.recuInfo("vi", "{} a rejoint le chat vocal".format(self.autresCo[emet]))
  269. if nature == "vq":
  270. #un client quitte le chat vocal
  271. self.recuInfo("vq", "{} a quitte le chat vocal".format(self.autresCo[emet]))
  272. if nature == "vf":
  273. #fermeture du serveur vocal
  274. self.recuInfo("vf", "{} a ferme le serveur vocal".format(self.autresCo[emet]))
  275. elif categorie == "p":
  276. if nature == "pi":
  277. #nouveau plateau créé
  278. logProg.info("nouveau plateau: {}\n".format(emet, contenu))
  279. else:
  280. logProg.warning("Erreur: message illisible -> {}\n".format(msg))
  281. def recuInfo(self, nature, contenu):
  282. """signale une nouvelle information"""
  283. self.emit(SIGNAL("nouvelleInfo(QString, QString)"), QString.fromUtf8(nature), QString.fromUtf8(contenu))
  284. def recuMsg(self, emetteur, contenu):
  285. """signale un nouveau message texte pour le chat ecrit"""
  286. self.emit(SIGNAL("msgChat(QString, QString)"), QString.fromUtf8(emetteur), QString.fromUtf8(contenu))
  287. def envoi(self, nature, dest, msg, idFichier = ""):
  288. """envoie un message au serveur TCP
  289. - longueur du message (3 car)
  290. - nature du message (2 car)
  291. - exp: id de l'expediteur (2 car)
  292. - dest: id du destinataire (2 car)
  293. - msg: contenu du message (999 car max)
  294. - un identifiant pour le fichier le cas echeant"""
  295. exp = self.idClient
  296. if self.client_connecte:
  297. try:
  298. if len(msg) <= 999:
  299. if len(idFichier) == 0:
  300. msg = unicode(msg)
  301. lg = "%003.f"%len(msg.encode('utf-8')) #la longueur apres encodage peut changer
  302. txt = "{}{}{}{}{}".format(lg, nature, exp, dest, msg)
  303. txt = txt.encode('utf-8')
  304. else:
  305. lg = "%003.f"%(len(msg)+2)
  306. txt = "{}{}{}{}{}".format(lg, nature, exp, dest, idFichier)
  307. txt = txt.encode('utf-8') + msg
  308. retour = len(txt)
  309. self.cnn.sendall(txt)
  310. else:
  311. self.recuInfo(" ","999 caracteres au max.")
  312. retour = 0
  313. except:
  314. retour = 0
  315. logProg.warning("Envoi impossible")
  316. else:
  317. retour = ""
  318. logProg.warning("Le client n'est pas connecte au serveur")
  319. self.recuInfo(" ","Vous n'etes pas connecte a un serveur")
  320. return retour
  321. def envoiFichier(self, chemin, dest = "ac"):
  322. """intialise l'envoi d'un fichier"""
  323. if self.eFichier["id"] == "00":
  324. # on verifie si le fichier existe:
  325. fichier = None
  326. try:
  327. fichier = open(chemin, "rb")
  328. except:
  329. logProg.error("Le fichier '{}' est introuvable.".format(fichier))
  330. if not os.path.getsize(chemin) > 0:
  331. logProg.error("Envoi impossible - fichier vide")
  332. fichier.close()
  333. fichier == None
  334. if fichier:
  335. #on demande un identifiant au serveur
  336. self.eFichier["fichier"] = fichier
  337. self.eFichier["chemin"] = chemin
  338. self.eFichier["dest"] = dest
  339. self.eFichier["annule"] = False
  340. self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(os.path.basename(chemin)), QString.fromUtf8("0"))
  341. logProg.debug(self.eFichier)
  342. if dest == "ac":
  343. for idC in self.autresCo:
  344. self.destFichier[idC] = False
  345. else:
  346. self.destFichier[dest] = False
  347. self.envoi("f0","sa","")
  348. else:
  349. self.annuleEnvoiFichier()
  350. def envoiFichier_1(self, idFichier):
  351. """le fichier est pret a etre envoye, on attend confirmation des destinataires"""
  352. if len(idFichier) == 2:
  353. dest = self.eFichier["dest"]
  354. self.eFichier["id"] = idFichier
  355. nomFichier = os.path.basename(self.eFichier["chemin"])
  356. taille = os.path.getsize(self.eFichier["chemin"])
  357. logProg.debug("{} a pour id {}\n".format(nomFichier, idFichier))
  358. #on previent les destinataires, et on leur transmet l'identifiant et le nom*taille du fichier
  359. logProg.info("En attente des destinataires...")
  360. self.recuInfo("fi", "{} - En attente des destinataires...".format(nomFichier))
  361. self.envoi("fi", dest, "{}{}".format(idFichier, nomFichier))
  362. while not self.eFichier["envoi"] and not self.eFichier["annule"]:
  363. sleep(0.001)
  364. if not self.eFichier["annule"]:
  365. Thread(None, self.envoiFichier_2, None, (), {}).start()
  366. else:
  367. logProg.error("Erreur envoi fichier: identifiant incorrect -> {}".format(idFichier))
  368. self.annuleEnvoiFichier()
  369. def pretEnvoiFichier(self, idClient):
  370. """signale un destinataire comme etant pret a recevoir le fichier ou le paquet de donnees"""
  371. pretEnvoi = False
  372. if self.eFichier["dest"] == "ac":
  373. self.destFichier[idClient] = True
  374. pretEnvoi = True
  375. for idC in self.destFichier:
  376. if self.destFichier[idC] == False:
  377. pretEnvoi = False
  378. #si tous les clients sont prets, on enclenche l'envoi du fichier sous forme de thread
  379. else:
  380. if idClient == self.eFichier["dest"]:
  381. pretEnvoi = True
  382. #print("{} -> {}".format(idClient, pretEnvoi))
  383. self.eFichier["envoi"] = pretEnvoi
  384. def envoiFichier_2(self):
  385. """thread d'envoi de fichier"""
  386. if self.eFichier["envoi"] and not self.eFichier["annule"]:
  387. idFichier = self.eFichier["id"]
  388. taille = os.path.getsize(self.eFichier["chemin"])
  389. nomFichier = os.path.basename(self.eFichier["chemin"])
  390. dest = self.eFichier["dest"]
  391. envoye = 0
  392. essais = 0
  393. #on envoie les donnees
  394. data = self.eFichier["fichier"].read(512)
  395. while len(data) == 512:
  396. if self.eFichier["annule"]:
  397. #en cas d'annulation
  398. break
  399. self.envoi("fd", dest, data, idFichier)
  400. envoye += len(data)
  401. logProg.info("{} / {}".format(int(envoye/1000), int(taille/1000)))
  402. #on attend confirmation de reception:
  403. self.eFichier["envoi"] = False
  404. if dest == "ac":
  405. for idC in self.autresCo:
  406. self.destFichier[idC] = False
  407. else:
  408. self.destFichier[dest] = False
  409. while not self.eFichier["envoi"]:
  410. if self.eFichier["annule"]:
  411. break
  412. sleep(0.001)
  413. essais += 1
  414. if essais >= 1000:
  415. #on renvoie un message pour demander confirmation
  416. self.envoi("fd", dest, b'', idFichier)
  417. essais = 0
  418. taux = str((100*envoye)/taille)
  419. self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8(taux))
  420. data = self.eFichier["fichier"].read(512)
  421. #pour dernier paquet on ajoute au drapeau: "%003.f"%len(paquet)
  422. #on signale que c'est le dernier paquet et on ajoute 3 caracteres pour en specifier la taille:
  423. if not self.eFichier["annule"]:
  424. self.envoi("ff", dest, data, idFichier)
  425. envoye += len(data)
  426. logProg.info("{} / {}".format(int(envoye/1000), int(taille/1000)))
  427. self.eFichier["envoi"] = False
  428. while not self.eFichier["envoi"]:
  429. if self.eFichier["annule"]:
  430. break
  431. sleep(0.001)
  432. essais += 1
  433. if essais >= 1000:
  434. #on renvoie un message pour demander confirmation
  435. logProg.warning("envoi fichier - on redemande confirmation")
  436. self.envoi("fd", dest, b'', idFichier)
  437. essais = 0
  438. taux = str((100*envoye)/taille)
  439. self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8(taux))
  440. self.envoi("f1", "sa", "{}".format(idFichier))
  441. self.recuInfo("ff", "- {} a bien ete envoye -".format(nomFichier))
  442. self.emit(SIGNAL("inserImg(QString, QString)"), QString.fromUtf8(self.idClient), QString.fromUtf8(self.eFichier["chemin"]))
  443. logProg.info("\n- Fichier envoye -")
  444. self.eFichier = {"id": "00", "chemin": "", "fichier": None, "dest": "", "envoi" : False, "annule": False}
  445. else:
  446. self.eFichier = {"id": "00", "chemin": "", "fichier": None, "dest": "", "envoi" : False, "annule": True}
  447. else:
  448. logProg.error("Erreur envoi fichier: signal serveur non-recu -> {}".format(self.eFichier["id"]))
  449. self.eFichier = {"id": "00", "chemin": "", "fichier": None, "dest": "", "envoi" : False, "annule": False}
  450. def annuleEnvoiFichier(self):
  451. """annule l'envoi d'un fichier"""
  452. logProg.warning("Annulation de l'envoi")
  453. self.envoi("fa", self.eFichier["dest"], "{}".format(self.eFichier["id"]))
  454. self.eFichier["annule"] = True
  455. essais = 0
  456. while not self.eFichier["id"] == "00":
  457. sleep(0.01)
  458. essais += 1
  459. if essais > 100:
  460. logProg.error("Erreur: impossible d'annuler l'envoi")
  461. logProg.info("-> Envoi annulé")
  462. self.recuInfo("fa", "- Envoi du fichier annule -")
  463. self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("x"))
  464. def nouveauFichier(self, idFichier, nomFichier):
  465. """cree le fichier a recevoir - le renomme si un fichier portant ce nom existe deja - retourne le chemin complet"""
  466. k = 1
  467. tmp = nomFichier
  468. retour = ""
  469. try:
  470. while os.path.isfile(self.repReceptionFichiers+tmp):
  471. k += 1
  472. tmp = nomFichier.split(".")[0]+str(k)+"."+nomFichier.split(".")[1]
  473. if k == 100:
  474. tmp = ""
  475. break
  476. if len(tmp) > 0:
  477. self.receptionFichier[idFichier] = open((self.repReceptionFichiers + tmp), "wb")
  478. retour = tmp
  479. except:
  480. logProg.error("Impossible de creer le fichier")
  481. return retour
  482. def serveurDeco(self):
  483. """le serveur a ferme - on affiche a nouveau l'ecran de connexion"""
  484. #on annule les envois de fichier en cours
  485. if self.eFichier["fichier"] != None:
  486. self.eFichier["annule"] = True
  487. self.emit(SIGNAL("initEnvFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("x"))
  488. #on annule les receptions de fichiers en cours
  489. for idFichier in self.receptionFichier:
  490. logProg.warning("{} - reception annulee".format(self.receptionFichier[idFichier].name))
  491. self.receptionFichier[idFichier].close()
  492. os.remove(self.receptionFichier[idFichier].name)
  493. self.emit(SIGNAL("initRecFichier(QString, QString)"), QString.fromUtf8(""), QString.fromUtf8("x"))
  494. self.receptionFichier = {}
  495. self.recuInfo("sd", "(!) Le serveur a mis la clef sous la porte (!)")
  496. logProg.warning("Serveur deconnecte")
  497. self.cnn.close()
  498. self.client_connecte = False
  499. self.serveur_lance = False
  500. sleep(0.01)
  501. self.show()
  502. self.msg("")
  503. def recupParamCo(self):
  504. """recupere les derniers parametres de connexion enregistres s'il existent"""
  505. try:
  506. with open("parametresCo", 'rb') as input:
  507. dico = pickle.load(input)
  508. self.pseudo = dico["pseudo"]
  509. self.port = dico["port"]
  510. self.ip = dico["ip"]
  511. self.repReceptionFichiers = dico["repReceptionFichiers"]
  512. retour = dico
  513. input.close()
  514. except IOError:
  515. retour = ("Erreur: parametresCo introuvable")
  516. return retour
  517. def sauverParamCo(self):
  518. """sauvegarde les parametres de connexion pour une prochaine utilisation"""
  519. with open("parametresCo", 'wb') as output:
  520. dico = {"pseudo": str(self.pseudo), "port" : int(self.port), "ip": str(self.ip), "repReceptionFichiers" : str(self.repReceptionFichiers)}
  521. pickle.dump(dico, output, -1)
  522. output.close()
  523. chaine = "parametres sauvegarde."
  524. return chaine
  525. def fermer(self):
  526. """fermeture de la connexion, et du serveur le cas echeant"""
  527. if self.client_connecte:
  528. try:
  529. self.envoi("cd", "sa", "")
  530. except:
  531. logProg.warning("impossible de prévenir le serveur de la deco")
  532. self.client_connecte = False
  533. self.cnn.close()
  534. if self.eFichier["fichier"] != None:
  535. self.eFichier["annule"] = True
  536. sleep(0.001)
  537. logging.info(self.sauverParamCo())
  538. if self.serveur_connecte:
  539. self.s.stop()
  540. self.serveur_connecte = False
  541. sleep(0.001)
  542. self.close()
  543. def closeEvent(self, event):
  544. """sur fermeture de la fenetre"""
  545. if not self.client_connecte:
  546. self.fermer()
  547. class EcranChat(QGroupBox):
  548. """interface comprenant: chat ecrit, fenetre d'infos, lancer de des, echange de fichiers, lancement du chat vocal"""
  549. def __init__(self, connexion, parent=None):
  550. """initialisation de la fenetre"""
  551. super (EcranChat, self).__init__(parent)
  552. self.co = connexion
  553. self.connecte = True
  554. self.estServeurVoc = False
  555. self.estClientVoc = False
  556. self.createWidgets()
  557. def createWidgets(self):
  558. """construction de l'interface"""
  559. #construction de l'interface
  560. self.ui = Ui_chat()
  561. self.ui.setupUi(self)
  562. #connexion des commandes
  563. self.connect(self.ui.inChat, SIGNAL("returnPressed()"), self.envoiMsg)
  564. self.connect(self.ui.d20, SIGNAL("clicked()"), self.d20)
  565. self.connect(self.ui.d100, SIGNAL("clicked()"), self.d100)
  566. self.connect(self.ui.inJetDes, SIGNAL("returnPressed()"), self.autreJet)
  567. self.connect(self.ui.envoiFichier, SIGNAL("clicked()"), self.envoyerfichier)
  568. self.connect(self.ui.repReceptionFichiers, SIGNAL("clicked()"), self.repReception)
  569. self.connect(self.ui.listFichiers, SIGNAL("itemDoubleClicked(QTreeWidgetItem*, int)"), self.ouvrirFichier)
  570. self.connect(self.ui.chatVoc, SIGNAL("clicked()"), self.chatVoc)
  571. self.connect(self.ui.creerServeurVoc, SIGNAL("clicked()"), self.creerServeurVoc)
  572. #reception des signaux self.emit(SIGNAL("majAffichage()"))
  573. self.connect(self, SIGNAL("msgChat(QString, QString)"), self.ajoutChat)
  574. self.connect(self.co, SIGNAL("msgChat(QString, QString)"), self.ajoutChat)
  575. self.connect(self, SIGNAL("nouvelleInfo(QString, QString)"), self.ajoutInfo)
  576. self.connect(self.co, SIGNAL("nouvelleInfo(QString, QString)"), self.ajoutInfo)
  577. self.connect(self, SIGNAL("initRecFichier(QString, QString)"), self.afficheReception)
  578. self.connect(self.co, SIGNAL("initRecFichier(QString, QString)"), self.afficheReception)
  579. self.connect(self, SIGNAL("inserImg(QString, QString)"), self.inserImgChat)
  580. self.connect(self.co, SIGNAL("inserImg(QString, QString)"), self.inserImgChat)
  581. self.connect(self, SIGNAL("initEnvFichier(QString, QString)"), self.afficheEnvoi)
  582. self.connect(self.co, SIGNAL("initEnvFichier(QString, QString)"), self.afficheEnvoi)
  583. self.connect(self, SIGNAL("majAffichage()"), self.majStatut)
  584. self.connect(self.co, SIGNAL("majAffichage()"), self.majStatut)
  585. self.majStatut()
  586. def msg(self, txt):
  587. """affichage d'un message informatif sous forme de label"""
  588. self.ui.txt_msg.setText(QString.fromUtf8(txt))
  589. QApplication.processEvents()
  590. def ajoutChat(self, emetteur, msg):
  591. """ajoute une nouvelle ligne au chat ecrit"""
  592. emetteur = str(emetteur)
  593. if emetteur == self.co.idClient:
  594. txt = "<font color=\"blue\">{} : </font> {}".format(self.co.pseudo, msg)
  595. else:
  596. pseudo = self.co.autresCo["{}".format(emetteur)]
  597. txt = "<font color=\"green\">{} : </font> {}".format(pseudo, msg)
  598. txt = QString.fromUtf8(txt)
  599. item = QListWidgetItem(self.ui.listAffichage)
  600. self.ui.listAffichage.addItem(item)
  601. label = QLabel()
  602. label.setWordWrap(True)
  603. label.setText(txt)
  604. item.setSizeHint(QSize(120, label.heightForWidth (self.ui.listAffichage.width()) + 5))
  605. self.ui.listAffichage.setItemWidget(item, label)
  606. QApplication.processEvents()
  607. def ajoutInfo(self, nature, msg):
  608. """ajoute une nouvelle ligne a la liste des evenements"""
  609. #couleur selon nature de l'info:
  610. if nature[:1] == "c":
  611. txt = "<font color=\"green\">{}</font>".format(msg)
  612. elif nature == "id":
  613. txt = "<font color=\"red\">{}</font>".format(msg)
  614. elif nature[:1] == "f":
  615. txt = "<font color=\"blue\">{}</font>".format(msg)
  616. elif nature[:1] == "v":
  617. txt = "<font color=\"orange\">{}</font>".format(msg)
  618. else:
  619. txt = msg
  620. txt = QString.fromUtf8(txt)
  621. item = QListWidgetItem(self.ui.listEvenement)
  622. self.ui.listEvenement.addItem(item)
  623. label = QLabel()
  624. label.setWordWrap(True)
  625. label.setText(txt)
  626. item.setSizeHint(QSize(120, label.heightForWidth (self.ui.listEvenement.width()) + 5))
  627. self.ui.listEvenement.setItemWidget(item, label)
  628. QApplication.processEvents()
  629. def inserImgChat(self, emet, fichier):
  630. """insere une image dans le QListWidget chat"""
  631. emet = str(emet)
  632. fichier = str(fichier)
  633. try:
  634. typeFichier = fichier.split(".")[len(fichier.split("."))-1]
  635. except:
  636. typeFichier = ""
  637. if typeFichier.lower() in ["png", "jpeg", "jpg"]:
  638. #si c'est une image, on essaie de l'inserer dans le chat
  639. logProg.debug("ajout d'une image au chat : " + fichier)
  640. self.ajoutChat(str(emet), "<img src=\"{}\"/>".format(fichier))
  641. def envoiMsg(self):
  642. """envoie un message pour le chat ecrit"""
  643. msg = self.ui.inChat.text()
  644. self.co.envoi("m ", "tc", msg)
  645. self.ui.inChat.clear()
  646. QApplication.processEvents()
  647. def lancerD(self, expr):
  648. """fonction de lancer de des"""
  649. res, detail = jet(expr)
  650. if res > 0:
  651. txt = "{}: {} ({}) [{}]".format(self.co.pseudo, res, detail, expr)
  652. self.co.envoi("id", "tc", txt)
  653. else:
  654. self.ajoutInfo("id", "mmmhh, pas bon le jet")
  655. return res
  656. def d20(self):
  657. """lance un D20"""
  658. self.lancerD("1d20")
  659. def d100(self):
  660. """lance un D100"""
  661. self.lancerD("1d100")
  662. def autreJet(self):
  663. """lance un jet personnalise"""
  664. expr = str(self.ui.inJetDes.text())
  665. retour = self.lancerD(expr)
  666. self.ui.inJetDes.clear()
  667. def envoyerfichier(self):
  668. """selectionne et envoie un fichier ou annule l'envoi en cours s'il existe"""
  669. if self.co.eFichier["id"] == "00":
  670. fichier = QFileDialog.getOpenFileName(
  671. self,
  672. "Selectionnez un fichier a envoyer",
  673. "c:\\",
  674. "")
  675. if len(str(fichier)) > 0:
  676. self.co.envoiFichier(str(fichier))
  677. else:
  678. self.co.annuleEnvoiFichier()
  679. def ouvrirFichier(self):
  680. """ouvre un fichier depuis la liste des fichiers recus/envoyes"""
  681. item = self.ui.listFichiers.currentItem()
  682. eR = item.text(0)
  683. taux = item.text(1)
  684. nom = item.text(2)
  685. if eR == "R":
  686. if taux == "ok":
  687. #try:
  688. chemin = "{}\\{}".format(self.co.repReceptionFichiers, nom)
  689. Popen(chemin, shell=True, stdout=PIPE) #from subprocess
  690. #except:
  691. # print("impossible d'ouvrir le fichier")
  692. def repReception(self):
  693. """permet de choisir son repertoire de reception des fichiers"""
  694. dossier = QFileDialog.getExistingDirectory(self)
  695. if dossier[len(dossier)-1:] != "\\":
  696. dossier += "\\"
  697. self.co.repReceptionFichiers = str(dossier)
  698. self.majStatut()
  699. def afficheReception(self, fichier, taux):
  700. """ajoute une ligne a la liste des fichiers en cours de reception"""
  701. fichier = str(fichier)
  702. taux = str(taux)
  703. if len(fichier) > 0:
  704. self.ligneRFichier = QTreeWidgetItem(self.ui.listFichiers, ["R", taux, fichier])
  705. elif len(taux) > 0:
  706. if taux != self.ligneRFichier.text(1):
  707. self.ligneRFichier.setText(1, taux)
  708. def afficheEnvoi(self, fichier, taux):
  709. """ajoute une ligne a la liste des fichiers en cours d'envoi"""
  710. fichier = str(fichier)
  711. taux = str(taux)
  712. if len(fichier) > 0:
  713. self.ligneEFichier = QTreeWidgetItem(self.ui.listFichiers, ["E", taux, fichier])
  714. elif len(taux) > 0:
  715. if taux != self.ligneEFichier.text(1):
  716. self.ligneEFichier.setText(1, taux)
  717. def majStatut(self):
  718. """met a jour la ligne de statut au pied de l'interface"""
  719. if self.co.serveur_connecte:
  720. txt = "Serveur : Oui"
  721. else:
  722. txt = "Serveur : Non"
  723. txt += " \ Pseudo : {}".format(self.co.pseudo)
  724. if self.estServeurVoc:
  725. txt += " \ Serveur vocal"
  726. if self.estClientVoc:
  727. txt += " \ connecte au chat vocal"
  728. self.msg(txt)
  729. self.ui.txt_repReception.setText(QString.fromUtf8("Fichiers recus : '{}'".format(self.co.repReceptionFichiers)))
  730. QApplication.processEvents()
  731. def creerServeurVoc(self):
  732. """cree un serveur vocal"""
  733. if not self.estServeurVoc:
  734. self.sVoc = ServeurVoc(6660)
  735. self.co.ipServeurVoc = "localhost"
  736. txt = self.sVoc.creer()
  737. self.ajoutInfo("vs", txt)
  738. if self.sVoc.serveur_lance:
  739. self.co.envoi("vs","ac","{}".format("localhost"))
  740. self.estServeurVoc = True
  741. self.majStatut()
  742. else:
  743. self.fermerServeurVoc()
  744. def fermerServeurVoc(self):
  745. """ferme le serveur vocal"""
  746. txt = self.sVoc.stop()
  747. self.ajoutInfo("vs", txt)
  748. if not self.sVoc.serveur_lance:
  749. self.co.envoi("vf","ac","")
  750. self.estServeurVoc = False
  751. self.majStatut()
  752. def chatVoc(self):
  753. """connexion au chat vocal"""
  754. if not self.estClientVoc:
  755. if len(self.co.ipServeurVoc) > 0:
  756. self.cVoc = ClientVoc(self.co.idClient, self.co.ipServeurVoc, 6660)
  757. txt = self.cVoc.creer()
  758. if self.cVoc.connecte:
  759. self.ajoutInfo("vi", txt)
  760. self.co.envoi("vi","ac","")
  761. self.estClientVoc = True
  762. self.majStatut()
  763. else:
  764. self.quitterChatVoc()
  765. def quitterChatVoc(self):
  766. """deconnexion du chat vocal"""
  767. if self.estClientVoc:
  768. txt = self.cVoc.stop()
  769. self.ajoutInfo("vq", txt)
  770. self.co.envoi("vq","ac","")
  771. self.estClientVoc = False
  772. self.majStatut()
  773. def closeEvent(self, event):
  774. """sur fermeture de la fenetre"""
  775. if self.estClientVoc:
  776. self.cVoc.stop()
  777. if self.estServeurVoc:
  778. self.sVoc.stop()
  779. self.co.fermer()
  780. sleep(0.01)
  781. self.connecte = False
  782. self.close()
  783. if __name__ == "__main__":
  784. demarrageOk = True
  785. #repertoire de travail et creation des dossiers necessaires:
  786. try:
  787. repCourant = os.getcwd()
  788. except:
  789. repCourant = ""
  790. if not len(repCourant) > 0:
  791. logProg.error("Impossible de determiner le repertoire courant")
  792. demarrageOk = False
  793. else:
  794. try:
  795. #repertoire media (musiques+images)
  796. if not os.path.exists(repCourant+"\\media"):
  797. os.mkdir(repCourant+"\\media")
  798. #repertoire utilisateur (sauvegardes)
  799. if not os.path.exists(repCourant+"\\svg"):
  800. os.mkdir(repCourant+"\\svg")
  801. #repertoire reception des fichiers persos
  802. if not os.path.exists(repCourant+"\\FichiersRecus"):
  803. os.mkdir(repCourant+"\\FichiersRecus")
  804. except:
  805. logProg.error("Erreur de creation des repertoires de l'application")
  806. demarrageOk = False
  807. #verif si l'appli est deja lancee:
  808. nomAppli = "DMonde.exe"
  809. compte = 0
  810. for proc in process_iter(): #from psutil
  811. try:
  812. nomProc = proc.name()
  813. if nomProc == nomAppli:
  814. compte += 1
  815. if compte > 1:
  816. demarrageOk = False
  817. logProg.error("Une instance de l'application est deja en cours d'execution")
  818. break
  819. except:
  820. pass
  821. if demarrageOk:
  822. #lancement de l'appli
  823. app = QApplication(argv) #'argv' vient de 'sys'
  824. connexion = EcranConnexion()
  825. connexion.show()
  826. r = app.exec_()
  827. if r == 0 and connexion.client_connecte:
  828. #si pas d'erreur et client connecte, on ouvre l'interface principale
  829. ecranChat = EcranChat(connexion)
  830. ecranChat.show()
  831. r = app.exec_()
  832. exit(r) #'exit' vient de 'sys'