--- 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, ""