|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2007 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing an UML like class item. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 from PyQt5.QtGui import QFont |
|
13 from PyQt5.QtWidgets import QGraphicsSimpleTextItem, QStyle |
|
14 |
|
15 from .UMLItem import UMLModel, UMLItem |
|
16 |
|
17 import Utilities |
|
18 |
|
19 |
|
20 class ClassModel(UMLModel): |
|
21 """ |
|
22 Class implementing the class model. |
|
23 """ |
|
24 def __init__(self, name, methods=None, attributes=None): |
|
25 """ |
|
26 Constructor |
|
27 |
|
28 @param name the class name (string) |
|
29 @param methods list of method names of the class |
|
30 (list of strings) |
|
31 @param attributes list of attribute names of the class |
|
32 (list of strings) |
|
33 """ |
|
34 super(ClassModel, self).__init__(name) |
|
35 |
|
36 self.methods = [] if methods is None else methods[:] |
|
37 self.attributes = [] if attributes is None else attributes[:] |
|
38 |
|
39 def addMethod(self, method): |
|
40 """ |
|
41 Public method to add a method to the class model. |
|
42 |
|
43 @param method method name to be added (string) |
|
44 """ |
|
45 self.methods.append(method) |
|
46 |
|
47 def addAttribute(self, attribute): |
|
48 """ |
|
49 Public method to add an attribute to the class model. |
|
50 |
|
51 @param attribute attribute name to be added (string) |
|
52 """ |
|
53 self.attributes.append(attribute) |
|
54 |
|
55 def getMethods(self): |
|
56 """ |
|
57 Public method to retrieve the methods of the class. |
|
58 |
|
59 @return list of class methods (list of strings) |
|
60 """ |
|
61 return self.methods[:] |
|
62 |
|
63 def getAttributes(self): |
|
64 """ |
|
65 Public method to retrieve the attributes of the class. |
|
66 |
|
67 @return list of class attributes (list of strings) |
|
68 """ |
|
69 return self.attributes[:] |
|
70 |
|
71 |
|
72 class ClassItem(UMLItem): |
|
73 """ |
|
74 Class implementing an UML like class item. |
|
75 """ |
|
76 ItemType = "class" |
|
77 |
|
78 def __init__(self, model=None, external=False, x=0, y=0, |
|
79 rounded=False, noAttrs=False, parent=None, scene=None): |
|
80 """ |
|
81 Constructor |
|
82 |
|
83 @param model class model containing the class data (ClassModel) |
|
84 @param external flag indicating a class defined outside our scope |
|
85 (boolean) |
|
86 @param x x-coordinate (integer) |
|
87 @param y y-coordinate (integer) |
|
88 @keyparam rounded flag indicating a rounded corner (boolean) |
|
89 @keyparam noAttrs flag indicating, that no attributes should be shown |
|
90 (boolean) |
|
91 @keyparam parent reference to the parent object (QGraphicsItem) |
|
92 @keyparam scene reference to the scene object (QGraphicsScene) |
|
93 """ |
|
94 UMLItem.__init__(self, model, x, y, rounded, parent) |
|
95 |
|
96 self.external = external |
|
97 self.noAttrs = noAttrs |
|
98 |
|
99 scene.addItem(self) |
|
100 |
|
101 if self.model: |
|
102 self.__createTexts() |
|
103 self.__calculateSize() |
|
104 |
|
105 def __createTexts(self): |
|
106 """ |
|
107 Private method to create the text items of the class item. |
|
108 """ |
|
109 if self.model is None: |
|
110 return |
|
111 |
|
112 boldFont = QFont(self.font) |
|
113 boldFont.setBold(True) |
|
114 |
|
115 attrs = self.model.getAttributes() |
|
116 meths = self.model.getMethods() |
|
117 |
|
118 x = self.margin + self.rect().x() |
|
119 y = self.margin + self.rect().y() |
|
120 self.header = QGraphicsSimpleTextItem(self) |
|
121 self.header.setFont(boldFont) |
|
122 self.header.setText(self.model.getName()) |
|
123 self.header.setPos(x, y) |
|
124 y += self.header.boundingRect().height() + self.margin |
|
125 if not self.noAttrs and not self.external: |
|
126 if attrs: |
|
127 txt = "\n".join(attrs) |
|
128 else: |
|
129 txt = " " |
|
130 self.attrs = QGraphicsSimpleTextItem(self) |
|
131 self.attrs.setFont(self.font) |
|
132 self.attrs.setText(txt) |
|
133 self.attrs.setPos(x, y) |
|
134 y += self.attrs.boundingRect().height() + self.margin |
|
135 else: |
|
136 self.attrs = None |
|
137 if meths: |
|
138 txt = "\n".join(meths) |
|
139 else: |
|
140 txt = " " |
|
141 self.meths = QGraphicsSimpleTextItem(self) |
|
142 self.meths.setFont(self.font) |
|
143 self.meths.setText(txt) |
|
144 self.meths.setPos(x, y) |
|
145 |
|
146 def __calculateSize(self): |
|
147 """ |
|
148 Private method to calculate the size of the class item. |
|
149 """ |
|
150 if self.model is None: |
|
151 return |
|
152 |
|
153 width = self.header.boundingRect().width() |
|
154 height = self.header.boundingRect().height() |
|
155 if self.attrs: |
|
156 width = max(width, self.attrs.boundingRect().width()) |
|
157 height = height + self.attrs.boundingRect().height() + self.margin |
|
158 if self.meths: |
|
159 width = max(width, self.meths.boundingRect().width()) |
|
160 height = height + self.meths.boundingRect().height() |
|
161 self.setSize(width + 2 * self.margin, height + 2 * self.margin) |
|
162 |
|
163 def setModel(self, model): |
|
164 """ |
|
165 Public method to set the class model. |
|
166 |
|
167 @param model class model containing the class data (ClassModel) |
|
168 """ |
|
169 self.scene().removeItem(self.header) |
|
170 self.header = None |
|
171 if self.attrs: |
|
172 self.scene().removeItem(self.attrs) |
|
173 self.attrs = None |
|
174 if self.meths: |
|
175 self.scene().removeItem(self.meths) |
|
176 self.meths = None |
|
177 self.model = model |
|
178 self.__createTexts() |
|
179 self.__calculateSize() |
|
180 |
|
181 def paint(self, painter, option, widget=None): |
|
182 """ |
|
183 Public method to paint the item in local coordinates. |
|
184 |
|
185 @param painter reference to the painter object (QPainter) |
|
186 @param option style options (QStyleOptionGraphicsItem) |
|
187 @param widget optional reference to the widget painted on (QWidget) |
|
188 """ |
|
189 pen = self.pen() |
|
190 if (option.state & QStyle.State_Selected) == \ |
|
191 QStyle.State(QStyle.State_Selected): |
|
192 pen.setWidth(2) |
|
193 else: |
|
194 pen.setWidth(1) |
|
195 |
|
196 painter.setPen(pen) |
|
197 painter.setBrush(self.brush()) |
|
198 painter.setFont(self.font) |
|
199 |
|
200 offsetX = self.rect().x() |
|
201 offsetY = self.rect().y() |
|
202 w = self.rect().width() |
|
203 h = self.rect().height() |
|
204 |
|
205 painter.drawRect(offsetX, offsetY, w, h) |
|
206 y = self.margin + self.header.boundingRect().height() |
|
207 painter.drawLine(offsetX, offsetY + y, offsetX + w - 1, offsetY + y) |
|
208 if self.attrs: |
|
209 y += self.margin + self.attrs.boundingRect().height() |
|
210 painter.drawLine(offsetX, offsetY + y, |
|
211 offsetX + w - 1, offsetY + y) |
|
212 |
|
213 self.adjustAssociations() |
|
214 |
|
215 def isExternal(self): |
|
216 """ |
|
217 Public method returning the external state. |
|
218 |
|
219 @return external state (boolean) |
|
220 """ |
|
221 return self.external |
|
222 |
|
223 def buildItemDataString(self): |
|
224 """ |
|
225 Public method to build a string to persist the specific item data. |
|
226 |
|
227 This string must start with ", " and should be built like |
|
228 "attribute=value" with pairs separated by ", ". value must not |
|
229 contain ", " or newlines. |
|
230 |
|
231 @return persistence data (string) |
|
232 """ |
|
233 entries = [ |
|
234 "is_external={0}".format(self.external), |
|
235 "no_attributes={0}".format(self.noAttrs), |
|
236 "name={0}".format(self.model.getName()), |
|
237 ] |
|
238 attributes = self.model.getAttributes() |
|
239 if attributes: |
|
240 entries.append("attributes={0}".format("||".join(attributes))) |
|
241 methods = self.model.getMethods() |
|
242 if methods: |
|
243 entries.append("methods={0}".format("||".join(methods))) |
|
244 |
|
245 return ", " + ", ".join(entries) |
|
246 |
|
247 def parseItemDataString(self, version, data): |
|
248 """ |
|
249 Public method to parse the given persistence data. |
|
250 |
|
251 @param version version of the data (string) |
|
252 @param data persisted data to be parsed (string) |
|
253 @return flag indicating success (boolean) |
|
254 """ |
|
255 parts = data.split(", ") |
|
256 if len(parts) < 3: |
|
257 return False |
|
258 |
|
259 name = "" |
|
260 attributes = [] |
|
261 methods = [] |
|
262 |
|
263 for part in parts: |
|
264 key, value = part.split("=", 1) |
|
265 if key == "is_external": |
|
266 self.external = Utilities.toBool(value.strip()) |
|
267 elif key == "no_attributes": |
|
268 self.noAttrs = Utilities.toBool(value.strip()) |
|
269 elif key == "name": |
|
270 name = value.strip() |
|
271 elif key == "attributes": |
|
272 attributes = value.strip().split("||") |
|
273 elif key == "methods": |
|
274 methods = value.strip().split("||") |
|
275 else: |
|
276 return False |
|
277 |
|
278 self.model = ClassModel(name, methods, attributes) |
|
279 self.__createTexts() |
|
280 self.__calculateSize() |
|
281 |
|
282 return True |