eric6/Graphics/UMLClassDiagramBuilder.py

branch
maintenance
changeset 8400
b3eefd7e58d1
parent 8273
698ae46f40a4
parent 8295
3f5e8b0a338e
--- a/eric6/Graphics/UMLClassDiagramBuilder.py	Sat May 01 14:27:38 2021 +0200
+++ b/eric6/Graphics/UMLClassDiagramBuilder.py	Thu Jun 03 11:39:23 2021 +0200
@@ -8,6 +8,7 @@
 """
 
 from itertools import zip_longest
+import os
 
 from PyQt5.QtWidgets import QGraphicsTextItem
 
@@ -25,18 +26,28 @@
         """
         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 file file name of a python module to be shown (string)
+        @param dialog reference to the UML dialog
+        @type UMLDialog
+        @param view reference to the view object
+        @type UMLGraphicsView
+        @param project reference to the project object
+        @type Project
+        @param file file name of a python module to be shown
+        @type str
         @param noAttrs flag indicating, that no attributes should be shown
-            (boolean)
+        @type bool
         """
         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
+            ""
+        )
     
     def initialize(self):
         """
@@ -55,8 +66,10 @@
         """
         Private method to get the named shape.
         
-        @param name name of the shape (string)
-        @return shape (QGraphicsItem)
+        @param name name of the shape
+        @type str
+        @return shape
+        @rtype QGraphicsItem
         """
         return self.allClasses.get(name)
         
@@ -80,9 +93,10 @@
                 self.file, extensions=extensions, caching=False)
         except ImportError:
             ct = QGraphicsTextItem(None)
-            ct.setHtml(
+            ct.setHtml(self.buildErrorMessage(
                 self.tr("The module <b>'{0}'</b> could not be found.")
-                    .format(self.file))
+                    .format(self.file)
+            ))
             self.scene.addItem(ct)
             return
         
@@ -142,9 +156,10 @@
             self.umlView.autoAdjustSceneSize(limit=True)
         else:
             ct = QGraphicsTextItem(None)
-            ct.setHtml(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):
@@ -154,9 +169,12 @@
         The algorithm is borrowed from Boa Constructor.
         
         @param nodes list of nodes to arrange
+        @type list of str
         @param routes list of routes
+        @type list of tuple of (str, str)
         @param whiteSpaceFactor factor to increase whitespace between
-            items (float)
+            items
+        @type float
         """
         from . import GraphicsUtilities
         generations = GraphicsUtilities.sort(nodes, routes)
@@ -227,19 +245,27 @@
         """
         Private method to add a class defined in the module.
         
-        @param className name of the class to be as a dictionary key (string)
-        @param _class class to be shown (ModuleParser.Class)
-        @param x x-coordinate (float)
-        @param y y-coordinate (float)
-        @param isRbModule flag indicating a Ruby module (boolean)
+        @param className name of the class to be as a dictionary key
+        @type str
+        @param _class class to be shown
+        @type ModuleParser.Class
+        @param x x-coordinate
+        @type float
+        @param y y-coordinate
+        @type float
+        @param isRbModule flag indicating a Ruby module
+        @type bool
         """
         from .ClassItem import ClassItem, ClassModel
-        meths = sorted(_class.methods.keys())
-        attrs = sorted(_class.attributes.keys())
         name = _class.name
         if isRbModule:
             name = "{0} (Module)".format(name)
-        cl = ClassModel(name, meths[:], attrs[:])
+        cl = ClassModel(
+            name,
+            sorted(_class.methods.keys())[:],
+            sorted(_class.attributes.keys())[:],
+            sorted(_class.globals.keys())[:]
+        )
         cw = ClassItem(cl, False, x, y, noAttrs=self.noAttrs, scene=self.scene,
                        colors=self.umlView.getDrawingColors())
         cw.setId(self.umlView.getItemId())
@@ -254,9 +280,12 @@
         If the canvas is too small to take the shape, it
         is enlarged.
         
-        @param _class class to be shown (string)
-        @param x x-coordinate (float)
-        @param y y-coordinate (float)
+        @param _class class to be shown
+        @type ModuleParser.Class
+        @param x x-coordinate
+        @type float
+        @param y y-coordinate
+        @type float
         """
         from .ClassItem import ClassItem, ClassModel
         cl = ClassModel(_class)
@@ -272,6 +301,7 @@
         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:
@@ -288,7 +318,8 @@
         """
         Public method to get a string for data to be persisted.
         
-        @return persisted data string (string)
+        @return persisted data string
+        @rtype str
         """
         return "file={0}, no_attributes={1}".format(self.file, self.noAttrs)
     
@@ -296,9 +327,12 @@
         """
         Public method to parse persisted data.
         
-        @param version version of the data (string)
-        @param data persisted data to be parsed (string)
-        @return flag indicating success (boolean)
+        @param version version of the data
+        @type str
+        @param data persisted data to be parsed
+        @type str
+        @return flag indicating success
+        @rtype bool
         """
         parts = data.split(", ")
         if (
@@ -314,3 +348,60 @@
         self.initialize()
         
         return True
+    
+    def toDict(self):
+        """
+        Public method to collect data to be persisted.
+        
+        @return dictionary containing data to be persisted
+        @rtype dict
+        """
+        data = {
+            "project_name": self.project.getProjectName(),
+            "no_attributes": self.noAttrs,
+        }
+        
+        data["file"] = (
+            Utilities.fromNativeSeparators(self.__relFile)
+            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
+        @type dict
+        @return tuple containing a flag indicating success and an info
+            message in case the diagram belongs to a different project
+        @rtype tuple of (bool, str)
+        """
+        try:
+            self.noAttrs = data["no_attributes"]
+            
+            file = Utilities.toNativeSeparators(data["file"])
+            if os.path.isabs(file):
+                self.file = file
+                self.__relFile = ""
+            else:
+                # relative file paths indicate a project file
+                if data["project_name"] != self.project.getProjectName():
+                    msg = self.tr(
+                        "<p>The diagram belongs to project <b>{0}</b>."
+                        " 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