src/eric7/Graphics/UMLClassDiagramBuilder.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9278
36448ca469c2
diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Graphics/UMLClassDiagramBuilder.py
--- a/src/eric7/Graphics/UMLClassDiagramBuilder.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/Graphics/UMLClassDiagramBuilder.py	Wed Jul 13 14:55:47 2022 +0200
@@ -22,10 +22,11 @@
     """
     Class implementing a builder for UML like class diagrams.
     """
+
     def __init__(self, dialog, view, project, file, noAttrs=False):
         """
         Constructor
-        
+
         @param dialog reference to the UML dialog
         @type UMLDialog
         @param view reference to the view object
@@ -39,16 +40,16 @@
         """
         super().__init__(dialog, view, project)
         self.setObjectName("UMLClassDiagramBuilder")
-        
+
         self.file = file
         self.noAttrs = noAttrs
-        
+
         self.__relFile = (
             self.project.getRelativePath(self.file)
-            if self.project.isProjectSource(self.file) else
-            ""
+            if self.project.isProjectSource(self.file)
+            else ""
         )
-    
+
     def initialize(self):
         """
         Public method to initialize the object.
@@ -56,53 +57,55 @@
         pname = self.project.getProjectName()
         name = (
             self.tr("Class Diagram {0}: {1}").format(
-                pname, self.project.getRelativePath(self.file))
-            if pname and self.project.isProjectSource(self.file) else
-            self.tr("Class Diagram: {0}").format(self.file)
+                pname, self.project.getRelativePath(self.file)
+            )
+            if pname and self.project.isProjectSource(self.file)
+            else self.tr("Class Diagram: {0}").format(self.file)
         )
         self.umlView.setDiagramName(name)
-        
+
     def __getCurrentShape(self, name):
         """
         Private method to get the named shape.
-        
+
         @param name name of the shape
         @type str
         @return shape
         @rtype QGraphicsItem
         """
         return self.allClasses.get(name)
-        
+
     def buildDiagram(self):
         """
         Public method to build the class shapes of the class diagram.
-        
+
         The algorithm is borrowed from Boa Constructor.
         """
         import Utilities.ModuleParser
-        
+
         self.allClasses = {}
         self.allModules = {}
-        
+
         try:
-            extensions = (
-                Preferences.getPython("Python3Extensions") +
-                ['.rb']
+            extensions = Preferences.getPython("Python3Extensions") + [".rb"]
+            module = Utilities.ModuleParser.readModule(
+                self.file, extensions=extensions, caching=False
             )
-            module = Utilities.ModuleParser.readModule(
-                self.file, extensions=extensions, caching=False)
         except ImportError:
             ct = QGraphicsTextItem(None)
-            ct.setHtml(self.buildErrorMessage(
-                self.tr("The module <b>'{0}'</b> could not be found.")
-                    .format(self.file)
-            ))
+            ct.setHtml(
+                self.buildErrorMessage(
+                    self.tr("The module <b>'{0}'</b> could not be found.").format(
+                        self.file
+                    )
+                )
+            )
             self.scene.addItem(ct)
             return
-        
+
         if self.file not in self.allModules:
             self.allModules[self.file] = []
-        
+
         routes = []
         nodes = []
         todo = [module.createHierarchy()]
@@ -112,17 +115,18 @@
             for className in hierarchy:
                 classesFound = True
                 cw = self.__getCurrentShape(className)
-                if not cw and className.find('.') >= 0:
-                    cw = self.__getCurrentShape(className.split('.')[-1])
+                if not cw and className.find(".") >= 0:
+                    cw = self.__getCurrentShape(className.split(".")[-1])
                     if cw:
                         self.allClasses[className] = cw
                         if className not in self.allModules[self.file]:
                             self.allModules[self.file].append(className)
                 if cw and cw.noAttrs != self.noAttrs:
                     cw = None
-                if cw and not (cw.external and
-                               (className in module.classes or
-                                className in module.modules)):
+                if cw and not (
+                    cw.external
+                    and (className in module.classes or className in module.modules)
+                ):
                     if cw.scene() != self.scene:
                         self.scene.addItem(cw)
                         cw.setPos(10, 10)
@@ -131,43 +135,46 @@
                 else:
                     if className in module.classes:
                         # this is a local class (defined in this module)
-                        self.__addLocalClass(
-                            className, module.classes[className], 0, 0)
+                        self.__addLocalClass(className, module.classes[className], 0, 0)
                     elif className in module.modules:
                         # this is a local module (defined in this module)
                         self.__addLocalClass(
-                            className, module.modules[className], 0, 0, True)
+                            className, module.modules[className], 0, 0, True
+                        )
                     else:
                         self.__addExternalClass(className, 0, 0)
                     nodes.append(className)
-                
+
                 if hierarchy.get(className):
                     todo.append(hierarchy.get(className))
                     children = list(hierarchy.get(className).keys())
                     for child in children:
                         if (className, child) not in routes:
                             routes.append((className, child))
-            
+
             del todo[0]
-        
+
         if classesFound:
             self.__arrangeClasses(nodes, routes[:])
             self.__createAssociations(routes)
             self.umlView.autoAdjustSceneSize(limit=True)
         else:
             ct = QGraphicsTextItem(None)
-            ct.setHtml(self.buildErrorMessage(
-                self.tr("The module <b>'{0}'</b> does not contain any"
-                        " classes.").format(self.file)
-            ))
+            ct.setHtml(
+                self.buildErrorMessage(
+                    self.tr(
+                        "The module <b>'{0}'</b> does not contain any" " classes."
+                    ).format(self.file)
+                )
+            )
             self.scene.addItem(ct)
-        
+
     def __arrangeClasses(self, nodes, routes, whiteSpaceFactor=1.2):
         """
         Private method to arrange the shapes on the canvas.
