'''
'''
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)