VueEditionForme.py 16 KB

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