Graphics/ApplicationDiagram.py

Sat, 05 May 2012 19:23:18 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 05 May 2012 19:23:18 +0200
branch
5_2_x
changeset 1847
ed220d11ee1e
parent 1509
c0b5e693b0eb
child 2025
8bb085c59cd2
child 2303
0ed4ed026c16
permissions
-rw-r--r--

Fixed an issue parsing sources in the various UML like diagrams.

# -*- coding: utf-8 -*-

# Copyright (c) 2007 - 2012 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a dialog showing an imports diagram of the application.
"""

import os
import glob

from PyQt4.QtGui import QApplication, QProgressDialog

from .UMLDialog import UMLDialog
from .PackageItem import PackageItem, PackageModel
from .AssociationItem import AssociationItem, Imports

import Utilities.ModuleParser
import Utilities

import Preferences


class ApplicationDiagram(UMLDialog):
    """
    Class implementing a dialog showing an imports diagram of the application.
    """
    def __init__(self, project, parent=None, name=None,  noModules=False):
        """
        Constructor
        
        @param project reference to the project object
        @param parent parent widget of the view (QWidget)
        @param name name of the view widget (string)
        @keyparam noModules flag indicating, that no module names should be
            shown (boolean)
        """
        self.project = project
        self.noModules = noModules
        
        UMLDialog.__init__(self, self.project.ppath, parent)
        
        if not name:
            self.setObjectName("ApplicationDiagram")
        else:
            self.setObjectName(name)
        
        self.umlView.relayout.connect(self.relayout)
        
    def __buildModulesDict(self):
        """
        Private method to build a dictionary of modules contained in the application.
        
        @return dictionary of modules contained in the application.
        """
        extensions = Preferences.getPython("PythonExtensions") + \
            Preferences.getPython("Python3Extensions") + ['.rb']
        moduleDict = {}
        mods = self.project.pdata["SOURCES"]
        modules = []
        for module in mods:
            modules.append(Utilities.normabsjoinpath(self.project.ppath, module))
        tot = len(modules)
        try:
            prog = 0
            progress = QProgressDialog(self.trUtf8("Parsing modules..."),
                None, 0, tot, self)
            progress.show()
            QApplication.processEvents()
            for module in modules:
                progress.setValue(prog)
                QApplication.processEvents()
                prog += 1
                if module.endswith("__init__.py"):
                    continue
                try:
                    mod = Utilities.ModuleParser.readModule(module, extensions=extensions)
                except ImportError:
                    continue
                else:
                    name = mod.name
                    moduleDict[name] = mod
        finally:
            progress.setValue(tot)
        return moduleDict
        
    def __buildPackages(self):
        """
        Private method to build the packages shapes of the diagram.
        """
        project = os.path.splitdrive(self.project.getProjectPath())[1]\
            .replace(os.sep, '.')[1:]
        packages = {}
        shapes = {}
        p = 10
        y = 10
        maxHeight = 0
        sceneRect = self.umlView.sceneRect()
        
        modules = self.__buildModulesDict()
        sortedkeys = sorted(modules.keys())
        
        # step 1: build a dictionary of packages
        for module in sortedkeys:
            l = module.split('.')
            package = '.'.join(l[:-1])
            if package in packages:
                packages[package][0].append(l[-1])
            else:
                packages[package] = ([l[-1]], [])
                
        # step 2: assign modules to dictionaries and update import relationship
        for module in sortedkeys:
            l = module.split('.')
            package = '.'.join(l[:-1])
            impLst = []
            for i in modules[module].imports:
                if i in modules:
                    impLst.append(i)
                else:
                    if i.find('.') == -1:
                        n = "{0}.{1}".format(modules[module].package, i)
                        if n in modules:
                            impLst.append(n)
                        else:
                            n = "{0}.{1}".format(project, i)
                            if n in modules:
                                impLst.append(n)
                            elif n in packages:
                                n = "{0}.<<Dummy>>".format(n)
                                impLst.append(n)
                    else:
                        n = "{0}.{1}".format(project, i)
                        if n in modules:
                            impLst.append(n)
            for i in list(modules[module].from_imports.keys()):
                if i.startswith('.'):
                    dots = len(i) - len(i.lstrip('.'))
                    if dots == 1:
                        i = i[1:]
                    elif dots > 1:
                        packagePath = os.path.dirname(modules[module].file)
                        hasInit = True
                        ppath = packagePath
                        while hasInit:
                            ppath = os.path.dirname(ppath)
                            hasInit = \
                                len(glob.glob(os.path.join(ppath, '__init__.*'))) > 0
                        shortPackage = \
                            packagePath.replace(ppath, '').replace(os.sep, '.')[1:]
                        packageList = shortPackage.split('.')[1:]
                        packageListLen = len(packageList)
                        i = '.'.join(packageList[:packageListLen - dots + 1] + [i[dots:]])
                
                if i in modules:
                    impLst.append(i)
                else:
                    if i.find('.') == -1:
                        n = "{0}.{1}".format(modules[module].package, i)
                        if n in modules:
                            impLst.append(n)
                        else:
                            n = "{0}.{1}".format(project, i)
                            if n in modules:
                                impLst.append(n)
                            elif n in packages:
                                n = "{0}.<<Dummy>>".format(n)
                                impLst.append(n)
                    else:
                        n = "{0}.{1}".format(project, i)
                        if n in modules:
                            impLst.append(n)
            for imp in impLst:
                impPackage = '.'.join(imp.split('.')[:-1])
                if not impPackage in packages[package][1] and \
                   not impPackage == package:
                    packages[package][1].append(impPackage)
                    
        sortedkeys = sorted(packages.keys())
        for package in sortedkeys:
            if package:
                relPackage = package.replace(project, '')
                if relPackage and relPackage[0] == '.':
                    relPackage = relPackage[1:]
                else:
                    relPackage = self.trUtf8("<<Application>>")
            else:
                relPackage = self.trUtf8("<<Others>>")
            shape = self.__addPackage(relPackage, packages[package][0], 0.0, 0.0)
            shapeRect = shape.sceneBoundingRect()
            shapes[package] = (shape, packages[package][1])
            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)
        
    def __addPackage(self, name, modules, x, y):
        """
        Private method to add a package to the diagram.
        
        @param name package name to be shown (string)
        @param modules list of module names contained in the package
            (list of strings)
        @param x x-coordinate (float)
        @param y y-coordinate (float)
        """
        modules.sort()
        pm = PackageModel(name, modules)
        pw = PackageItem(pm, x, y, noModules=self.noModules, scene=self.scene)
        return pw
        
    def __createAssociations(self, shapes):
        """
        Private method to generate the associations between the package shapes.
        
        @param shapes list of shapes
        """
        for package in shapes:
            for rel in shapes[package][1]:
                assoc = AssociationItem(
                        shapes[package][0], shapes[rel][0],
                        Imports)
                self.scene.addItem(assoc)
        
    def show(self):
        """
        Overriden method to show the dialog.
        """
        self.__buildPackages()
        UMLDialog.show(self)
        
    def relayout(self):
        """
        Method to relayout the diagram.
        """
        self.__buildPackages()

eric ide

mercurial