-        
+
         The algorithm is borrowed from Boa Constructor.
-        
+
         @param nodes list of nodes to arrange
         @type list of str
         @param routes list of routes
@@ -177,16 +184,16 @@
         @type float
         """
         from . import GraphicsUtilities
+
         generations = GraphicsUtilities.sort(nodes, routes)
-        
+
         # calculate width and height of all elements
         sizes = []
         for generation in generations:
             sizes.append([])
             for child in generation:
-                sizes[-1].append(
-                    self.__getCurrentShape(child).sceneBoundingRect())
-        
+                sizes[-1].append(self.__getCurrentShape(child).sceneBoundingRect())
+
         # calculate total width and total height
         width = 0
         height = 0
@@ -195,56 +202,53 @@
         for generation in sizes:
             currentWidth = 0
             currentHeight = 0
-            
+
             for rect in generation:
                 if rect.bottom() > currentHeight:
                     currentHeight = rect.bottom()
                 currentWidth += rect.right()
-            
+
             # update totals
             if currentWidth > width:
                 width = currentWidth
             height += currentHeight
-            
+
             # store generation info
             widths.append(currentWidth)
             heights.append(currentHeight)
-        
+
         # add in some whitespace
         width *= whiteSpaceFactor
         height = height * whiteSpaceFactor - 20
         verticalWhiteSpace = 40.0
-        
+
         sceneRect = self.umlView.sceneRect()
         width += 50.0
         height += 50.0
         swidth = sceneRect.width() if width < sceneRect.width() else width
         sheight = sceneRect.height() if height < sceneRect.height() else height
         self.umlView.setSceneSize(swidth, sheight)
-        
+
         # distribute each generation across the width and the
         # generations across height
         y = 10.0
-        for currentWidth, currentHeight, generation in (
-                zip_longest(widths, heights, generations)
+        for currentWidth, currentHeight, generation in zip_longest(
+            widths, heights, generations
         ):
             x = 10.0
             # whiteSpace is the space between any two elements
-            whiteSpace = (
-                (width - currentWidth - 20) /
-                (len(generation) - 1.0 or 2.0)
-            )
+            whiteSpace = (width - currentWidth - 20) / (len(generation) - 1.0 or 2.0)
             for className in generation:
                 cw = self.__getCurrentShape(className)
                 cw.setPos(x, y)
                 rect = cw.sceneBoundingRect()
                 x = x + rect.width() + whiteSpace
             y = y + currentHeight + verticalWhiteSpace
-        
+
     def __addLocalClass(self, className, _class, x, y, isRbModule=False):
         """
         Private method to add a class defined in the module.
-        
+
         @param className name of the class to be as a dictionary key
         @type str
         @param _class class to be shown
