Viewer.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. '''
  2. '''
  3. from PyQt5 import uic
  4. from PyQt5.Qt import Qt, QEvent, QGraphicsScene, QPointF, QFileDialog, \
  5. QApplication, QMessageBox, QTreeWidgetItem, \
  6. QGraphicsTextItem, QGraphicsItem, QGraphicsRectItem, \
  7. QBrush, QColor, QGraphicsLineItem, QLineF, \
  8. QPen, QPainter, QSvgGenerator, QSize, QRect
  9. from PyQt5.QtWidgets import QMainWindow, QGraphicsView
  10. from path import Path
  11. from core import AccessObject
  12. import core
  13. Ui_window, _ = uic.loadUiType(Path(__file__).parent / 'qt_viewer.ui')
  14. palette = {
  15. "tables": QColor(240, 240, 20),
  16. "queries": QColor(210, 50, 210),
  17. "modules": QColor(220, 10, 33),
  18. "relations": QColor(122, 50, 209),
  19. "reports": QColor(25, 10, 220),
  20. "forms": QColor(10, 180, 220),
  21. "scripts": QColor(40, 220, 10),
  22. }
  23. v_spacing = 120
  24. cell_width = 150
  25. cell_spacing = 25
  26. class GraphicsObject(QGraphicsTextItem):
  27. items = []
  28. def __init__(self, obj, parent=None):
  29. super(GraphicsObject, self).__init__("", parent=parent)
  30. self.obj = obj
  31. self._text = "[{}] <b>{}</b>".format(obj.type_, obj.nom)
  32. self.links = []
  33. self._x = 0
  34. self._y = 0
  35. self.setHtml(self._text)
  36. self.setTextWidth(cell_width)
  37. self.setFlag(QGraphicsItem.ItemIsMovable, True)
  38. self.setFlag(QGraphicsItem.ItemIsSelectable, True)
  39. self.setFlag(QGraphicsItem.ItemIsFocusable, True)
  40. self._addRect()
  41. self._addAnchors()
  42. GraphicsObject.items.append(self)
  43. def _addRect(self):
  44. pen = QPen(palette[self.obj.type_])
  45. self.rect = QGraphicsRectItem(self.boundingRect(), parent=self)
  46. self.rect.setPen(pen)
  47. def topAnchorCenter(self):
  48. return self.mapToScene(QPointF((self.boundingRect().topLeft().x() + self.boundingRect().topRight().x() / 2), self.boundingRect().topLeft().y()))
  49. def bottomAnchorCenter(self):
  50. return self.mapToScene(QPointF((self.boundingRect().topLeft().x() + self.boundingRect().topRight().x() / 2), self.boundingRect().bottomLeft().y()))
  51. def _addAnchors(self):
  52. x = (self.boundingRect().topLeft().x() + self.boundingRect().topRight().x() / 2)
  53. ytop = self.boundingRect().topLeft().y()
  54. ybottom = self.boundingRect().bottomLeft().y()
  55. self.top_anchor = QGraphicsRectItem(x - 3, ytop - 3, 6, 6, parent=self)
  56. self.bottom_anchor = QGraphicsRectItem(x - 3, ybottom - 3, 6, 6, parent=self)
  57. self.top_anchor.setBrush(QBrush(QColor(255, 153, 51)))
  58. self.bottom_anchor.setBrush(QBrush(QColor(255, 153, 51)))
  59. def update(self):
  60. if self.pos() is not QPointF(self._x, self._y):
  61. self.setPos(QPointF(self._x, self._y))
  62. for l in self.links:
  63. l.update()
  64. def paint(self, *args, **kwargs):
  65. super(GraphicsObject, self).paint(*args, **kwargs)
  66. self._x, self._y = self.pos().x(), self.pos().y()
  67. self.update()
  68. class GraphicsRootObject(GraphicsObject):
  69. def __init__(self, obj, parent=None):
  70. super(GraphicsRootObject, self).__init__(obj, parent=parent)
  71. self.level = 0
  72. self.deps = []
  73. self.refs = []
  74. def xleft(self):
  75. return 0
  76. # def compute_coords(self):
  77. # return 0, 0
  78. def compute_coords(self):
  79. return (0.5 * self.emprise() - (cell_width / 2 + cell_spacing)), 0
  80. def emprise(self):
  81. return max([self.dep_emprise(), self.ref_emprise()])
  82. def dep_emprise(self):
  83. return sum([d.dep_emprise() for d in self.deps]) if self.deps else (cell_width + 2 * cell_spacing)
  84. def ref_emprise(self):
  85. return sum([r.ref_emprise() for r in self.refs]) if self.refs else (cell_width + 2 * cell_spacing)
  86. class GraphicsDepObject(GraphicsObject):
  87. def __init__(self, obj, parentItem, parent=None):
  88. super(GraphicsDepObject, self).__init__(obj, parent=parent)
  89. self.deps = []
  90. self.parentItem = parentItem
  91. if parentItem:
  92. parentItem.deps.append(self)
  93. self.level = parentItem.level + 1
  94. def xleft(self):
  95. x0 = sum([n.dep_emprise() for n in self.parentItem.deps[0:self.parentItem.deps.index(self)]])
  96. return self.parentItem.xleft() + x0
  97. def compute_coords(self):
  98. x0 = sum([n.dep_emprise() for n in self.parentItem.deps[0:self.parentItem.deps.index(self)]])
  99. x = self.parentItem.xleft() + (0.5 * self.dep_emprise() - (cell_width / 2 + cell_spacing)) + x0
  100. y = v_spacing * self.level
  101. return x, y
  102. def dep_emprise(self):
  103. return sum([d.dep_emprise() for d in self.deps]) if self.deps else (cell_width + 2 * cell_spacing)
  104. class GraphicsRefObject(GraphicsObject):
  105. def __init__(self, obj, childItem, parent=None):
  106. super(GraphicsRefObject, self).__init__(obj, parent=parent)
  107. self.refs = []
  108. self.childItem = childItem
  109. if childItem:
  110. childItem.refs.append(self)
  111. self.level = childItem.level - 1
  112. def xleft(self):
  113. x0 = sum([n.ref_emprise() for n in self.childItem.refs[0:self.childItem.refs.index(self)]])
  114. return self.childItem.xleft() + x0
  115. def compute_coords(self):
  116. x0 = sum([n.ref_emprise() for n in self.childItem.refs[0:self.childItem.refs.index(self)]])
  117. return self.childItem.xleft() + (0.5 * self.ref_emprise() - (cell_width / 2 + cell_spacing)) + x0, v_spacing * self.level
  118. def ref_emprise(self):
  119. return sum([d.ref_emprise() for d in self.refs]) if self.refs else (cell_width + 2 * cell_spacing)
  120. class GraphicsLink(QGraphicsLineItem):
  121. items = []
  122. def __init__(self, topGraphicsObject, bottomGraphicsObject, parent=None):
  123. self.topGraphicsObject = topGraphicsObject
  124. self.topGraphicsObject.links.append(self)
  125. self.bottomGraphicsObject = bottomGraphicsObject
  126. self.bottomGraphicsObject.links.append(self)
  127. super(QGraphicsLineItem, self).__init__(parent=parent)
  128. self.update()
  129. def update(self):
  130. line = QLineF(self.mapToScene(self.topGraphicsObject.bottomAnchorCenter()), self.mapToScene(self.bottomGraphicsObject.topAnchorCenter()))
  131. self.setLine(line)
  132. def select(self, active):
  133. pen = QPen()
  134. if active:
  135. pen.setColor(QColor("blue"))
  136. pen.setWidth(2)
  137. else:
  138. pen.setColor(QColor("black"))
  139. pen.setWidth(1)
  140. self.setPen(pen)
  141. class Viewer(QMainWindow):
  142. def __init__(self):
  143. super (Viewer, self).__init__()
  144. self.createWidgets()
  145. # ## UI related methods
  146. def createWidgets(self):
  147. self.ui = Ui_window()
  148. self.ui.setupUi(self)
  149. self.ui.progressBar.setVisible(False)
  150. self.ui.stackedWidget.setCurrentIndex(0)
  151. self.ui.btn_test.clicked.connect(self.test)
  152. self.ui.btn_select.clicked.connect(self.run)
  153. self.ui.btn_zoom_plus.clicked.connect(self.zoom_plus)
  154. self.ui.btn_zoom_minus.clicked.connect(self.zoom_minus)
  155. self.ui.btn_zoom_view.clicked.connect(self.fit_in_view)
  156. self.ui.btn_save.clicked.connect(self.save_to_png)
  157. self.ui.treeWidget.itemClicked.connect(self.treeItemSelected)
  158. core.Analyse.report = self.update_progression
  159. self.ui.view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
  160. self.ui.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
  161. self.ui.view.viewport().installEventFilter(self)
  162. self._scene = QGraphicsScene()
  163. self._scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex)
  164. self.ui.view.setScene(self._scene)
  165. self.fit_in_view()
  166. def eventFilter(self, _, event):
  167. if event.type() == QEvent.Wheel:
  168. if event.angleDelta().y() > 0:
  169. self.zoom_plus()
  170. elif event.angleDelta().y() < 0:
  171. self.zoom_minus()
  172. return True
  173. return False
  174. def fit_in_view(self):
  175. self.ui.view.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio)
  176. def zoom_plus(self):
  177. self.ui.view.scale(1.2, 1.2)
  178. def zoom_minus(self):
  179. self.ui.view.scale(0.8, 0.8)
  180. def save_to_png(self):
  181. fileName, _ = QFileDialog.getSaveFileName(self, "Save File", "", "Svg File (*.svg)")
  182. if not fileName:
  183. return
  184. gen = QSvgGenerator()
  185. gen.setFileName(fileName)
  186. gen.setSize(QSize(1000, 1000))
  187. gen.setViewBox(QRect(0, 0, 1000, 1000))
  188. gen.setTitle("SVG Generator Example Drawing")
  189. gen.setDescription("An SVG drawing created by the SVG Generator "
  190. "Example provided with Qt.")
  191. painter = QPainter(gen)
  192. self._scene.render(painter)
  193. painter.end()
  194. def update_progression(self, i, total, msg=""):
  195. self.ui.progressBar.setMaximum(total)
  196. self.ui.progressBar.setValue(i)
  197. if msg:
  198. self.ui.txtPanel.append(msg)
  199. QApplication.processEvents()
  200. def run(self):
  201. source_dir = QFileDialog.getExistingDirectory(self, "Sélectionner le répertoire des sources", "", QFileDialog.ShowDirsOnly)
  202. # source_dir = r"C:\dev\access\AGRHum\source"
  203. self.ui.progressBar.setVisible(True)
  204. self.ui.lbl_repertoire.setText(source_dir)
  205. self.ui.txtPanel.clear()
  206. self.ui.treeWidget.clear()
  207. QApplication.setOverrideCursor(Qt.WaitCursor)
  208. core.Analyse.run(source_dir)
  209. QApplication.restoreOverrideCursor()
  210. QMessageBox.information(self, "test", "{} objets chargés".format(len(core.Analyse.objects)))
  211. self.ui.progressBar.setVisible(False)
  212. self.ui.stackedWidget.setCurrentIndex(1)
  213. self.ui.txtPanel.clear()
  214. topitem = QTreeWidgetItem()
  215. topitem.setText(0, "Objets")
  216. self.ui.treeWidget.addTopLevelItem(topitem)
  217. groupes = {}
  218. for index, obj in enumerate(core.Analyse.objects):
  219. if not obj.type_ in groupes:
  220. item = QTreeWidgetItem()
  221. item.setText(0, obj.type_)
  222. groupes[obj.type_] = item
  223. topitem.addChild(item)
  224. item = QTreeWidgetItem()
  225. item.setText(0, obj.nom)
  226. item.setData(1, 0, index)
  227. groupes[obj.type_].addChild(item)
  228. self.ui.treeWidget.setColumnHidden(1, True)
  229. self.ui.treeWidget.expandToDepth(1)
  230. def clear_scene(self):
  231. self._scene.clear()
  232. GraphicsObject.items = []
  233. def maj_scene_with(self, root_object):
  234. self.clear_scene()
  235. root = GraphicsRootObject(root_object)
  236. self._scene.addItem(root)
  237. def _addDeps(go):
  238. for dep in go.obj.deps:
  239. if dep in [go.obj for go in GraphicsObject.items]:
  240. print("ref circulaire: {}".format(dep))
  241. continue
  242. gdep = GraphicsDepObject(dep, go)
  243. self._scene.addItem(gdep)
  244. link = GraphicsLink(go, gdep)
  245. self._scene.addItem(link)
  246. if dep.deps:
  247. _addDeps(gdep)
  248. _addDeps(root)
  249. def _addRefs(go):
  250. for ref in go.obj.refs:
  251. if ref in [go.obj for go in GraphicsObject.items]:
  252. print("ref circulaire: {}".format(ref))
  253. continue
  254. gref = GraphicsRefObject(ref, go)
  255. self._scene.addItem(gref)
  256. link = GraphicsLink(gref, go)
  257. self._scene.addItem(link)
  258. if ref.refs:
  259. _addRefs(gref)
  260. _addRefs(root)
  261. for item in GraphicsObject.items:
  262. item._x, item._y = item.compute_coords()
  263. item.update()
  264. self.ui.btn_save.setEnabled(True)
  265. self.fit_in_view()
  266. def treeItemSelected(self, item):
  267. index = item.data(1, 0)
  268. obj = core.Analyse.objects[index]
  269. self.maj_scene_with(obj)
  270. def test(self):
  271. a = AccessObject("queries", "testA", "")
  272. b = AccessObject("queries", "testB", "")
  273. c = AccessObject("tables", "testC", "")
  274. d = AccessObject("tables", "testD", "")
  275. e = AccessObject("modules", "testE", "")
  276. f = AccessObject("relations", "testF", "")
  277. g = AccessObject("reports", "testG", "")
  278. h = AccessObject("forms", "testH", "")
  279. i = AccessObject("scripts", "testI", "")
  280. j = AccessObject("reports", "testJ", "")
  281. k = AccessObject("forms", "testK", "")
  282. l = AccessObject("scripts", "testL", "")
  283. b.deps = [e, f, g]
  284. c.deps = [h, i]
  285. a.deps = [b, c, d]
  286. j.refs = [k, l]
  287. a.refs = [j]
  288. self.maj_scene_with(a)