Graphics/UMLDialog.py

changeset 2034
8de0fc1f7fef
parent 2033
4b99609f6a87
child 2101
5bac7dee9e1a
equal deleted inserted replaced
2033:4b99609f6a87 2034:8de0fc1f7fef
20 20
21 class UMLDialog(QMainWindow): 21 class UMLDialog(QMainWindow):
22 """ 22 """
23 Class implementing a dialog showing UML like diagrams. 23 Class implementing a dialog showing UML like diagrams.
24 """ 24 """
25 NoDiagram = 255
25 ClassDiagram = 0 26 ClassDiagram = 0
26 PackageDiagram = 1 27 PackageDiagram = 1
27 ImportsDiagram = 2 28 ImportsDiagram = 2
28 ApplicationDiagram = 3 29 ApplicationDiagram = 3
29 30
31 FileVersions = ["1.0"]
32
30 def __init__(self, diagramType, project, path="", parent=None, initBuilder=True, 33 def __init__(self, diagramType, project, path="", parent=None, initBuilder=True,
31 **kwargs): 34 **kwargs):
32 """ 35 """
33 Constructor 36 Constructor
34 37
35 @param diagramType type of the diagram 38 @param diagramType type of the diagram (one of ApplicationDiagram, ClassDiagram,
36 (one of ApplicationDiagram, ClassDiagram, ImportsDiagram, PackageDiagram) 39 ImportsDiagram, NoDiagram, PackageDiagram)
37 @param project reference to the project object (Project) 40 @param project reference to the project object (Project)
38 @param path file or directory path to build the diagram from (string) 41 @param path file or directory path to build the diagram from (string)
39 @param parent parent widget of the dialog (QWidget) 42 @param parent parent widget of the dialog (QWidget)
40 @keyparam initBuilder flag indicating to initialize the diagram builder (boolean) 43 @keyparam initBuilder flag indicating to initialize the diagram builder (boolean)
41 @param kwargs diagram specific data 44 @param kwargs diagram specific data
42 """ 45 """
43 super().__init__(parent) 46 super().__init__(parent)
44 self.setObjectName("UMLDialog") 47 self.setObjectName("UMLDialog")
45 48
46 self.__diagramType = diagramType 49 self.__diagramType = diagramType
50 self.__project = project
47 51
48 self.scene = QGraphicsScene(0.0, 0.0, 800.0, 600.0) 52 self.scene = QGraphicsScene(0.0, 0.0, 800.0, 600.0)
49 self.umlView = UMLGraphicsView(self.scene, parent=self) 53 self.umlView = UMLGraphicsView(self.scene, parent=self)
50 self.builder = self.__diagramBuilder(self.__diagramType, project, path, **kwargs) 54 self.builder = self.__diagramBuilder(self.__diagramType, path, **kwargs)
51 if initBuilder: 55 if self.builder and initBuilder:
52 self.builder.initialize() 56 self.builder.initialize()
53 57
54 self.__fileName = "" 58 self.__fileName = ""
55 59
56 self.__initActions() 60 self.__initActions()
67 self.closeAct = \ 71 self.closeAct = \
68 QAction(UI.PixmapCache.getIcon("close.png"), 72 QAction(UI.PixmapCache.getIcon("close.png"),
69 self.trUtf8("Close"), self) 73 self.trUtf8("Close"), self)
70 self.closeAct.triggered[()].connect(self.close) 74 self.closeAct.triggered[()].connect(self.close)
71 75
76 self.openAct = \
77 QAction(UI.PixmapCache.getIcon("open.png"),
78 self.trUtf8("Load"), self)
79 self.openAct.triggered[()].connect(self.load)
80
72 self.saveAct = \ 81 self.saveAct = \
73 QAction(UI.PixmapCache.getIcon("fileSave.png"), 82 QAction(UI.PixmapCache.getIcon("fileSave.png"),
74 self.trUtf8("Save"), self) 83 self.trUtf8("Save"), self)
75 self.saveAct.triggered[()].connect(self.__save) 84 self.saveAct.triggered[()].connect(self.__save)
76 85
102 self.windowToolBar.setIconSize(UI.Config.ToolBarIconSize) 111 self.windowToolBar.setIconSize(UI.Config.ToolBarIconSize)
103 self.windowToolBar.addAction(self.closeAct) 112 self.windowToolBar.addAction(self.closeAct)
104 113
105 self.fileToolBar = QToolBar(self.trUtf8("File"), self) 114 self.fileToolBar = QToolBar(self.trUtf8("File"), self)
106 self.fileToolBar.setIconSize(UI.Config.ToolBarIconSize) 115 self.fileToolBar.setIconSize(UI.Config.ToolBarIconSize)
116 self.fileToolBar.addAction(self.openAct)
117 self.fileToolBar.addSeparator()
107 self.fileToolBar.addAction(self.saveAct) 118 self.fileToolBar.addAction(self.saveAct)
108 self.fileToolBar.addAction(self.saveAsAct) 119 self.fileToolBar.addAction(self.saveAsAct)
109 self.fileToolBar.addAction(self.saveImageAct) 120 self.fileToolBar.addAction(self.saveImageAct)
110 self.fileToolBar.addSeparator() 121 self.fileToolBar.addSeparator()
111 self.fileToolBar.addAction(self.printPreviewAct) 122 self.fileToolBar.addAction(self.printPreviewAct)
115 126
116 self.addToolBar(Qt.TopToolBarArea, self.fileToolBar) 127 self.addToolBar(Qt.TopToolBarArea, self.fileToolBar)
117 self.addToolBar(Qt.TopToolBarArea, self.windowToolBar) 128 self.addToolBar(Qt.TopToolBarArea, self.windowToolBar)
118 self.addToolBar(Qt.TopToolBarArea, self.umlToolBar) 129 self.addToolBar(Qt.TopToolBarArea, self.umlToolBar)
119 130
120 def show(self): 131 def show(self, fromFile=False):
121 """ 132 """
122 Overriden method to show the dialog. 133 Public method to show the dialog.
123 """ 134
124 self.builder.buildDiagram() 135 @keyparam fromFile flag indicating, that the diagram was loaded
136 from file (boolean)
137 """
138 if not fromFile and self.builder:
139 self.builder.buildDiagram()
125 super().show() 140 super().show()
126 141
127 def __relayout(self): 142 def __relayout(self):
128 """ 143 """
129 Private method to relayout the diagram. 144 Private method to relayout the diagram.
130 """ 145 """
131 self.builder.buildDiagram() 146 if self.builder:
132 147 self.builder.buildDiagram()
133 def __diagramBuilder(self, diagramType, project, path, **kwargs): 148
149 def __diagramBuilder(self, diagramType, path, **kwargs):
134 """ 150 """
135 Private method to instantiate a diagram builder object. 151 Private method to instantiate a diagram builder object.
136 152
137 @param diagramType type of the diagram 153 @param diagramType type of the diagram
138 (one of ApplicationDiagram, ClassDiagram, ImportsDiagram, PackageDiagram) 154 (one of ApplicationDiagram, ClassDiagram, ImportsDiagram, PackageDiagram)
139 @param project reference to the project object (Project)
140 @param path file or directory path to build the diagram from (string) 155 @param path file or directory path to build the diagram from (string)
141 @param kwargs diagram specific data 156 @param kwargs diagram specific data
142 """ 157 """
143 if diagramType == UMLDialog.ClassDiagram: 158 if diagramType == UMLDialog.ClassDiagram:
144 from .UMLClassDiagramBuilder import UMLClassDiagramBuilder 159 from .UMLClassDiagramBuilder import UMLClassDiagramBuilder
145 return UMLClassDiagramBuilder(self, self.umlView, project, path, **kwargs) 160 return UMLClassDiagramBuilder(self, self.umlView, self.__project, path,
161 **kwargs)
146 elif diagramType == UMLDialog.PackageDiagram: 162 elif diagramType == UMLDialog.PackageDiagram:
147 from .PackageDiagramBuilder import PackageDiagramBuilder 163 from .PackageDiagramBuilder import PackageDiagramBuilder
148 return PackageDiagramBuilder(self, self.umlView, project, path, **kwargs) 164 return PackageDiagramBuilder(self, self.umlView, self.__project, path,
165 **kwargs)
149 elif diagramType == UMLDialog.ImportsDiagram: 166 elif diagramType == UMLDialog.ImportsDiagram:
150 from .ImportsDiagramBuilder import ImportsDiagramBuilder 167 from .ImportsDiagramBuilder import ImportsDiagramBuilder
151 return ImportsDiagramBuilder(self, self.umlView, project, path, **kwargs) 168 return ImportsDiagramBuilder(self, self.umlView, self.__project, path,
169 **kwargs)
152 elif diagramType == UMLDialog.ApplicationDiagram: 170 elif diagramType == UMLDialog.ApplicationDiagram:
153 from .ApplicationDiagramBuilder import ApplicationDiagramBuilder 171 from .ApplicationDiagramBuilder import ApplicationDiagramBuilder
154 return ApplicationDiagramBuilder(self, self.umlView, project, **kwargs) 172 return ApplicationDiagramBuilder(self, self.umlView, self.__project,
173 **kwargs)
174 elif diagramType == UMLDialog.NoDiagram:
175 return None
155 else: 176 else:
156 raise ValueError( 177 raise ValueError(
157 self.trUtf8("Illegal diagram type '{0}' given.").format(diagramType)) 178 self.trUtf8("Illegal diagram type '{0}' given.").format(diagramType))
158 179
159 def __diagramTypeString(self): 180 def __diagramTypeString(self):
227 f.close() 248 f.close()
228 except (IOError, OSError) as err: 249 except (IOError, OSError) as err:
229 E5MessageBox.critical(self, 250 E5MessageBox.critical(self,
230 self.trUtf8("Save Diagram"), 251 self.trUtf8("Save Diagram"),
231 self.trUtf8("""<p>The file <b>{0}</b> could not be saved.</p>""" 252 self.trUtf8("""<p>The file <b>{0}</b> could not be saved.</p>"""
232 """<p>Reason: {1}</p>""").format(fname, str(err))) 253 """<p>Reason: {1}</p>""").format(filename, str(err)))
254 return
233 255
234 self.__fileName = filename 256 self.__fileName = filename
235 257
236 @classmethod 258 def load(self):
237 def generateDialogFromFile(cls, project, parent=None): 259 """
238 """ 260 Public method to load a diagram from a file.
239 Class method to generate a dialog reading data from a file. 261
240 262 @return flag indicating success (boolean)
241 @param project reference to the project object (Project) 263 """
242 @param parent parent widget of the dialog (QWidget) 264 filename = E5FileDialog.getOpenFileName(
243 @return generated dialog (UMLDialog) 265 self,
244 """ 266 self.trUtf8("Load Diagram"),
245 # TODO: implement this 267 "",
246 return None 268 self.trUtf8("Eric5 Graphics File (*.e5g);;All Files (*)"))
269 if not filename:
270 # Cancelled by user
271 return False
272
273 try:
274 f = open(filename, "r", encoding="utf-8")
275 data = f.read()
276 f.close()
277 except (IOError, OSError) as err:
278 E5MessageBox.critical(self,
279 self.trUtf8("Load Diagram"),
280 self.trUtf8("""<p>The file <b>{0}</b> could not be read.</p>"""
281 """<p>Reason: {1}</p>""").format(filename, str(err)))
282 return False
283
284 lines = data.splitlines()
285 if len(lines) < 3:
286 self.__showInvalidDataMessage(filename)
287 return False
288
289 try:
290 # step 1: check version
291 linenum = 0
292 key, value = lines[linenum].split(": ", 1)
293 if key.strip() != "version" or value.strip() not in UMLDialog.FileVersions:
294 self.__showInvalidDataMessage(filename, linenum)
295 return False
296 else:
297 version = value
298
299 # step 2: extract diagram type
300 linenum += 1
301 key, value = lines[linenum].split(": ", 1)
302 if key.strip() != "diagram_type":
303 self.__showInvalidDataMessage(filename, linenum)
304 return False
305 try:
306 self.__diagramType = int(value.strip().split(None, 1)[0])
307 except ValueError:
308 self.__showInvalidDataMessage(filename, linenum)
309 return False
310 self.scene.clear()
311 self.builder = self.__diagramBuilder(self.__diagramType, "")
312
313 # step 3: extract scene size
314 linenum += 1
315 key, value = lines[linenum].split(": ", 1)
316 if key.strip() != "scene_size":
317 self.__showInvalidDataMessage(filename, linenum)
318 return False
319 try:
320 width, height = [float(v.strip()) for v in value.split(";")]
321 except ValueError:
322 self.__showInvalidDataMessage(filename, linenum)
323 return False
324 self.umlView.setSceneSize(width, height)
325
326 # step 4: extract builder data if available
327 linenum += 1
328 key, value = lines[linenum].split(": ", 1)
329 if key.strip() == "builder_data":
330 ok = self.builder.parsePersistenceData(version, value)
331 if not ok:
332 self.__showInvalidDataMessage(filename, linenum)
333 return False
334 linenum += 1
335
336 # step 5: extract the graphics items
337 ok, vlinenum = self.umlView.parsePersistenceData(version, lines[linenum:])
338 if not ok:
339 self.__showInvalidDataMessage(filename, linenum + vlinenum)
340 return False
341
342 except IndexError:
343 self.__showInvalidDataMessage(filename)
344 return False
345
346 # everything worked fine, so remember the file name
347 self.__fileName = filename
348 return True
349
350 def __showInvalidDataMessage(self, filename, linenum=-1):
351 """
352 Private slot to show a message dialog indicating an invalid data file.
353
354 @param filename name of the file containing the invalid data (string)
355 @param linenum number of the invalid line (integer)
356 """
357 if linenum < 0:
358 msg = self.trUtf8("""<p>The file <b>{0}</b> does not contain"""
359 """ valid data.</p>""").format(filename)
360 else:
361 msg = self.trUtf8("""<p>The file <b>{0}</b> does not contain"""
362 """ valid data.</p><p>Invalid line: {1}</p>"""
363 ).format(filename, linenum + 1)
364 E5MessageBox.critical(self, self.trUtf8("Load Diagram"), msg)

eric ide

mercurial