eric6/Graphics/UMLGraphicsView.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 6989
8b8cadf8d7e9
child 7198
684261ef2165
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a subclass of E5GraphicsView for our diagrams.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import pyqtSignal, Qt, QSignalMapper, QFileInfo, QEvent, \
13 QRectF
14 from PyQt5.QtWidgets import QGraphicsView, QAction, QToolBar, QDialog
15 from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
16
17 from E5Graphics.E5GraphicsView import E5GraphicsView
18
19 from E5Gui import E5MessageBox, E5FileDialog
20 from E5Gui.E5ZoomWidget import E5ZoomWidget
21
22 from .UMLItem import UMLItem
23
24 import UI.Config
25 import UI.PixmapCache
26
27 import Preferences
28 from Globals import qVersionTuple
29
30
31 class UMLGraphicsView(E5GraphicsView):
32 """
33 Class implementing a specialized E5GraphicsView for our diagrams.
34
35 @signal relayout() emitted to indicate a relayout of the diagram
36 is requested
37 """
38 relayout = pyqtSignal()
39
40 def __init__(self, scene, parent=None):
41 """
42 Constructor
43
44 @param scene reference to the scene object (QGraphicsScene)
45 @param parent parent widget of the view (QWidget)
46 """
47 E5GraphicsView.__init__(self, scene, parent)
48 self.setObjectName("UMLGraphicsView")
49 self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
50
51 self.diagramName = "Unnamed"
52 self.__itemId = -1
53
54 self.border = 10
55 self.deltaSize = 100.0
56
57 self.__zoomWidget = E5ZoomWidget(
58 UI.PixmapCache.getPixmap("zoomOut.png"),
59 UI.PixmapCache.getPixmap("zoomIn.png"),
60 UI.PixmapCache.getPixmap("zoomReset.png"), self)
61 parent.statusBar().addPermanentWidget(self.__zoomWidget)
62 self.__zoomWidget.setMapping(
63 E5GraphicsView.ZoomLevels, E5GraphicsView.ZoomLevelDefault)
64 self.__zoomWidget.valueChanged.connect(self.setZoom)
65 self.zoomValueChanged.connect(self.__zoomWidget.setValue)
66
67 self.__initActions()
68
69 scene.changed.connect(self.__sceneChanged)
70
71 self.grabGesture(Qt.PinchGesture)
72
73 def __initActions(self):
74 """
75 Private method to initialize the view actions.
76 """
77 self.alignMapper = QSignalMapper(self)
78 self.alignMapper.mapped[int].connect(self.__alignShapes)
79
80 self.deleteShapeAct = \
81 QAction(UI.PixmapCache.getIcon("deleteShape.png"),
82 self.tr("Delete shapes"), self)
83 self.deleteShapeAct.triggered.connect(self.__deleteShape)
84
85 self.incWidthAct = \
86 QAction(UI.PixmapCache.getIcon("sceneWidthInc.png"),
87 self.tr("Increase width by {0} points").format(
88 self.deltaSize),
89 self)
90 self.incWidthAct.triggered.connect(self.__incWidth)
91
92 self.incHeightAct = \
93 QAction(UI.PixmapCache.getIcon("sceneHeightInc.png"),
94 self.tr("Increase height by {0} points").format(
95 self.deltaSize),
96 self)
97 self.incHeightAct.triggered.connect(self.__incHeight)
98
99 self.decWidthAct = \
100 QAction(UI.PixmapCache.getIcon("sceneWidthDec.png"),
101 self.tr("Decrease width by {0} points").format(
102 self.deltaSize),
103 self)
104 self.decWidthAct.triggered.connect(self.__decWidth)
105
106 self.decHeightAct = \
107 QAction(UI.PixmapCache.getIcon("sceneHeightDec.png"),
108 self.tr("Decrease height by {0} points").format(
109 self.deltaSize),
110 self)
111 self.decHeightAct.triggered.connect(self.__decHeight)
112
113 self.setSizeAct = \
114 QAction(UI.PixmapCache.getIcon("sceneSize.png"),
115 self.tr("Set size"), self)
116 self.setSizeAct.triggered.connect(self.__setSize)
117
118 self.rescanAct = \
119 QAction(UI.PixmapCache.getIcon("rescan.png"),
120 self.tr("Re-Scan"), self)
121 self.rescanAct.triggered.connect(self.__rescan)
122
123 self.relayoutAct = \
124 QAction(UI.PixmapCache.getIcon("relayout.png"),
125 self.tr("Re-Layout"), self)
126 self.relayoutAct.triggered.connect(self.__relayout)
127
128 self.alignLeftAct = \
129 QAction(UI.PixmapCache.getIcon("shapesAlignLeft.png"),
130 self.tr("Align Left"), self)
131 self.alignMapper.setMapping(self.alignLeftAct, Qt.AlignLeft)
132 self.alignLeftAct.triggered.connect(self.alignMapper.map)
133
134 self.alignHCenterAct = \
135 QAction(UI.PixmapCache.getIcon("shapesAlignHCenter.png"),
136 self.tr("Align Center Horizontal"), self)
137 self.alignMapper.setMapping(self.alignHCenterAct, Qt.AlignHCenter)
138 self.alignHCenterAct.triggered.connect(self.alignMapper.map)
139
140 self.alignRightAct = \
141 QAction(UI.PixmapCache.getIcon("shapesAlignRight.png"),
142 self.tr("Align Right"), self)
143 self.alignMapper.setMapping(self.alignRightAct, Qt.AlignRight)
144 self.alignRightAct.triggered.connect(self.alignMapper.map)
145
146 self.alignTopAct = \
147 QAction(UI.PixmapCache.getIcon("shapesAlignTop.png"),
148 self.tr("Align Top"), self)
149 self.alignMapper.setMapping(self.alignTopAct, Qt.AlignTop)
150 self.alignTopAct.triggered.connect(self.alignMapper.map)
151
152 self.alignVCenterAct = \
153 QAction(UI.PixmapCache.getIcon("shapesAlignVCenter.png"),
154 self.tr("Align Center Vertical"), self)
155 self.alignMapper.setMapping(self.alignVCenterAct, Qt.AlignVCenter)
156 self.alignVCenterAct.triggered.connect(self.alignMapper.map)
157
158 self.alignBottomAct = \
159 QAction(UI.PixmapCache.getIcon("shapesAlignBottom.png"),
160 self.tr("Align Bottom"), self)
161 self.alignMapper.setMapping(self.alignBottomAct, Qt.AlignBottom)
162 self.alignBottomAct.triggered.connect(self.alignMapper.map)
163
164 def __checkSizeActions(self):
165 """
166 Private slot to set the enabled state of the size actions.
167 """
168 diagramSize = self._getDiagramSize(10)
169 sceneRect = self.scene().sceneRect()
170 if (sceneRect.width() - self.deltaSize) < diagramSize.width():
171 self.decWidthAct.setEnabled(False)
172 else:
173 self.decWidthAct.setEnabled(True)
174 if (sceneRect.height() - self.deltaSize) < diagramSize.height():
175 self.decHeightAct.setEnabled(False)
176 else:
177 self.decHeightAct.setEnabled(True)
178
179 def __sceneChanged(self, areas):
180 """
181 Private slot called when the scene changes.
182
183 @param areas list of rectangles that contain changes (list of QRectF)
184 """
185 if len(self.scene().selectedItems()) > 0:
186 self.deleteShapeAct.setEnabled(True)
187 else:
188 self.deleteShapeAct.setEnabled(False)
189
190 sceneRect = self.scene().sceneRect()
191 newWidth = width = sceneRect.width()
192 newHeight = height = sceneRect.height()
193 rect = self.scene().itemsBoundingRect()
194 # calculate with 10 pixel border on each side
195 if sceneRect.right() - 10 < rect.right():
196 newWidth = rect.right() + 10
197 if sceneRect.bottom() - 10 < rect.bottom():
198 newHeight = rect.bottom() + 10
199
200 if newHeight != height or newWidth != width:
201 self.setSceneSize(newWidth, newHeight)
202 self.__checkSizeActions()
203
204 def initToolBar(self):
205 """
206 Public method to populate a toolbar with our actions.
207
208 @return the populated toolBar (QToolBar)
209 """
210 toolBar = QToolBar(self.tr("Graphics"), self)
211 toolBar.setIconSize(UI.Config.ToolBarIconSize)
212 toolBar.addAction(self.deleteShapeAct)
213 toolBar.addSeparator()
214 toolBar.addAction(self.alignLeftAct)
215 toolBar.addAction(self.alignHCenterAct)
216 toolBar.addAction(self.alignRightAct)
217 toolBar.addAction(self.alignTopAct)
218 toolBar.addAction(self.alignVCenterAct)
219 toolBar.addAction(self.alignBottomAct)
220 toolBar.addSeparator()
221 toolBar.addAction(self.incWidthAct)
222 toolBar.addAction(self.incHeightAct)
223 toolBar.addAction(self.decWidthAct)
224 toolBar.addAction(self.decHeightAct)
225 toolBar.addAction(self.setSizeAct)
226 toolBar.addSeparator()
227 toolBar.addAction(self.rescanAct)
228 toolBar.addAction(self.relayoutAct)
229
230 return toolBar
231
232 def filteredItems(self, items, itemType=UMLItem):
233 """
234 Public method to filter a list of items.
235
236 @param items list of items as returned by the scene object
237 (QGraphicsItem)
238 @param itemType type to be filtered (class)
239 @return list of interesting collision items (QGraphicsItem)
240 """
241 return [itm for itm in items if isinstance(itm, itemType)]
242
243 def selectItems(self, items):
244 """
245 Public method to select the given items.
246
247 @param items list of items to be selected (list of QGraphicsItemItem)
248 """
249 # step 1: deselect all items
250 self.unselectItems()
251
252 # step 2: select all given items
253 for itm in items:
254 if isinstance(itm, UMLItem):
255 itm.setSelected(True)
256
257 def selectItem(self, item):
258 """
259 Public method to select an item.
260
261 @param item item to be selected (QGraphicsItemItem)
262 """
263 if isinstance(item, UMLItem):
264 item.setSelected(not item.isSelected())
265
266 def __deleteShape(self):
267 """
268 Private method to delete the selected shapes from the display.
269 """
270 for item in self.scene().selectedItems():
271 item.removeAssociations()
272 item.setSelected(False)
273 self.scene().removeItem(item)
274 del item
275
276 def __incWidth(self):
277 """
278 Private method to handle the increase width context menu entry.
279 """
280 self.resizeScene(self.deltaSize, True)
281 self.__checkSizeActions()
282
283 def __incHeight(self):
284 """
285 Private method to handle the increase height context menu entry.
286 """
287 self.resizeScene(self.deltaSize, False)
288 self.__checkSizeActions()
289
290 def __decWidth(self):
291 """
292 Private method to handle the decrease width context menu entry.
293 """
294 self.resizeScene(-self.deltaSize, True)
295 self.__checkSizeActions()
296
297 def __decHeight(self):
298 """
299 Private method to handle the decrease height context menu entry.
300 """
301 self.resizeScene(-self.deltaSize, False)
302 self.__checkSizeActions()
303
304 def __setSize(self):
305 """
306 Private method to handle the set size context menu entry.
307 """
308 from .UMLSceneSizeDialog import UMLSceneSizeDialog
309 rect = self._getDiagramRect(10)
310 sceneRect = self.scene().sceneRect()
311 dlg = UMLSceneSizeDialog(sceneRect.width(), sceneRect.height(),
312 rect.width(), rect.height(), self)
313 if dlg.exec_() == QDialog.Accepted:
314 width, height = dlg.getData()
315 self.setSceneSize(width, height)
316 self.__checkSizeActions()
317
318 def autoAdjustSceneSize(self, limit=False):
319 """
320 Public method to adjust the scene size to the diagram size.
321
322 @param limit flag indicating to limit the scene to the
323 initial size (boolean)
324 """
325 super(UMLGraphicsView, self).autoAdjustSceneSize(limit=limit)
326 self.__checkSizeActions()
327
328 def saveImage(self):
329 """
330 Public method to handle the save context menu entry.
331 """
332 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
333 self,
334 self.tr("Save Diagram"),
335 "",
336 self.tr("Portable Network Graphics (*.png);;"
337 "Scalable Vector Graphics (*.svg)"),
338 "",
339 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
340 if fname:
341 ext = QFileInfo(fname).suffix()
342 if not ext:
343 ex = selectedFilter.split("(*")[1].split(")")[0]
344 if ex:
345 fname += ex
346 if QFileInfo(fname).exists():
347 res = E5MessageBox.yesNo(
348 self,
349 self.tr("Save Diagram"),
350 self.tr("<p>The file <b>{0}</b> already exists."
351 " Overwrite it?</p>").format(fname),
352 icon=E5MessageBox.Warning)
353 if not res:
354 return
355
356 success = super(UMLGraphicsView, self).saveImage(
357 fname, QFileInfo(fname).suffix().upper())
358 if not success:
359 E5MessageBox.critical(
360 self,
361 self.tr("Save Diagram"),
362 self.tr(
363 """<p>The file <b>{0}</b> could not be saved.</p>""")
364 .format(fname))
365
366 def __relayout(self):
367 """
368 Private slot to handle the re-layout context menu entry.
369 """
370 self.__itemId = -1
371 self.scene().clear()
372 self.relayout.emit()
373
374 def __rescan(self):
375 """
376 Private slot to handle the re-scan context menu entry.
377 """
378 # 1. save positions of all items and names of selected items
379 itemPositions = {}
380 selectedItems = []
381 for item in self.filteredItems(self.scene().items(), UMLItem):
382 name = item.getName()
383 if name:
384 itemPositions[name] = (item.x(), item.y())
385 if item.isSelected():
386 selectedItems.append(name)
387
388 # 2. save
389
390 # 2. re-layout the diagram
391 self.__relayout()
392
393 # 3. move known items to the saved positions
394 for item in self.filteredItems(self.scene().items(), UMLItem):
395 name = item.getName()
396 if name in itemPositions:
397 item.setPos(*itemPositions[name])
398 if name in selectedItems:
399 item.setSelected(True)
400
401 def printDiagram(self):
402 """
403 Public slot called to print the diagram.
404 """
405 printer = QPrinter(mode=QPrinter.ScreenResolution)
406 printer.setFullPage(True)
407 if Preferences.getPrinter("ColorMode"):
408 printer.setColorMode(QPrinter.Color)
409 else:
410 printer.setColorMode(QPrinter.GrayScale)
411 if Preferences.getPrinter("FirstPageFirst"):
412 printer.setPageOrder(QPrinter.FirstPageFirst)
413 else:
414 printer.setPageOrder(QPrinter.LastPageFirst)
415 printer.setPageMargins(
416 Preferences.getPrinter("LeftMargin") * 10,
417 Preferences.getPrinter("TopMargin") * 10,
418 Preferences.getPrinter("RightMargin") * 10,
419 Preferences.getPrinter("BottomMargin") * 10,
420 QPrinter.Millimeter
421 )
422 printerName = Preferences.getPrinter("PrinterName")
423 if printerName:
424 printer.setPrinterName(printerName)
425
426 printDialog = QPrintDialog(printer, self)
427 if printDialog.exec_():
428 super(UMLGraphicsView, self).printDiagram(
429 printer, self.diagramName)
430
431 def printPreviewDiagram(self):
432 """
433 Public slot called to show a print preview of the diagram.
434 """
435 from PyQt5.QtPrintSupport import QPrintPreviewDialog
436
437 printer = QPrinter(mode=QPrinter.ScreenResolution)
438 printer.setFullPage(True)
439 if Preferences.getPrinter("ColorMode"):
440 printer.setColorMode(QPrinter.Color)
441 else:
442 printer.setColorMode(QPrinter.GrayScale)
443 if Preferences.getPrinter("FirstPageFirst"):
444 printer.setPageOrder(QPrinter.FirstPageFirst)
445 else:
446 printer.setPageOrder(QPrinter.LastPageFirst)
447 printer.setPageMargins(
448 Preferences.getPrinter("LeftMargin") * 10,
449 Preferences.getPrinter("TopMargin") * 10,
450 Preferences.getPrinter("RightMargin") * 10,
451 Preferences.getPrinter("BottomMargin") * 10,
452 QPrinter.Millimeter
453 )
454 printerName = Preferences.getPrinter("PrinterName")
455 if printerName:
456 printer.setPrinterName(printerName)
457
458 preview = QPrintPreviewDialog(printer, self)
459 preview.paintRequested[QPrinter].connect(self.__printPreviewPrint)
460 preview.exec_()
461
462 def __printPreviewPrint(self, printer):
463 """
464 Private slot to generate a print preview.
465
466 @param printer reference to the printer object (QPrinter)
467 """
468 super(UMLGraphicsView, self).printDiagram(printer, self.diagramName)
469
470 def setDiagramName(self, name):
471 """
472 Public slot to set the diagram name.
473
474 @param name diagram name (string)
475 """
476 self.diagramName = name
477
478 def __alignShapes(self, alignment):
479 """
480 Private slot to align the selected shapes.
481
482 @param alignment alignment type (Qt.AlignmentFlag)
483 """
484 # step 1: get all selected items
485 items = self.scene().selectedItems()
486 if len(items) <= 1:
487 return
488
489 # step 2: find the index of the item to align in relation to
490 amount = None
491 for i, item in enumerate(items):
492 rect = item.sceneBoundingRect()
493 if alignment == Qt.AlignLeft:
494 if amount is None or rect.x() < amount:
495 amount = rect.x()
496 index = i
497 elif alignment == Qt.AlignRight:
498 if amount is None or rect.x() + rect.width() > amount:
499 amount = rect.x() + rect.width()
500 index = i
501 elif alignment == Qt.AlignHCenter:
502 if amount is None or rect.width() > amount:
503 amount = rect.width()
504 index = i
505 elif alignment == Qt.AlignTop:
506 if amount is None or rect.y() < amount:
507 amount = rect.y()
508 index = i
509 elif alignment == Qt.AlignBottom:
510 if amount is None or rect.y() + rect.height() > amount:
511 amount = rect.y() + rect.height()
512 index = i
513 elif alignment == Qt.AlignVCenter:
514 if amount is None or rect.height() > amount:
515 amount = rect.height()
516 index = i
517 rect = items[index].sceneBoundingRect()
518
519 # step 3: move the other items
520 for i, item in enumerate(items):
521 if i == index:
522 continue
523 itemrect = item.sceneBoundingRect()
524 xOffset = yOffset = 0
525 if alignment == Qt.AlignLeft:
526 xOffset = rect.x() - itemrect.x()
527 elif alignment == Qt.AlignRight:
528 xOffset = (rect.x() + rect.width()) - \
529 (itemrect.x() + itemrect.width())
530 elif alignment == Qt.AlignHCenter:
531 xOffset = (rect.x() + rect.width() // 2) - \
532 (itemrect.x() + itemrect.width() // 2)
533 elif alignment == Qt.AlignTop:
534 yOffset = rect.y() - itemrect.y()
535 elif alignment == Qt.AlignBottom:
536 yOffset = (rect.y() + rect.height()) - \
537 (itemrect.y() + itemrect.height())
538 elif alignment == Qt.AlignVCenter:
539 yOffset = (rect.y() + rect.height() // 2) - \
540 (itemrect.y() + itemrect.height() // 2)
541 item.moveBy(xOffset, yOffset)
542
543 self.scene().update()
544
545 def __itemsBoundingRect(self, items):
546 """
547 Private method to calculate the bounding rectangle of the given items.
548
549 @param items list of items to operate on (list of UMLItem)
550 @return bounding rectangle (QRectF)
551 """
552 rect = self.scene().sceneRect()
553 right = rect.left()
554 bottom = rect.top()
555 left = rect.right()
556 top = rect.bottom()
557 for item in items:
558 rect = item.sceneBoundingRect()
559 left = min(rect.left(), left)
560 right = max(rect.right(), right)
561 top = min(rect.top(), top)
562 bottom = max(rect.bottom(), bottom)
563 return QRectF(left, top, right - left, bottom - top)
564
565 def keyPressEvent(self, evt):
566 """
567 Protected method handling key press events.
568
569 @param evt reference to the key event (QKeyEvent)
570 """
571 key = evt.key()
572 if key in [Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right]:
573 items = self.filteredItems(self.scene().selectedItems())
574 if items:
575 if evt.modifiers() & Qt.ControlModifier:
576 stepSize = 50
577 else:
578 stepSize = 5
579 if key == Qt.Key_Up:
580 dx = 0
581 dy = -stepSize
582 elif key == Qt.Key_Down:
583 dx = 0
584 dy = stepSize
585 elif key == Qt.Key_Left:
586 dx = -stepSize
587 dy = 0
588 else:
589 dx = stepSize
590 dy = 0
591 for item in items:
592 item.moveBy(dx, dy)
593 evt.accept()
594 return
595
596 super(UMLGraphicsView, self).keyPressEvent(evt)
597
598 def wheelEvent(self, evt):
599 """
600 Protected method to handle wheel events.
601
602 @param evt reference to the wheel event (QWheelEvent)
603 """
604 if evt.modifiers() & Qt.ControlModifier:
605 if qVersionTuple() >= (5, 0, 0):
606 delta = evt.angleDelta().y()
607 else:
608 delta = evt.delta()
609 if delta < 0:
610 self.zoomOut()
611 elif delta > 0:
612 self.zoomIn()
613 evt.accept()
614 return
615
616 super(UMLGraphicsView, self).wheelEvent(evt)
617
618 def event(self, evt):
619 """
620 Public method handling events.
621
622 @param evt reference to the event (QEvent)
623 @return flag indicating, if the event was handled (boolean)
624 """
625 if evt.type() == QEvent.Gesture:
626 self.gestureEvent(evt)
627 return True
628
629 return super(UMLGraphicsView, self).event(evt)
630
631 def gestureEvent(self, evt):
632 """
633 Protected method handling gesture events.
634
635 @param evt reference to the gesture event (QGestureEvent
636 """
637 pinch = evt.gesture(Qt.PinchGesture)
638 if pinch:
639 if pinch.state() == Qt.GestureStarted:
640 pinch.setTotalScaleFactor(self.zoom() / 100.0)
641 elif pinch.state() == Qt.GestureUpdated:
642 self.setZoom(int(pinch.totalScaleFactor() * 100))
643 evt.accept()
644
645 def getItemId(self):
646 """
647 Public method to get the ID to be assigned to an item.
648
649 @return item ID (integer)
650 """
651 self.__itemId += 1
652 return self.__itemId
653
654 def findItem(self, itemId):
655 """
656 Public method to find an UML item based on the ID.
657
658 @param itemId of the item to search for (integer)
659 @return item found (UMLItem) or None
660 """
661 for item in self.scene().items():
662 try:
663 if item.getId() == itemId:
664 return item
665 except AttributeError:
666 continue
667
668 return None
669
670 def findItemByName(self, name):
671 """
672 Public method to find an UML item based on its name.
673
674 @param name name to look for (string)
675 @return item found (UMLItem) or None
676 """
677 for item in self.scene().items():
678 try:
679 if item.getName() == name:
680 return item
681 except AttributeError:
682 continue
683
684 return None
685
686 def getPersistenceData(self):
687 """
688 Public method to get a list of data to be persisted.
689
690 @return list of data to be persisted (list of strings)
691 """
692 lines = [
693 "diagram_name: {0}".format(self.diagramName),
694 ]
695
696 for item in self.filteredItems(self.scene().items(), UMLItem):
697 lines.append("item: id={0}, x={1}, y={2}, item_type={3}{4}".format(
698 item.getId(), item.x(), item.y(), item.getItemType(),
699 item.buildItemDataString()))
700
701 from .AssociationItem import AssociationItem
702 for item in self.filteredItems(self.scene().items(), AssociationItem):
703 lines.append("association: {0}".format(
704 item.buildAssociationItemDataString()))
705
706 return lines
707
708 def parsePersistenceData(self, version, data):
709 """
710 Public method to parse persisted data.
711
712 @param version version of the data (string)
713 @param data persisted data to be parsed (list of string)
714 @return tuple of flag indicating success (boolean) and faulty line
715 number (integer)
716 """
717 umlItems = {}
718
719 if not data[0].startswith("diagram_name:"):
720 return False, 0
721 self.diagramName = data[0].split(": ", 1)[1].strip()
722
723 from .ClassItem import ClassItem
724 from .ModuleItem import ModuleItem
725 from .PackageItem import PackageItem
726 from .AssociationItem import AssociationItem
727
728 linenum = 0
729 for line in data[1:]:
730 linenum += 1
731 if not line.startswith(("item:", "association:")):
732 return False, linenum
733
734 key, value = line.split(": ", 1)
735 if key == "item":
736 itemId, x, y, itemType, itemData = value.split(", ", 4)
737 try:
738 itemId = int(itemId.split("=", 1)[1].strip())
739 x = float(x.split("=", 1)[1].strip())
740 y = float(y.split("=", 1)[1].strip())
741 itemType = itemType.split("=", 1)[1].strip()
742 if itemType == ClassItem.ItemType:
743 itm = ClassItem(x=x, y=y, scene=self.scene())
744 elif itemType == ModuleItem.ItemType:
745 itm = ModuleItem(x=x, y=y, scene=self.scene())
746 elif itemType == PackageItem.ItemType:
747 itm = PackageItem(x=x, y=y, scene=self.scene())
748 itm.setId(itemId)
749 umlItems[itemId] = itm
750 if not itm.parseItemDataString(version, itemData):
751 return False, linenum
752 except ValueError:
753 return False, linenum
754 elif key == "association":
755 srcId, dstId, assocType, topToBottom = \
756 AssociationItem.parseAssociationItemDataString(
757 value.strip())
758 assoc = AssociationItem(umlItems[srcId], umlItems[dstId],
759 assocType, topToBottom)
760 self.scene().addItem(assoc)
761
762 return True, -1

eric ide

mercurial