1
|
|
2
|
|
3
|
|
4
|
import sys
|
5
|
import os
|
6
|
import math
|
7
|
import numpy as np
|
8
|
from PyQt5.QtGui import *
|
9
|
from PyQt5.QtCore import *
|
10
|
from PyQt5.QtSvg import *
|
11
|
from PyQt5.QtWidgets import (QApplication, QGraphicsItem)
|
12
|
from PyQt5.QtXml import *
|
13
|
|
14
|
from AppDocData import *
|
15
|
from EngineeringConnectorItem import QEngineeringConnectorItem
|
16
|
from EngineeringAbstractItem import QEngineeringAbstractItem
|
17
|
from EngineeringConnectorItem import QEngineeringConnectorItem
|
18
|
from UserInputAttribute import UserInputAttribute
|
19
|
import SelectAttributeCommand
|
20
|
|
21
|
|
22
|
class SymbolSvgItem(QGraphicsSvgItem, QEngineeringAbstractItem):
|
23
|
""" This is symbolsvgitem class """
|
24
|
|
25
|
clicked = pyqtSignal(QGraphicsSvgItem)
|
26
|
ZVALUE = 50
|
27
|
HOVER_COLOR = 'url(#hover)'
|
28
|
|
29
|
'''
|
30
|
@history 18.04.11 Jeongwoo Add Variable (Name, Type)
|
31
|
18.05.11 Jeongwoo Declare variable self.color
|
32
|
18.05.25 Jeongwoo Call setColor() method
|
33
|
18.05.30 Jeongwoo Add self variables (parentSymbol, childSymbol)
|
34
|
'''
|
35
|
|
36
|
def __init__(self, path, uid=None, flip=0):
|
37
|
import uuid
|
38
|
from SymbolAttr import SymbolProp
|
39
|
|
40
|
QGraphicsSvgItem.__init__(self)
|
41
|
QEngineeringAbstractItem.__init__(self)
|
42
|
|
43
|
self.setFlags(
|
44
|
QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemSendsGeometryChanges)
|
45
|
|
46
|
self.dbUid = None
|
47
|
self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid)
|
48
|
self.name = ''
|
49
|
self.type = ''
|
50
|
self.angle = 0
|
51
|
self.origin = None
|
52
|
self.loc = None
|
53
|
self.size = None
|
54
|
self._owner = None
|
55
|
self.parentSymbol = ''
|
56
|
self.childSymbol = ''
|
57
|
self.hasInstrumentLabel = 0
|
58
|
self.flip = flip
|
59
|
|
60
|
self.attribute = ''
|
61
|
self._properties = {SymbolProp(None, 'Supplied By', 'String'): None}
|
62
|
|
63
|
self.setAcceptDrops(True)
|
64
|
self.setAcceptHoverEvents(True)
|
65
|
self.setAcceptedMouseButtons(Qt.LeftButton)
|
66
|
self.setAcceptTouchEvents(True)
|
67
|
|
68
|
self.currentCursor = 0
|
69
|
self.transfer = Transfer()
|
70
|
|
71
|
self._angle = 0
|
72
|
|
73
|
self.in_out_connector = [[], []]
|
74
|
self.break_connector = []
|
75
|
|
76
|
try:
|
77
|
f = QFile(path)
|
78
|
f.open(QIODevice.ReadOnly)
|
79
|
array = f.readAll()
|
80
|
self._document = QDomDocument()
|
81
|
self._document.setContent(array)
|
82
|
self._renderer = QSvgRenderer(self._document.toByteArray())
|
83
|
self.setSharedRenderer(self._renderer)
|
84
|
|
85
|
self._color = self.get_attribute('fill')
|
86
|
except Exception as ex:
|
87
|
print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
88
|
sys.exc_info()[-1].tb_lineno))
|
89
|
finally:
|
90
|
f.close()
|
91
|
|
92
|
self.setZValue(SymbolSvgItem.ZVALUE)
|
93
|
|
94
|
app_doc_data = AppDocData.instance()
|
95
|
configs = app_doc_data.getConfigs('Symbol Style', 'Opacity')
|
96
|
self.setOpacity(float(configs[0].value) / 100 if configs else 0.5)
|
97
|
|
98
|
def has_in_out_connector(self):
|
99
|
""" return True if item has in or out connector """
|
100
|
if len(self.in_out_connector[0]) > 0 and len(self.in_out_connector[1]) > 0:
|
101
|
return True
|
102
|
else:
|
103
|
return False
|
104
|
|
105
|
def has_break_connector(self):
|
106
|
""" return True if item has break connector """
|
107
|
if len(self.break_connector) > 0:
|
108
|
return True
|
109
|
else:
|
110
|
return False
|
111
|
|
112
|
def __str__(self):
|
113
|
""" return string represent uuid """
|
114
|
return str(self.uid)
|
115
|
|
116
|
'''
|
117
|
@breif getter owner
|
118
|
@author humkyung
|
119
|
@date 2018.05.10
|
120
|
'''
|
121
|
|
122
|
@property
|
123
|
def owner(self):
|
124
|
import uuid
|
125
|
|
126
|
if self._owner and type(self._owner) is uuid.UUID:
|
127
|
matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
|
128
|
if matches: self._owner = matches[0]
|
129
|
|
130
|
if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
|
131
|
return self._owner
|
132
|
else:
|
133
|
self._owner = None
|
134
|
return None
|
135
|
|
136
|
'''
|
137
|
@brief setter owner
|
138
|
@author humkyung
|
139
|
@date 2018.05.10
|
140
|
@history 2018.05.17 Jeongwoo Add Calling setColor if self._owner is None or not
|
141
|
'''
|
142
|
|
143
|
@owner.setter
|
144
|
def owner(self, value):
|
145
|
self._owner = value
|
146
|
|
147
|
if self._owner is None:
|
148
|
self._color = self.DEFAULT_COLOR
|
149
|
self.setColor(self._color)
|
150
|
|
151
|
@property
|
152
|
def properties(self):
|
153
|
""" getter of properties """
|
154
|
import uuid
|
155
|
|
156
|
for prop, value in self._properties.items():
|
157
|
try:
|
158
|
if prop.is_selectable and type(value) is uuid.UUID and self.scene():
|
159
|
matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(value)]
|
160
|
if matches: self._properties[prop] = matches[0]
|
161
|
|
162
|
if prop.Expression:
|
163
|
item = self._properties[prop]
|
164
|
self._properties[prop] = eval(prop.Expression)
|
165
|
except Exception as ex:
|
166
|
from App import App
|
167
|
|
168
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
169
|
sys.exc_info()[-1].tb_lineno)
|
170
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
171
|
|
172
|
return self._properties
|
173
|
|
174
|
@properties.setter
|
175
|
def properties(self, value):
|
176
|
""" setter of properties """
|
177
|
self._properties = value
|
178
|
|
179
|
def set_property(self, property, value):
|
180
|
""" set property with given value """
|
181
|
if issubclass(type(value), QEngineeringAbstractItem): self.add_assoc_item(value, 0)
|
182
|
matches = [prop for prop, _ in self._properties.items() if prop.Attribute == property]
|
183
|
if matches: self._properties[matches[0]] = value
|
184
|
|
185
|
def prop(self, name):
|
186
|
""" return the value of given property with name """
|
187
|
matches = [(prop, value) for prop, value in self.properties.items() if prop.Attribute == name]
|
188
|
if matches: return matches[0][1]
|
189
|
|
190
|
return None
|
191
|
|
192
|
@property
|
193
|
def Size(self):
|
194
|
""" return valve's size """
|
195
|
from QEngineeringSizeTextItem import QEngineeringSizeTextItem
|
196
|
|
197
|
matches = [assoc for assoc in self.associations() if type(assoc) is QEngineeringSizeTextItem]
|
198
|
if matches:
|
199
|
return matches[0].text()
|
200
|
else:
|
201
|
return None
|
202
|
|
203
|
@property
|
204
|
def EvaluatedSize(self):
|
205
|
from EngineeringReducerItem import QEngineeringReducerItem
|
206
|
try:
|
207
|
if self.Size: return self.Size
|
208
|
if self.owner:
|
209
|
matches = [run for run in self.owner.runs if self in run.items]
|
210
|
if matches:
|
211
|
at = matches[0].items.index(self)
|
212
|
upstream = matches[0].items[:at]
|
213
|
upstream.reverse()
|
214
|
prev = self
|
215
|
for item in upstream:
|
216
|
if type(item) is QEngineeringReducerItem:
|
217
|
if item.connectors[0].connectedItem is prev:
|
218
|
if item.MainSubSize: return item.MainSubSize[0]
|
219
|
elif item.connectors[1].connectedItem is prev:
|
220
|
if item.MainSubSize: return item.MainSubSize[1]
|
221
|
else:
|
222
|
if item.Size: return item.Size
|
223
|
prev = item
|
224
|
|
225
|
downstream = matches[0].items[at:]
|
226
|
prev = self
|
227
|
for item in downstream:
|
228
|
if type(item) is QEngineeringReducerItem:
|
229
|
if item.connectors[0].connectedItem is prev:
|
230
|
if item.MainSubSize: return item.MainSubSize[0]
|
231
|
elif item.connectors[1].connectedItem is prev:
|
232
|
if item.MainSubSize: return item.MainSubSize[1]
|
233
|
else:
|
234
|
if item.Size: return item.Size
|
235
|
prev = item
|
236
|
|
237
|
if 'Drain' == matches[0].Type: return AppDocData.instance().drain_size
|
238
|
|
239
|
return self.owner.Size
|
240
|
|
241
|
return None
|
242
|
except Exception as ex:
|
243
|
from App import App
|
244
|
from AppDocData import MessageType
|
245
|
|
246
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
247
|
sys.exc_info()[-1].tb_lineno)
|
248
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
249
|
|
250
|
def validate(self):
|
251
|
'''
|
252
|
@brief validation check
|
253
|
@author euisung
|
254
|
@date 2019.04.16
|
255
|
'''
|
256
|
from EngineeringAbstractItem import QEngineeringAbstractItem
|
257
|
from EngineeringLineItem import QEngineeringLineItem
|
258
|
from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
|
259
|
from EngineeringTextItem import QEngineeringTextItem
|
260
|
from EngineeringEquipmentItem import QEngineeringEquipmentItem
|
261
|
errors = []
|
262
|
|
263
|
try:
|
264
|
docdata = AppDocData.instance()
|
265
|
dataPath = docdata.getErrorItemSvgPath()
|
266
|
|
267
|
|
268
|
for connector in self.connectors:
|
269
|
errors.extend(connector.validate())
|
270
|
|
271
|
|
272
|
if len(self.connectors) is 2:
|
273
|
if (type(self.connectors[0].connectedItem) is QEngineeringLineItem and self.connectors[
|
274
|
0]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT) and \
|
275
|
(type(self.connectors[1].connectedItem) is QEngineeringLineItem and self.connectors[
|
276
|
1]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT):
|
277
|
indices = [0, 0]
|
278
|
|
279
|
center = self.connectors[0].center()
|
280
|
dx, dy = [0, 0], [0, 0]
|
281
|
dx[0] = center[0] - self.connectors[0].connectedItem.line().p1().x()
|
282
|
dy[0] = center[1] - self.connectors[0].connectedItem.line().p1().y()
|
283
|
dx[1] = center[0] - self.connectors[0].connectedItem.line().p2().x()
|
284
|
dy[1] = center[1] - self.connectors[0].connectedItem.line().p2().y()
|
285
|
indices[0] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
|
286
|
|
287
|
center = self.connectors[1].center()
|
288
|
dx[0] = center[0] - self.connectors[1].connectedItem.line().p1().x()
|
289
|
dy[0] = center[1] - self.connectors[1].connectedItem.line().p1().y()
|
290
|
dx[1] = center[0] - self.connectors[1].connectedItem.line().p2().x()
|
291
|
dy[1] = center[1] - self.connectors[1].connectedItem.line().p2().y()
|
292
|
indices[1] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
|
293
|
|
294
|
if indices[0] == indices[1]:
|
295
|
error = SymbolSvgItem.createItem('Error', dataPath)
|
296
|
error.parent = self
|
297
|
error.msg = self.tr('flow direction error')
|
298
|
error.setToolTip(error.msg)
|
299
|
error.area = 'Drawing'
|
300
|
error.name = 'Error'
|
301
|
errors.append(error)
|
302
|
|
303
|
|
304
|
disconnect = False
|
305
|
if len(self.connectors) is not 0:
|
306
|
disconnect = True
|
307
|
for connector in self.connectors:
|
308
|
if connector.connectedItem is not None:
|
309
|
disconnect = False
|
310
|
break
|
311
|
|
312
|
if disconnect:
|
313
|
error = SymbolSvgItem.createItem('Error', dataPath)
|
314
|
error.parent = self
|
315
|
error.msg = 'disconnected'
|
316
|
error.setToolTip(error.msg)
|
317
|
error.area = 'Drawing'
|
318
|
error.name = 'Error'
|
319
|
errors.append(error)
|
320
|
|
321
|
|
322
|
if self.size[0] == 0 or self.size[1] == 0:
|
323
|
error = SymbolSvgItem.createItem('Error', dataPath)
|
324
|
error.parent = self
|
325
|
error.msg = self.tr('size error')
|
326
|
error.setToolTip(error.msg)
|
327
|
error.area = 'Drawing'
|
328
|
error.name = 'Error'
|
329
|
errors.append(error)
|
330
|
|
331
|
|
332
|
for assoc in self.associations():
|
333
|
if issubclass(type(assoc), QEngineeringTextItem) and not assoc.owner:
|
334
|
error = SymbolSvgItem.createItem('Error', dataPath)
|
335
|
error.parent = self
|
336
|
error.msg = self.tr('association error')
|
337
|
error.setToolTip(error.msg)
|
338
|
error.area = self.area
|
339
|
error.name = 'Error'
|
340
|
errors.append(error)
|
341
|
|
342
|
|
343
|
for error in errors:
|
344
|
error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
|
345
|
|
346
|
connectedUid = []
|
347
|
for connector in self.connectors:
|
348
|
|
349
|
if connector.connectedItem and issubclass(type(connector.connectedItem), QEngineeringAbstractItem):
|
350
|
connectedUid.append(str(connector.connectedItem.uid))
|
351
|
|
352
|
if issubclass(type(connector.connectedItem), SymbolSvgItem) and type(
|
353
|
connector.connectedItem) is not QEngineeringEquipmentItem:
|
354
|
matches = [conn for conn in connector.connectedItem.connectors if conn.connectedItem is self]
|
355
|
|
356
|
if not matches:
|
357
|
error = SymbolSvgItem.createItem('Error', dataPath)
|
358
|
error.setPosition(connector.center())
|
359
|
error.parent = self
|
360
|
error.msg = self.tr('disconnected from opposite side')
|
361
|
error.setToolTip(error.msg)
|
362
|
error.area = self.area
|
363
|
error.name = 'Error'
|
364
|
errors.append(error)
|
365
|
|
366
|
|
367
|
if len(connectedUid) is not len(set(connectedUid)):
|
368
|
error = SymbolSvgItem.createItem('Error', dataPath)
|
369
|
error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
|
370
|
error.parent = self
|
371
|
error.msg = self.tr('duplicated connection')
|
372
|
error.setToolTip(error.msg)
|
373
|
error.area = self.area
|
374
|
error.name = 'Error'
|
375
|
errors.append(error)
|
376
|
except Exception as ex:
|
377
|
from App import App
|
378
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
379
|
sys.exc_info()[-1].tb_lineno)
|
380
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
381
|
|
382
|
return errors
|
383
|
|
384
|
def includes(self, pt, margin=0):
|
385
|
"""
|
386
|
return True if symbol contains given point else return False
|
387
|
"""
|
388
|
rect = self.sceneBoundingRect()
|
389
|
allowed_error = 0.1
|
390
|
|
391
|
if abs(rect.x() - 0) <= allowed_error and abs(rect.y() - 0) <= allowed_error:
|
392
|
|
393
|
minX = self.loc[0] - margin
|
394
|
minY = self.loc[1] - margin
|
395
|
maxX = minX + self.size[0] + margin
|
396
|
maxY = minY + self.size[1] + margin
|
397
|
else:
|
398
|
minX = rect.x() - margin
|
399
|
minY = rect.y() - margin
|
400
|
maxX = minX + rect.width() + margin
|
401
|
maxY = minY + rect.height() + margin
|
402
|
|
403
|
return True if (pt[0] >= minX and pt[0] <= maxX and pt[1] >= minY and pt[1] <= maxY) else False
|
404
|
|
405
|
'''
|
406
|
def associations(self):
|
407
|
""" return associated instance """
|
408
|
# move to abstractitem
|
409
|
import uuid
|
410
|
|
411
|
res = []
|
412
|
for key in self._associations.keys():
|
413
|
index = 0
|
414
|
for assoc in self._associations[key]:
|
415
|
# find owner with uid
|
416
|
if assoc and type(assoc) is uuid.UUID:
|
417
|
matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(assoc)]
|
418
|
if matches:
|
419
|
res.append(matches[0]) # TODO: need to update association with instance
|
420
|
self._associations[key][index] = matches[0]
|
421
|
index += 1
|
422
|
# up to here
|
423
|
elif assoc:
|
424
|
res.append(assoc)
|
425
|
|
426
|
for key in self.attrs.keys():
|
427
|
if type(key.AssocItem) is uuid.UUID:
|
428
|
for assoc in res:
|
429
|
if str(key.AssocItem) == str(assoc.uid):
|
430
|
key.AssocItem = assoc
|
431
|
|
432
|
return res
|
433
|
|
434
|
def texts(self):
|
435
|
""" return text type of associations """
|
436
|
from EngineeringTextItem import QEngineeringTextItem
|
437
|
|
438
|
res = []
|
439
|
for text in [x for x in self.associations() if issubclass(type(x), QEngineeringTextItem)]:
|
440
|
consumed = False
|
441
|
for key in list(self.attrs.keys()):
|
442
|
if key.AssocItem and key.AssocItem is text:
|
443
|
consumed = True
|
444
|
if not consumed:
|
445
|
res.append(text)
|
446
|
|
447
|
return res
|
448
|
|
449
|
def symbols(self):
|
450
|
""" return symbol type of associations """
|
451
|
res = []
|
452
|
for symbol in [x for x in self.associations() if issubclass(type(x), SymbolSvgItem)]:
|
453
|
consumed = False
|
454
|
for key in list(self.attrs.keys()):
|
455
|
if key.AssocItem and key.AssocItem is symbol:
|
456
|
consumed = True
|
457
|
if not consumed:
|
458
|
res.append(symbol)
|
459
|
|
460
|
return res
|
461
|
'''
|
462
|
|
463
|
def itemChange(self, change, value):
|
464
|
""" call signals when item's position or rotation is changed """
|
465
|
if not self.scene(): return super().itemChange(change, value)
|
466
|
|
467
|
if change == QGraphicsItem.ItemPositionChange or change == QGraphicsItem.ItemRotationChange:
|
468
|
for conn in self.connectors:
|
469
|
conn.sceneConnectPoint = (conn.sceneBoundingRect().center().x(), conn.sceneBoundingRect().center().y())
|
470
|
|
471
|
from EngineeringLineItem import QEngineeringLineItem
|
472
|
for connector in self.connectors:
|
473
|
if connector.connectedItem is not None and type(connector.connectedItem) == QEngineeringLineItem:
|
474
|
line = connector.connectedItem
|
475
|
line.reDrawLine(self, connector.center())
|
476
|
line.update_arrow()
|
477
|
|
478
|
self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(
|
479
|
self.sceneBoundingRect().height())
|
480
|
|
481
|
return value
|
482
|
|
483
|
return super().itemChange(change, value)
|
484
|
|
485
|
def toSql_Components(self):
|
486
|
""" convert Components data to sql query """
|
487
|
from AppDocData import AppDocData
|
488
|
|
489
|
cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner',
|
490
|
'Connected', '[Supplied By]', \
|
491
|
'SpecialItemTypes_UID', 'OriginIndex', '[From]', '[To]', '[Freeze]', '[Connected Item]', '[Flip]', 'SceneOriginPoint']
|
492
|
values = ['?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?']
|
493
|
param = [(str(self.uid), str(AppDocData.instance().activeDrawing.UID), self.dbUid, self.loc[0], self.loc[1],
|
494
|
self.size[0], self.size[1], self.angle,
|
495
|
self.area, str(self.owner) if self.owner else None, \
|
496
|
str(self.conns[0]) if self.conns else None, \
|
497
|
self.prop('Supplied By'), \
|
498
|
str(self.special_item_type) if self.special_item_type else None, \
|
499
|
self.currentPointModeIndex, \
|
500
|
self.prop('From'), \
|
501
|
self.prop('To'), \
|
502
|
self.prop('Freeze') if self.prop('Freeze') else 0, \
|
503
|
str(self.prop('Connected Item')) if self.prop('Connected Item') else None, \
|
504
|
self.flip, \
|
505
|
'{},{}'.format(self.origin[0], self.origin[1]))]
|
506
|
sql = 'insert into Components({}) values({})'.format(','.join(cols), ','.join(values))
|
507
|
|
508
|
return (sql, tuple(param))
|
509
|
|
510
|
def toSql_return_separately(self):
|
511
|
""" convert valve data to sql query """
|
512
|
import uuid
|
513
|
from AppDocData import AppDocData
|
514
|
|
515
|
res = []
|
516
|
resLater = []
|
517
|
app_doc_data = AppDocData.instance()
|
518
|
|
519
|
res.append(self.toSql_Components())
|
520
|
|
521
|
_attrs = self.getAttributes()
|
522
|
if _attrs:
|
523
|
cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value', 'Association_UID', 'Freeze']
|
524
|
values = ['?', '?', '?', '?', '?', '?']
|
525
|
params = []
|
526
|
for key in _attrs.keys():
|
527
|
params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), str(_attrs[key]), str(key.AssocItem),
|
528
|
str(key.Freeze)))
|
529
|
sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
|
530
|
res.append((sql, tuple(params)))
|
531
|
|
532
|
if self.associations():
|
533
|
cols = ['UID', '[Type]', 'Components_UID', 'Association']
|
534
|
values = ['?', '?', '?', '?']
|
535
|
params = []
|
536
|
for assoc in self.associations():
|
537
|
params.append(
|
538
|
(str(uuid.uuid4()), QEngineeringAbstractItem.assoc_type(assoc), str(self.uid), str(assoc.uid)))
|
539
|
sql = 'insert into Associations({}) values({})'.format(','.join(cols), ','.join(values))
|
540
|
resLater.append((sql, tuple(params)))
|
541
|
|
542
|
|
543
|
if self.connectors:
|
544
|
cols = ['Components_UID', '[Index]', 'X', 'Y', 'Connected', 'Connected_At']
|
545
|
values = ['?', '?', '?', '?', '?', '?']
|
546
|
params = []
|
547
|
index = 1
|
548
|
for connector in self.connectors:
|
549
|
params.append( \
|
550
|
(
|
551
|
str(self.uid), index, connector.sceneConnectPoint[0], connector.sceneConnectPoint[1], \
|
552
|
str(connector.connectedItem.uid) if connector.connectedItem else None, \
|
553
|
str(connector._connected_at)) \
|
554
|
)
|
555
|
index += 1
|
556
|
sql = 'insert into Points({}) values({})'.format(','.join(cols), ','.join(values))
|
557
|
resLater.append((sql, tuple(params)))
|
558
|
|
559
|
|
560
|
return res, resLater
|
561
|
|
562
|
'''
|
563
|
@brief build symbol item
|
564
|
@author humkyung
|
565
|
@date 2018.05.02
|
566
|
@history 2018.05.09 Jeongwoo Clear self.connectors
|
567
|
2018.05.30 Jeongwoo Add parameters (parentSymbol, childSymbol)
|
568
|
'''
|
569
|
|
570
|
def buildItem(self, name, _type, angle, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel,
|
571
|
dbUid=None):
|
572
|
from SpecialItemTypesDialog import SpecialItemTypes
|
573
|
|
574
|
try:
|
575
|
docData = AppDocData.instance()
|
576
|
self.name = name
|
577
|
self.type = _type
|
578
|
self.angle = angle
|
579
|
self.loc = loc
|
580
|
self.size = size if size else [0, 0]
|
581
|
self.origin = origin
|
582
|
if dbUid is None:
|
583
|
symbolInfo = docData.getSymbolByQuery('name', name)
|
584
|
else:
|
585
|
symbolInfo = docData.getSymbolByQuery('UID', dbUid)
|
586
|
self.dbUid = symbolInfo.uid
|
587
|
originalPoint = symbolInfo.getOriginalPoint().split(',')
|
588
|
self.symbolOrigin = [float(originalPoint[0]), float(originalPoint[1])]
|
589
|
|
590
|
|
591
|
connectionPoints = symbolInfo.getConnectionPoint().split('/')
|
592
|
for index in range(len(connectionPoints)):
|
593
|
if connectionPoints[index] == '':
|
594
|
break
|
595
|
tokens = connectionPoints[index].split(',')
|
596
|
|
597
|
direction = 'AUTO'
|
598
|
symbol_idx = '0'
|
599
|
if len(tokens) == 2:
|
600
|
x = float(tokens[0])
|
601
|
y = float(tokens[1])
|
602
|
elif len(tokens) == 3:
|
603
|
direction = tokens[0]
|
604
|
x = float(tokens[1])
|
605
|
y = float(tokens[2])
|
606
|
elif len(tokens) == 4:
|
607
|
direction = tokens[0]
|
608
|
x = float(tokens[1])
|
609
|
y = float(tokens[2])
|
610
|
symbol_idx = tokens[3]
|
611
|
elif len(tokens) > 4:
|
612
|
direction = tokens[0]
|
613
|
x = float(tokens[1])
|
614
|
y = float(tokens[2])
|
615
|
symbol_idx = tokens[3]
|
616
|
if tokens[4] == 'In':
|
617
|
self.in_out_connector[0].append(index)
|
618
|
elif tokens[4] == 'Out':
|
619
|
self.in_out_connector[1].append(index)
|
620
|
if tokens[5] == 'O':
|
621
|
self.break_connector.append(index)
|
622
|
|
623
|
self.setConnector(index + 1)
|
624
|
self.connectors[index].direction = direction
|
625
|
self.connectors[index].symbol_idx = symbol_idx
|
626
|
self.connectors[index].setPos((x, y))
|
627
|
self.connectors[index].connectPoint = (x, y)
|
628
|
self.connectors[index].sceneConnectPoint = (connPts[index][0], connPts[index][1]) if len(
|
629
|
connPts[index]) == 2 else \
|
630
|
(connPts[index][1], connPts[index][2]) if len(connPts[index]) == 3 else \
|
631
|
(connPts[index][1], connPts[index][2]) if len(connPts[index]) == 4 else None
|
632
|
self.parentSymbol = parentSymbol
|
633
|
self.childSymbol = childSymbol
|
634
|
self.hasInstrumentLabel = hasInstrumentLabel
|
635
|
self.currentPointModeIndex = 0
|
636
|
self.special_item_type = SpecialItemTypes.instance().find_match_exactly(self)
|
637
|
|
638
|
tooltip = '<b>{}</b><br>{}={}'.format(str(self.uid), self.type, self.name)
|
639
|
self.setToolTip(tooltip)
|
640
|
|
641
|
return True
|
642
|
except Exception as ex:
|
643
|
from App import App
|
644
|
|
645
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
646
|
sys.exc_info()[-1].tb_lineno)
|
647
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
648
|
|
649
|
return False
|
650
|
|
651
|
'''
|
652
|
@brief return bounding box of symbol
|
653
|
@author humkyung
|
654
|
@date 2018.04.08
|
655
|
'''
|
656
|
|
657
|
def rect(self):
|
658
|
return self.sceneBoundingRect()
|
659
|
|
660
|
'''
|
661
|
@brief return true if line is able to connect symbol
|
662
|
@author humkyung
|
663
|
@date 2018.04.13
|
664
|
'''
|
665
|
|
666
|
def is_connectable(self, item, toler=10):
|
667
|
|
668
|
|
669
|
'''
|
670
|
if False:#type(item) is QEngineeringLineItem:
|
671
|
line = item
|
672
|
start_pt = line.startPoint()
|
673
|
end_pt = line.endPoint()
|
674
|
for connector in self.connectors:
|
675
|
dx = connector.sceneConnectPoint[0] - (start_pt[0])
|
676
|
dy = connector.sceneConnectPoint[1] - (start_pt[1])
|
677
|
if (math.sqrt(dx*dx + dy*dy) < toler): return True
|
678
|
dx = connector.sceneConnectPoint[0] - (end_pt[0])
|
679
|
dy = connector.sceneConnectPoint[1] - (end_pt[1])
|
680
|
if (math.sqrt(dx*dx + dy*dy) < toler): return True
|
681
|
elif True:#issubclass(type(item), SymbolSvgItem):
|
682
|
'''
|
683
|
for connector in self.connectors:
|
684
|
for iConnector in item.connectors:
|
685
|
dx = connector.sceneConnectPoint[0] - iConnector.sceneConnectPoint[0]
|
686
|
dy = connector.sceneConnectPoint[1] - iConnector.sceneConnectPoint[1]
|
687
|
if (math.sqrt(dx * dx + dy * dy) < toler): return True
|
688
|
|
689
|
return False
|
690
|
|
691
|
'''
|
692
|
@author humkyung
|
693
|
@date 2018.07.03
|
694
|
'''
|
695
|
|
696
|
def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
|
697
|
""" check if given item is connected to self """
|
698
|
|
699
|
_connectors = [connector for connector in self.connectors if
|
700
|
(connector.connectedItem == item and (connector._connected_at == at if at else True))]
|
701
|
return len(_connectors) > 0
|
702
|
|
703
|
def next_connected(self, lhs, rhs):
|
704
|
""" check given two item's are next connected(ex: 0-1, 2-3) """
|
705
|
|
706
|
lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
|
707
|
rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
|
708
|
if lhs_matches and rhs_matches:
|
709
|
return (lhs_matches[0] in [0, 1] and rhs_matches[0] in [0, 1]) or (
|
710
|
lhs_matches[0] in [2, 3] and rhs_matches[0] in [2, 3])
|
711
|
|
712
|
return False
|
713
|
|
714
|
def canBeSecondary(self, line):
|
715
|
""" check given line is not connected(ex: 0-1, 2-3) """
|
716
|
preItem = None
|
717
|
|
718
|
item = [item.connectedItem for item in self.connectors if
|
719
|
item.connectedItem is not None and item.connectedItem.owner is not None]
|
720
|
if item:
|
721
|
preItem = item[0]
|
722
|
|
723
|
if preItem is not None and not self.next_connected(line, preItem):
|
724
|
return True
|
725
|
else:
|
726
|
return False
|
727
|
|
728
|
'''
|
729
|
@brief connect line and symbol is able to be connected and return line
|
730
|
@author humkyung
|
731
|
@date 2018.04.16
|
732
|
@history humkyung 2018.05.08 check if symbol is possible to be connected
|
733
|
Jeongwoo 2018.05.15 Connect each symbol and line
|
734
|
'''
|
735
|
|
736
|
def connect_if_possible(self, obj, toler=10):
|
737
|
from shapely.geometry import Point
|
738
|
from EngineeringLineItem import QEngineeringLineItem
|
739
|
|
740
|
res = []
|
741
|
try:
|
742
|
if type(obj) is QEngineeringLineItem:
|
743
|
startPt = obj.startPoint()
|
744
|
endPt = obj.endPoint()
|
745
|
for i in range(len(self.connectors)):
|
746
|
if (Point(startPt[0], startPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0],
|
747
|
self.connectors[i].sceneConnectPoint[1])) < toler):
|
748
|
if self.connectors[i].connectedItem is None:
|
749
|
self.connectors[i].connect(obj)
|
750
|
if obj.connectors[0].connectedItem is None:
|
751
|
obj.connectors[0].connect(self)
|
752
|
|
753
|
res.append(obj)
|
754
|
if (Point(endPt[0], endPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0],
|
755
|
self.connectors[i].sceneConnectPoint[1])) < toler):
|
756
|
if self.connectors[i].connectedItem is None:
|
757
|
self.connectors[i].connect(obj)
|
758
|
if obj.connectors[1].connectedItem is None:
|
759
|
obj.connectors[1].connect(self)
|
760
|
|
761
|
res.append(obj)
|
762
|
elif issubclass(type(obj), SymbolSvgItem):
|
763
|
for i in range(len(self.connectors)):
|
764
|
if i > 3: break
|
765
|
for j in range(len(obj.connectors)):
|
766
|
if j > 3: break
|
767
|
_pt = Point(obj.connectors[j].sceneConnectPoint[0], obj.connectors[j].sceneConnectPoint[1])
|
768
|
if (_pt.distance(Point(self.connectors[i].sceneConnectPoint[0],
|
769
|
self.connectors[i].sceneConnectPoint[1])) < toler):
|
770
|
if self.connectors[i].connectedItem is None:
|
771
|
self.connectors[i].connect(obj)
|
772
|
if obj.connectors[j].connectedItem is None:
|
773
|
obj.connectors[j].connect(self)
|
774
|
|
775
|
res.append(obj)
|
776
|
except Exception as ex:
|
777
|
from App import App
|
778
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
779
|
sys.exc_info()[-1].tb_lineno)
|
780
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
781
|
|
782
|
return res
|
783
|
|
784
|
'''
|
785
|
@brief disconnect connector item
|
786
|
@author kyouho
|
787
|
@date 2018.08.30
|
788
|
'''
|
789
|
|
790
|
def disconnectedItemAtConnector(self, connector):
|
791
|
for conn in self.connectors:
|
792
|
if conn.isOverlapConnector(connector):
|
793
|
conn.connectedItem = None
|
794
|
|
795
|
'''
|
796
|
@brief get connection point close to given point in tolerance
|
797
|
@author humkyung
|
798
|
@dat
|
799
|
'''
|
800
|
|
801
|
def getConnectionPointCloseTo(self, pt, toler=10):
|
802
|
import math
|
803
|
|
804
|
for connector in self.connectors:
|
805
|
dx = connector.sceneConnectPoint[0] - pt[0]
|
806
|
dy = connector.sceneConnectPoint[1] - pt[1]
|
807
|
if math.sqrt(dx * dx + dy * dy) < toler: return connPt
|
808
|
|
809
|
return None
|
810
|
|
811
|
'''
|
812
|
@brief return center of symbol
|
813
|
@author humkyung
|
814
|
@date 2018.04.08
|
815
|
'''
|
816
|
|
817
|
def center(self):
|
818
|
return self.sceneBoundingRect().center()
|
819
|
|
820
|
'''
|
821
|
@brief highlight connector and attribute
|
822
|
@authro humkyung
|
823
|
@date 2018.05.02
|
824
|
'''
|
825
|
|
826
|
def hoverEnterEvent(self, event):
|
827
|
self.highlight(True)
|
828
|
|
829
|
'''
|
830
|
@brief unhighlight connector and attribute
|
831
|
@author humkyung
|
832
|
@date 2018.05.02
|
833
|
@history kyouho 2018.07.18 edit ArrowCursor
|
834
|
'''
|
835
|
|
836
|
def hoverLeaveEvent(self, event):
|
837
|
self.highlight(False)
|
838
|
|
839
|
def highlight(self, flag):
|
840
|
""" highlight/unhighlight the symbol """
|
841
|
|
842
|
try:
|
843
|
self.hover = flag
|
844
|
self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
|
845
|
self.update()
|
846
|
|
847
|
for assoc in self.associations():
|
848
|
assoc.highlight(flag)
|
849
|
|
850
|
for connector in self.connectors:
|
851
|
connector.highlight(flag)
|
852
|
except Exception as ex:
|
853
|
from App import App
|
854
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
855
|
sys.exc_info()[-1].tb_lineno)
|
856
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
857
|
|
858
|
'''
|
859
|
@brief set highlight
|
860
|
@author kyouho
|
861
|
@date 2018.08.27
|
862
|
'''
|
863
|
|
864
|
def setHightlight(self):
|
865
|
self.setColor('url(#hover)')
|
866
|
self.update()
|
867
|
|
868
|
'''
|
869
|
@brief unset highlight
|
870
|
@author kyouho
|
871
|
@date 2018.08.27
|
872
|
'''
|
873
|
|
874
|
def unsetHightlight(self):
|
875
|
self.setColor('url(#normal)')
|
876
|
self.update()
|
877
|
|
878
|
'''
|
879
|
@brief change cursor to CrossCursor if mouse point is close to connection point
|
880
|
@author humkyung
|
881
|
@date 2018.04.28
|
882
|
'''
|
883
|
|
884
|
def hoverMoveEvent(self, event):
|
885
|
pass
|
886
|
|
887
|
'''
|
888
|
@brief Mouse Press Event
|
889
|
@author Jeongwoo
|
890
|
@date 18.04.11
|
891
|
@history kyouho 2018.07.18 add isClick logic
|
892
|
'''
|
893
|
|
894
|
def mousePressEvent(self, event):
|
895
|
if event.buttons() == Qt.LeftButton:
|
896
|
self.clicked.emit(self)
|
897
|
|
898
|
'''
|
899
|
@brief Mouse Release Event
|
900
|
@author kyouho
|
901
|
@date 18.07.17
|
902
|
'''
|
903
|
|
904
|
def mouseReleaseEvent(self, event):
|
905
|
if hasattr(self, '_rotating') and event.button() == Qt.RightButton:
|
906
|
self.angle = -self._angle if self._angle > -math.pi and self._angle < 0 else 2 * math.pi - self._angle
|
907
|
self.ungrabMouse()
|
908
|
del self._rotating
|
909
|
|
910
|
super().mouseReleaseEvent(event)
|
911
|
|
912
|
def mouseMoveEvent(self, event):
|
913
|
""" rotate symbol accroding to current mouse point """
|
914
|
if hasattr(self, '_rotating'):
|
915
|
|
916
|
origin = self.origin
|
917
|
|
918
|
|
919
|
dx = (event.scenePos().x() - origin[0])
|
920
|
dy = (event.scenePos().y() - origin[1])
|
921
|
length = math.sqrt(dx * dx + dy * dy)
|
922
|
|
923
|
self._angle = 0
|
924
|
if length > 0:
|
925
|
self._angle = math.acos(dx / length)
|
926
|
cross = int(np.cross([1, 0], [dx, dy]))
|
927
|
self._angle = -self._angle if cross < 0 else self._angle
|
928
|
|
929
|
self.rotate(self.getCurrentPoint(), -self._angle)
|
930
|
|
931
|
def removeSelfAttr(self, attributeName):
|
932
|
target = None
|
933
|
for attr in self.attrs:
|
934
|
if attr.Attribute == attributeName:
|
935
|
target = attr
|
936
|
break
|
937
|
|
938
|
if target:
|
939
|
del self.attrs[attr]
|
940
|
|
941
|
'''
|
942
|
@brief Find TextItem contain Point
|
943
|
@author kyouho
|
944
|
@date 18.07.17
|
945
|
'''
|
946
|
|
947
|
def findTextItemInPoint(self, point):
|
948
|
from EngineeringTextItem import QEngineeringTextItem
|
949
|
|
950
|
scene = self.scene()
|
951
|
|
952
|
for item in scene.items():
|
953
|
if type(item) is QEngineeringTextItem:
|
954
|
if self.isOverlapItemAndPoint(item, point):
|
955
|
return (True, item)
|
956
|
|
957
|
return (False,)
|
958
|
|
959
|
'''
|
960
|
@brief Check Overlap
|
961
|
@author kyouho
|
962
|
@date 18.07.17
|
963
|
'''
|
964
|
|
965
|
def isOverlapItemAndPoint(self, item, point):
|
966
|
x = point.x()
|
967
|
y = point.y()
|
968
|
loc = item.loc
|
969
|
size = item.size
|
970
|
|
971
|
if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
|
972
|
return True
|
973
|
else:
|
974
|
return False
|
975
|
|
976
|
'''
|
977
|
@brief remove item when user press delete key
|
978
|
@author humkyung
|
979
|
@date 2018.04.23
|
980
|
@history 2018.05.17 Jeongwoo Add if-statement and move 'break'
|
981
|
2018.05.25 Jeongwoo Seperate delete item method
|
982
|
'''
|
983
|
|
984
|
def keyPressEvent(self, event):
|
985
|
from EngineeringErrorItem import QEngineeringErrorItem
|
986
|
from RotateSymbolDialog import QRotateSymbolDialog
|
987
|
|
988
|
if not self.isSelected():
|
989
|
return
|
990
|
if event.key() == Qt.Key_Delete:
|
991
|
self.deleteSvgItemFromScene()
|
992
|
elif event.key() == Qt.Key_R and type(self) is not QEngineeringErrorItem:
|
993
|
self.rotateSymbol()
|
994
|
elif event.key() == Qt.Key_O and type(self) is not QEngineeringErrorItem:
|
995
|
self.changeStandardPoint()
|
996
|
elif event.key() == Qt.Key_C and type(self) is not QEngineeringErrorItem:
|
997
|
self.changeConnPoint()
|
998
|
elif event.key() == Qt.Key_F and type(self) is not QEngineeringErrorItem:
|
999
|
self.flipSymbol()
|
1000
|
elif event.key() == Qt.Key_X:
|
1001
|
dialog = QRotateSymbolDialog(None, self.angle, self.origin, self.zValue())
|
1002
|
(isAccept, angle, x, y, z) = dialog.showDialog()
|
1003
|
|
1004
|
if isAccept:
|
1005
|
self.angle = angle
|
1006
|
self.loc = [x - self.symbolOrigin[0], y - self.symbolOrigin[1]]
|
1007
|
self.origin = [x, y]
|
1008
|
|
1009
|
|
1010
|
|
1011
|
self.rotate(self.getCurrentPoint(), self.angle)
|
1012
|
self.setZValue(z)
|
1013
|
elif event.key() == Qt.Key_Escape:
|
1014
|
if hasattr(self, '_rotating'):
|
1015
|
self.ungrabMouse()
|
1016
|
|
1017
|
transform = QTransform()
|
1018
|
transform.translate((self.loc[0] + self.symbolOrigin[0]), (self.loc[1] + self.symbolOrigin[1]))
|
1019
|
transform.rotateRadians(self.angle)
|
1020
|
transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
|
1021
|
self.setTransform(transform)
|
1022
|
|
1023
|
del self._rotating
|
1024
|
elif event.key() == Qt.Key_Up:
|
1025
|
self.loc[1] = self.loc[1] - 1
|
1026
|
self.origin[1] = self.origin[1] - 1
|
1027
|
self.rotate(self.getCurrentPoint(), self.angle)
|
1028
|
elif event.key() == Qt.Key_Down:
|
1029
|
self.loc[1] = self.loc[1] + 1
|
1030
|
self.origin[1] = self.origin[1] + 1
|
1031
|
self.rotate(self.getCurrentPoint(), self.angle)
|
1032
|
elif event.key() == Qt.Key_Left:
|
1033
|
self.loc[0] = self.loc[0] - 1
|
1034
|
self.origin[0] = self.origin[0] - 1
|
1035
|
self.rotate(self.getCurrentPoint(), self.angle)
|
1036
|
elif event.key() == Qt.Key_Right:
|
1037
|
self.loc[0] = self.loc[0] + 1
|
1038
|
self.origin[0] = self.origin[0] + 1
|
1039
|
self.rotate(self.getCurrentPoint(), self.angle)
|
1040
|
|
1041
|
'''
|
1042
|
@brief connect attribute
|
1043
|
@author humkyung
|
1044
|
@date 2018.05.02
|
1045
|
@history humkyung 2018.05.09 append only nearest size attribute
|
1046
|
'''
|
1047
|
|
1048
|
def connectAttribute(self, attributes, clear=True):
|
1049
|
import math
|
1050
|
from EngineeringTextItem import QEngineeringTextItem
|
1051
|
from QEngineeringSizeTextItem import QEngineeringSizeTextItem
|
1052
|
from EngineeringInstrumentItem import QEngineeringInstrumentItem
|
1053
|
from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
|
1054
|
|
1055
|
try:
|
1056
|
if clear:
|
1057
|
self.clear_attr_and_assoc_item()
|
1058
|
|
1059
|
configs = AppDocData.instance().getConfigs('Range', 'Detection Ratio')
|
1060
|
ratio = float(configs[0].value) if 1 == len(configs) else 1.5
|
1061
|
|
1062
|
dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
|
1063
|
center = self.sceneBoundingRect().center()
|
1064
|
|
1065
|
minDist = None
|
1066
|
selected = None
|
1067
|
for attr in attributes:
|
1068
|
|
1069
|
if False:
|
1070
|
dx = attr.center().x() - center.x()
|
1071
|
dy = attr.center().y() - center.y()
|
1072
|
length = math.sqrt(dx * dx + dy * dy)
|
1073
|
if (length < dist) and (minDist is None or length < minDist):
|
1074
|
minDist = length
|
1075
|
selected = attr
|
1076
|
elif type(attr) is QEngineeringInstrumentItem:
|
1077
|
if not attr.is_connected:
|
1078
|
dx = attr.center().x() - center.x()
|
1079
|
dy = attr.center().y() - center.y()
|
1080
|
if math.sqrt(dx * dx + dy * dy) < dist:
|
1081
|
if self.add_assoc_item(attr):
|
1082
|
attr.owner = self
|
1083
|
|
1084
|
if selected is not None:
|
1085
|
if self.add_assoc_item(selected):
|
1086
|
selected.owner = self
|
1087
|
|
1088
|
except Exception as ex:
|
1089
|
from App import App
|
1090
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
1091
|
sys.exc_info()[-1].tb_lineno)
|
1092
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
1093
|
|
1094
|
'''
|
1095
|
@brief start rotating
|
1096
|
@author euisung
|
1097
|
@date 2019.04.16
|
1098
|
'''
|
1099
|
|
1100
|
def mouseDoubleClickEvent(self, event):
|
1101
|
if not hasattr(self, '_rotating'):
|
1102
|
self._rotating = True
|
1103
|
self.grabMouse()
|
1104
|
|
1105
|
'''
|
1106
|
@brief get attribute
|
1107
|
@author humkyung
|
1108
|
@date 2018.06.14
|
1109
|
@history kyouho 2018.07.18 Add only attr QEngineeringTextItem
|
1110
|
'''
|
1111
|
'''
|
1112
|
def getAttributes(self):
|
1113
|
_attrs = {}
|
1114
|
try:
|
1115
|
from AppDocData import AppDocData
|
1116
|
from EngineeringAbstractItem import QEngineeringAbstractItem
|
1117
|
from EngineeringTextItem import QEngineeringTextItem
|
1118
|
from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
|
1119
|
|
1120
|
""" get attributes of item from database """
|
1121
|
docData = AppDocData.instance()
|
1122
|
symbolAttrs = docData.getSymbolAttribute(self.type)
|
1123
|
|
1124
|
_texts = self.texts()
|
1125
|
_symbols = self.symbols()
|
1126
|
for attr in symbolAttrs:
|
1127
|
matches = [_attr for _attr,_ in self.attrs.items() if _attr.UID == attr.UID]
|
1128
|
if matches:
|
1129
|
attr.Freeze = matches[0].Freeze ### update freeze value
|
1130
|
attr.AssocItem = matches[0].AssocItem
|
1131
|
_attrs[attr] = self.attrs[matches[0]] ### copy attribute value
|
1132
|
else:
|
1133
|
_attrs[attr] = ''
|
1134
|
|
1135
|
if attr.Freeze: continue ### do not evalulate value if attribute is frozen
|
1136
|
if attr.AttributeType == 'Size Text Item' or attr.AttributeType == 'Text Item' or attr.AttributeType == 'Valve Oper Code':
|
1137
|
at = int(attr.AttrAt)
|
1138
|
items = [text for text in _texts if QEngineeringAbstractItem.assoc_type(text) == attr.AttributeType]
|
1139
|
if not attr.AssocItem and len(items) > at:
|
1140
|
attr.AssocItem = items[at]
|
1141
|
item = attr.AssocItem
|
1142
|
_attrs[attr] = eval(attr.Expression) if attr.Expression else ''
|
1143
|
elif attr.AssocItem:
|
1144
|
item = attr.AssocItem
|
1145
|
_attrs[attr] = eval(attr.Expression) if attr.Expression else ''
|
1146
|
else:
|
1147
|
_attrs[attr] = ''
|
1148
|
elif attr.AttributeType == 'Symbol Item':
|
1149
|
at = int(attr.AttrAt)
|
1150
|
if not attr.AssocItem and len(_symbols) > at:
|
1151
|
attr.AssocItem = _symbols[at]
|
1152
|
item = attr.AssocItem
|
1153
|
_attrs[attr] = eval(attr.Expression) if attr.Expression else ''
|
1154
|
elif attr.AssocItem:
|
1155
|
item = attr.AssocItem
|
1156
|
_attrs[attr] = eval(attr.Expression) if attr.Expression else ''
|
1157
|
else:
|
1158
|
_attrs[attr] = ''
|
1159
|
|
1160
|
self.attrs = _attrs ### assign self.attrs
|
1161
|
except Exception as ex:
|
1162
|
from App import App
|
1163
|
from AppDocData import MessageType
|
1164
|
|
1165
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
|
1166
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
1167
|
|
1168
|
return self.attrs
|
1169
|
'''
|
1170
|
|
1171
|
'''
|
1172
|
@brief generate xml code
|
1173
|
@author humkyung
|
1174
|
@date 2018.04.23
|
1175
|
@history humkyung 2018.04.25 add angle xml node
|
1176
|
humkyung 2018.04.27 add originalpoint xml node
|
1177
|
humkyung 2018.05.02 add attribute of symbol
|
1178
|
Jeongwoo 2018.05.30 add attribute of symbol (parentSymbol, childSymbol, type, connectionPoint)
|
1179
|
yecheol 2018.07.11 add attribute of symbol (hasInstrumentLabel)
|
1180
|
humkyung 2018.07.20 write owner's uid to xml
|
1181
|
humkyung 2018.07.23 write connected item's uid to xml
|
1182
|
kyouho 2018.07.31
|
1183
|
humkyung 2018.09.06 write area to xml
|
1184
|
'''
|
1185
|
|
1186
|
def toXml(self):
|
1187
|
import uuid
|
1188
|
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
|
1189
|
from EngineeringAbstractItem import QEngineeringAbstractItem
|
1190
|
from EngineeringTextItem import QEngineeringTextItem
|
1191
|
from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
|
1192
|
from SymbolAttr import SymbolAttr
|
1193
|
|
1194
|
try:
|
1195
|
node = Element('SYMBOL')
|
1196
|
uidNode = Element('UID')
|
1197
|
uidNode.text = str(self.uid)
|
1198
|
node.append(uidNode)
|
1199
|
|
1200
|
dbUidNode = Element('DBUID')
|
1201
|
dbUidNode.text = str(self.dbUid)
|
1202
|
node.append(dbUidNode)
|
1203
|
|
1204
|
nameNode = Element('NAME')
|
1205
|
nameNode.text = self.name
|
1206
|
node.append(nameNode)
|
1207
|
|
1208
|
attributeValueNode = Element('ASSOCIATIONS')
|
1209
|
for key, value in self._associations.items():
|
1210
|
for assoc in value:
|
1211
|
assoc_node = Element('ASSOCIATION')
|
1212
|
assoc_node.attrib['TYPE'] = str(key)
|
1213
|
assoc_node.text = str(assoc)
|
1214
|
attributeValueNode.append(assoc_node)
|
1215
|
node.append(attributeValueNode)
|
1216
|
|
1217
|
typeNode = Element('TYPE')
|
1218
|
typeNode.text = self.type
|
1219
|
node.append(typeNode)
|
1220
|
|
1221
|
|
1222
|
ownerNode = Element('OWNER')
|
1223
|
if self.owner is not None:
|
1224
|
ownerNode.text = str(self.owner)
|
1225
|
else:
|
1226
|
ownerNode.text = 'None'
|
1227
|
node.append(ownerNode)
|
1228
|
|
1229
|
|
1230
|
originNode = Element('ORIGINALPOINT')
|
1231
|
originNode.text = '{},{}'.format(self.origin[0], self.origin[1])
|
1232
|
node.append(originNode)
|
1233
|
|
1234
|
connectorsNode = Element('CONNECTORS')
|
1235
|
for connector in self.connectors:
|
1236
|
connectorsNode.append(connector.toXml())
|
1237
|
node.append(connectorsNode)
|
1238
|
|
1239
|
connectionNode = Element('CONNECTIONPOINT')
|
1240
|
connection_point = []
|
1241
|
if self.connectors is not None:
|
1242
|
for connector in self.connectors:
|
1243
|
connection_point.append(repr(connector))
|
1244
|
connectionNode.text = '/'.join(connection_point)
|
1245
|
node.append(connectionNode)
|
1246
|
|
1247
|
locNode = Element('LOCATION')
|
1248
|
locNode.text = '{},{}'.format(self.loc[0], self.loc[1])
|
1249
|
'''
|
1250
|
# calculate symbol's left-top corner
|
1251
|
transform = QTransform()
|
1252
|
transform.translate(self.scenePos().x(), self.scenePos().y())
|
1253
|
transform.rotateRadians(-self.angle)
|
1254
|
loc = transform.map(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
|
1255
|
# up to here
|
1256
|
locNode.text = '{},{}'.format(loc.x() - self.symbolOrigin[0], loc.y() - self.symbolOrigin[1])
|
1257
|
'''
|
1258
|
node.append(locNode)
|
1259
|
|
1260
|
sizeNode = Element('SIZE')
|
1261
|
sizeNode.text = '{},{}'.format(self.size[0], self.size[1])
|
1262
|
node.append(sizeNode)
|
1263
|
|
1264
|
angleNode = Element('ANGLE')
|
1265
|
angleNode.text = str(self.angle)
|
1266
|
node.append(angleNode)
|
1267
|
|
1268
|
parentSymbolNode = Element('PARENT')
|
1269
|
parentSymbolNode.text = str(self.parentSymbol)
|
1270
|
node.append(parentSymbolNode)
|
1271
|
|
1272
|
childSymbolNode = Element('CHILD')
|
1273
|
childSymbolNode.text = str(self.childSymbol)
|
1274
|
node.append(childSymbolNode)
|
1275
|
|
1276
|
hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
|
1277
|
hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
|
1278
|
node.append(hasInstrumentLabelNode)
|
1279
|
|
1280
|
areaNode = Element('AREA')
|
1281
|
areaNode.text = self.area
|
1282
|
node.append(areaNode)
|
1283
|
|
1284
|
flipNode = Element('FLIP')
|
1285
|
flipNode.text = str(self.flip)
|
1286
|
node.append(flipNode)
|
1287
|
|
1288
|
properties_node = Element('PROPERTIES')
|
1289
|
for prop, value in self.properties.items():
|
1290
|
prop_node = prop.toXml()
|
1291
|
prop_node.text = str(value) if value else ''
|
1292
|
properties_node.append(prop_node)
|
1293
|
node.append(properties_node)
|
1294
|
|
1295
|
attributesNode = Element('SYMBOLATTRIBUTES')
|
1296
|
_attrs = self.getAttributes()
|
1297
|
for attr in _attrs:
|
1298
|
if type(attr) is SymbolAttr:
|
1299
|
_node = attr.toXml()
|
1300
|
if attr.AttributeType != 'Spec':
|
1301
|
_node.text = str(_attrs[attr])
|
1302
|
elif attr.AttributeType == 'Spec':
|
1303
|
_node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
|
1304
|
attributesNode.append(_node)
|
1305
|
|
1306
|
node.append(attributesNode)
|
1307
|
|
1308
|
currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
|
1309
|
currentPointModeIndexNode.text = str(self.currentPointModeIndex)
|
1310
|
node.append(currentPointModeIndexNode)
|
1311
|
except Exception as ex:
|
1312
|
from App import App
|
1313
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
1314
|
sys.exc_info()[-1].tb_lineno)
|
1315
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
1316
|
|
1317
|
return None
|
1318
|
|
1319
|
return node
|
1320
|
|
1321
|
@staticmethod
|
1322
|
def from_database(component):
|
1323
|
""" create a item related to given component from database """
|
1324
|
import uuid
|
1325
|
from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
|
1326
|
from EngineeringEndBreakItem import QEngineeringEndBreakItem
|
1327
|
from SymbolAttr import SymbolAttr
|
1328
|
item = None
|
1329
|
|
1330
|
try:
|
1331
|
app_doc_data = AppDocData.instance()
|
1332
|
|
1333
|
uid = component['UID']
|
1334
|
pt = [float(component['X']), float(component['Y'])]
|
1335
|
size = [float(component['Width']), float(component['Height'])]
|
1336
|
|
1337
|
dbUid = int(component['Symbol_UID'])
|
1338
|
dbData = app_doc_data.getSymbolByQuery('UID', dbUid)
|
1339
|
name = dbData.sName
|
1340
|
_type = dbData.sType
|
1341
|
angle = float(component['Rotation'])
|
1342
|
origin = [float(x) for x in component['SceneOriginPoint'].split(',')] if component['SceneOriginPoint'] is not None else pt
|
1343
|
connPts = []
|
1344
|
if component['ConnectionPoint']:
|
1345
|
for conn_pt in component['ConnectionPoint'].split('/'):
|
1346
|
tokens = conn_pt.split(',')
|
1347
|
connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else \
|
1348
|
(tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else \
|
1349
|
(tokens[0], float(tokens[1]), float(tokens[2]), tokens[3]))
|
1350
|
|
1351
|
baseSymbol = dbData.baseSymbol
|
1352
|
|
1353
|
childSymbolNode = component['AdditionalSymbol']
|
1354
|
childSymbol = childSymbolNode if childSymbolNode is not None else ''
|
1355
|
|
1356
|
owner = component['Owner'] if component['Owner'] is not None and component['Owner'] != 'None' else None
|
1357
|
|
1358
|
hasInstrumentLabelNode = component['HasInstrumentLabel']
|
1359
|
hasInstrumentLabel = hasInstrumentLabelNode if hasInstrumentLabelNode is not None else 'False'
|
1360
|
|
1361
|
flipLabelNode = component['Flip']
|
1362
|
flipLabel = int(flipLabelNode) if flipLabelNode is not None else 0
|
1363
|
|
1364
|
project = app_doc_data.getCurrentProject()
|
1365
|
svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
|
1366
|
if os.path.isfile(svgFilePath):
|
1367
|
item = SymbolSvgItem.createItem(_type, svgFilePath, uid, owner=owner, flip=flipLabel)
|
1368
|
item.setVisible(False)
|
1369
|
|
1370
|
|
1371
|
symbolInfo = None
|
1372
|
if dbUid is None:
|
1373
|
symbolInfo = app_doc_data.getSymbolByQuery('name', name)
|
1374
|
else:
|
1375
|
symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
|
1376
|
if symbolInfo:
|
1377
|
childSymbol = symbolInfo.additionalSymbol
|
1378
|
|
1379
|
if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
|
1380
|
hasInstrumentLabel, dbUid=dbUid):
|
1381
|
pass
|
1382
|
else:
|
1383
|
return None
|
1384
|
|
1385
|
for key in item._properties.keys():
|
1386
|
for compo in component.keys():
|
1387
|
if key.Attribute == compo:
|
1388
|
item._properties[key] = key.parse_value(component[key.Attribute]) if component[
|
1389
|
key.Attribute] else ''
|
1390
|
|
1391
|
|
1392
|
areaNode = component['Area']
|
1393
|
if areaNode is None:
|
1394
|
for area in app_doc_data.getAreaList():
|
1395
|
if area.contains(pt):
|
1396
|
item.area = area.name
|
1397
|
break
|
1398
|
else:
|
1399
|
item.area = areaNode
|
1400
|
|
1401
|
|
1402
|
connectors = app_doc_data.get_component_connectors(uid)
|
1403
|
if connectors:
|
1404
|
iterIndex = 0
|
1405
|
for connector in connectors:
|
1406
|
item.connectors[iterIndex].parse_record(connector)
|
1407
|
iterIndex += 1
|
1408
|
|
1409
|
|
1410
|
associations = app_doc_data.get_component_associations(uid)
|
1411
|
if associations:
|
1412
|
for assoc in associations:
|
1413
|
_attrType = assoc['Type']
|
1414
|
if not _attrType in item._associations:
|
1415
|
item._associations[_attrType] = []
|
1416
|
item._associations[_attrType].append(
|
1417
|
uuid.UUID(assoc['Association']) if assoc['Association'] != 'None' else None)
|
1418
|
|
1419
|
|
1420
|
attributes = app_doc_data.get_component_attributes(uid)
|
1421
|
if attributes:
|
1422
|
for attr in attributes:
|
1423
|
_attr = SymbolAttr.from_record(attr)
|
1424
|
if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
|
1425
|
item.attrs[_attr] = attr['Value']
|
1426
|
else:
|
1427
|
if _attr.AttributeType == 'Spec':
|
1428
|
item.attrs[_attr] = [attr['Value'].split(',')[0], attr['Value'].split(',')[1]]
|
1429
|
else:
|
1430
|
item.attrs[_attr] = attr['Value']
|
1431
|
'''
|
1432
|
elif _attr.Attribute == 'UpStream':
|
1433
|
for initKey in item.attrs.keys():
|
1434
|
if initKey.Attribute == 'UpStream':
|
1435
|
item.attrs[initKey] = attr['Value']
|
1436
|
elif _attr.Attribute == 'DownStream':
|
1437
|
for initKey in item.attrs.keys():
|
1438
|
if initKey.Attribute == 'DownStream':
|
1439
|
item.attrs[initKey] = attr['Value']
|
1440
|
'''
|
1441
|
|
1442
|
currentPointModeIndex = component['OriginIndex']
|
1443
|
if currentPointModeIndex is not None:
|
1444
|
item.currentPointModeIndex = int(currentPointModeIndex)
|
1445
|
except Exception as ex:
|
1446
|
from App import App
|
1447
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
1448
|
sys.exc_info()[-1].tb_lineno)
|
1449
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
1450
|
|
1451
|
return None
|
1452
|
|
1453
|
return item
|
1454
|
|
1455
|
'''
|
1456
|
@brief parse xml code
|
1457
|
@author humkyung
|
1458
|
@date 2018.07.20
|
1459
|
@history humkyung 2018.07.20 parse uid from xml node
|
1460
|
humkyung 2018.07.23 parse connected item's uid from xml node
|
1461
|
kyouho 2018.07.31
|
1462
|
humkyung 2018.09.06 assign area to item
|
1463
|
'''
|
1464
|
|
1465
|
@staticmethod
|
1466
|
def fromXml(node):
|
1467
|
import uuid
|
1468
|
from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
|
1469
|
from EngineeringEndBreakItem import QEngineeringEndBreakItem
|
1470
|
from SymbolAttr import SymbolAttr
|
1471
|
item = None
|
1472
|
|
1473
|
try:
|
1474
|
appDocData = AppDocData.instance()
|
1475
|
|
1476
|
uidNode = node.find('UID')
|
1477
|
uid = uidNode.text if uidNode is not None else uuid.uuid4()
|
1478
|
|
1479
|
pt = [float(x) for x in node.find('LOCATION').text.split(',')]
|
1480
|
size = [float(x) for x in node.find('SIZE').text.split(',')]
|
1481
|
|
1482
|
dbUidNode = node.find('DBUID')
|
1483
|
dbUid = int(dbUidNode.text) if dbUidNode is not None else None
|
1484
|
if dbUid:
|
1485
|
dbData = appDocData.getSymbolByQuery('UID', dbUid)
|
1486
|
name = node.find('NAME').text if dbUidNode is None else dbData.sName
|
1487
|
|
1488
|
angle = float(node.find('ANGLE').text)
|
1489
|
_type = node.find('TYPE').text if dbUidNode is None else dbData.sType
|
1490
|
origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
|
1491
|
connPts = []
|
1492
|
if node.find('CONNECTIONPOINT').text is not None:
|
1493
|
for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
|
1494
|
tokens = conn_pt.split(',')
|
1495
|
connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else \
|
1496
|
(tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else \
|
1497
|
(tokens[0], float(tokens[1]), float(tokens[2]), tokens[3]))
|
1498
|
baseSymbol = node.find('PARENT').text if dbUidNode is None else dbData.baseSymbol
|
1499
|
childSymbolNode = node.find('CHILD')
|
1500
|
childSymbol = ''
|
1501
|
if childSymbolNode is not None:
|
1502
|
childSymbol = childSymbolNode.text
|
1503
|
|
1504
|
ownerNode = node.find('OWNER')
|
1505
|
owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
|
1506
|
|
1507
|
hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
|
1508
|
hasInstrumentLabel = hasInstrumentLabelNode.text if hasInstrumentLabelNode is not None else 'False'
|
1509
|
|
1510
|
flipLabelNode = node.find('FLIP')
|
1511
|
flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
|
1512
|
|
1513
|
project = appDocData.getCurrentProject()
|
1514
|
svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
|
1515
|
if os.path.isfile(svgFilePath):
|
1516
|
item = SymbolSvgItem.createItem(_type, svgFilePath, uid, owner=owner, flip=flipLabel)
|
1517
|
item.setVisible(False)
|
1518
|
|
1519
|
|
1520
|
symbolInfo = None
|
1521
|
if dbUid is None:
|
1522
|
symbolInfo = appDocData.getSymbolByQuery('name', name)
|
1523
|
else:
|
1524
|
symbolInfo = appDocData.getSymbolByQuery('UID', dbUid)
|
1525
|
if symbolInfo:
|
1526
|
childSymbol = symbolInfo.additionalSymbol
|
1527
|
|
1528
|
if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
|
1529
|
hasInstrumentLabel, dbUid=dbUid):
|
1530
|
pass
|
1531
|
else:
|
1532
|
return None
|
1533
|
|
1534
|
for prop_node in node.iter('PROPERTY'):
|
1535
|
matches = [prop for prop in item._properties.keys() if
|
1536
|
prop.Attribute == prop_node.attrib['Attribute']]
|
1537
|
if matches:
|
1538
|
item._properties[matches[0]] = matches[0].parse_value(prop_node.text) if prop_node.text else ''
|
1539
|
|
1540
|
|
1541
|
areaNode = node.find('AREA')
|
1542
|
if areaNode is None:
|
1543
|
for area in appDocData.getAreaList():
|
1544
|
if area.contains(pt):
|
1545
|
item.area = area.name
|
1546
|
break
|
1547
|
else:
|
1548
|
item.area = areaNode.text
|
1549
|
|
1550
|
|
1551
|
connectors = node.find('CONNECTORS')
|
1552
|
if connectors is not None:
|
1553
|
iterIndex = 0
|
1554
|
for connector in connectors.iter('CONNECTOR'):
|
1555
|
item.connectors[iterIndex].parse_xml(connector)
|
1556
|
iterIndex += 1
|
1557
|
|
1558
|
|
1559
|
attributeValue = node.find('ASSOCIATIONS')
|
1560
|
if attributeValue is not None:
|
1561
|
for assoc in attributeValue.iter('ASSOCIATION'):
|
1562
|
_attrType = assoc.attrib['TYPE']
|
1563
|
if not _attrType in item._associations:
|
1564
|
item._associations[_attrType] = []
|
1565
|
item._associations[_attrType].append(uuid.UUID(assoc.text) if assoc.text != 'None' else None)
|
1566
|
|
1567
|
|
1568
|
attributes = node.find('SYMBOLATTRIBUTES')
|
1569
|
if attributes is not None:
|
1570
|
|
1571
|
if _type == 'Segment Breaks':
|
1572
|
specBreakAttrs = appDocData.getSymbolAttribute('Segment Breaks')
|
1573
|
|
1574
|
for attr in attributes.iter('ATTRIBUTE'):
|
1575
|
_attr = SymbolAttr.fromXml(attr)
|
1576
|
if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
|
1577
|
item.attrs[_attr] = attr.text
|
1578
|
else:
|
1579
|
|
1580
|
matchAttr = [cAttr for cAttr in specBreakAttrs if _attr.Attribute == cAttr.Attribute]
|
1581
|
matchAttr = matchAttr[0] if matchAttr else None
|
1582
|
if matchAttr:
|
1583
|
_attr.UID = matchAttr.UID
|
1584
|
_attr.DisplayAttribute = matchAttr.DisplayAttribute
|
1585
|
_attr.AttributeType = matchAttr.AttributeType
|
1586
|
_attr.AttrAt = matchAttr.AttrAt
|
1587
|
_attr.Expression = matchAttr.Expression
|
1588
|
_attr.Length = matchAttr.Length
|
1589
|
|
1590
|
if _attr.AttributeType == 'Spec':
|
1591
|
item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
|
1592
|
else:
|
1593
|
|
1594
|
_attr.AssocItem = uuid.UUID(attr.text) if attr.text and attr.text != 'None' else None
|
1595
|
|
1596
|
item.attrs[_attr] = attr.text
|
1597
|
'''
|
1598
|
elif _attr.Attribute == 'UpStream':
|
1599
|
for initKey in item.attrs.keys():
|
1600
|
if initKey.Attribute == 'UpStream':
|
1601
|
item.attrs[initKey] = attr.text
|
1602
|
elif _attr.Attribute == 'DownStream':
|
1603
|
for initKey in item.attrs.keys():
|
1604
|
if initKey.Attribute == 'DownStream':
|
1605
|
item.attrs[initKey] = attr.text
|
1606
|
'''
|
1607
|
|
1608
|
currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
|
1609
|
if currentPointModeIndex is not None:
|
1610
|
item.currentPointModeIndex = int(currentPointModeIndex.text)
|
1611
|
except Exception as ex:
|
1612
|
from App import App
|
1613
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
1614
|
sys.exc_info()[-1].tb_lineno)
|
1615
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
1616
|
|
1617
|
return None
|
1618
|
|
1619
|
return item
|
1620
|
|
1621
|
'''
|
1622
|
@brief create item corresponding to given type
|
1623
|
@author humkyung
|
1624
|
@date 2018.05.02
|
1625
|
@history 2018.05.08 Jeongwoo Change type name (Piping OPC''S → Piping OPC's)
|
1626
|
humkyung 2018.05.10 change symbol's color to blue
|
1627
|
humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
|
1628
|
'''
|
1629
|
|
1630
|
@staticmethod
|
1631
|
def createItem(type, path, uid=None, owner=None, flip=0):
|
1632
|
from QEngineeringOPCItem import QEngineeringOPCItem
|
1633
|
from EngineeringEquipmentItem import QEngineeringEquipmentItem
|
1634
|
from EngineeringInstrumentItem import QEngineeringInstrumentItem
|
1635
|
from EngineeringNozzleItem import QEngineeringNozzleItem
|
1636
|
from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
|
1637
|
from EngineeringReducerItem import QEngineeringReducerItem
|
1638
|
from EngineeringErrorItem import QEngineeringErrorItem
|
1639
|
from EngineeringEndBreakItem import QEngineeringEndBreakItem
|
1640
|
from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
|
1641
|
from AppDocData import AppDocData
|
1642
|
import uuid
|
1643
|
|
1644
|
docData = AppDocData.instance()
|
1645
|
|
1646
|
item = None
|
1647
|
cateogry = docData.getSymbolCategoryByType(type)
|
1648
|
if type == "Piping OPC's":
|
1649
|
item = QEngineeringOPCItem(path, uid, flip=flip)
|
1650
|
elif cateogry == 'Equipment':
|
1651
|
item = QEngineeringEquipmentItem(path, uid, flip=flip)
|
1652
|
elif cateogry == 'Instrumentation':
|
1653
|
item = QEngineeringInstrumentItem(path, uid, flip=flip)
|
1654
|
|
1655
|
|
1656
|
elif type == 'Segment Breaks':
|
1657
|
item = QEngineeringSpecBreakItem(path, uid, flip=flip)
|
1658
|
elif type == 'Reducers':
|
1659
|
item = QEngineeringReducerItem(path, uid, flip=flip)
|
1660
|
elif type == 'Error':
|
1661
|
item = QEngineeringErrorItem(path, uid, flip=flip)
|
1662
|
elif type == 'End Break':
|
1663
|
item = QEngineeringEndBreakItem(path, uid, flip=flip)
|
1664
|
|
1665
|
|
1666
|
else:
|
1667
|
item = SymbolSvgItem(path, uid, flip=flip)
|
1668
|
|
1669
|
if owner is not None:
|
1670
|
item.owner = uuid.UUID(owner)
|
1671
|
|
1672
|
return item
|
1673
|
|
1674
|
'''
|
1675
|
@brief change svg's color
|
1676
|
@author humkyung
|
1677
|
@date 2018.05.10
|
1678
|
@history 2018.05.11 Jeongwoo Override QEngineeringAbstractItem's
|
1679
|
humkyung 2018.05.13 update after change color
|
1680
|
'''
|
1681
|
|
1682
|
def setColor(self, color):
|
1683
|
self.changeAttributes('fill', color)
|
1684
|
self.changeAttributes('stroke', color)
|
1685
|
self.renderer().load(self._document.toByteArray())
|
1686
|
self.update()
|
1687
|
|
1688
|
def getColor(self):
|
1689
|
""" return hover color if mouse is over otherwise reutrn owner's color if owner exist else this color """
|
1690
|
return SymbolSvgItem.HOVER_COLOR if self.hover else (
|
1691
|
self.owner._color if self.owner and hasattr(self.owner, '_color') else self._color)
|
1692
|
|
1693
|
'''
|
1694
|
@brief get attributes from svg file
|
1695
|
@author humkyung
|
1696
|
@date 2019.03.08
|
1697
|
'''
|
1698
|
|
1699
|
def get_attribute(self, attName):
|
1700
|
root = self._document.documentElement()
|
1701
|
node = root.firstChild()
|
1702
|
while not node.isNull():
|
1703
|
if node.isElement():
|
1704
|
element = node.toElement()
|
1705
|
if element.hasAttribute(attName):
|
1706
|
return element.attribute(attName)
|
1707
|
|
1708
|
if element.hasChildNodes():
|
1709
|
att_val = self.recursive_get_attribute(element.firstChild(), attName)
|
1710
|
if att_val is not None: return att_val
|
1711
|
|
1712
|
node = node.nextSibling()
|
1713
|
|
1714
|
return None
|
1715
|
|
1716
|
'''
|
1717
|
@brief get recursively attribute
|
1718
|
@author humkyung
|
1719
|
@date 2019.03.08
|
1720
|
'''
|
1721
|
|
1722
|
def recursive_get_attribute(self, node, attName):
|
1723
|
while not node.isNull():
|
1724
|
if node.isElement():
|
1725
|
element = node.toElement()
|
1726
|
if element.hasAttribute(attName):
|
1727
|
return element.attribute(attName)
|
1728
|
|
1729
|
if node.hasChildNodes():
|
1730
|
att_val = self.recursive_get_attribute(node.firstChild(), attName)
|
1731
|
if att_val is not None: return att_val
|
1732
|
|
1733
|
node = node.nextSibling()
|
1734
|
|
1735
|
return None
|
1736
|
|
1737
|
'''
|
1738
|
@brief change attributes
|
1739
|
@author humkyung
|
1740
|
@date 2018.05.10
|
1741
|
'''
|
1742
|
|
1743
|
def changeAttributes(self, attName, attValue):
|
1744
|
root = self._document.documentElement()
|
1745
|
node = root.firstChild()
|
1746
|
while not node.isNull():
|
1747
|
if node.isElement():
|
1748
|
element = node.toElement()
|
1749
|
if element.hasAttribute(attName):
|
1750
|
element.setAttribute(attName, attValue)
|
1751
|
|
1752
|
if element.hasChildNodes():
|
1753
|
recursiveChangeAttributes(element.firstChild(), attName, attValue)
|
1754
|
|
1755
|
node = node.nextSibling()
|
1756
|
|
1757
|
'''
|
1758
|
@brief change attribute
|
1759
|
@author humkyung
|
1760
|
@date 2018.05.10
|
1761
|
'''
|
1762
|
|
1763
|
def recursiveChangeAttributes(self, node, attName, attValue):
|
1764
|
while not node.isNull():
|
1765
|
if node.isElement():
|
1766
|
element = node.toElement()
|
1767
|
if element.hasAttribute(attName):
|
1768
|
element.setAttribute(attName, attValue)
|
1769
|
|
1770
|
if node.hasChildNodes():
|
1771
|
recursiveChangeAttributes(node.firstChild(), attName, attValue)
|
1772
|
|
1773
|
node = node.nextSibling()
|
1774
|
|
1775
|
'''
|
1776
|
@brief draw rect when item is selected
|
1777
|
@author humkyung
|
1778
|
@date 2018.07.07
|
1779
|
'''
|
1780
|
|
1781
|
def drawFocusRect(self, painter):
|
1782
|
self.focuspen = QPen(Qt.DotLine)
|
1783
|
self.focuspen.setColor(Qt.black)
|
1784
|
self.focuspen.setWidthF(1.5)
|
1785
|
hilightColor = QColor(255, 0, 0, 127)
|
1786
|
painter.setBrush(QBrush(hilightColor))
|
1787
|
painter.setPen(self.focuspen)
|
1788
|
painter.drawRect(self.boundingRect())
|
1789
|
|
1790
|
'''
|
1791
|
@brief override paint(draw connection points)
|
1792
|
@author humkyung
|
1793
|
@date 2018.04.21
|
1794
|
'''
|
1795
|
|
1796
|
def paint(self, painter, options=None, widget=None):
|
1797
|
from EngineeringAbstractItem import QEngineeringAbstractItem
|
1798
|
from EngineeringTextItem import QEngineeringTextItem
|
1799
|
|
1800
|
self.setColor(self.getColor())
|
1801
|
|
1802
|
painter.setClipRect(options.exposedRect)
|
1803
|
QGraphicsSvgItem.paint(self, painter, options, widget)
|
1804
|
for attr in self.attrs:
|
1805
|
if issubclass(type(attr), QEngineeringTextItem):
|
1806
|
color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (
|
1807
|
self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
|
1808
|
attr.setColor(color)
|
1809
|
elif issubclass(type(attr), SymbolSvgItem):
|
1810
|
attr.setColor(self.getColor())
|
1811
|
attr.update()
|
1812
|
|
1813
|
if self.isSelected():
|
1814
|
self.drawFocusRect(painter)
|
1815
|
|
1816
|
'''
|
1817
|
@brief Add Svg Item into ImageViewer's Scene
|
1818
|
@author Jeongwoo
|
1819
|
@date 2018.05.03
|
1820
|
@history add connectors which's parent is symbol
|
1821
|
kyouho 2018.07.30 remove connectors logic
|
1822
|
'''
|
1823
|
|
1824
|
def addSvgItemToScene(self, scene):
|
1825
|
self.rotate(self.getCurrentPoint(), self.angle)
|
1826
|
scene.addItem(self)
|
1827
|
self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
|
1828
|
for conn in self.connectors:
|
1829
|
conn.sceneConnectPoint = (conn.sceneBoundingRect().center().x(), conn.sceneBoundingRect().center().y())
|
1830
|
|
1831
|
'''
|
1832
|
@brief
|
1833
|
@author humkyung
|
1834
|
@date 2018.07.27
|
1835
|
'''
|
1836
|
|
1837
|
def onConnectorPosChaned(self, connector):
|
1838
|
pass
|
1839
|
|
1840
|
'''
|
1841
|
@brief set connector
|
1842
|
@author kyouho
|
1843
|
@date 2018.07.26
|
1844
|
'''
|
1845
|
|
1846
|
def setConnector(self, index):
|
1847
|
connector = QEngineeringConnectorItem(parent=self, index=index)
|
1848
|
connector.setParentItem(self)
|
1849
|
self.connectors.append(connector)
|
1850
|
|
1851
|
'''
|
1852
|
'''
|
1853
|
|
1854
|
def refreshConnector(self):
|
1855
|
for connector in self.connectors:
|
1856
|
connector.buildItem()
|
1857
|
|
1858
|
'''
|
1859
|
@brief Delete svg item
|
1860
|
@author Jeongwoo
|
1861
|
@date 2018.05.25
|
1862
|
'''
|
1863
|
|
1864
|
def deleteSvgItemFromScene(self):
|
1865
|
for connector in self.connectors:
|
1866
|
if connector.connectedItem is not None:
|
1867
|
try:
|
1868
|
connector.connectedItem.removeSymbol(self)
|
1869
|
except Exception as ex:
|
1870
|
from App import App
|
1871
|
from AppDocData import MessageType
|
1872
|
|
1873
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
1874
|
sys.exc_info()[-1].tb_lineno)
|
1875
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
1876
|
break
|
1877
|
|
1878
|
self.transfer.onRemoved.emit(self)
|
1879
|
|
1880
|
'''
|
1881
|
@brief Return real item position
|
1882
|
@author Jeongwoo
|
1883
|
@date 2018.05.25
|
1884
|
'''
|
1885
|
|
1886
|
def boundingRectOnScene(self):
|
1887
|
rect = self.boundingRect()
|
1888
|
rect.moveTo(self.loc[0], self.loc[1])
|
1889
|
return rect
|
1890
|
|
1891
|
def flipSymbol(self):
|
1892
|
'''
|
1893
|
@brief remove item when user press delete key
|
1894
|
@author humkyung
|
1895
|
@date 2018.04.23
|
1896
|
'''
|
1897
|
if self.flip is 0:
|
1898
|
self.flip = 1
|
1899
|
else:
|
1900
|
self.flip = 0
|
1901
|
|
1902
|
currentPoint = self.getCurrentPoint()
|
1903
|
self.rotate(currentPoint, self.angle)
|
1904
|
|
1905
|
'''
|
1906
|
@brief rotate Symbol
|
1907
|
@author kyouho
|
1908
|
@date 2018.07.24
|
1909
|
'''
|
1910
|
|
1911
|
def rotateSymbol(self, angle=None):
|
1912
|
if angle is None:
|
1913
|
|
1914
|
if 0 == self.angle:
|
1915
|
self.angle = 1.57
|
1916
|
|
1917
|
elif (1.57 == self.angle):
|
1918
|
self.angle = 3.14
|
1919
|
|
1920
|
elif 3.14 == self.angle:
|
1921
|
self.angle = 4.71
|
1922
|
|
1923
|
elif 4.71 == self.angle:
|
1924
|
self.angle = 0
|
1925
|
else:
|
1926
|
self.angle = 0
|
1927
|
else:
|
1928
|
self.angle = angle
|
1929
|
|
1930
|
currentPoint = self.getCurrentPoint()
|
1931
|
self.rotate(currentPoint, self.angle)
|
1932
|
|
1933
|
def get_transform(self, pt, angle):
|
1934
|
""" return a transform """
|
1935
|
transform = QTransform()
|
1936
|
transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
|
1937
|
transform.rotateRadians(-angle)
|
1938
|
transform.translate(-pt[0], -pt[1])
|
1939
|
|
1940
|
if self.flip is 1:
|
1941
|
transform.scale(-1.0, 1.0)
|
1942
|
transform.translate(-2 * self.symbolOrigin[0], 0)
|
1943
|
|
1944
|
return transform
|
1945
|
|
1946
|
'''
|
1947
|
@brief resetting rotate Symbol
|
1948
|
@author kyouho
|
1949
|
@date 2018.07.24
|
1950
|
'''
|
1951
|
|
1952
|
def rotate(self, standardPoint, angle):
|
1953
|
self.setTransform(self.get_transform(standardPoint, angle))
|
1954
|
self.setRotation(0)
|
1955
|
|
1956
|
'''
|
1957
|
@brief change Conn point
|
1958
|
@author kyouho
|
1959
|
@date 2018.07.25
|
1960
|
'''
|
1961
|
|
1962
|
def changeConnPoint(self):
|
1963
|
if len(self.connectors) == 2:
|
1964
|
conn1Item = self.connectors[0].connectedItem
|
1965
|
conn2Item = self.connectors[1].connectedItem
|
1966
|
self.connectors[0].connectedItem = conn2Item
|
1967
|
self.connectors[1].connectedItem = conn1Item
|
1968
|
|
1969
|
currentPoint = self.getCurrentPoint()
|
1970
|
self.reSettingSymbol(currentPoint, self.angle)
|
1971
|
|
1972
|
'''
|
1973
|
@brief change standard point
|
1974
|
@author kyouho
|
1975
|
@date 2018.07.24
|
1976
|
'''
|
1977
|
|
1978
|
def changeStandardPoint(self):
|
1979
|
connPtsCount = len(self.connectors)
|
1980
|
|
1981
|
if self.currentPointModeIndex < connPtsCount:
|
1982
|
self.currentPointModeIndex += 1
|
1983
|
else:
|
1984
|
self.currentPointModeIndex = 0
|
1985
|
|
1986
|
currentPoint = self.getCurrentPoint()
|
1987
|
self.reSettingSymbol(currentPoint, self.angle)
|
1988
|
|
1989
|
'''
|
1990
|
@brief get standard point
|
1991
|
@author kyouho
|
1992
|
@date 2018.07.25
|
1993
|
'''
|
1994
|
|
1995
|
def getCurrentPoint(self):
|
1996
|
pointList = []
|
1997
|
pointList.append(self.symbolOrigin)
|
1998
|
for connector in self.connectors:
|
1999
|
pointList.append(connector.connectPoint)
|
2000
|
|
2001
|
return pointList[self.currentPointModeIndex]
|
2002
|
|
2003
|
'''
|
2004
|
@brief 심볼 회전 시 서로 다른 기준점으로 회전하기 때문에 기준점을 이후 개발한 SymbolSvgItem 기준의 회전좌표로 이동하기 위해서 만듬 (loc 기준으로 회전해야함)
|
2005
|
@author kyouho
|
2006
|
@date 18.08.06
|
2007
|
'''
|
2008
|
|
2009
|
def reCalculationRotatedItem(self):
|
2010
|
goPoint = self.get_transform(self.getCurrentPoint(), self.angle).map(
|
2011
|
QPoint(self.symbolOrigin[0], self.symbolOrigin[1]))
|
2012
|
self.loc = [self.loc[0] + self.origin[0] - goPoint.x(), self.loc[1] + self.origin[1] - goPoint.y()]
|
2013
|
|
2014
|
|
2015
|
def recursiveChangeAttributes(node, attName, attValue):
|
2016
|
while not node.isNull():
|
2017
|
if node.isElement():
|
2018
|
element = node.toElement()
|
2019
|
if element.hasAttribute(attName):
|
2020
|
element.setAttribute(attName, attValue)
|
2021
|
|
2022
|
if node.hasChildNodes():
|
2023
|
recursiveChangeAttributes(node.firstChild(), attName, attValue)
|
2024
|
|
2025
|
node = node.nextSibling()
|
2026
|
|
2027
|
|
2028
|
'''
|
2029
|
@brief The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
|
2030
|
@author Jeongwoo
|
2031
|
@date 2018.06.18
|
2032
|
'''
|
2033
|
|
2034
|
|
2035
|
class Transfer(QObject):
|
2036
|
on_pos_changed = pyqtSignal(QGraphicsItem)
|
2037
|
onRemoved = pyqtSignal(QGraphicsItem)
|
2038
|
|
2039
|
def __init__(self, parent=None):
|
2040
|
QObject.__init__(self, parent)
|
2041
|
|
2042
|
|
2043
|
if __name__ == '__main__':
|
2044
|
f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
|
2045
|
f.open(QIODevice.ReadOnly)
|
2046
|
array = f.readAll()
|
2047
|
document = QDomDocument()
|
2048
|
document.setContent(array)
|
2049
|
|
2050
|
root = document.documentElement()
|
2051
|
node = root.firstChild()
|
2052
|
while not node.isNull():
|
2053
|
if node.isElement():
|
2054
|
element = node.toElement()
|
2055
|
if element.hasAttribute('fill'):
|
2056
|
element.setAttribute('fill', '#FFFFF')
|
2057
|
|
2058
|
if element.hasChildNodes():
|
2059
|
recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
|
2060
|
|
2061
|
node = node.nextSibling()
|