Pion.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #from __future__ import unicode_literals
  2. # -*- coding: utf-8 -*-
  3. from PyQt4.QtCore import QPointF, Qt, QRectF
  4. from PyQt4.QtGui import QGraphicsItem, QColor, QGraphicsPolygonItem, QPen, \
  5. QGraphicsDropShadowEffect, QGraphicsPixmapItem, QGraphicsSimpleTextItem, \
  6. QFont, QGraphicsObject
  7. from Forme import Forme
  8. from lib.rsc import RImage
  9. class Pion(QGraphicsObject):
  10. """pion du plateau de combat"""
  11. def __init__(self, parent=None):
  12. super(Pion, self).__init__()
  13. #caracteristiques du pion
  14. self.numero = -1
  15. self.couleur = QColor(200, 200, 200)
  16. self.logo = RImage()
  17. self.img = ImgPion()
  18. self.etiquette = EtiquettePion()
  19. #infos liees au plateau (forme et position)
  20. self.plateau = None
  21. self.numComplementaire = "" #numero complementaire si necessaire
  22. #(si plusieurs pions portent le meme nom)
  23. self.position = (-1, -1)
  24. self.z = 0
  25. self.hauteur = 1
  26. self.forme = None
  27. self.formeDef = {"H":[], "C":[]}
  28. self.etat = 0 #de 0 (indemne) a 4 (mort ou hors combat)
  29. self.statuts = []
  30. #objets et parametres graphiques
  31. self.pixGraphique = None
  32. self.etiquetteGraphique = None
  33. self._l0 = 0
  34. self._h0 = 0
  35. self.polygoneGraphique = None
  36. self.nbRotations = 0
  37. def __getstate__(self):
  38. nePasSvg = ["plateau", "brillance", "shadow", "creature", "polygonesForme", "pixGraphique", "etiquetteGraphique"]
  39. state = {key:value for key, value in self.__dict__.items() if not key in nePasSvg}
  40. return (state)
  41. def __setstate__(self, state):
  42. self.__dict__ = state
  43. self.pixGraphique = None
  44. self.etiquetteGraphique = None
  45. super(Pion, self).__init__()
  46. def paint(self, painter, option, widget = None):
  47. """reimplemente de QGraphicsItem: on ne peint pas cet item, seulement ses items enfants"""
  48. pass
  49. def txtId(self):
  50. """renvoie le nom et le numero complementaire du pion"""
  51. return "{} {}".format(self._nom, self.numComplementaire)
  52. def icone(self):
  53. """renvoie l'image a afficher dans les listes"""
  54. return self.logo
  55. def yReel(self):
  56. """renvoie le y reel (pour les contructions graphiques)"""
  57. if 1 == (self.position[0] % 2):
  58. y = self.position[1] + 0.5
  59. else:
  60. y = self.position[1]
  61. return y
  62. ###attributs du pion
  63. def position(self):
  64. """retourne la position actuelle du pion"""
  65. return self.position
  66. def casesOccupees(self):
  67. """retourne la liste des cases occupees sur le plateau par le pion (x,y,z)"""
  68. retour = []
  69. if self.plateau:
  70. for x, y in self.forme.listeCases(self.position, self.nbRotations):
  71. for z in range(1, self.hauteur + 1):
  72. retour.append((x, y, (self.plateau.cases[(x, y)].altitude + self.z + z)))
  73. return retour
  74. def majZ(self, valeur):
  75. """met a jour l'altitude Z du pion"""
  76. if valeur != self.z:
  77. self.z = valeur
  78. def zAbs(self):
  79. """retourne la coord z absolue du pion"""
  80. return (self.plateau.cases[self.position].altitude + self.z)
  81. ########### fonctions graphiques et geometriques ##############
  82. def ajouterAuPlateau(self, plateau):
  83. """cerre l'objet graphique representant le pion et le place sur le plateau"""
  84. self.plateau = plateau
  85. #definition de la forme (interpretation de formeDef)
  86. self.forme = Forme(self.plateau.formeCases)
  87. if len(self.formeDef[self.plateau.formeCases]) > 0:
  88. self.forme.definirForme(self.formeDef[self.plateau.formeCases])
  89. #creation du polygone
  90. polygone = self.plateau.polygoneAgglo(self.forme.listeCases((0,0)))
  91. self.polygoneGraphique = QGraphicsPolygonItem()
  92. self.polygoneGraphique.setPolygon(polygone)
  93. self.polygoneGraphique.setAcceptHoverEvents(True)
  94. self.polygoneGraphique.setFlag(QGraphicsItem.ItemIsFocusable) #l'item peut recevoir des commandes souris/clavier
  95. self.polygoneGraphique.setParentItem(self)
  96. self.polygoneGraphique.setPos(QPointF(0,0))
  97. if self.plateau.formeCases == "H":
  98. self.polygoneGraphique.setTransformOriginPoint(QPointF(2*0.2886*self.plateau.hCase, 0.5*self.plateau.hCase))
  99. else:
  100. self.polygoneGraphique.setTransformOriginPoint(QPointF(0.5*self.plateau.hCase,0.5*self.plateau.hCase))
  101. #parametres de l'objet graphique
  102. self.setZValue(10)
  103. self.setFlag(QGraphicsItem.ItemHasNoContents)
  104. self.setHandlesChildEvents(True)
  105. pinceau = QPen()
  106. pinceau.setColor(self.couleur.darker(130))
  107. pinceau.setWidth(10)
  108. self.polygoneGraphique.setPen(pinceau)
  109. if self.couleur.isValid():
  110. self.polygoneGraphique.setBrush(self.couleur)
  111. else:
  112. self.polygoneGraphique.setBrush(QColor(255, 0, 0, 150))
  113. self.shadow = QGraphicsDropShadowEffect()
  114. self.shadow.setColor(QColor(50, 50, 50))
  115. self.shadow.setXOffset(1)
  116. self.shadow.setYOffset(2)
  117. self.shadow.setBlurRadius(3)
  118. self.shadow.setEnabled(True)
  119. self.polygoneGraphique.setGraphicsEffect(self.shadow)
  120. self.polygoneBrillance = QGraphicsPolygonItem()
  121. self.polygoneBrillance.setPolygon(self.polygoneGraphique.polygon())
  122. self.polygoneBrillance.setVisible(False)
  123. self.polygoneGraphique.setFlag(QGraphicsItem.ItemIsFocusable)
  124. self.setAcceptHoverEvents(True) #accepte les evenements survol souris
  125. self.polygoneBrillance.setParentItem(self.polygoneGraphique)
  126. #on ajoute l'objet au plateau
  127. self.plateau.addItem(self)
  128. self.majPosition(self.position, self.nbRotations)
  129. def majPosition(self, nouvellePosition, nbRotations = 0):
  130. """met a jour la position de l'objet graphique et de sa forme en fonction de sa position enregistree"""
  131. if self.plateau:
  132. #on met a jour l'occupation des cases
  133. if self.position != (-1,-1):
  134. for coord in self.forme.listeCases(self.position, self.nbRotations):
  135. for z in range(self.z, (self.z + self.hauteur)):
  136. self.plateau.cases[coord].liberer(z)
  137. #on met a jour la position du pion
  138. self.position = nouvellePosition
  139. self.majNbRotation(nbRotations)
  140. #on replace
  141. if self.plateau.formeCases == "H":
  142. angleRotation = 60
  143. positionGraphique = QPointF(self.position[0] * 0.866 * self.plateau.hCase, self.yReel() * self.plateau.hCase)
  144. else:
  145. angleRotation = 90
  146. positionGraphique = QPointF(self.position[0] * self.plateau.hCase, self.position[1] * self.plateau.hCase)
  147. self.prepareGeometryChange()
  148. self.setPos(positionGraphique)
  149. self.polygoneGraphique.setRotation(self.nbRotations*angleRotation)
  150. #maj de l'image
  151. self.majImage()
  152. #on met a jour l'occupation des cases
  153. for coord in self.forme.listeCases(self.position, self.nbRotations):
  154. for z in range(self.z, (self.z + self.hauteur)):
  155. self.plateau.cases[coord].occuper(self.numero, z)
  156. def majImage(self):
  157. """met a jour la taille, la position et l'orientation de l'image"""
  158. if self.img.rimage.estValide():
  159. pix = self.img.rimage.pix()
  160. if not pix.isNull():
  161. if not self.pixGraphique:
  162. self.pixGraphique = QGraphicsPixmapItem()
  163. self.pixGraphique.setZValue(10)
  164. if pix.height() >= pix.width():
  165. pix = pix.scaledToHeight(self.plateau.hCase*0.9, Qt.SmoothTransformation)
  166. else:
  167. pix = pix.scaledToWidth(self.plateau.hCase*0.9, Qt.SmoothTransformation)
  168. self._l0 = pix.width()
  169. self._h0 = pix.height()
  170. pix = pix.scaled((self.img.kx/10)*pix.width(), \
  171. (self.img.ky/10)*pix.height(), \
  172. Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
  173. self.pixGraphique.setPixmap(pix)
  174. ## deltaX = self.img.dx + 0.5*(self.plateau.hCase*1.1544 - self._l0)
  175. ## deltaY = self.img.dy + 0.5*(self.plateau.hCase - self._h0)
  176. deltaX = self.img.dx + 0.5*(self.plateau.hCase*1.1544 - self.pixGraphique.pixmap().width())
  177. deltaY = self.img.dy + 0.5*(self.plateau.hCase - self.pixGraphique.pixmap().height())
  178. if self.img.rimage.idR() == self.logo.idR():
  179. #si l'image est le logo, elle ne doit pas pivoter
  180. self.pixGraphique.setParentItem(self)
  181. else:
  182. self.pixGraphique.setParentItem(self.polygoneGraphique)
  183. self.pixGraphique.setRotation(self.img.rotation)
  184. self.pixGraphique.setPos(QPointF(deltaX, deltaY))
  185. def majEtiquette(self):
  186. """met a jour la taille, le format et l'orientation de l'etiquette"""
  187. self.etiquetteGraphique = QGraphicsSimpleTextItem("{}".format(self.txtId()))
  188. self.etiquetteGraphique.setPos(QPointF(self.etiquette.dx - 0.112*self.plateau.hCase, \
  189. self.etiquette.dy - 0.5*self.plateau.hCase))
  190. police = QFont("Verdana", self.etiquette.taille_police)
  191. police.setBold(self.etiquette.gras)
  192. self.etiquetteGraphique.setFont(police)
  193. self.etiquetteGraphique.setParentItem(self)
  194. def majNbRotation(self, nbRotations):
  195. """ajoute/retranche le nombre au nombre total de rotations du pion"""
  196. self.nbRotations = nbRotations
  197. if self.plateau.formeCases == "H":
  198. rotationsTour = 6
  199. else:
  200. rotationsTour = 4
  201. if self.nbRotations >= 0:
  202. self.nbRotations = self.nbRotations % rotationsTour
  203. else:
  204. self.nbRotations = self.nbRotations % (-rotationsTour)
  205. def retirerDuPlateau(self):
  206. """'deconnecte' les items enfants avant de supprimer du pion du plateau"""
  207. for coord in self.forme.listeCases(self.position, self.nbRotations):
  208. for z in range(self.z, (self.z + self.hauteur)):
  209. self.plateau.cases[coord].liberer(z)
  210. self.polygoneBrillance.prepareGeometryChange()
  211. self.polygoneBrillance.setParentItem(None)
  212. if self.pixGraphique != None:
  213. self.pixGraphique.prepareGeometryChange()
  214. self.pixGraphique.setParentItem(None)
  215. self.polygoneGraphique.prepareGeometryChange()
  216. self.polygoneGraphique.setParentItem(None)
  217. if self.etiquetteGraphique:
  218. self.etiquetteGraphique.prepareGeometryChange()
  219. self.etiquetteGraphique.setParentItem(None)
  220. self.plateau.removeItem(self)
  221. self.plateau = None
  222. ###effets graphiques
  223. def afficheOmbreSelection(self, actif = False):
  224. """modifie l'ombre du pion en fonction de si celui-ci est selectionne ou non"""
  225. if actif:
  226. self.shadow.setXOffset(3)
  227. self.shadow.setYOffset(3)
  228. else:
  229. self.shadow.setXOffset(1)
  230. self.shadow.setYOffset(2)
  231. def surbrillance(self, active, opacite = 0.7, couleur = "white"):
  232. """active/desactive la surbrillance"""
  233. if active:
  234. self.polygoneBrillance.setOpacity(opacite)
  235. couleur = self.couleurSurbrillance(couleur)
  236. pinceau = self.polygoneGraphique.pen()
  237. self.polygoneBrillance.setBrush(couleur)
  238. self.polygoneBrillance.setPen(pinceau)
  239. self.polygoneBrillance.setVisible(active)
  240. def estCibleAttaque(self, estCible, possible = True):
  241. """le pion s'affiche comme etant cible d'une attaque"""
  242. if not possible:
  243. couleur = "red"
  244. else:
  245. couleur = "white"
  246. self.surbrillance(estCible, 0.8, couleur)
  247. def couleurSurbrillance(self, couleur = "white"):
  248. """renvoie une QColor visible pour la surbrillance, selon la couleur du pion"""
  249. retour = QColor(couleur)
  250. if self.polygoneGraphique.brush().color().lightness() > 220:
  251. retour = retour.darker(140)
  252. elif self.polygoneGraphique.brush().color().lightness() < 80:
  253. retour = retour.lighter(140)
  254. return retour
  255. ##################
  256. ############### evenements clavier et souris ##############
  257. def boundingRect(self):
  258. return QRectF()
  259. #######################
  260. class ImgPion():
  261. def __init__(self):
  262. self.rimage = RImage() #ressource: image
  263. self.kx = 10 #coeff d'agrandissement horizontal
  264. self.ky = 10 #coeff d'agrandissement vertical
  265. self.dx = 0 #decalage horizontal
  266. self.dy = 0 #decalage vertical
  267. self.rotation = 0 #rotation(en degres)
  268. self.pivote = False #l'image pivote avec le pion?
  269. self.masqueAuto = False #creer un masque automatiquement
  270. class EtiquettePion():
  271. def __init__(self):
  272. self.txt = ""
  273. self.taille_police = 28 #taille de la police
  274. self.gras = False #en gras
  275. self.dx = 0 #decalage horizontal
  276. self.dy = 0 #decalage vertical