src/eric7/Graphics/ClassItem.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an UML like class item.
8 """
9
10 from PyQt6.QtCore import QCoreApplication
11 from PyQt6.QtGui import QFont
12 from PyQt6.QtWidgets import QGraphicsSimpleTextItem, QStyle
13
14 from .UMLItem import UMLModel, UMLItem
15
16 import Utilities
17
18
19 class ClassModel(UMLModel):
20 """
21 Class implementing the class model.
22 """
23 def __init__(self, name, methods=None, instanceAttributes=None,
24 classAttributes=None):
25 """
26 Constructor
27
28 @param name the class name
29 @type str
30 @param methods list of method names of the class
31 @type list of str
32 @param instanceAttributes list of instance attribute names of the class
33 @type list of str
34 @param classAttributes list of class attribute names of the class
35 @type list of str
36 """
37 super().__init__(name)
38
39 self.methods = [] if methods is None else methods[:]
40 self.instanceAttributes = (
41 []
42 if instanceAttributes is None else
43 instanceAttributes[:]
44 )
45 self.classAttributes = (
46 []
47 if classAttributes is None else
48 classAttributes[:]
49 )
50
51 def addMethod(self, method):
52 """
53 Public method to add a method to the class model.
54
55 @param method method name to be added
56 @type str
57 """
58 self.methods.append(method)
59
60 def addInstanceAttribute(self, attribute):
61 """
62 Public method to add an instance attribute to the class model.
63
64 @param attribute instance attribute name to be added
65 @type str
66 """
67 self.instanceAttributes.append(attribute)
68
69 def addClassAttribute(self, attribute):
70 """
71 Public method to add a class attribute to the class model.
72
73 @param attribute class attribute name to be added
74 @type str
75 """
76 self.classAttributes.append(attribute)
77
78 def getMethods(self):
79 """
80 Public method to retrieve the methods of the class.
81
82 @return list of class methods
83 @rtype list of str
84 """
85 return self.methods[:]
86
87 def getInstanceAttributes(self):
88 """
89 Public method to retrieve the attributes of the class.
90
91 @return list of instance attributes
92 @rtype list of str
93 """
94 return self.instanceAttributes[:]
95
96 def getClassAttributes(self):
97 """
98 Public method to retrieve the global attributes of the class.
99
100 @return list of class attributes
101 @rtype list of str
102 """
103 return self.classAttributes[:]
104
105
106 class ClassItem(UMLItem):
107 """
108 Class implementing an UML like class item.
109 """
110 ItemType = "class"
111
112 def __init__(self, model=None, external=False, x=0, y=0,
113 rounded=False, noAttrs=False, colors=None, parent=None,
114 scene=None):
115 """
116 Constructor
117
118 @param model class model containing the class data
119 @type ClassModel
120 @param external flag indicating a class defined outside our scope
121 @type boolean
122 @param x x-coordinate
123 @type int
124 @param y y-coordinate
125 @type int
126 @param rounded flag indicating a rounded corner
127 @type bool
128 @param noAttrs flag indicating, that no attributes should be shown
129 @type bool
130 @param colors tuple containing the foreground and background colors
131 @type tuple of (QColor, QColor)
132 @param parent reference to the parent object
133 @type QGraphicsItem
134 @param scene reference to the scene object
135 @type QGraphicsScene
136 """
137 UMLItem.__init__(self, model, x, y, rounded, colors, parent)
138
139 self.external = external
140 self.noAttrs = noAttrs
141
142 if scene:
143 scene.addItem(self)
144
145 if self.model:
146 self.__createTexts()
147 self.__calculateSize()
148
149 def __createTexts(self):
150 """
151 Private method to create the text items of the class item.
152 """
153 if self.model is None:
154 return
155
156 boldFont = QFont(self.font)
157 boldFont.setBold(True)
158 boldFont.setUnderline(True)
159
160 classAttributes = self.model.getClassAttributes()
161 attrs = self.model.getInstanceAttributes()
162 meths = self.model.getMethods()
163
164 x = self.margin + int(self.rect().x())
165 y = self.margin + int(self.rect().y())
166 self.header = QGraphicsSimpleTextItem(self)
167 self.header.setBrush(self._colors[0])
168 self.header.setFont(boldFont)
169 self.header.setText(self.model.getName())
170 self.header.setPos(x, y)
171 y += int(self.header.boundingRect().height()) + self.margin
172
173 if self.external:
174 self.classAttributes = None
175 else:
176 txt = QCoreApplication.translate(
177 "ClassItem", "Class Attributes:\n ")
178 txt += (
179 "\n ".join(classAttributes)
180 if globals else
181 " " + QCoreApplication.translate("ClassItem", "none")
182 )
183 self.classAttributes = QGraphicsSimpleTextItem(self)
184 self.classAttributes.setBrush(self._colors[0])
185 self.classAttributes.setFont(self.font)
186 self.classAttributes.setText(txt)
187 self.classAttributes.setPos(x, y)
188 y += (int(self.classAttributes.boundingRect().height()) +
189 self.margin)
190
191 if not self.noAttrs and not self.external:
192 txt = QCoreApplication.translate(
193 "ClassItem", "Instance Attributes:\n ")
194 txt += (
195 "\n ".join(attrs)
196 if attrs else
197 " " + QCoreApplication.translate("ClassItem", "none")
198 )
199 self.attrs = QGraphicsSimpleTextItem(self)
200 self.attrs.setBrush(self._colors[0])
201 self.attrs.setFont(self.font)
202 self.attrs.setText(txt)
203 self.attrs.setPos(x, y)
204 y += int(self.attrs.boundingRect().height()) + self.margin
205 else:
206 self.attrs = None
207
208 if self.external:
209 txt = " "
210 else:
211 txt = QCoreApplication.translate("ClassItem", "Methods:\n ")
212 txt += (
213 "\n ".join(meths)
214 if meths else
215 " " + QCoreApplication.translate("ClassItem", "none")
216 )
217 self.meths = QGraphicsSimpleTextItem(self)
218 self.meths.setBrush(self._colors[0])
219 self.meths.setFont(self.font)
220 self.meths.setText(txt)
221 self.meths.setPos(x, y)
222
223 def __calculateSize(self):
224 """
225 Private method to calculate the size of the class item.
226 """
227 if self.model is None:
228 return
229
230 width = int(self.header.boundingRect().width())
231 height = int(self.header.boundingRect().height())
232 if self.classAttributes:
233 width = max(width,
234 int(self.classAttributes.boundingRect().width()))
235 height += (
236 int(self.classAttributes.boundingRect().height()) + self.margin
237 )
238 if self.attrs:
239 width = max(width,
240 int(self.attrs.boundingRect().width()))
241 height = (
242 height + int(self.attrs.boundingRect().height()) + self.margin
243 )
244 if self.meths:
245 width = max(width,
246 int(self.meths.boundingRect().width()))
247 height += int(self.meths.boundingRect().height())
248
249 self.setSize(width + 2 * self.margin, height + 2 * self.margin)
250
251 def setModel(self, model):
252 """
253 Public method to set the class model.
254
255 @param model class model containing the class data
256 @type ClassModel
257 """
258 self.scene().removeItem(self.header)
259 self.header = None
260 if self.classAttributes:
261 self.scene().removeItem(self.classAttributes)
262 self.classAttributes = None
263 if self.attrs:
264 self.scene().removeItem(self.attrs)
265 self.attrs = None
266 if self.meths:
267 self.scene().removeItem(self.meths)
268 self.meths = None
269 self.model = model
270 self.__createTexts()
271 self.__calculateSize()
272
273 def paint(self, painter, option, widget=None):
274 """
275 Public method to paint the item in local coordinates.
276
277 @param painter reference to the painter object
278 @type QPainter
279 @param option style options
280 @type QStyleOptionGraphicsItem
281 @param widget optional reference to the widget painted on
282 @type QWidget
283 """
284 pen = self.pen()
285 if (
286 (option.state & QStyle.StateFlag.State_Selected) ==
287 QStyle.StateFlag.State_Selected
288 ):
289 pen.setWidth(2)
290 else:
291 pen.setWidth(1)
292
293 painter.setPen(pen)
294 painter.setBrush(self.brush())
295 painter.setFont(self.font)
296
297 offsetX = int(self.rect().x())
298 offsetY = int(self.rect().y())
299 w = int(self.rect().width())
300 h = int(self.rect().height())
301
302 painter.drawRect(offsetX, offsetY, w, h)
303 y = self.margin + int(self.header.boundingRect().height())
304 painter.drawLine(offsetX, offsetY + y, offsetX + w - 1, offsetY + y)
305 if self.classAttributes:
306 y += (
307 self.margin +
308 int(self.classAttributes.boundingRect().height())
309 )
310 painter.drawLine(offsetX, offsetY + y,
311 offsetX + w - 1, offsetY + y)
312 if self.attrs:
313 y += self.margin + int(self.attrs.boundingRect().height())
314 painter.drawLine(offsetX, offsetY + y,
315 offsetX + w - 1, offsetY + y)
316
317 self.adjustAssociations()
318
319 def isExternal(self):
320 """
321 Public method returning the external state.
322
323 @return external state
324 @rtype bool
325 """
326 return self.external
327
328 def parseItemDataString(self, version, data):
329 """
330 Public method to parse the given persistence data.
331
332 @param version version of the data
333 @type str
334 @param data persisted data to be parsed
335 @type str
336 @return flag indicating success
337 @rtype bool
338 """
339 parts = data.split(", ")
340 if len(parts) < 3:
341 return False
342
343 name = ""
344 instanceAttributes = []
345 methods = []
346 classAttributes = []
347
348 for part in parts:
349 key, value = part.split("=", 1)
350 if key == "is_external":
351 self.external = Utilities.toBool(value.strip())
352 elif key == "no_attributes":
353 self.noAttrs = Utilities.toBool(value.strip())
354 elif key == "name":
355 name = value.strip()
356 elif key == "attributes":
357 instanceAttributes = value.strip().split("||")
358 elif key == "methods":
359 methods = value.strip().split("||")
360 elif key == "class_attributes":
361 classAttributes = value.strip().split("||")
362 else:
363 return False
364
365 self.model = ClassModel(name, methods, instanceAttributes,
366 classAttributes)
367 self.__createTexts()
368 self.__calculateSize()
369
370 return True
371
372 def toDict(self):
373 """
374 Public method to collect data to be persisted.
375
376 @return dictionary containing data to be persisted
377 @rtype dict
378 """
379 return {
380 "id": self.getId(),
381 "x": self.x(),
382 "y": self.y(),
383 "type": self.getItemType(),
384 "is_external": self.external,
385 "no_attributes": self.noAttrs,
386 "model_name": self.model.getName(),
387 "attributes": self.model.getInstanceAttributes(),
388 "methods": self.model.getMethods(),
389 "class_attributes": self.model.getClassAttributes(),
390 }
391
392 @classmethod
393 def fromDict(cls, data, colors=None):
394 """
395 Class method to create a class item from persisted data.
396
397 @param data dictionary containing the persisted data as generated
398 by toDict()
399 @type dict
400 @param colors tuple containing the foreground and background colors
401 @type tuple of (QColor, QColor)
402 @return created class item
403 @rtype ClassItem
404 """
405 try:
406 model = ClassModel(data["model_name"],
407 data["methods"],
408 data["attributes"],
409 data["class_attributes"])
410 itm = cls(model=model,
411 external=data["is_external"],
412 x=0,
413 y=0,
414 noAttrs=data["no_attributes"],
415 colors=colors)
416 itm.setPos(data["x"], data["y"])
417 itm.setId(data["id"])
418 return itm
419 except KeyError:
420 return None

eric ide

mercurial