eric7/Graphics/ClassItem.py

branch
eric7
changeset 8312
800c432b34c8
parent 8295
3f5e8b0a338e
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an UML like class item.
8 """
9
10 from PyQt5.QtCore import QCoreApplication
11 from PyQt5.QtGui import QFont
12 from PyQt5.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 + self.rect().x()
165 y = self.margin + 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 += 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 += self.classAttributes.boundingRect().height() + self.margin
189
190 if not self.noAttrs and not self.external:
191 txt = QCoreApplication.translate(
192 "ClassItem", "Instance Attributes:\n ")
193 txt += (
194 "\n ".join(attrs)
195 if attrs else
196 " " + QCoreApplication.translate("ClassItem", "none")
197 )
198 self.attrs = QGraphicsSimpleTextItem(self)
199 self.attrs.setBrush(self._colors[0])
200 self.attrs.setFont(self.font)
201 self.attrs.setText(txt)
202 self.attrs.setPos(x, y)
203 y += self.attrs.boundingRect().height() + self.margin
204 else:
205 self.attrs = None
206
207 if self.external:
208 txt = " "
209 else:
210 txt = QCoreApplication.translate("ClassItem", "Methods:\n ")
211 txt += (
212 "\n ".join(meths)
213 if meths else
214 " " + QCoreApplication.translate("ClassItem", "none")
215 )
216 self.meths = QGraphicsSimpleTextItem(self)
217 self.meths.setBrush(self._colors[0])
218 self.meths.setFont(self.font)
219 self.meths.setText(txt)
220 self.meths.setPos(x, y)
221
222 def __calculateSize(self):
223 """
224 Private method to calculate the size of the class item.
225 """
226 if self.model is None:
227 return
228
229 width = self.header.boundingRect().width()
230 height = self.header.boundingRect().height()
231 if self.classAttributes:
232 width = max(width, self.classAttributes.boundingRect().width())
233 height += (
234 self.classAttributes.boundingRect().height() + self.margin
235 )
236 if self.attrs:
237 width = max(width, self.attrs.boundingRect().width())
238 height = height + self.attrs.boundingRect().height() + self.margin
239 if self.meths:
240 width = max(width, self.meths.boundingRect().width())
241 height += self.meths.boundingRect().height()
242
243 self.setSize(width + 2 * self.margin, height + 2 * self.margin)
244
245 def setModel(self, model):
246 """
247 Public method to set the class model.
248
249 @param model class model containing the class data
250 @type ClassModel
251 """
252 self.scene().removeItem(self.header)
253 self.header = None
254 if self.classAttributes:
255 self.scene().removeItem(self.classAttributes)
256 self.classAttributes = None
257 if self.attrs:
258 self.scene().removeItem(self.attrs)
259 self.attrs = None
260 if self.meths:
261 self.scene().removeItem(self.meths)
262 self.meths = None
263 self.model = model
264 self.__createTexts()
265 self.__calculateSize()
266
267 def paint(self, painter, option, widget=None):
268 """
269 Public method to paint the item in local coordinates.
270
271 @param painter reference to the painter object
272 @type QPainter
273 @param option style options
274 @type QStyleOptionGraphicsItem
275 @param widget optional reference to the widget painted on
276 @type QWidget
277 """
278 pen = self.pen()
279 if (
280 (option.state & QStyle.StateFlag.State_Selected) ==
281 QStyle.State(QStyle.StateFlag.State_Selected)
282 ):
283 pen.setWidth(2)
284 else:
285 pen.setWidth(1)
286
287 painter.setPen(pen)
288 painter.setBrush(self.brush())
289 painter.setFont(self.font)
290
291 offsetX = self.rect().x()
292 offsetY = self.rect().y()
293 w = self.rect().width()
294 h = self.rect().height()
295
296 painter.drawRect(offsetX, offsetY, w, h)
297 y = self.margin + self.header.boundingRect().height()
298 painter.drawLine(offsetX, offsetY + y, offsetX + w - 1, offsetY + y)
299 if self.classAttributes:
300 y += self.margin + self.classAttributes.boundingRect().height()
301 painter.drawLine(offsetX, offsetY + y,
302 offsetX + w - 1, offsetY + y)
303 if self.attrs:
304 y += self.margin + self.attrs.boundingRect().height()
305 painter.drawLine(offsetX, offsetY + y,
306 offsetX + w - 1, offsetY + y)
307
308 self.adjustAssociations()
309
310 def isExternal(self):
311 """
312 Public method returning the external state.
313
314 @return external state
315 @rtype bool
316 """
317 return self.external
318
319 def buildItemDataString(self):
320 """
321 Public method to build a string to persist the specific item data.
322
323 This string must start with ", " and should be built like
324 "attribute=value" with pairs separated by ", ". value must not
325 contain ", " or newlines.
326
327 @return persistence data
328 @rtype str
329 """
330 entries = [
331 "is_external={0}".format(self.external),
332 "no_attributes={0}".format(self.noAttrs),
333 "name={0}".format(self.model.getName()),
334 ]
335 instanceAttributes = self.model.getInstanceAttributes()
336 if instanceAttributes:
337 entries.append("attributes={0}".format(
338 "||".join(instanceAttributes)))
339 methods = self.model.getMethods()
340 if methods:
341 entries.append("methods={0}".format(
342 "||".join(methods)))
343 classAttributes = self.model.getClassAttributes()
344 if classAttributes:
345 entries.append("class_attributes={0}".format(
346 "||".join(classAttributes)))
347
348 return ", " + ", ".join(entries)
349
350 def parseItemDataString(self, version, data):
351 """
352 Public method to parse the given persistence data.
353
354 @param version version of the data
355 @type str
356 @param data persisted data to be parsed
357 @type str
358 @return flag indicating success
359 @rtype bool
360 """
361 parts = data.split(", ")
362 if len(parts) < 3:
363 return False
364
365 name = ""
366 instanceAttributes = []
367 methods = []
368 classAttributes = []
369
370 for part in parts:
371 key, value = part.split("=", 1)
372 if key == "is_external":
373 self.external = Utilities.toBool(value.strip())
374 elif key == "no_attributes":
375 self.noAttrs = Utilities.toBool(value.strip())
376 elif key == "name":
377 name = value.strip()
378 elif key == "attributes":
379 instanceAttributes = value.strip().split("||")
380 elif key == "methods":
381 methods = value.strip().split("||")
382 elif key == "class_attributes":
383 classAttributes = value.strip().split("||")
384 else:
385 return False
386
387 self.model = ClassModel(name, methods, instanceAttributes,
388 classAttributes)
389 self.__createTexts()
390 self.__calculateSize()
391
392 return True
393
394 def toDict(self):
395 """
396 Public method to collect data to be persisted.
397
398 @return dictionary containing data to be persisted
399 @rtype dict
400 """
401 return {
402 "id": self.getId(),
403 "x": self.x(),
404 "y": self.y(),
405 "type": self.getItemType(),
406 "is_external": self.external,
407 "no_attributes": self.noAttrs,
408 "model_name": self.model.getName(),
409 "attributes": self.model.getInstanceAttributes(),
410 "methods": self.model.getMethods(),
411 "class_attributes": self.model.getClassAttributes(),
412 }
413
414 @classmethod
415 def fromDict(cls, data, colors=None):
416 """
417 Class method to create a class item from persisted data.
418
419 @param data dictionary containing the persisted data as generated
420 by toDict()
421 @type dict
422 @param colors tuple containing the foreground and background colors
423 @type tuple of (QColor, QColor)
424 @return created class item
425 @rtype ClassItem
426 """
427 try:
428 model = ClassModel(data["model_name"],
429 data["methods"],
430 data["attributes"],
431 data["class_attributes"])
432 itm = cls(model=model,
433 external=data["is_external"],
434 x=0,
435 y=0,
436 noAttrs=data["no_attributes"],
437 colors=colors)
438 itm.setPos(data["x"], data["y"])
439 itm.setId(data["id"])
440 return itm
441 except KeyError:
442 return None

eric ide

mercurial