VueEditionForme.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. #from __future__ import unicode_literals
  2. # -*- coding: utf-8 -*-
  3. from __future__ import division
  4. from PyQt4.QtCore import QPointF, Qt, QSize, QString
  5. from PyQt4.QtGui import QGraphicsScene, QColor, QGraphicsPolygonItem, \
  6. QGraphicsItem, QPolygonF, QPen, QGraphicsPixmapItem, QApplication, \
  7. QGraphicsSimpleTextItem, QFont
  8. from Pion import ImgPion, EtiquettePion
  9. from lib import dmF
  10. class VueEditionForme(QGraphicsScene):
  11. """vue graphique permettant d'afficher et de modifier la forme des creatures/decors"""
  12. def __init__(self, vue, parent=None):
  13. super(VueEditionForme, self).__init__()
  14. self.vue = vue
  15. self._vefImage = VefImage(self)
  16. self._vefEtiquette = VefEtiquette(self)
  17. self._couleur = QColor(190,190,190,255)
  18. self._cases = {}
  19. self._formeCases = "H"
  20. self._autoriseModifForme = False
  21. self._l0 = 0
  22. self._h0 = 0
  23. self._rotation = 0
  24. self.selection = None
  25. self.vue.setScene(self)
  26. self.vue.centerOn(self.vue.mapFromScene(QPointF(60,60))) #centre la vue sur la case 0,0
  27. self.vue.scale(0.22, 0.22)
  28. def setCursor(self, curseur):
  29. self.vue.setCursor(curseur)
  30. #acces aux donnees
  31. def chargerFormeDef(self, formeDef):
  32. self._formeDef = formeDef
  33. self._majFormeGraphique()
  34. def formeDef(self):
  35. """renvoie la definition de forme (selon la forme des cases actuelle)"""
  36. retour = []
  37. for coord in self._cases:
  38. if self._cases[coord].estSelectionnee() and coord != (0,0):
  39. retour.append(coord)
  40. return retour
  41. def chargerEtiquetteDef(self, etiquetteDef):
  42. self._vefEtiquette.charger(etiquetteDef)
  43. def etiquetteDef(self):
  44. return self._vefEtiquette.definition()
  45. def chargerImageDef(self, imgDef):
  46. self._vefImage.charger(imgDef)
  47. def nouvelleImageDef(self, rimage):
  48. imgDef = ImgPion()
  49. imgDef.rimage = rimage
  50. self._vefImage.charger(imgDef)
  51. def imageDef(self):
  52. return self._vefImage.definition()
  53. def majCouleur(self, couleur):
  54. """met a jour la couleur du pion"""
  55. self._couleur = couleur
  56. self._majCouleurGraphique()
  57. def couleur(self):
  58. return self._couleur
  59. def formeCases(self):
  60. return self._formeCases
  61. #creation, suppression, mises a jour...
  62. def creer(self, formeCases = "H"):
  63. self._formeCases = formeCases
  64. for x in range(-5,6):
  65. for y in range(-5,6):
  66. if not ((x%2)==1 and y == -5):
  67. c = VefCase(self)
  68. c.creer(x, y, self._couleur, self._formeCases)
  69. self._cases[(x,y)] = c
  70. def vider(self):
  71. for coord in self._cases:
  72. self.removeItem(self._cases[coord])
  73. self._cases = {}
  74. def _majFormeGraphique(self):
  75. """met a jour la forme affichee sur le graphique"""
  76. if self._formeDef:
  77. for coord in self._formeDef:
  78. if coord in self._cases:
  79. self._cases[coord].selectionner(True)
  80. self._cases[coord].setBrush(self._couleur)
  81. def _majCouleurGraphique(self):
  82. for coord in self._cases:
  83. if self._cases[coord].estSelectionnee():
  84. if coord == (0,0):
  85. c = self._couleur.darker(150)
  86. self._cases[coord].setBrush(c)
  87. else:
  88. self._cases[coord].setBrush(self._couleur)
  89. def majSelection(self, coord):
  90. """met a jour l'affichage des cases selectionnees pour l'affichage de la forme"""
  91. if self._autoriseModifForme:
  92. if self._cases[coord].estSelectionnee():
  93. valide = True
  94. for coordVerif in self._cases:
  95. #on parcourt les autres cases selectionnees
  96. if coordVerif != (0,0) and coordVerif != coord and self._cases[coordVerif].estSelectionnee():
  97. #on liste les cases connectees a cette case, de proche en proche
  98. connectees = [coordVerif]
  99. for coordVerif2 in connectees:
  100. for coordVoisin in self._cases[coordVerif2].voisins():
  101. if coordVoisin in self._cases and not coordVoisin in connectees:
  102. if self._cases[coordVoisin].estSelectionnee():
  103. connectees.append(coordVoisin)
  104. if not (0,0) in connectees:
  105. valide = False
  106. break
  107. if valide:
  108. self._cases[coord].selectionner(False)
  109. else:
  110. #on s'assure que la case est bien adjacente a une case selectionnee
  111. valide = False
  112. for coordVerif in self._cases[coord].voisins():
  113. if coordVerif in self._cases:
  114. if self._cases[coordVerif].estSelectionnee():
  115. valide = True
  116. break
  117. if valide:
  118. self._cases[coord].selectionner(True)
  119. self._majCouleurGraphique()
  120. def afficherMsg(self, msg):
  121. print msg
  122. def autoriserModifForme(self, actif):
  123. self._autoriseModifForme = actif
  124. class VefCase(QGraphicsPolygonItem):
  125. """case utilisee dans une VueEditionForme()"""
  126. def __init__(self, scene, parent=None):
  127. super(VefCase, self).__init__()
  128. self.scene = scene
  129. self._position = (0,0)
  130. self._estSelectionnee = False
  131. self._voisins = []
  132. self._couleur = None
  133. #interactions graphiques
  134. self.setFlag(QGraphicsItem.ItemIsFocusable)
  135. self.setAcceptHoverEvents(True)
  136. def estSelectionnee(self):
  137. return self._estSelectionnee
  138. def selectionner(self, actif):
  139. if actif:
  140. self.setBrush(self._couleur)
  141. else:
  142. self.setBrush(QColor(255,255,255,50))
  143. self._estSelectionnee = actif
  144. def voisins(self):
  145. return self._voisins
  146. def creer(self, x, y, couleur, formeCases="H"):
  147. """creation du polygone et enregistrement des donnees geometriques"""
  148. self._position = (x, y)
  149. polygone = QPolygonF()
  150. if formeCases == "H":
  151. my = 0
  152. if (x % 2) == 1: my = -0.5
  153. polygone << QPointF(((x*0.866)+0.2886)*120, (y+my)*120) \
  154. << QPointF(((x*0.866)+0.866)*120, (y+my)*120) \
  155. << QPointF(((x*0.866)+1.1547)*120, (y+my+0.5)*120) \
  156. << QPointF(((x*0.866)+0.866)*120, (y+my+1)*120) \
  157. << QPointF(((x*0.866)+0.2886)*120, (y+my+1)*120) \
  158. << QPointF( (x*0.866)*120, (y+my+0.5)*120)
  159. else:
  160. polygone << QPointF(x*120, y*120) \
  161. << QPointF((x+1)*120, y*120) \
  162. << QPointF((x+1)*120, (y+1)*120) \
  163. << QPointF(x*120, (y+1)*120)
  164. self.setPolygon(polygone)
  165. #enregistrement des cases voisines:
  166. if formeCases == "H":
  167. self._voisins = [(x, y-1), (x+1, y-1), (x+1, y), (x, y+1), (x-1, y), (x-1, y-1)]
  168. else:
  169. self._voisins.append((x, y-1))
  170. self._voisins.append((x, y+1))
  171. self._voisins.append((x-1, y))
  172. self._voisins.append((x+1, y))
  173. #pour afficher les coordonnees des cases:
  174. ## text = QGraphicsSimpleTextItem("{} {}".format(x, y), parent=self)
  175. ## police = QFont()
  176. ## police.setPointSize(20)
  177. ## text.setFont(police)
  178. ## k = 0
  179. ## if (x % 2) == 1: k = - 0.5
  180. ## text.setPos(QPointF(((x*0.866)+0.5773)*120, (y+0.5+k)*120))
  181. #couleur de fond par defaut
  182. if couleur.__class__.__name__ == "QColor":
  183. if couleur.isValid():
  184. self._couleur = couleur
  185. else:
  186. self._couleur = QColor("grey")
  187. else:
  188. self._couleur = QColor("grey")
  189. if self._position == (0, 0):
  190. couleur = self._couleur.darker(150)
  191. self.setBrush(couleur)
  192. self._estSelectionnee = True
  193. else:
  194. self.selectionner(False)
  195. #creation de l'objet graphique sur le plateau
  196. pinceau = QPen()
  197. pinceau.setWidth(2)
  198. pinceau.setColor(QColor(150,150,150,150))
  199. self.setPen(pinceau)
  200. self.scene.addItem(self)
  201. def mousePressEvent(self, event):
  202. """evenement lors du clic souris"""
  203. super(VefCase, self).mousePressEvent(event)
  204. if event.button() == 1: #sur clic gauche
  205. if self._position != (0, 0):
  206. self.scene.majSelection(self._position)
  207. class VefImage(QGraphicsPixmapItem):
  208. """etiquette utilisee dans une VueEditionForme()"""
  209. def __init__(self, scene, parent=None):
  210. super(VefImage, self).__init__()
  211. self.scene = scene
  212. self._def = ImgPion()
  213. self._pix = None
  214. self._l0 = 0
  215. self._h0 = 0
  216. self.setAcceptDrops(True)
  217. self.setAcceptHoverEvents(True)
  218. self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable)
  219. self.scene.addItem(self)
  220. self.setZValue(10)
  221. def charger(self, definition):
  222. self._pix = None
  223. self._l0 = 0
  224. self._h0 = 0
  225. self._def = definition
  226. self._maj()
  227. def definition(self):
  228. return self._def
  229. def _maj(self):
  230. if self._def.rimage.estValide():
  231. if self._pix == None:
  232. self._pix = self._def.rimage.pix()
  233. if not self._pix.isNull():
  234. if self._pix.height() >= self._pix.width():
  235. self._pix = self._pix.scaledToHeight(120*0.9, Qt.SmoothTransformation)
  236. else:
  237. self._pix = self._pix.scaledToWidth(120*0.9, Qt.SmoothTransformation)
  238. self._l0 = self._pix.width()
  239. self._h0 = self._pix.height()
  240. else:
  241. self._pix = None
  242. else:
  243. self._pix = None
  244. if self._pix:
  245. taille = QSize((self._def.kx/10)*self._l0, (self._def.ky/10)*self._h0)
  246. self._pix = self._pix.scaled(taille, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
  247. k = 1
  248. if self.scene.formeCases() == "H": k = 1.1544
  249. deltaX = self._def.dx + 0.5*(120*k - self._pix.width())
  250. deltaY = self._def.dy + 0.5*(120 - self._pix.height())
  251. self.setPixmap(self._pix)
  252. self.setPos(QPointF(deltaX, deltaY))
  253. self.setRotation(self._def.rotation)
  254. def width(self):
  255. return self.boundingRect().width()
  256. def height(self):
  257. return self.boundingRect().height()
  258. def majPos(self, pos):
  259. k = 1
  260. if self.scene.formeCases() == "H": k = 1.1544
  261. self._def.dx = pos.x() - 0.5*(120*k - self._pix.width())
  262. self._def.dy = pos.y() - 0.5*(120 - self._pix.height())
  263. def mousePressEvent(self, event):
  264. self.prepareGeometryChange()
  265. self.scene.afficherMsg("Utilisez les touches Z, Q, S, D pour modifier la taille de l'image\n" \
  266. "Utilisez les touches W et X pour faire pivoter l'image")
  267. def mouseReleaseEvent(self, event):
  268. self.scene.afficherMsg("")
  269. super(VefImage, self).mouseReleaseEvent(event)
  270. QApplication.processEvents()
  271. self.majPos(self.scenePos())
  272. def keyPressEvent(self, event):
  273. if event.key() in [Qt.Key_Z, Qt.Key_Q, Qt.Key_S, Qt.Key_D, Qt.Key_W, Qt.Key_X]:
  274. if event.key() == Qt.Key_Z:
  275. self._def.ky += 2
  276. elif event.key() == Qt.Key_Q:
  277. self._def.kx -= 2
  278. elif event.key() == Qt.Key_S:
  279. self._def.ky -= 2
  280. elif event.key() == Qt.Key_D:
  281. self._def.kx += 2
  282. elif event.key() == Qt.Key_W:
  283. self._def.rotation -= 5
  284. elif event.key() == Qt.Key_X:
  285. self._def.rotation += 5
  286. self.majPos(self.scenePos())
  287. self._maj()
  288. class VefEtiquette(QGraphicsSimpleTextItem):
  289. """etiquette utilisee dans une VueEditionForme()"""
  290. def __init__(self, scene, parent=None):
  291. super(VefEtiquette, self).__init__()
  292. self.scene = scene
  293. self._def = EtiquettePion()
  294. self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable)
  295. self.setAcceptDrops(True)
  296. self.setAcceptHoverEvents(True)
  297. self.scene.addItem(self)
  298. self.setZValue(10)
  299. def charger(self, definition):
  300. self._def = definition
  301. self._maj()
  302. def definition(self):
  303. return self._def
  304. def _maj(self):
  305. if len(self._def.txt) > 0:
  306. txt = dmF.contractTxt(self._def.txt, 20)
  307. self.setText(QString.fromUtf8(txt))
  308. self.setPos(QPointF(self._def.dx - 0.112*120, self._def.dy - 0.5*120))
  309. police = QFont("Verdana", self._def.taille_police)
  310. police.setBold(self._def.gras)
  311. self.setFont(police)
  312. def majPos(self, pos):
  313. self._def.dx = pos.x() - 0.112*120
  314. self._def.dy = pos.y() - 0.5*120
  315. def mousePressEvent(self, event):
  316. self.prepareGeometryChange()
  317. self.scene.afficherMsg("Utilisez les touches HAUT et BAS pour modifier la taille de la police\n" \
  318. "Utilisez la touche G pour activer/desactiver l'écriture en gras")
  319. def mouseReleaseEvent(self, event):
  320. self.scene.afficherMsg("")
  321. super(VefEtiquette, self).mouseReleaseEvent(event)
  322. QApplication.processEvents()
  323. self.majPos(self.scenePos())
  324. def keyPressEvent(self, event):
  325. if event.key() == Qt.Key_Up:
  326. self._def.taille_police += 4
  327. self.majPos(self.scenePos())
  328. self._maj()
  329. elif event.key() == Qt.Key_Down:
  330. self._def.taille_police -= 4
  331. self.majPos(self.scenePos())
  332. self._maj()
  333. elif event.key() == Qt.Key_G:
  334. if not self._def.gras:
  335. self._def.gras = True
  336. else:
  337. self._def.gras = False
  338. self.majPos(self.scenePos())
  339. self._maj()