Sun, 09 Sep 2012 17:40:32 +0200
Refactored the UML graphics code.
# -*- coding: utf-8 -*- # Copyright (c) 2007 - 2012 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a dialog showing an imports diagram of a package. """ import glob import os from PyQt4.QtGui import QProgressDialog, QApplication, QGraphicsTextItem from .UMLDiagramBuilder import UMLDiagramBuilder from .ModuleItem import ModuleItem, ModuleModel from .AssociationItem import AssociationItem, Imports import Utilities.ModuleParser import Utilities import Preferences class ImportsDiagramBuilder(UMLDiagramBuilder): """ Class implementing a builder for imports diagrams of a package. Note: Only package internal imports are shown in order to maintain some readability. """ def __init__(self, dialog, view, project, package, showExternalImports=False): """ Constructor @param dialog reference to the UML dialog (UMLDialog) @param view reference to the view object (UMLGraphicsView) @param project reference to the project object (Project) @param package name of a python package to show the import relationships (string) @keyparam showExternalImports flag indicating to show exports from outside the package (boolean) """ super().__init__(dialog, view, project) self.setObjectName("ImportsDiagram") self.showExternalImports = showExternalImports self.packagePath = Utilities.normabspath(package) self.package = os.path.splitdrive(self.packagePath)[1].replace(os.sep, '.')[1:] hasInit = True ppath = self.packagePath while hasInit: ppath = os.path.dirname(ppath) hasInit = len(glob.glob(os.path.join(ppath, '__init__.*'))) > 0 self.shortPackage = self.packagePath.replace(ppath, '').replace(os.sep, '.')[1:] self.umlView.setPersistenceData("package={0}".format(self.packagePath)) pname = project.getProjectName() if pname: name = self.trUtf8("Imports Diagramm {0}: {1}").format( pname, project.getRelativePath(self.packagePath)) else: name = self.trUtf8("Imports Diagramm: {0}").format(self.packagePath) self.umlView.setDiagramName(name) def __buildModulesDict(self): """ Private method to build a dictionary of modules contained in the package. @return dictionary of modules contained in the package. """ extensions = Preferences.getPython("PythonExtensions") + \ Preferences.getPython("Python3Extensions") moduleDict = {} modules = [] for ext in Preferences.getPython("PythonExtensions") + \ Preferences.getPython("Python3Extensions"): modules.extend( glob.glob(Utilities.normjoinpath(self.packagePath, '*{0}'.format(ext)))) tot = len(modules) try: prog = 0 progress = QProgressDialog(self.trUtf8("Parsing modules..."), None, 0, tot, self.parent()) progress.show() QApplication.processEvents() for module in modules: progress.setValue(prog) QApplication.processEvents() prog = prog + 1 try: mod = Utilities.ModuleParser.readModule(module, extensions=extensions, caching=False) except ImportError: continue else: name = mod.name if name.startswith(self.package): name = name[len(self.package) + 1:] moduleDict[name] = mod finally: progress.setValue(tot) return moduleDict def buildDiagram(self): """ Public method to build the modules shapes of the diagram. """ initlist = glob.glob(os.path.join(self.packagePath, '__init__.*')) if len(initlist) == 0: ct = QGraphicsTextItem(None, self.scene) ct.setHtml( self.trUtf8("The directory <b>'{0}'</b> is not a Python package.")\ .format(self.package)) return shapes = {} p = 10 y = 10 maxHeight = 0 sceneRect = self.umlView.sceneRect() modules = self.__buildModulesDict() sortedkeys = sorted(modules.keys()) externalMods = [] packageList = self.shortPackage.split('.') packageListLen = len(packageList) for module in sortedkeys: impLst = [] for i in modules[module].imports: if i.startswith(self.package): n = i[len(self.package) + 1:] else: n = i if i in modules: impLst.append(n) elif self.showExternalImports: impLst.append(n) if not n in externalMods: externalMods.append(n) for i in list(modules[module].from_imports.keys()): if i.startswith('.'): dots = len(i) - len(i.lstrip('.')) if dots == 1: n = i[1:] i = n else: if self.showExternalImports: n = '.'.join( packageList[:packageListLen - dots + 1] + [i[dots:]]) else: n = i elif i.startswith(self.package): n = i[len(self.package) + 1:] else: n = i if i in modules: impLst.append(n) elif self.showExternalImports: impLst.append(n) if not n in externalMods: externalMods.append(n) classNames = [] for cls in list(modules[module].classes.keys()): className = modules[module].classes[cls].name if className not in classNames: classNames.append(className) shape = self.__addModule(module, classNames, 0.0, 0.0) shapeRect = shape.sceneBoundingRect() shapes[module] = (shape, impLst) pn = p + shapeRect.width() + 10 maxHeight = max(maxHeight, shapeRect.height()) if pn > sceneRect.width(): p = 10 y += maxHeight + 10 maxHeight = shapeRect.height() shape.setPos(p, y) p += shapeRect.width() + 10 else: shape.setPos(p, y) p = pn for module in externalMods: shape = self.__addModule(module, [], 0.0, 0.0) shapeRect = shape.sceneBoundingRect() shapes[module] = (shape, []) pn = p + shapeRect.width() + 10 maxHeight = max(maxHeight, shapeRect.height()) if pn > sceneRect.width(): p = 10 y += maxHeight + 10 maxHeight = shapeRect.height() shape.setPos(p, y) p += shapeRect.width() + 10 else: shape.setPos(p, y) p = pn rect = self.umlView._getDiagramRect(10) sceneRect = self.umlView.sceneRect() if rect.width() > sceneRect.width(): sceneRect.setWidth(rect.width()) if rect.height() > sceneRect.height(): sceneRect.setHeight(rect.height()) self.umlView.setSceneSize(sceneRect.width(), sceneRect.height()) self.__createAssociations(shapes) self.umlView.autoAdjustSceneSize(limit=True) def __addModule(self, name, classes, x, y): """ Private method to add a module to the diagram. @param name module name to be shown (string) @param classes list of class names contained in the module (list of strings) @param x x-coordinate (float) @param y y-coordinate (float) """ classes.sort() impM = ModuleModel(name, classes) impW = ModuleItem(impM, x, y, scene=self.scene) impW.setId(self.umlView.getItemId()) return impW def __createAssociations(self, shapes): """ Private method to generate the associations between the module shapes. @param shapes list of shapes """ for module in list(shapes.keys()): for rel in shapes[module][1]: assoc = AssociationItem( shapes[module][0], shapes[rel][0], Imports) self.scene.addItem(assoc)