''' ''' from PyQt5 import uic from PyQt5.Qt import Qt, QEvent, QGraphicsScene, QPointF, QFileDialog, \ QApplication, QMessageBox, QTreeWidgetItem, \ QGraphicsTextItem, QGraphicsItem, QGraphicsRectItem, \ QBrush, QColor, QGraphicsLineItem, QLineF, \ QPen, QPainter, QSvgGenerator, QSize, QRect, QGraphicsItemGroup from PyQt5.QtWidgets import QMainWindow, QGraphicsView from path import Path from core import AccessObject import core Ui_window, _ = uic.loadUiType(Path(__file__).parent / 'qt_viewer.ui') palette = { "tables": QColor(240, 240, 20), "queries": QColor(210, 50, 210), "modules": QColor(220, 10, 33), "relations": QColor(122, 50, 209), "reports": QColor(25, 10, 220), "forms": QColor(10, 180, 220), "scripts": QColor(40, 220, 10), } v_spacing = 120 cell_width = 150 cell_spacing = 25 class GraphicsObject(QGraphicsItemGroup): items = [] def __init__(self, obj, parent=None): super(GraphicsObject, self).__init__(parent=parent) self.obj = obj self._text = "[{}]
{}".format(obj.type_, obj.nom) self.links = [] self._x = 0 self._y = 0 self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.label = QGraphicsTextItem() self.label.setHtml(self._text) self.label.setTextWidth(cell_width) self.label.setZValue(2) self.addToGroup(self.label) pen = QPen(palette[self.obj.type_].darker(150)) pen.setWidth(2) self.rect = QGraphicsRectItem(self.label.boundingRect()) self.rect.setPen(pen) self.rect.setBrush(palette[self.obj.type_].lighter(150)) self.rect.setZValue(0) self.addToGroup(self.rect) self.topAnchorCoords = ((self.boundingRect().topLeft().x() + self.boundingRect().topRight().x() / 2), self.boundingRect().topLeft().y()) self.bottomAnchorCoords = ((self.boundingRect().topLeft().x() + self.boundingRect().topRight().x() / 2), self.boundingRect().bottomLeft().y()) self.top_anchor = QGraphicsRectItem(self.topAnchorCoords[0] - 3, self.topAnchorCoords[1] - 3, 6, 6) self.bottom_anchor = QGraphicsRectItem(self.bottomAnchorCoords[0] - 3, self.bottomAnchorCoords[1] - 3, 6, 6) for anchor in (self.top_anchor, self.bottom_anchor): anchor.setBrush(QBrush(QColor(255, 153, 51))) anchor.setZValue(1) self.addToGroup(anchor) GraphicsObject.items.append(self) def topAnchorCenter(self): return self.mapToScene(QPointF(*self.topAnchorCoords)) def bottomAnchorCenter(self): return self.mapToScene(QPointF(*self.bottomAnchorCoords)) def update(self): if self.pos() is not QPointF(self._x, self._y): self.setPos(QPointF(self._x, self._y)) for l in self.links: l.update() def paint(self, *args, **kwargs): super(GraphicsObject, self).paint(*args, **kwargs) self._x, self._y = self.pos().x(), self.pos().y() self.update() class GraphicsRootObject(GraphicsObject): def __init__(self, obj, parent=None): super(GraphicsRootObject, self).__init__(obj, parent=parent) self.level = 0 self.deps = [] self.refs = [] self._dep_emprise = 0 self._ref_emprise = 0 def xleft(self): return 0 # def compute_coords(self): # return 0, 0 def compute_coords(self): return (0.5 * self.emprise() - (cell_width / 2 + cell_spacing)), 0 def emprise(self): return max([self.dep_emprise(), self.ref_emprise()]) def dep_emprise(self): if not self._dep_emprise: self._dep_emprise = sum([d.dep_emprise() for d in self.deps]) if self.deps else (cell_width + 2 * cell_spacing) return self._dep_emprise def ref_emprise(self): if not self._ref_emprise: self._ref_emprise = sum([r.ref_emprise() for r in self.refs]) if self.refs else (cell_width + 2 * cell_spacing) return self._ref_emprise class GraphicsDepObject(GraphicsObject): def __init__(self, obj, parentItem, parent=None): super(GraphicsDepObject, self).__init__(obj, parent=parent) self.deps = [] self.parentItem = parentItem if parentItem: parentItem.deps.append(self) self.level = parentItem.level + 1 self._dep_emprise = 0 def xleft(self): x0 = sum([n.dep_emprise() for n in self.parentItem.deps[0:self.parentItem.deps.index(self)]]) return self.parentItem.xleft() + x0 def compute_coords(self): x0 = sum([n.dep_emprise() for n in self.parentItem.deps[0:self.parentItem.deps.index(self)]]) x = self.parentItem.xleft() + (0.5 * self.dep_emprise() - (cell_width / 2 + cell_spacing)) + x0 y = v_spacing * self.level return x, y def dep_emprise(self): if not self._dep_emprise: self._dep_emprise = sum([d.dep_emprise() for d in self.deps]) if self.deps else (cell_width + 2 * cell_spacing) return self._dep_emprise class GraphicsRefObject(GraphicsObject): def __init__(self, obj, childItem, parent=None): super(GraphicsRefObject, self).__init__(obj, parent=parent) self.refs = [] self.childItem = childItem if childItem: childItem.refs.append(self) self.level = childItem.level - 1 self._ref_emprise = 0 def xleft(self): x0 = sum([n.ref_emprise() for n in self.childItem.refs[0:self.childItem.refs.index(self)]]) return self.childItem.xleft() + x0 def compute_coords(self): x0 = sum([n.ref_emprise() for n in self.childItem.refs[0:self.childItem.refs.index(self)]]) return self.childItem.xleft() + (0.5 * self.ref_emprise() - (cell_width / 2 + cell_spacing)) + x0, v_spacing * self.level def ref_emprise(self): if not self._ref_emprise: self._ref_emprise = sum([d.ref_emprise() for d in self.refs]) if self.refs else (cell_width + 2 * cell_spacing) return self._ref_emprise class GraphicsLink(QGraphicsLineItem): items = [] def __init__(self, topGraphicsObject, bottomGraphicsObject, parent=None): self.topGraphicsObject = topGraphicsObject self.topGraphicsObject.links.append(self) self.bottomGraphicsObject = bottomGraphicsObject self.bottomGraphicsObject.links.append(self) super(QGraphicsLineItem, self).__init__(parent=parent) self.update() def update(self): line = QLineF(self.mapToScene(self.topGraphicsObject.bottomAnchorCenter()), self.mapToScene(self.bottomGraphicsObject.topAnchorCenter())) self.setLine(line) def select(self, active): pen = QPen() if active: pen.setColor(QColor("blue")) pen.setWidth(2) else: pen.setColor(QColor("black")) pen.setWidth(1) self.setPen(pen) class Viewer(QMainWindow): def __init__(self): super (Viewer, self).__init__() self.createWidgets() # ## UI related methods def createWidgets(self): self.ui = Ui_window() self.ui.setupUi(self) self.ui.progressBar.setVisible(False) self.ui.stackedWidget.setCurrentIndex(0) self.ui.btn_test.clicked.connect(self.test) self.ui.btn_select.clicked.connect(self.run) self.ui.btn_zoom_plus.clicked.connect(self.zoom_plus) self.ui.btn_zoom_minus.clicked.connect(self.zoom_minus) self.ui.btn_zoom_view.clicked.connect(self.fit_in_view) self.ui.btn_save.clicked.connect(self.save_to_png) self.ui.treeWidget.itemClicked.connect(self.treeItemSelected) core.Analyse.report = self.update_progression self.ui.view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate) self.ui.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.ui.view.viewport().installEventFilter(self) self._scene = QGraphicsScene() self._scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex) self.ui.view.setScene(self._scene) self.fit_in_view() def eventFilter(self, _, event): if event.type() == QEvent.Wheel: if event.angleDelta().y() > 0: self.zoom_plus() elif event.angleDelta().y() < 0: self.zoom_minus() return True return False def fit_in_view(self): self.ui.view.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio) def zoom_plus(self): self.ui.view.scale(1.2, 1.2) def zoom_minus(self): self.ui.view.scale(0.8, 0.8) def save_to_png(self): fileName, _ = QFileDialog.getSaveFileName(self, "Save File", "", "Svg File (*.svg)") if not fileName: return gen = QSvgGenerator() gen.setFileName(fileName) gen.setSize(QSize(1000, 1000)) gen.setViewBox(QRect(0, 0, 1000, 1000)) gen.setTitle("SVG Generator Example Drawing") gen.setDescription("An SVG drawing created by the SVG Generator " "Example provided with Qt.") painter = QPainter(gen) self._scene.render(painter) painter.end() def update_progression(self, i, total, msg=""): self.ui.progressBar.setMaximum(total) self.ui.progressBar.setValue(i) if msg: self.ui.txtPanel.append(msg) QApplication.processEvents() def run(self): source_dir = QFileDialog.getExistingDirectory(self, "Sélectionner le répertoire des sources", "", QFileDialog.ShowDirsOnly) self.ui.progressBar.setVisible(True) self.ui.lbl_repertoire.setText(source_dir) self.ui.txtPanel.clear() self.ui.treeWidget.clear() QApplication.setOverrideCursor(Qt.WaitCursor) core.Analyse.run(source_dir) QApplication.restoreOverrideCursor() QMessageBox.information(self, "test", "{} objets chargés".format(len(core.Analyse.objects))) self.ui.progressBar.setVisible(False) self.ui.stackedWidget.setCurrentIndex(1) self.ui.txtPanel.clear() topitem = QTreeWidgetItem() topitem.setText(0, "Objets") self.ui.treeWidget.addTopLevelItem(topitem) groupes = {} for index, obj in enumerate(core.Analyse.objects): if not obj.type_ in groupes: item = QTreeWidgetItem() item.setText(0, obj.type_) groupes[obj.type_] = item topitem.addChild(item) item = QTreeWidgetItem() item.setText(0, obj.nom) item.setData(1, 0, index) groupes[obj.type_].addChild(item) self.ui.treeWidget.setColumnHidden(1, True) self.ui.treeWidget.expandToDepth(1) def clear_scene(self): self._scene.clear() GraphicsObject.items = [] def maj_scene_with(self, root_object): self.clear_scene() root = GraphicsRootObject(root_object) self._scene.addItem(root) def _addDeps(go): for dep in go.obj.deps: if dep in [go.obj for go in GraphicsObject.items]: print("ref circulaire: {}".format(dep)) continue gdep = GraphicsDepObject(dep, go) self._scene.addItem(gdep) link = GraphicsLink(go, gdep) self._scene.addItem(link) if dep.deps: _addDeps(gdep) _addDeps(root) def _addRefs(go): for ref in go.obj.refs: if ref in [go.obj for go in GraphicsObject.items]: print("ref circulaire: {}".format(ref)) continue gref = GraphicsRefObject(ref, go) self._scene.addItem(gref) link = GraphicsLink(gref, go) self._scene.addItem(link) if ref.refs: _addRefs(gref) _addRefs(root) for item in GraphicsObject.items: item._x, item._y = item.compute_coords() item.update() self.ui.btn_save.setEnabled(True) self.fit_in_view() def treeItemSelected(self, item): index = item.data(1, 0) obj = core.Analyse.objects[index] self.maj_scene_with(obj) def test(self): a = AccessObject("queries", "testA_", "") b = AccessObject("queries", "testB", "") c = AccessObject("tables", "testC", "") d = AccessObject("tables", "testD", "") e = AccessObject("modules", "testE", "") f = AccessObject("relations", "testF", "") g = AccessObject("reports", "testG", "") h = AccessObject("forms", "testH", "") i = AccessObject("scripts", "testI", "") j = AccessObject("reports", "testJ", "") k = AccessObject("forms", "testK", "") l = AccessObject("scripts", "testL", "") b.deps = [e, f, g] c.deps = [h, i] a.deps = [b, c, d] j.refs = [k, l] a.refs = [j] self.maj_scene_with(a) def keyPressEvent(self, e): if e.key() == Qt.Key_Control: self.ui.view.setDragMode(QGraphicsView.ScrollHandDrag) e.accept() def keyReleaseEvent(self, e): if e.key() == Qt.Key_Control: self.ui.view.setDragMode(QGraphicsView.RubberBandDrag)