eric6/Graphics/AssociationItem.py

changeset 8270
6ba3564b7161
parent 8268
6b8128e0c9d1
child 8287
30eb7bc13d63
equal deleted inserted replaced
8269:87f521f359d5 8270:6ba3564b7161
5 5
6 """ 6 """
7 Module implementing a graphics item for an association between two items. 7 Module implementing a graphics item for an association between two items.
8 """ 8 """
9 9
10 import enum
11
10 from PyQt5.QtCore import QPointF, QRectF, QLineF 12 from PyQt5.QtCore import QPointF, QRectF, QLineF
11 from PyQt5.QtWidgets import QGraphicsItem 13 from PyQt5.QtWidgets import QGraphicsItem
12 14
13 from E5Graphics.E5ArrowItem import E5ArrowItem, E5ArrowType 15 from E5Graphics.E5ArrowItem import E5ArrowItem, E5ArrowType
14 16
15 import Utilities 17 import Utilities
16 18
17 19
18 # TODO: convert to Enum 20 class AssociationType(enum.Enum):
19 Normal = 0 21 """
20 Generalisation = 1 22 Class defining the association types.
21 Imports = 2 23 """
22 24 NORMAL = 0
23 # TODO: convert to Enum 25 GENERALISATION = 1
24 NoRegion = 0 26 IMPORTS = 2
25 West = 1 27
26 North = 2 28
27 East = 3 29 class AssociationPointRegion(enum.Enum):
28 South = 4 30 """
29 NorthWest = 5 31 Class defining the regions for an association end point.
30 NorthEast = 6 32 """
31 SouthEast = 7 33 NO_REGION = 0
32 SouthWest = 8 34 WEST = 1
33 Center = 9 35 NORTH = 2
36 EAST = 3
37 SOUTH = 4
38 NORTH_WEST = 5
39 NORTH_EAST = 6
40 SOUTH_EAST = 7
41 SOUTH_WEST = 8
42 CENTER = 9
34 43
35 44
36 class AssociationItem(E5ArrowItem): 45 class AssociationItem(E5ArrowItem):
37 """ 46 """
38 Class implementing a graphics item for an association between two items. 47 Class implementing a graphics item for an association between two items.
39 48
40 The association is drawn as an arrow starting at the first items and 49 The association is drawn as an arrow starting at the first items and
41 ending at the second. 50 ending at the second.
42 """ 51 """
43 def __init__(self, itemA, itemB, assocType=Normal, topToBottom=False, 52 def __init__(self, itemA, itemB, assocType=AssociationType.NORMAL,
44 colors=None, parent=None): 53 topToBottom=False, colors=None, parent=None):
45 """ 54 """
46 Constructor 55 Constructor
47 56
48 @param itemA first widget of the association 57 @param itemA first widget of the association
58 @type UMLItem
49 @param itemB second widget of the association 59 @param itemB second widget of the association
50 @param assocType type of the association. This must be one of 60 @type UMLItem
51 <ul> 61 @param assocType type of the association
52 <li>Normal (default)</li> 62 @type AssociationType
53 <li>Generalisation</li>
54 <li>Imports</li>
55 </ul>
56 @param topToBottom flag indicating to draw the association 63 @param topToBottom flag indicating to draw the association
57 from item A top to item B bottom 64 from item A top to item B bottom
58 @type bool 65 @type bool
59 @param colors tuple containing the foreground and background colors 66 @param colors tuple containing the foreground and background colors
60 @type tuple of (QColor, QColor) 67 @type tuple of (QColor, QColor)
61 @param parent reference to the parent object 68 @param parent reference to the parent object
62 @type QGraphicsItem 69 @type QGraphicsItem
63 """ 70 """
64 if assocType in (Normal, Imports): 71 if assocType in (AssociationType.NORMAL, AssociationType.IMPORTS):
65 arrowType = E5ArrowType.NORMAL 72 arrowType = E5ArrowType.NORMAL
66 arrowFilled = True 73 arrowFilled = True
67 elif assocType == Generalisation: 74 elif assocType == AssociationType.GENERALISATION:
68 arrowType = E5ArrowType.WIDE 75 arrowType = E5ArrowType.WIDE
69 arrowFilled = False 76 arrowFilled = False
70 77
71 E5ArrowItem.__init__(self, QPointF(0, 0), QPointF(100, 100), 78 E5ArrowItem.__init__(self, QPointF(0, 0), QPointF(100, 100),
72 arrowFilled, arrowType, colors, parent) 79 arrowFilled, arrowType, colors, parent)
85 self.itemA = itemA 92 self.itemA = itemA
86 self.itemB = itemB 93 self.itemB = itemB
87 self.assocType = assocType 94 self.assocType = assocType
88 self.topToBottom = topToBottom 95 self.topToBottom = topToBottom
89 96
90 self.regionA = NoRegion 97 self.regionA = AssociationPointRegion.NO_REGION
91 self.regionB = NoRegion 98 self.regionB = AssociationPointRegion.NO_REGION
92 99
93 self.calculateEndingPoints() 100 self.calculateEndingPoints()
94 101
95 self.itemA.addAssociation(self) 102 self.itemA.addAssociation(self)
96 self.itemB.addAssociation(self) 103 self.itemB.addAssociation(self)
214 221
215 # find itemA region 222 # find itemA region
216 rc = QRectF(xA, yA, rectA.width(), rectA.height()) 223 rc = QRectF(xA, yA, rectA.width(), rectA.height())
217 self.regionA = self.__findPointRegion(rc, xB, yB) 224 self.regionA = self.__findPointRegion(rc, xB, yB)
218 # move some regions to the standard ones 225 # move some regions to the standard ones
219 if self.regionA == NorthWest: 226 if self.regionA == AssociationPointRegion.NORTH_WEST:
220 self.regionA = North 227 self.regionA = AssociationPointRegion.NORTH
221 elif self.regionA == NorthEast: 228 elif self.regionA == AssociationPointRegion.NORTH_EAST:
222 self.regionA = East 229 self.regionA = AssociationPointRegion.EAST
223 elif self.regionA == SouthEast: 230 elif self.regionA == AssociationPointRegion.SOUTH_EAST:
224 self.regionA = South 231 self.regionA = AssociationPointRegion.SOUTH
225 elif self.regionA in (SouthWest, Center): 232 elif self.regionA in (
226 self.regionA = West 233 AssociationPointRegion.SOUTH_WEST,
234 AssociationPointRegion.CENTER
235 ):
236 self.regionA = AssociationPointRegion.WEST
227 237
228 self.__updateEndPoint(self.regionA, True) 238 self.__updateEndPoint(self.regionA, True)
229 239
230 # now do the same for itemB 240 # now do the same for itemB
231 rc = QRectF(xB, yB, rectB.width(), rectB.height()) 241 rc = QRectF(xB, yB, rectB.width(), rectB.height())
232 self.regionB = self.__findPointRegion(rc, xA, yA) 242 self.regionB = self.__findPointRegion(rc, xA, yA)
233 # move some regions to the standard ones 243 # move some regions to the standard ones
234 if self.regionB == NorthWest: 244 if self.regionB == AssociationPointRegion.NORTH_WEST:
235 self.regionB = North 245 self.regionB = AssociationPointRegion.NORTH
236 elif self.regionB == NorthEast: 246 elif self.regionB == AssociationPointRegion.NORTH_EAST:
237 self.regionB = East 247 self.regionB = AssociationPointRegion.EAST
238 elif self.regionB == SouthEast: 248 elif self.regionB == AssociationPointRegion.SOUTH_EAST:
239 self.regionB = South 249 self.regionB = AssociationPointRegion.SOUTH
240 elif self.regionB in (SouthWest, Center): 250 elif self.regionB in (
241 self.regionB = West 251 AssociationPointRegion.SOUTH_WEST,
252 AssociationPointRegion.CENTER
253 ):
254 self.regionB = AssociationPointRegion.WEST
242 255
243 self.__updateEndPoint(self.regionB, False) 256 self.__updateEndPoint(self.regionB, False)
244 257
245 def __findPointRegion(self, rect, posX, posY): 258 def __findPointRegion(self, rect, posX, posY):
246 """ 259 """
271 b2 = x + w / 2.0 - y * slope2 284 b2 = x + w / 2.0 - y * slope2
272 285
273 eval1 = slope1 * posY + b1 286 eval1 = slope1 * posY + b1
274 eval2 = slope2 * posY + b2 287 eval2 = slope2 * posY + b2
275 288
276 result = NoRegion 289 result = AssociationPointRegion.NO_REGION
277 290
278 # inside region 1 291 # inside region 1
279 if eval1 > posX and eval2 > posX: 292 if eval1 > posX and eval2 > posX:
280 result = West 293 result = AssociationPointRegion.WEST
281 294
282 #inside region 2 295 #inside region 2
283 elif eval1 > posX and eval2 < posX: 296 elif eval1 > posX and eval2 < posX:
284 result = North 297 result = AssociationPointRegion.NORTH
285 298
286 # inside region 3 299 # inside region 3
287 elif eval1 < posX and eval2 < posX: 300 elif eval1 < posX and eval2 < posX:
288 result = East 301 result = AssociationPointRegion.EAST
289 302
290 # inside region 4 303 # inside region 4
291 elif eval1 < posX and eval2 > posX: 304 elif eval1 < posX and eval2 > posX:
292 result = South 305 result = AssociationPointRegion.SOUTH
293 306
294 # inside region 5 307 # inside region 5
295 elif eval1 == posX and eval2 < posX: 308 elif eval1 == posX and eval2 < posX:
296 result = NorthWest 309 result = AssociationPointRegion.NORTH_WEST
297 310
298 # inside region 6 311 # inside region 6
299 elif eval1 < posX and eval2 == posX: 312 elif eval1 < posX and eval2 == posX:
300 result = NorthEast 313 result = AssociationPointRegion.NORTH_EAST
301 314
302 # inside region 7 315 # inside region 7
303 elif eval1 == posX and eval2 > posX: 316 elif eval1 == posX and eval2 > posX:
304 result = SouthEast 317 result = AssociationPointRegion.SOUTH_EAST
305 318
306 # inside region 8 319 # inside region 8
307 elif eval1 > posX and eval2 == posX: 320 elif eval1 > posX and eval2 == posX:
308 result = SouthWest 321 result = AssociationPointRegion.SOUTH_WEST
309 322
310 # inside region 9 323 # inside region 9
311 elif eval1 == posX and eval2 == posX: 324 elif eval1 == posX and eval2 == posX:
312 result = Center 325 result = AssociationPointRegion.CENTER
313 326
314 return result 327 return result
315 328
316 def __updateEndPoint(self, region, isWidgetA): 329 def __updateEndPoint(self, region, isWidgetA):
317 """ 330 """
318 Private method to update an endpoint. 331 Private method to update an endpoint.
319 332
320 @param region the region for the endpoint (integer) 333 @param region the region for the endpoint (integer)
321 @param isWidgetA flag indicating update for itemA is done (boolean) 334 @param isWidgetA flag indicating update for itemA is done (boolean)
322 """ 335 """
323 if region == NoRegion: 336 if region == AssociationPointRegion.NO_REGION:
324 return 337 return
325 338
326 rect = ( 339 rect = (
327 self.__mapRectFromItem(self.itemA) 340 self.__mapRectFromItem(self.itemA)
328 if isWidgetA else 341 if isWidgetA else
333 ww = rect.width() 346 ww = rect.width()
334 wh = rect.height() 347 wh = rect.height()
335 ch = wh / 2.0 348 ch = wh / 2.0
336 cw = ww / 2.0 349 cw = ww / 2.0
337 350
338 if region == West: 351 if region == AssociationPointRegion.WEST:
339 px = x 352 px = x
340 py = y + ch 353 py = y + ch
341 elif region == North: 354 elif region == AssociationPointRegion.NORTH:
342 px = x + cw 355 px = x + cw
343 py = y 356 py = y
344 elif region == East: 357 elif region == AssociationPointRegion.EAST:
345 px = x + ww 358 px = x + ww
346 py = y + ch 359 py = y + ch
347 elif region in (South, Center): 360 elif region in (
361 AssociationPointRegion.SOUTH,
362 AssociationPointRegion.CENTER
363 ):
348 px = x + cw 364 px = x + cw
349 py = y + wh 365 py = y + wh
350 366
351 if isWidgetA: 367 if isWidgetA:
352 self.setStartPoint(px, py) 368 self.setStartPoint(px, py)
532 @return persistence data (string) 548 @return persistence data (string)
533 """ 549 """
534 entries = [ 550 entries = [
535 "src={0}".format(self.itemA.getId()), 551 "src={0}".format(self.itemA.getId()),
536 "dst={0}".format(self.itemB.getId()), 552 "dst={0}".format(self.itemB.getId()),
537 "type={0}".format(self.assocType), 553 "type={0}".format(self.assocType.value),
538 "topToBottom={0}".format(self.topToBottom) 554 "topToBottom={0}".format(self.topToBottom)
539 ] 555 ]
540 return ", ".join(entries) 556 return ", ".join(entries)
541 557
542 @classmethod 558 @classmethod
549 the association type and a flag indicating to associate from top 565 the association type and a flag indicating to associate from top
550 to bottom (integer, integer, integer, boolean) 566 to bottom (integer, integer, integer, boolean)
551 """ 567 """
552 src = -1 568 src = -1
553 dst = -1 569 dst = -1
554 assocType = Normal 570 assocType = AssociationType.NORMAL
555 topToBottom = False 571 topToBottom = False
556 for entry in data.split(", "): 572 for entry in data.split(", "):
557 if "=" in entry: 573 if "=" in entry:
558 key, value = entry.split("=", 1) 574 key, value = entry.split("=", 1)
559 if key == "src": 575 if key == "src":
560 src = int(value) 576 src = int(value)
561 elif key == "dst": 577 elif key == "dst":
562 dst = int(value) 578 dst = int(value)
563 elif key == "type": 579 elif key == "type":
564 assocType = int(value) 580 assocType = AssociationType(int(value))
565 elif key == "topToBottom": 581 elif key == "topToBottom":
566 topToBottom = Utilities.toBool(value) 582 topToBottom = Utilities.toBool(value)
567 583
568 return src, dst, assocType, topToBottom 584 return src, dst, assocType, topToBottom

eric ide

mercurial