--- a/src/eric7/Graphics/UMLDialog.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Graphics/UMLDialog.py Wed Jul 13 14:55:47 2022 +0200 @@ -26,6 +26,7 @@ """ Class defining the UML dialog types. """ + CLASS_DIAGRAM = 0 PACKAGE_DIAGRAM = 1 IMPORTS_DIAGRAM = 2 @@ -37,25 +38,31 @@ """ Class implementing a dialog showing UML like diagrams. """ - FileVersions = ("1.0", ) - JsonFileVersions = ("1.0", ) - + + FileVersions = ("1.0",) + JsonFileVersions = ("1.0",) + UMLDialogType2String = { - UMLDialogType.CLASS_DIAGRAM: - QCoreApplication.translate("UMLDialog", "Class Diagram"), - UMLDialogType.PACKAGE_DIAGRAM: - QCoreApplication.translate("UMLDialog", "Package Diagram"), - UMLDialogType.IMPORTS_DIAGRAM: - QCoreApplication.translate("UMLDialog", "Imports Diagram"), - UMLDialogType.APPLICATION_DIAGRAM: - QCoreApplication.translate("UMLDialog", "Application Diagram"), + UMLDialogType.CLASS_DIAGRAM: QCoreApplication.translate( + "UMLDialog", "Class Diagram" + ), + UMLDialogType.PACKAGE_DIAGRAM: QCoreApplication.translate( + "UMLDialog", "Package Diagram" + ), + UMLDialogType.IMPORTS_DIAGRAM: QCoreApplication.translate( + "UMLDialog", "Imports Diagram" + ), + UMLDialogType.APPLICATION_DIAGRAM: QCoreApplication.translate( + "UMLDialog", "Application Diagram" + ), } - - def __init__(self, diagramType, project, path="", parent=None, - initBuilder=True, **kwargs): + + def __init__( + self, diagramType, project, path="", parent=None, initBuilder=True, **kwargs + ): """ Constructor - + @param diagramType type of the diagram @type UMLDialogType @param project reference to the project object @@ -72,33 +79,33 @@ """ super().__init__(parent) self.setObjectName("UMLDialog") - + self.__project = project self.__diagramType = diagramType - + from .UMLGraphicsView import UMLGraphicsView + self.scene = QGraphicsScene(0.0, 0.0, 800.0, 600.0) self.umlView = UMLGraphicsView(self.scene, parent=self) - self.builder = self.__diagramBuilder( - self.__diagramType, path, **kwargs) + self.builder = self.__diagramBuilder(self.__diagramType, path, **kwargs) if self.builder and initBuilder: self.builder.initialize() - + self.__fileName = "" - + self.__initActions() self.__initToolBars() - + self.setCentralWidget(self.umlView) - + self.umlView.relayout.connect(self.__relayout) - + self.setWindowTitle(self.__getDiagramTitel(self.__diagramType)) - + def __getDiagramTitel(self, diagramType): """ Private method to get a textual description for the diagram type. - + @param diagramType diagram type string @type str @return titel of the diagram @@ -107,47 +114,40 @@ return UMLDialog.UMLDialogType2String.get( diagramType, self.tr("Illegal Diagram Type") ) - + def __initActions(self): """ Private slot to initialize the actions. """ - self.closeAct = QAction( - UI.PixmapCache.getIcon("close"), - self.tr("Close"), self) + self.closeAct = QAction(UI.PixmapCache.getIcon("close"), self.tr("Close"), self) self.closeAct.triggered.connect(self.close) - - self.openAct = QAction( - UI.PixmapCache.getIcon("open"), - self.tr("Load"), self) + + self.openAct = QAction(UI.PixmapCache.getIcon("open"), self.tr("Load"), self) self.openAct.triggered.connect(self.load) - + self.saveAct = QAction( - UI.PixmapCache.getIcon("fileSave"), - self.tr("Save"), self) + UI.PixmapCache.getIcon("fileSave"), self.tr("Save"), self + ) self.saveAct.triggered.connect(self.__save) - + self.saveAsAct = QAction( - UI.PixmapCache.getIcon("fileSaveAs"), - self.tr("Save As..."), self) + UI.PixmapCache.getIcon("fileSaveAs"), self.tr("Save As..."), self + ) self.saveAsAct.triggered.connect(self.__saveAs) - + self.saveImageAct = QAction( - UI.PixmapCache.getIcon("fileSavePixmap"), - self.tr("Save as Image"), self) + UI.PixmapCache.getIcon("fileSavePixmap"), self.tr("Save as Image"), self + ) self.saveImageAct.triggered.connect(self.umlView.saveImage) - - self.printAct = QAction( - UI.PixmapCache.getIcon("print"), - self.tr("Print"), self) + + self.printAct = QAction(UI.PixmapCache.getIcon("print"), self.tr("Print"), self) self.printAct.triggered.connect(self.umlView.printDiagram) - + self.printPreviewAct = QAction( - UI.PixmapCache.getIcon("printPreview"), - self.tr("Print Preview"), self) - self.printPreviewAct.triggered.connect( - self.umlView.printPreviewDiagram) - + UI.PixmapCache.getIcon("printPreview"), self.tr("Print Preview"), self + ) + self.printPreviewAct.triggered.connect(self.umlView.printPreviewDiagram) + def __initToolBars(self): """ Private slot to initialize the toolbars. @@ -155,7 +155,7 @@ self.windowToolBar = QToolBar(self.tr("Window"), self) self.windowToolBar.setIconSize(UI.Config.ToolBarIconSize) self.windowToolBar.addAction(self.closeAct) - + self.fileToolBar = QToolBar(self.tr("File"), self) self.fileToolBar.setIconSize(UI.Config.ToolBarIconSize) self.fileToolBar.addAction(self.openAct) @@ -166,17 +166,17 @@ self.fileToolBar.addSeparator() self.fileToolBar.addAction(self.printPreviewAct) self.fileToolBar.addAction(self.printAct) - + self.umlToolBar = self.umlView.initToolBar() - + self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.fileToolBar) self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.windowToolBar) self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.umlToolBar) - + def show(self, fromFile=False): """ Public method to show the dialog. - + @param fromFile flag indicating, that the diagram was loaded from file @type bool @@ -184,18 +184,18 @@ if not fromFile and self.builder: self.builder.buildDiagram() super().show() - + def __relayout(self): """ Private method to re-layout the diagram. """ if self.builder: self.builder.buildDiagram() - + def __diagramBuilder(self, diagramType, path, **kwargs): """ Private method to instantiate a diagram builder object. - + @param diagramType type of the diagram @type UMLDialogType @param path file or directory path to build the diagram from @@ -207,34 +207,42 @@ """ if diagramType == UMLDialogType.CLASS_DIAGRAM: from .UMLClassDiagramBuilder import UMLClassDiagramBuilder + return UMLClassDiagramBuilder( - self, self.umlView, self.__project, path, **kwargs) + self, self.umlView, self.__project, path, **kwargs + ) elif diagramType == UMLDialogType.PACKAGE_DIAGRAM: from .PackageDiagramBuilder import PackageDiagramBuilder + return PackageDiagramBuilder( - self, self.umlView, self.__project, path, **kwargs) + self, self.umlView, self.__project, path, **kwargs + ) elif diagramType == UMLDialogType.IMPORTS_DIAGRAM: from .ImportsDiagramBuilder import ImportsDiagramBuilder + return ImportsDiagramBuilder( - self, self.umlView, self.__project, path, **kwargs) + self, self.umlView, self.__project, path, **kwargs + ) elif diagramType == UMLDialogType.APPLICATION_DIAGRAM: from .ApplicationDiagramBuilder import ApplicationDiagramBuilder + return ApplicationDiagramBuilder( - self, self.umlView, self.__project, **kwargs) + self, self.umlView, self.__project, **kwargs + ) else: return None - + def __save(self): """ Private slot to save the diagram with the current name. """ self.__saveAs(self.__fileName) - + @pyqtSlot() def __saveAs(self, filename=""): """ Private slot to save the diagram. - + @param filename name of the file to write to @type str """ @@ -243,14 +251,17 @@ self, self.tr("Save Diagram"), "", - self.tr("Eric Graphics File (*.egj);;" - "Eric Text Graphics File (*.e5g);;" - "All Files (*)"), + self.tr( + "Eric Graphics File (*.egj);;" + "Eric Text Graphics File (*.e5g);;" + "All Files (*)" + ), "", - EricFileDialog.DontConfirmOverwrite) + EricFileDialog.DontConfirmOverwrite, + ) if not fname: return - + fpath = pathlib.Path(fname) if not fpath.suffix: ex = selectedFilter.split("(*")[1].split(")")[0] @@ -260,24 +271,26 @@ res = EricMessageBox.yesNo( self, self.tr("Save Diagram"), - self.tr("<p>The file <b>{0}</b> already exists." - " Overwrite it?</p>").format(fpath), - icon=EricMessageBox.Warning) + self.tr( + "<p>The file <b>{0}</b> already exists." " Overwrite it?</p>" + ).format(fpath), + icon=EricMessageBox.Warning, + ) if not res: return filename = str(fpath) - + res = self.__writeJsonGraphicsFile(filename) - + if res: # save the file name only in case of success self.__fileName = filename - + # Note: remove loading of eric6 line based diagram format after 22.6 def load(self, filename=""): """ Public method to load a diagram from a file. - + @param filename name of the file to be loaded @type str @return flag indicating success @@ -288,30 +301,34 @@ self, self.tr("Load Diagram"), "", - self.tr("Eric Graphics File (*.egj);;" - "Eric Text Graphics File (*.e5g);;" - "All Files (*)")) + self.tr( + "Eric Graphics File (*.egj);;" + "Eric Text Graphics File (*.e5g);;" + "All Files (*)" + ), + ) if not filename: # Canceled by user return False - + return ( self.__readLineBasedGraphicsFile(filename) - if filename.endswith(".e5g") else + if filename.endswith(".e5g") + else # JSON format is the default self.__readJsonGraphicsFile(filename) ) - + ####################################################################### ## Methods to read and write eric graphics files of the old line ## based file format. ####################################################################### - + def __readLineBasedGraphicsFile(self, filename): """ Private method to read an eric graphics file using the old line based file format. - + @param filename name of the file to be read @type str @return flag indicating success @@ -326,27 +343,26 @@ self.tr("Load Diagram"), self.tr( """<p>The file <b>{0}</b> could not be read.</p>""" - """<p>Reason: {1}</p>""").format(filename, str(err))) + """<p>Reason: {1}</p>""" + ).format(filename, str(err)), + ) return False - + lines = data.splitlines() if len(lines) < 3: self.__showInvalidDataMessage(filename) return False - + try: # step 1: check version linenum = 0 key, value = lines[linenum].split(": ", 1) - if ( - key.strip() != "version" or - value.strip() not in UMLDialog.FileVersions - ): + if key.strip() != "version" or value.strip() not in UMLDialog.FileVersions: self.__showInvalidDataMessage(filename, linenum) return False else: version = value - + # step 2: extract diagram type linenum += 1 key, value = lines[linenum].split(": ", 1) @@ -361,7 +377,7 @@ return False self.scene.clear() self.builder = self.__diagramBuilder(self.__diagramType, "") - + # step 3: extract scene size linenum += 1 key, value = lines[linenum].split(": ", 1) @@ -374,7 +390,7 @@ self.__showInvalidDataMessage(filename, linenum) return False self.umlView.setSceneSize(width, height) - + # step 4: extract builder data if available linenum += 1 key, value = lines[linenum].split(": ", 1) @@ -384,54 +400,55 @@ self.__showInvalidDataMessage(filename, linenum) return False linenum += 1 - + # step 5: extract the graphics items - ok, vlinenum = self.umlView.parsePersistenceData( - version, lines[linenum:]) + ok, vlinenum = self.umlView.parsePersistenceData(version, lines[linenum:]) if not ok: self.__showInvalidDataMessage(filename, linenum + vlinenum) return False - + except IndexError: self.__showInvalidDataMessage(filename) return False - + # everything worked fine, so remember the file name and set the # window title self.setWindowTitle(self.__getDiagramTitel(self.__diagramType)) self.__fileName = filename - + return True - + def __showInvalidDataMessage(self, filename, linenum=-1): """ Private slot to show a message dialog indicating an invalid data file. - + @param filename name of the file containing the invalid data @type str @param linenum number of the invalid line @type int """ msg = ( - self.tr("""<p>The file <b>{0}</b> does not contain""" - """ valid data.</p>""").format(filename) - if linenum < 0 else - self.tr("""<p>The file <b>{0}</b> does not contain""" - """ valid data.</p><p>Invalid line: {1}</p>""" - ).format(filename, linenum + 1) + self.tr( + """<p>The file <b>{0}</b> does not contain""" """ valid data.</p>""" + ).format(filename) + if linenum < 0 + else self.tr( + """<p>The file <b>{0}</b> does not contain""" + """ valid data.</p><p>Invalid line: {1}</p>""" + ).format(filename, linenum + 1) ) EricMessageBox.critical(self, self.tr("Load Diagram"), msg) - + ####################################################################### ## Methods to read and write eric graphics files of the JSON based ## file format. ####################################################################### - + def __writeJsonGraphicsFile(self, filename): """ Private method to write an eric graphics file using the JSON based file format. - + @param filename name of the file to write to @type str @return flag indicating a successful write @@ -446,7 +463,7 @@ "builder": self.builder.toDict(), "view": self.umlView.toDict(), } - + try: jsonString = json.dumps(data, indent=2) with open(filename, "w") as f: @@ -458,15 +475,16 @@ self.tr("Save Diagram"), self.tr( """<p>The file <b>{0}</b> could not be saved.</p>""" - """<p>Reason: {1}</p>""").format(filename, str(err)) + """<p>Reason: {1}</p>""" + ).format(filename, str(err)), ) return False - + def __readJsonGraphicsFile(self, filename): """ Private method to read an eric graphics file using the JSON based file format. - + @param filename name of the file to be read @type str @return flag indicating a successful read @@ -482,10 +500,11 @@ self.tr("Load Diagram"), self.tr( """<p>The file <b>{0}</b> could not be read.</p>""" - """<p>Reason: {1}</p>""").format(filename, str(err)) + """<p>Reason: {1}</p>""" + ).format(filename, str(err)), ) return False - + try: # step 1: check version if data["version"] in UMLDialog.JsonFileVersions: @@ -493,7 +512,7 @@ else: self.__showInvalidDataMessage(filename) return False - + # step 2: set diagram type try: self.__diagramType = UMLDialogType(data["type"]) @@ -502,10 +521,10 @@ return False self.scene.clear() self.builder = self.__diagramBuilder(self.__diagramType, "") - + # step 3: set scene size self.umlView.setSceneSize(data["width"], data["height"]) - + # step 4: extract builder data if available ok, msg = self.builder.fromDict(version, data["builder"]) if not ok: @@ -515,7 +534,8 @@ self.tr("Load Diagram"), msg, EricMessageBox.Abort | EricMessageBox.Ignore, - EricMessageBox.Abort) + EricMessageBox.Abort, + ) if res == EricMessageBox.Abort: return False else: @@ -523,7 +543,7 @@ else: self.__showInvalidDataMessage(filename) return False - + # step 5: extract the graphics items ok = self.umlView.fromDict(version, data["view"]) if not ok: @@ -532,10 +552,10 @@ except KeyError: self.__showInvalidDataMessage(filename) return False - + # everything worked fine, so remember the file name and set the # window title self.setWindowTitle(self.__getDiagramTitel(self.__diagramType)) self.__fileName = filename - + return True