eric6/Graphics/UMLDialog.py

changeset 8291
3d79b1e5bf3c
parent 8289
871b40c5a77a
child 8295
3f5e8b0a338e
equal deleted inserted replaced
8290:6970128e3d71 8291:3d79b1e5bf3c
6 """ 6 """
7 Module implementing a dialog showing UML like diagrams. 7 Module implementing a dialog showing UML like diagrams.
8 """ 8 """
9 9
10 import enum 10 import enum
11 11 import json
12 from PyQt5.QtCore import pyqtSlot, Qt, QFileInfo 12
13 from PyQt5.QtCore import pyqtSlot, Qt, QFileInfo, QCoreApplication
13 from PyQt5.QtWidgets import QAction, QToolBar, QGraphicsScene 14 from PyQt5.QtWidgets import QAction, QToolBar, QGraphicsScene
14 15
15 from E5Gui import E5MessageBox, E5FileDialog 16 from E5Gui import E5MessageBox, E5FileDialog
16 from E5Gui.E5MainWindow import E5MainWindow 17 from E5Gui.E5MainWindow import E5MainWindow
17 18
34 """ 35 """
35 Class implementing a dialog showing UML like diagrams. 36 Class implementing a dialog showing UML like diagrams.
36 """ 37 """
37 FileVersions = ("1.0") 38 FileVersions = ("1.0")
38 39
40 UMLDialogType2String = {
41 UMLDialogType.CLASS_DIAGRAM:
42 QCoreApplication.translate("UMLDialog", "Class Diagram"),
43 UMLDialogType.PACKAGE_DIAGRAM:
44 QCoreApplication.translate("UMLDialog", "Package Diagram"),
45 UMLDialogType.IMPORTS_DIAGRAM:
46 QCoreApplication.translate("UMLDialog", "Imports Diagram"),
47 UMLDialogType.APPLICATION_DIAGRAM:
48 QCoreApplication.translate("UMLDialog", "Application Diagram"),
49 }
50
39 def __init__(self, diagramType, project, path="", parent=None, 51 def __init__(self, diagramType, project, path="", parent=None,
40 initBuilder=True, **kwargs): 52 initBuilder=True, **kwargs):
41 """ 53 """
42 Constructor 54 Constructor
43 55
58 super().__init__(parent) 70 super().__init__(parent)
59 self.setObjectName("UMLDialog") 71 self.setObjectName("UMLDialog")
60 72
61 self.__project = project 73 self.__project = project
62 self.__diagramType = diagramType 74 self.__diagramType = diagramType
63 self.__diagramTypeString = {
64 UMLDialogType.CLASS_DIAGRAM: "Class Diagram",
65 UMLDialogType.PACKAGE_DIAGRAM: "Package Diagram",
66 UMLDialogType.IMPORTS_DIAGRAM: "Imports Diagram",
67 UMLDialogType.APPLICATION_DIAGRAM: "Application Diagram",
68 }.get(diagramType, "Illegal Diagram Type")
69 75
70 from .UMLGraphicsView import UMLGraphicsView 76 from .UMLGraphicsView import UMLGraphicsView
71 self.scene = QGraphicsScene(0.0, 0.0, 800.0, 600.0) 77 self.scene = QGraphicsScene(0.0, 0.0, 800.0, 600.0)
72 self.umlView = UMLGraphicsView(self.scene, parent=self) 78 self.umlView = UMLGraphicsView(self.scene, parent=self)
73 self.builder = self.__diagramBuilder( 79 self.builder = self.__diagramBuilder(
82 88
83 self.setCentralWidget(self.umlView) 89 self.setCentralWidget(self.umlView)
84 90
85 self.umlView.relayout.connect(self.__relayout) 91 self.umlView.relayout.connect(self.__relayout)
86 92
87 self.setWindowTitle(self.__diagramTypeString) 93 self.setWindowTitle(self.__getDiagramTitel(self.__diagramType))
94
95 def __getDiagramTitel(self, diagramType):
96 """
97 Private method to get a textual description for the diagram type.
98
99 @param diagramType diagram type string
100 @type str
101 @return titel of the diagram
102 @rtype str
103 """
104 return UMLDialog.UMLDialogType2String.get(
105 diagramType, self.tr("Illegal Diagram Type")
106 )
88 107
89 def __initActions(self): 108 def __initActions(self):
90 """ 109 """
91 Private slot to initialize the actions. 110 Private slot to initialize the actions.
92 """ 111 """
206 """ 225 """
207 Private slot to save the diagram with the current name. 226 Private slot to save the diagram with the current name.
208 """ 227 """
209 self.__saveAs(self.__fileName) 228 self.__saveAs(self.__fileName)
210 229
211 # TODO: change this to save file in JSON format
212 @pyqtSlot() 230 @pyqtSlot()
213 def __saveAs(self, filename=""): 231 def __saveAs(self, filename=""):
214 """ 232 """
215 Private slot to save the diagram. 233 Private slot to save the diagram.
216 234
220 if not filename: 238 if not filename:
221 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( 239 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
222 self, 240 self,
223 self.tr("Save Diagram"), 241 self.tr("Save Diagram"),
224 "", 242 "",
225 self.tr("Eric Graphics File (*.e5g);;All Files (*)"), 243 self.tr("Eric Graphics File (*.egj);;"
244 "Eric Text Graphics File (*.e5g);;"
245 "All Files (*)"),
226 "", 246 "",
227 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) 247 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
228 if not fname: 248 if not fname:
229 return 249 return
230 ext = QFileInfo(fname).suffix() 250 ext = QFileInfo(fname).suffix()
241 icon=E5MessageBox.Warning) 261 icon=E5MessageBox.Warning)
242 if not res: 262 if not res:
243 return 263 return
244 filename = fname 264 filename = fname
245 265
246 lines = [ 266 res = (
247 "version: 1.0", 267 self.__writeLineBasedGraphicsFile(filename)
248 "diagram_type: {0} ({1})".format( 268 if filename.endswith(".e5g") else
249 self.__diagramType.value, self.__diagramTypeString), 269 # JSON format is the default
250 "scene_size: {0};{1}".format(self.scene.width(), 270 self.__writeJsonGraphicsFile(filename)
251 self.scene.height()), 271 )
252 ] 272
253 persistenceData = self.builder.getPersistenceData() 273 if res:
254 if persistenceData: 274 # save the file name only in case of success
255 lines.append("builder_data: {0}".format(persistenceData)) 275 self.__fileName = filename
256 lines.extend(self.umlView.getPersistenceData())
257
258 try:
259 with open(filename, "w", encoding="utf-8") as f:
260 f.write("\n".join(lines))
261 except OSError as err:
262 E5MessageBox.critical(
263 self,
264 self.tr("Save Diagram"),
265 self.tr(
266 """<p>The file <b>{0}</b> could not be saved.</p>"""
267 """<p>Reason: {1}</p>""").format(filename, str(err)))
268 return
269
270 self.__fileName = filename
271 276
272 # TODO: add loading of file in JSON format 277 # TODO: add loading of file in JSON format
273 # TODO: eric7: delete the current one 278 # TODO: eric7: delete the current one
274 def load(self, filename=""): 279 def load(self, filename=""):
275 """ 280 """
288 self.tr("Eric Graphics File (*.e5g);;All Files (*)")) 293 self.tr("Eric Graphics File (*.e5g);;All Files (*)"))
289 if not filename: 294 if not filename:
290 # Canceled by user 295 # Canceled by user
291 return False 296 return False
292 297
298 if filename.endswith(".e5g"):
299 return self.__readLineBasedGraphicsFile(filename)
300 else:
301 return False
302
303 #######################################################################
304 ## Methods to read and write eric graphics files of the old line
305 ## based file format.
306 #######################################################################
307
308 def __readLineBasedGraphicsFile(self, filename):
309 """
310 Private method to read an eric graphics file using the old line
311 based file format.
312
313 @param filename name of the file to be read
314 @type str
315 @return flag indicating success
316 @rtype bool
317 """
293 try: 318 try:
294 with open(filename, "r", encoding="utf-8") as f: 319 with open(filename, "r", encoding="utf-8") as f:
295 data = f.read() 320 data = f.read()
296 except OSError as err: 321 except OSError as err:
297 E5MessageBox.critical( 322 E5MessageBox.critical(
325 key, value = lines[linenum].split(": ", 1) 350 key, value = lines[linenum].split(": ", 1)
326 if key.strip() != "diagram_type": 351 if key.strip() != "diagram_type":
327 self.__showInvalidDataMessage(filename, linenum) 352 self.__showInvalidDataMessage(filename, linenum)
328 return False 353 return False
329 try: 354 try:
330 diagramType, diagramTypeString = value.strip().split(None, 1) 355 diagramType = value.strip().split(None, 1)[0]
331 self.__diagramType = UMLDialogType(int(diagramType)) 356 self.__diagramType = UMLDialogType(int(diagramType))
332 self.__diagramTypeString = diagramTypeString[1:-1]
333 # remove opening and closing bracket
334 except ValueError: 357 except ValueError:
335 self.__showInvalidDataMessage(filename, linenum) 358 self.__showInvalidDataMessage(filename, linenum)
336 return False 359 return False
337 self.scene.clear() 360 self.scene.clear()
338 self.builder = self.__diagramBuilder(self.__diagramType, "") 361 self.builder = self.__diagramBuilder(self.__diagramType, "")
371 self.__showInvalidDataMessage(filename) 394 self.__showInvalidDataMessage(filename)
372 return False 395 return False
373 396
374 # everything worked fine, so remember the file name and set the 397 # everything worked fine, so remember the file name and set the
375 # window title 398 # window title
376 self.setWindowTitle(self.__diagramTypeString) 399 self.setWindowTitle(self.__getDiagramTitel(self.__diagramType))
377 self.__fileName = filename 400 self.__fileName = filename
378 401
379 return True 402 return True
403
404 def __writeLineBasedGraphicsFile(self, filename):
405 """
406 Private method to write an eric graphics file using the old line
407 based file format.
408
409 @param filename name of the file to write to
410 @type str
411 @return flag indicating a successful write
412 @rtype bool
413 """
414 lines = [
415 "version: 1.0",
416 "diagram_type: {0} ({1})".format(
417 self.__diagramType.value,
418 self.__getDiagramTitel(self.__diagramType)),
419 "scene_size: {0};{1}".format(self.scene.width(),
420 self.scene.height()),
421 ]
422 persistenceData = self.builder.getPersistenceData()
423 if persistenceData:
424 lines.append("builder_data: {0}".format(persistenceData))
425 lines.extend(self.umlView.getPersistenceData())
426
427 try:
428 with open(filename, "w", encoding="utf-8") as f:
429 f.write("\n".join(lines))
430 return True
431 except OSError as err:
432 E5MessageBox.critical(
433 self,
434 self.tr("Save Diagram"),
435 self.tr(
436 """<p>The file <b>{0}</b> could not be saved.</p>"""
437 """<p>Reason: {1}</p>""").format(filename, str(err)))
438 return False
380 439
381 def __showInvalidDataMessage(self, filename, linenum=-1): 440 def __showInvalidDataMessage(self, filename, linenum=-1):
382 """ 441 """
383 Private slot to show a message dialog indicating an invalid data file. 442 Private slot to show a message dialog indicating an invalid data file.
384 443
394 self.tr("""<p>The file <b>{0}</b> does not contain""" 453 self.tr("""<p>The file <b>{0}</b> does not contain"""
395 """ valid data.</p><p>Invalid line: {1}</p>""" 454 """ valid data.</p><p>Invalid line: {1}</p>"""
396 ).format(filename, linenum + 1) 455 ).format(filename, linenum + 1)
397 ) 456 )
398 E5MessageBox.critical(self, self.tr("Load Diagram"), msg) 457 E5MessageBox.critical(self, self.tr("Load Diagram"), msg)
458
459 #######################################################################
460 ## Methods to read and write eric graphics files of the JSON based
461 ## file format.
462 #######################################################################
463
464 def __writeJsonGraphicsFile(self, filename):
465 """
466 Private method to write an eric graphics file using the JSON based
467 file format.
468
469 @param filename name of the file to write to
470 @type str
471 @return flag indicating a successful write
472 @rtype bool
473 """
474 data = {
475 "version": "1.0",
476 "type": self.__diagramType.value,
477 "title": self.__getDiagramTitel(self.__diagramType),
478 "width": self.scene.width(),
479 "height": self.scene.height(),
480 "builder": self.builder.toDict(),
481 "view": self.umlView.toDict(),
482 }
483
484 try:
485 jsonString = json.dumps(data, indent=2)
486 with open(filename, "w") as f:
487 f.write(jsonString)
488 return True
489 except (TypeError, OSError) as err:
490 E5MessageBox.critical(
491 self,
492 self.tr("Save Diagram"),
493 self.tr(
494 """<p>The file <b>{0}</b> could not be saved.</p>"""
495 """<p>Reason: {1}</p>""").format(filename, str(err)))
496 return False

eric ide

mercurial