@@ -257,6 +261,7 @@
         @type bool
         """
         from .ClassItem import ClassItem, ClassModel
+
         name = _class.name
         if isRbModule:
             name = "{0} (Module)".format(name)
@@ -264,22 +269,29 @@
             name,
             sorted(_class.methods.keys())[:],
             sorted(_class.attributes.keys())[:],
-            sorted(_class.globals.keys())[:]
+            sorted(_class.globals.keys())[:],
         )
-        cw = ClassItem(cl, False, x, y, noAttrs=self.noAttrs, scene=self.scene,
-                       colors=self.umlView.getDrawingColors())
+        cw = ClassItem(
+            cl,
+            False,
+            x,
+            y,
+            noAttrs=self.noAttrs,
+            scene=self.scene,
+            colors=self.umlView.getDrawingColors(),
+        )
         cw.setId(self.umlView.getItemId())
         self.allClasses[className] = cw
         if _class.name not in self.allModules[self.file]:
             self.allModules[self.file].append(_class.name)
-        
+
     def __addExternalClass(self, _class, x, y):
         """
         Private method to add a class defined outside the module.
-        
+
         If the canvas is too small to take the shape, it
         is enlarged.
-        
+
         @param _class class to be shown
         @type ModuleParser.Class
         @param x x-coordinate
@@ -288,22 +300,31 @@
         @type float
         """
         from .ClassItem import ClassItem, ClassModel
+
         cl = ClassModel(_class)
-        cw = ClassItem(cl, True, x, y, noAttrs=self.noAttrs, scene=self.scene,
-                       colors=self.umlView.getDrawingColors())
+        cw = ClassItem(
+            cl,
+            True,
+            x,
+            y,
+            noAttrs=self.noAttrs,
+            scene=self.scene,
+            colors=self.umlView.getDrawingColors(),
+        )
         cw.setId(self.umlView.getItemId())
         self.allClasses[_class] = cw
         if _class not in self.allModules[self.file]:
             self.allModules[self.file].append(_class)
-        
+
     def __createAssociations(self, routes):
         """
         Private method to generate the associations between the class shapes.
-        
+
         @param routes list of relationsships
         @type list of tuple of (str, str)
         """
         from .AssociationItem import AssociationItem, AssociationType
+
         for route in routes:
             if len(route) > 1:
                 assoc = AssociationItem(
@@ -311,13 +332,14 @@
                     self.__getCurrentShape(route[0]),
                     AssociationType.GENERALISATION,
                     topToBottom=True,
-                    colors=self.umlView.getDrawingColors())
+                    colors=self.umlView.getDrawingColors(),
+                )
                 self.scene.addItem(assoc)
-    
+
     def parsePersistenceData(self, version, data):
         """
         Public method to parse persisted data.
-        
+
         @param version version of the data
         @type str
         @param data persisted data to be parsed
@@ -327,23 +349,23 @@
         """
         parts = data.split(", ")
         if (
-            len(parts) != 2 or
-            not parts[0].startswith("file=") or
-            not parts[1].startswith("no_attributes=")
+            len(parts) != 2
+            or not parts[0].startswith("file=")
+            or not parts[1].startswith("no_attributes=")
         ):
             return False
-        
+
         self.file = parts[0].split("=", 1)[1].strip()
         self.noAttrs = Utilities.toBool(parts[1].split("=", 1)[1].strip())
-        
+
         self.initialize()
-        
+
         return True
-    
+
     def toDict(self):
         """
         Public method to collect data to be persisted.
-        
+
         @return dictionary containing data to be persisted
         @rtype dict
         """
@@ -351,19 +373,19 @@
             "project_name": self.project.getProjectName(),
             "no_attributes": self.noAttrs,
         }
-        
+
         data["file"] = (
             Utilities.fromNativeSeparators(self.__relFile)
-            if self.__relFile else
-            Utilities.fromNativeSeparators(self.file)
+            if self.__relFile
+            else Utilities.fromNativeSeparators(self.file)
         )
-        
+
         return data
-    
+
     def fromDict(self, version, data):
         """
         Public method to populate the class with data persisted by 'toDict()'.
-        
+
         @param version version of the data
         @type str
         @param data dictionary containing the persisted data
@@ -374,7 +396,7 @@
         """
         try:
             self.noAttrs = data["no_attributes"]
-            
+
             file = Utilities.toNativeSeparators(data["file"])
             if os.path.isabs(file):
                 self.file = file
@@ -387,12 +409,12 @@
                         " Please open it and try again.</p>"
                     ).format(data["project_name"])
                     return False, msg
-                
+
                 self.__relFile = file
                 self.file = self.project.getAbsolutePath(file)
         except KeyError:
             return False, ""
-        
+
         self.initialize()
-        
+
         return True, ""

eric ide

mercurial