Graphics/ImportsDiagramBuilder.py

changeset 2031
c36c2eb62a75
parent 2030
db11a2fe9bbc
child 2033
4b99609f6a87
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Graphics/ImportsDiagramBuilder.py	Sun Sep 09 17:40:32 2012 +0200
@@ -0,0 +1,238 @@
+# -*- 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)

eric ide

mercurial