프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ 948eb153

이력 | 보기 | 이력해설 | 다운로드 (81 KB)

1
#!/usr/bin/env/python3
2
# coding: utf-8
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  # symbol UID
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
        # attributeType uid
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 = [[], []] # 0 : in, 1 : out
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]  # assign item
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:  ### Main Size
218
                                if item.MainSubSize: return item.MainSubSize[0]
219
                            elif item.connectors[1].connectedItem is prev:  ### Sub Size
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:  ### Main Size
230
                                if item.MainSubSize: return item.MainSubSize[0]
231
                            elif item.connectors[1].connectedItem is prev:  ### Sub Size
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
            # validate connectors
268
            for connector in self.connectors:
269
                errors.extend(connector.validate())
270

    
271
            # check if connected two lines has same direction
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
            # check disconnected point
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
            # check if symbol size if 0
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
            # check if association item's owner exists
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
            # set error position
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
                # for duplicattion check
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
                    # check if two items are connected each other
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
            # check duplicated connection
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
            # when first recognition step, symbols are not in scene(not yet added) therefore cannot use scenebounding rect
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
        # save connectors to database
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
                    (  # str(connector.uid),
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
        # up to here
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
            # setting connectors
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
        # from EngineeringLineItem import QEngineeringLineItem
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
            # get origin point of symbol
916
            origin = self.origin  # self.sceneBoundingRect().center()
917
            # up to here
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
                # scene = self.scene()
1009
                # scene.removeItem(self)
1010
                # self.addSvgItemToScene(scene)
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:  ### translate up/down/left/right symbol
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
                # size text and operation code text will find onwer themselves in findowner method
1069
                if False:  # type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
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
            # write owner's uid to xml
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
            # up to here
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
                # if additional symbol was changed, change symbol info
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
                ## assign area
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
                ## up to here
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
                # get associations 
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
                # up to here
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()  # generate UUID
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
                # if additional symbol was changed, change symbol info
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
                ## assign area
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
                ## up to here
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
                # get associations 
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
                # up to here
1567

    
1568
                attributes = node.find('SYMBOLATTRIBUTES')
1569
                if attributes is not None:
1570
                    ## for old spec break item that has not uid currectly, may not necessary new data
1571
                    if _type == 'Segment Breaks':
1572
                        specBreakAttrs = appDocData.getSymbolAttribute('Segment Breaks')
1573
                    ## up to here 1
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
                            ## for old spec break item that has not uid currectly, may not necessary new data
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
                            ## up to here 2
1590
                            if _attr.AttributeType == 'Spec':
1591
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
1592
                            else:
1593
                                ## for old spec break item that has not uid currectly, may not necessary new data
1594
                                _attr.AssocItem = uuid.UUID(attr.text) if attr.text and attr.text != 'None' else None
1595
                                ## up to here 3
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
        # elif type == 'Nozzles':
1655
        #    item = QEngineeringNozzleItem(path, uid, flip=flip)
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
        # elif type == 'Flow Mark':
1665
        #    item = QEngineeringFlowMarkItem(path, uid, flip=flip)
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
            # degree 0
1914
            if 0 == self.angle:
1915
                self.angle = 1.57
1916
            # degree 90
1917
            elif (1.57 == self.angle):
1918
                self.angle = 3.14
1919
            # degree 180
1920
            elif 3.14 == self.angle:
1921
                self.angle = 4.71
1922
            # degree 270
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)  # force to emit itemChange signal
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()