''' ''' 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, \ QGraphicsColorizeEffect, QFont from PyQt5.QtWidgets import QMainWindow, QGraphicsView from path import Path import core Ui_window, _ = uic.loadUiType(Path(__file__).parent / 'qt_viewer.ui') palette = { "Table": QColor(240, 240, 20), "Query": QColor(210, 50, 210), "Module": QColor(220, 10, 33), "Relation": QColor(122, 50, 209), "Report": QColor(25, 10, 220), "Form": QColor(10, 180, 220), "Macro": 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.links = [] self._x = 0 self._y = 0 self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.setAcceptHoverEvents(True) self.label = QGraphicsTextItem() self.label.setTextWidth(cell_width) self.label.setZValue(2) self.setText() 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) effect = QGraphicsColorizeEffect() effect.setEnabled(False) self.rect.setGraphicsEffect(effect) GraphicsObject.items.append(self) def setText(self): self.label.setHtml(self.html()) def html(self): return "[{}]
{}".format(self.obj.type_, self.obj.nom) 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() def setShining(self, active): self.rect.graphicsEffect().setEnabled(active) def hoverEnterEvent(self, *args, **kwargs): self.setShining(True) def hoverLeaveEvent(self, *args, **kwargs): self.setShining(False) 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 pen = QPen(QColor("red")) pen.setWidth(2) self.rect.setPen(pen) def xleft(self): return 0 def compute_coords(self): return 0, 0 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 GraphicsCloneDepObject(GraphicsDepObject): def __init__(self, obj, childItem, parent=None): super(GraphicsCloneDepObject, self).__init__(obj, childItem, parent) self.bottom_anchor.setBrush(QColor("red")) self.clone_of = next((item for item in GraphicsObject.items if item.obj == obj and type(item) is GraphicsDepObject or type(item) is GraphicsRefObject)) def html(self): return super(GraphicsCloneDepObject, self).html() + "
(!) Clône" @property def deps(self): return [] @deps.setter def deps(self, value): pass def hoverEnterEvent(self, *args, **kwargs): self.setShining(True) self.clone_of.setShining(True) def hoverLeaveEvent(self, *args, **kwargs): self.setShining(False) self.clone_of.setShining(False) 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 GraphicsCloneRefObject(GraphicsRefObject): def __init__(self, obj, childItem, parent=None): super(GraphicsCloneRefObject, self).__init__(obj, childItem, parent) self.top_anchor.setBrush(QColor("red")) self.clone_of = next((item for item in GraphicsObject.items if item.obj == obj and type(item) is GraphicsDepObject or type(item) is GraphicsRefObject)) def html(self): return super(GraphicsCloneRefObject, self).html() + "
(!) Clône" @property def refs(self): return [] @refs.setter def refs(self, value): pass def hoverEnterEvent(self, *args, **kwargs): self.clone_of.setShining(True) def hoverLeaveEvent(self, *args, **kwargs): self.clone_of.setShining(False) 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() 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.selectSourceDir) 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) self._title = "" 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) helpLabel = QGraphicsTextItem() helpLabel.setPlainText("Sélectionnez le répertoire contenant les sources de la base à analyser,\npuis sélectionnez dans la liste de droite un objet à partir duquel afficher les dépendances...") helpLabel.setTextWidth(600) font = QFont() font.setItalic(True) font.setWeight(63) helpLabel.setFont(font) self._scene.addItem(helpLabel) 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): rect = self._scene.itemsBoundingRect() rect.adjust(-50, -50, 50, 50) self._scene.setSceneRect(rect) 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("Access Analyser") gen.setDescription("Access Analyser Report for {}".format(self._title)) 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 selectSourceDir(self): source_dir = QFileDialog.getExistingDirectory(self, "Sélectionner le répertoire des sources", "", QFileDialog.ShowDirsOnly) if not source_dir: return self.run(source_dir) def run(self, source_dir): source_dir = Path(source_dir) self._title = source_dir self.ui.progressBar.setVisible(True) self.ui.lbl_repertoire.setText(source_dir) self.ui.txtPanel.clear() self.ui.treeWidget.clear() if self.ui.radioRefsOnly.isChecked(): mode = core.REFS_ONLY elif self.ui.radioDepsOnly.isChecked(): mode = core.DEPS_ONLY else: mode = core.DEPS_AND_REFS print(mode) QApplication.setOverrideCursor(Qt.WaitCursor) core.Analyse.run(source_dir, mode) QApplication.restoreOverrideCursor() if core.Analyse.duplicated_names: QMessageBox.warning(self, "Risque d'instabilités", "Attention! Des doublons ont été trouvés dans les noms des objets suivants:\n{}".format(", ".join(core.Analyse.duplicated_names))) 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 not in [go.obj for go in GraphicsObject.items]: gdep = GraphicsDepObject(dep, go) else: gdep = GraphicsCloneDepObject(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 not in [go.obj for go in GraphicsObject.items]: gref = GraphicsRefObject(ref, go) else: gref = GraphicsCloneRefObject(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() if isinstance(item, GraphicsDepObject): item._x -= (root.dep_emprise() - (cell_width + 2 * cell_spacing)) / 2 if isinstance(item, GraphicsRefObject): item._x -= (root.ref_emprise() - (cell_width + 2 * cell_spacing)) / 2 item.update() self.ui.btn_save.setEnabled(True) self.fit_in_view() def treeItemSelected(self, item): index = item.data(1, 0) if not index: return obj = core.Analyse.objects[index] self.maj_scene_with(obj) def test(self): self.run(Path(__file__).parent / r"test\source") 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)