프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ e7e6914b

이력 | 보기 | 이력해설 | 다운로드 (83.8 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, name, 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 = name
49
        self.type = ''
50
        self.iType = -2
51
        self.angle = 0
52
        self.origin = None
53
        self.loc = None
54
        self.size = None
55
        self._owner = None
56
        self.parentSymbol = ''
57
        self.childSymbol = ''
58
        self.hasInstrumentLabel = 0
59
        self.flip = flip
60
        self.hit_ratio = None
61
        # attributeType uid
62
        self.attribute = ''
63
        self._properties = {SymbolProp(None, 'Supplied By', 'String'): None}
64

    
65
        self.setAcceptDrops(True)
66
        self.setAcceptHoverEvents(True)
67
        self.setAcceptedMouseButtons(Qt.LeftButton)
68
        self.setAcceptTouchEvents(True)
69

    
70
        self.currentCursor = 0
71
        self.transfer = Transfer()
72

    
73
        self._angle = 0
74

    
75
        self.in_out_connector = [[], []] # 0 : in, 1 : out
76
        self.break_connector = []
77
        self.conn_type = []
78

    
79
        try:
80
            app_doc_data = AppDocData.instance()
81
            svg = None
82
            if self.name:
83
                _, svg = app_doc_data.read_symbol_shape(self.name)
84

    
85
            if not svg and path and os.path.isfile(path):
86
                f = QFile(path)
87
                f.open(QIODevice.ReadOnly)
88
                svg = f.readAll()
89
                f.close()
90

    
91
            self._document = QDomDocument()
92
            self._document.setContent(svg)
93
            self._renderer = QSvgRenderer(self._document.toByteArray())
94
            self.setSharedRenderer(self._renderer)
95

    
96
            self._color = self.get_attribute('fill')
97
        except Exception as ex:
98
            from App import App
99

    
100
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
101
                                                           sys.exc_info()[-1].tb_lineno)
102
            App.mainWnd().addMessage.emit(MessageType.Error, message)
103

    
104
        self.setZValue(SymbolSvgItem.ZVALUE)
105

    
106
        app_doc_data = AppDocData.instance()
107
        configs = app_doc_data.getConfigs('Symbol Style', 'Opacity')
108
        self.setOpacity(float(configs[0].value) / 100 if configs else 0.5)
109

    
110
    def has_in_out_connector(self):
111
        """ return True if item has in or out connector """
112
        if len(self.in_out_connector[0]) > 0 and len(self.in_out_connector[1]) > 0:
113
            return True
114
        else:
115
            return False
116

    
117
    def has_break_connector(self):
118
        """ return True if item has break connector """
119
        if len(self.break_connector) > 0:
120
            return True
121
        else:
122
            return False
123

    
124
    def __str__(self):
125
        """ return string represent uuid """
126
        return str(self.uid)
127

    
128
    '''
129
        @breif  getter owner
130
        @author humkyung
131
        @date   2018.05.10
132
    '''
133

    
134
    @property
135
    def owner(self):
136
        import uuid
137

    
138
        if self._owner and type(self._owner) is uuid.UUID:
139
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
140
            if matches: self._owner = matches[0]
141

    
142
        if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
143
            return self._owner
144
        else:
145
            self._owner = None
146
            return None
147

    
148
    '''
149
        @brief  setter owner
150
        @author humkyung
151
        @date   2018.05.10
152
        @history    2018.05.17  Jeongwoo    Add Calling setColor if self._owner is None or not
153
    '''
154

    
155
    @owner.setter
156
    def owner(self, value):
157
        self._owner = value
158

    
159
        if self._owner is None:
160
            self._color = self.DEFAULT_COLOR
161
        self.setColor(self._color)
162

    
163
    '''
164
    @property
165
    def properties(self):
166
        """ getter of properties """
167
        import uuid
168

169
        for prop, value in self._properties.items():
170
            try:
171
                if prop.is_selectable and type(value) is uuid.UUID and self.scene():
172
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(value)]
173
                    if matches: self._properties[prop] = matches[0]
174

175
                if prop.Expression:
176
                    item = self._properties[prop]  # assign item
177
                    self._properties[prop] = eval(prop.Expression)
178
            except Exception as ex:
179
                from App import App
180

181
                message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
182
                                                              sys.exc_info()[-1].tb_lineno)
183
                App.mainWnd().addMessage.emit(MessageType.Error, message)
184

185
        return self._properties
186

187
    def set_property(self, property, value):
188
        """ set property with given value """
189
        if issubclass(type(value), QEngineeringAbstractItem): self.add_assoc_item(value, 0)
190
        matches = [prop for prop, _ in self._properties.items() if prop.Attribute == property]
191
        if matches: self._properties[matches[0]] = value
192

193
    def prop(self, name):
194
        """ return the value of given property with name """
195
        matches = [(prop, value) for prop, value in self.properties.items() if prop.Attribute == name]
196
        if matches: return matches[0][1]
197

198
        return None
199
    '''
200

    
201
    @property
202
    def Size(self):
203
        """ return valve's size """
204
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
205

    
206
        matches = [assoc for assoc in self.associations() if type(assoc) is QEngineeringSizeTextItem]
207
        if matches:
208
            return matches[0].text()
209
        else:
210
            return None
211

    
212
    @property
213
    def EvaluatedSize(self):
214
        from EngineeringReducerItem import QEngineeringReducerItem
215
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
216

    
217
        try:
218
            if self.Size: return self.Size
219
            if self.owner and issubclass(type(self.owner), QEngineeringLineNoTextItem):
220
                matches = [run for run in self.owner.runs if self in run.items]
221
                if matches:
222
                    at = matches[0].items.index(self)
223
                    upstream = matches[0].items[:at]
224
                    upstream.reverse()
225
                    prev = self
226
                    for item in upstream:
227
                        if type(item) is QEngineeringReducerItem:
228
                            if item.connectors[0].connectedItem is prev:  ### Main Size
229
                                if item.MainSubSize: return item.MainSubSize[0]
230
                            elif item.connectors[1].connectedItem is prev:  ### Sub Size
231
                                if item.MainSubSize: return item.MainSubSize[1]
232
                        else:
233
                            if item.Size: return item.Size
234
                        prev = item
235

    
236
                    downstream = matches[0].items[at:]
237
                    prev = self
238
                    for item in downstream:
239
                        if type(item) is QEngineeringReducerItem:
240
                            if item.connectors[0].connectedItem is prev:  ### Main Size
241
                                if item.MainSubSize: return item.MainSubSize[0]
242
                            elif item.connectors[1].connectedItem is prev:  ### Sub Size
243
                                if item.MainSubSize: return item.MainSubSize[1]
244
                        else:
245
                            if item.Size: return item.Size
246
                        prev = item
247

    
248
                    if 'Drain' == matches[0].Type: return AppDocData.instance().drain_size
249

    
250
                return self.owner.Size
251

    
252
            return None
253
        except Exception as ex:
254
            from App import App
255
            from AppDocData import MessageType
256

    
257
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
258
                                                          sys.exc_info()[-1].tb_lineno)
259
            App.mainWnd().addMessage.emit(MessageType.Error, str(self.uid) + self.name + message)
260

    
261
    def validate(self):
262
        '''
263
            @brief  validation check
264
            @author euisung
265
            @date   2019.04.16
266
        '''
267
        from EngineeringAbstractItem import QEngineeringAbstractItem
268
        from EngineeringLineItem import QEngineeringLineItem
269
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
270
        from EngineeringTextItem import QEngineeringTextItem
271
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
272
        errors = []
273

    
274
        try:
275
            docdata = AppDocData.instance()
276
            dataPath = docdata.getErrorItemSvgPath()
277

    
278
            # validate connectors
279
            for connector in self.connectors:
280
                errors.extend(connector.validate())
281

    
282
            # check if connected two lines has same direction
283
            if len(self.connectors) is 2:
284
                if (type(self.connectors[0].connectedItem) is QEngineeringLineItem and self.connectors[
285
                    0]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT) and \
286
                        (type(self.connectors[1].connectedItem) is QEngineeringLineItem and self.connectors[
287
                            1]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT):
288
                    indices = [0, 0]
289

    
290
                    center = self.connectors[0].center()
291
                    dx, dy = [0, 0], [0, 0]
292
                    dx[0] = center[0] - self.connectors[0].connectedItem.line().p1().x()
293
                    dy[0] = center[1] - self.connectors[0].connectedItem.line().p1().y()
294
                    dx[1] = center[0] - self.connectors[0].connectedItem.line().p2().x()
295
                    dy[1] = center[1] - self.connectors[0].connectedItem.line().p2().y()
296
                    indices[0] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
297

    
298
                    center = self.connectors[1].center()
299
                    dx[0] = center[0] - self.connectors[1].connectedItem.line().p1().x()
300
                    dy[0] = center[1] - self.connectors[1].connectedItem.line().p1().y()
301
                    dx[1] = center[0] - self.connectors[1].connectedItem.line().p2().x()
302
                    dy[1] = center[1] - self.connectors[1].connectedItem.line().p2().y()
303
                    indices[1] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
304

    
305
                    if indices[0] == indices[1]:
306
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
307
                        error.parent = self
308
                        error.msg = self.tr('flow direction error')
309
                        error.setToolTip(error.msg)
310
                        error.area = 'Drawing'
311
                        error.name = 'Error'
312
                        errors.append(error)
313

    
314
            # check disconnected point
315
            disconnect = False
316
            if len(self.connectors) is not 0:
317
                disconnect = True
318
                for connector in self.connectors:
319
                    if connector.connectedItem is not None:
320
                        disconnect = False
321
                        break
322

    
323
            if disconnect:
324
                error = SymbolSvgItem.createItem('Error', None, dataPath)
325
                error.parent = self
326
                error.msg = 'disconnected'
327
                error.setToolTip(error.msg)
328
                error.area = 'Drawing'
329
                error.name = 'Error'
330
                errors.append(error)
331

    
332
            # check if symbol size if 0
333
            if self.size[0] == 0 or self.size[1] == 0:
334
                error = SymbolSvgItem.createItem('Error', None, dataPath)
335
                error.parent = self
336
                error.msg = self.tr('size error')
337
                error.setToolTip(error.msg)
338
                error.area = 'Drawing'
339
                error.name = 'Error'
340
                errors.append(error)
341

    
342
            # check if association item's owner exists
343
            for assoc in self.associations():
344
                if issubclass(type(assoc), QEngineeringTextItem) and not assoc.owner:
345
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
346
                    error.parent = self
347
                    error.msg = self.tr('association error')
348
                    error.setToolTip(error.msg)
349
                    error.area = self.area
350
                    error.name = 'Error'
351
                    errors.append(error)
352

    
353
            # set error position
354
            for error in errors:
355
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
356

    
357
            connectedUid = []
358
            for connector in self.connectors:
359
                # for duplicattion check
360
                if connector.connectedItem and issubclass(type(connector.connectedItem), QEngineeringAbstractItem):
361
                    connectedUid.append(str(connector.connectedItem.uid))
362

    
363
                if (issubclass(type(connector.connectedItem), SymbolSvgItem) and \
364
                        type(connector.connectedItem) is not QEngineeringEquipmentItem) or \
365
                                type(connector.connectedItem) is QEngineeringLineItem:
366
                    matches = [conn for conn in connector.connectedItem.connectors if conn.connectedItem is self]
367
                    # check if two items are connected each other
368
                    if not matches:
369
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
370
                        error.setPosition(connector.center())
371
                        error.parent = self
372
                        error.msg = self.tr('disconnected from opposite side')
373
                        error.setToolTip(error.msg)
374
                        error.area = self.area
375
                        error.name = 'Error'
376
                        errors.append(error)
377

    
378
            # check duplicated connection
379
            if len(connectedUid) is not len(set(connectedUid)):
380
                error = SymbolSvgItem.createItem('Error', None, dataPath)
381
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
382
                error.parent = self
383
                error.msg = self.tr('duplicated connection')
384
                error.setToolTip(error.msg)
385
                error.area = self.area
386
                error.name = 'Error'
387
                errors.append(error)
388
        except Exception as ex:
389
            from App import App
390
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
391
                                                          sys.exc_info()[-1].tb_lineno)
392
            App.mainWnd().addMessage.emit(MessageType.Error, message)
393

    
394
        return errors
395

    
396
    def includes(self, pt, margin=0):
397
        """return True if symbol contains given point else return False"""
398
        rect = self.sceneBoundingRect()
399
        allowed_error = 0.1
400

    
401
        if abs(rect.x() - 0) <= allowed_error and abs(rect.y() - 0) <= allowed_error:
402
            # when first recognition step, symbols are not in scene(not yet added) therefore cannot use scenebounding rect
403
            minX = self.loc[0] - margin
404
            minY = self.loc[1] - margin
405
            maxX = minX + self.size[0] + margin
406
            maxY = minY + self.size[1] + margin
407
        else:
408
            minX = rect.x() - margin
409
            minY = rect.y() - margin
410
            maxX = minX + rect.width() + margin
411
            maxY = minY + rect.height() + margin
412

    
413
        return True if (pt[0] >= minX and pt[0] <= maxX and pt[1] >= minY and pt[1] <= maxY) else False
414

    
415
    '''
416
    def associations(self):
417
        """ return associated instance """
418
        # move to abstractitem
419
        import uuid
420

421
        res = []
422
        for key in self._associations.keys():
423
            index = 0
424
            for assoc in self._associations[key]:
425
                # find owner with uid
426
                if assoc and type(assoc) is uuid.UUID:
427
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(assoc)]
428
                    if matches:
429
                        res.append(matches[0]) # TODO: need to update association with instance
430
                        self._associations[key][index] = matches[0]
431
                    index += 1
432
                # up to here
433
                elif assoc:
434
                    res.append(assoc)
435

436
        for key in self.attrs.keys():
437
            if type(key.AssocItem) is uuid.UUID:
438
                for assoc in res:
439
                    if str(key.AssocItem) == str(assoc.uid):
440
                        key.AssocItem = assoc
441

442
        return res
443

444
    def texts(self):
445
        """ return text type of associations """
446
        from EngineeringTextItem import QEngineeringTextItem
447

448
        res = []
449
        for text in [x for x in self.associations() if issubclass(type(x), QEngineeringTextItem)]:
450
            consumed = False
451
            for key in list(self.attrs.keys()):
452
                if key.AssocItem and key.AssocItem is text:
453
                    consumed = True
454
            if not consumed:
455
                res.append(text)
456

457
        return res
458

459
    def symbols(self):
460
        """ return symbol type of associations """
461
        res = []
462
        for symbol in [x for x in self.associations() if issubclass(type(x), SymbolSvgItem)]:
463
            consumed = False
464
            for key in list(self.attrs.keys()):
465
                if key.AssocItem and key.AssocItem is symbol:
466
                    consumed = True
467
            if not consumed:
468
                res.append(symbol)
469

470
        return res
471
    '''
472

    
473
    def itemChange(self, change, value):
474
        """ call signals when item's position or rotation is changed """
475
        if not self.scene(): return super().itemChange(change, value)
476

    
477
        if change == QGraphicsItem.ItemPositionHasChanged or change == QGraphicsItem.ItemRotationChange:
478
            for conn in self.connectors:
479
                conn.sceneConnectPoint = (conn.sceneBoundingRect().center().x(), conn.sceneBoundingRect().center().y())
480

    
481
            from EngineeringLineItem import QEngineeringLineItem
482
            for connector in self.connectors:
483
                if connector.connectedItem is not None and type(connector.connectedItem) == QEngineeringLineItem:
484
                    line = connector.connectedItem
485
                    line.reDrawLine(self, connector.center())
486
                    line.update_arrow()
487

    
488
            self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(
489
                self.sceneBoundingRect().height())
490

    
491
            self.scene().contents_changed.emit()
492

    
493
            return value
494

    
495
        return super().itemChange(change, value)
496

    
497
    def toSql_Components(self):
498
        """ convert Components data to sql query """
499
        from AppDocData import AppDocData
500

    
501
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner',
502
                'Connected', '[Supplied By]', \
503
                'SpecialItemTypes_UID', 'OriginIndex', '[From]', '[To]', '[Freeze]', '[Connected Item]', '[Flip]', 'SceneOriginPoint']
504
        values = ['?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?']
505
        param = [(str(self.uid), str(AppDocData.instance().activeDrawing.UID), self.dbUid, self.loc[0], self.loc[1],
506
                  self.size[0], self.size[1], self.angle,
507
                  self.area, str(self.owner) if self.owner else None, \
508
                  str(self.conns[0]) if self.conns else None, \
509
                  self.prop('Supplied By'), \
510
                  str(self.special_item_type) if self.special_item_type else None, \
511
                  self.currentPointModeIndex, \
512
                  None, \
513
                  None, \
514
                  self.prop('Freeze') if self.prop('Freeze') else 0, \
515
                  str(self.prop('Connected Item')) if self.prop('Connected Item') else None, \
516
                  self.flip, \
517
                  '{},{}'.format(self.origin[0], self.origin[1]))]
518
        sql = 'insert into Components({}) values({})'.format(','.join(cols), ','.join(values))
519

    
520
        return (sql, tuple(param))
521

    
522
    def toSql_return_separately(self):
523
        """ convert valve data to sql query """
524
        import uuid
525

    
526
        res = []
527
        resLater = []
528

    
529
        res.append(self.toSql_Components())
530

    
531
        _attrs = self.getAttributes()
532
        if _attrs:
533
            cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value', 'Association_UID', 'Freeze']
534
            values = ['?', '?', '?', '?', '?', '?']
535
            params = []
536
            for key in _attrs.keys():
537
                if key.AttributeType != 'Spec':
538
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), str(_attrs[key]), str(key.AssocItem),
539
                                   str(key.Freeze)))
540
                elif key.AttributeType == 'Spec':
541
                    if type(_attrs[key]) is not list: continue
542
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), (str(_attrs[key][0]) + ',' + str(_attrs[key][1])), str(key.AssocItem),
543
                                   str(key.Freeze)))
544
            sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
545
            res.append((sql, tuple(params)))
546

    
547
        if self.associations():
548
            cols = ['UID', '[Type]', 'Components_UID', 'Association']
549
            values = ['?', '?', '?', '?']
550
            params = []
551
            for assoc in self.associations():
552
                params.append(
553
                    (str(uuid.uuid4()), QEngineeringAbstractItem.assoc_type(assoc), str(self.uid), str(assoc.uid)))
554
            sql = 'insert into Associations({}) values({})'.format(','.join(cols), ','.join(values))
555
            resLater.append((sql, tuple(params)))
556

    
557
        # save connectors to database
558
        if self.connectors:
559
            cols = ['Components_UID', '[Index]', 'X', 'Y', 'Connected', 'Connected_At']
560
            values = ['?', '?', '?', '?', '?', '?']
561
            params = []
562
            index = 1
563
            for connector in self.connectors:
564
                params.append( \
565
                    (  # str(connector.uid),
566
                        str(self.uid), index, connector.sceneConnectPoint[0], connector.sceneConnectPoint[1], \
567
                        str(connector.connectedItem.uid) if connector.connectedItem else None, \
568
                        str(connector._connected_at)) \
569
                    )
570
                index += 1
571
            sql = 'insert into Points({}) values({})'.format(','.join(cols), ','.join(values))
572
            resLater.append((sql, tuple(params)))
573
        # up to here
574

    
575
        return res, resLater
576

    
577
    '''
578
        @brief  build symbol item
579
        @author humkyung
580
        @date   2018.05.02
581
        @history    2018.05.09  Jeongwoo    Clear self.connectors
582
                    2018.05.30  Jeongwoo    Add parameters (parentSymbol, childSymbol)
583
    '''
584

    
585
    def buildItem(self, name, _type, angle, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel,
586
                  dbUid=None):
587
        from SpecialItemTypesDialog import SpecialItemTypes
588

    
589
        try:
590
            docData = AppDocData.instance()
591
            self.name = name
592
            self.type = _type
593
            self.angle = angle
594
            self.loc = loc
595
            self.size = size if size else [0, 0]
596
            self.origin = origin
597
            if dbUid is None:
598
                symbolInfo = docData.getSymbolByQuery('name', name)
599
            else:
600
                symbolInfo = docData.getSymbolByQuery('UID', dbUid)
601
            self.dbUid = symbolInfo.uid
602
            self.iType = symbolInfo.iType
603
            originalPoint = symbolInfo.getOriginalPoint().split(',')
604
            self.symbolOrigin = [float(originalPoint[0]), float(originalPoint[1])]
605

    
606
            # setting connectors
607
            connectionPoints = symbolInfo.getConnectionPoint().split('/')
608
            for index in range(len(connectionPoints)):
609
                if connectionPoints[index] == '':
610
                    break
611
                tokens = connectionPoints[index].split(',')
612

    
613
                direction = 'AUTO'
614
                symbol_idx = '0'
615
                if len(tokens) == 2:
616
                    x = float(tokens[0])
617
                    y = float(tokens[1])
618
                elif len(tokens) >= 3:
619
                    direction = connPts[index][0]
620
                    x = float(tokens[1])
621
                    y = float(tokens[2])
622
                if len(tokens) >= 4:
623
                    symbol_idx = tokens[3]
624
                if len(tokens) >= 6:
625
                    if tokens[4] == 'In':
626
                        self.in_out_connector[0].append(index)
627
                    elif tokens[4] == 'Out':
628
                        self.in_out_connector[1].append(index)
629
                    if tokens[5] == 'O':
630
                        self.break_connector.append(index)
631
                if len(tokens) >= 7:
632
                    self.conn_type.append(tokens[6])
633

    
634
                self.setConnector(index + 1)
635
                self.connectors[index].direction = direction
636
                self.connectors[index].symbol_idx = symbol_idx
637
                self.connectors[index].setPos((x, y))
638
                self.connectors[index].connectPoint = (x, y)
639
                self.connectors[index].sceneConnectPoint = (connPts[index][0], connPts[index][1]) if \
640
                    len(connPts[index]) == 2 else (connPts[index][1], connPts[index][2]) if \
641
                    len(connPts[index]) == 3 else (connPts[index][1], connPts[index][2]) if \
642
                    len(connPts[index]) == 4 else None
643
            self.parentSymbol = parentSymbol
644
            self.childSymbol = childSymbol
645
            self.hasInstrumentLabel = hasInstrumentLabel
646
            self.currentPointModeIndex = 0
647
            self.special_item_type = SpecialItemTypes.instance().find_match_exactly(self)
648

    
649
            tooltip = f"<b>{str(self.uid)}</b><br>{self.type}={self.name}"
650
            if self.hit_ratio:
651
                tooltip += f"<br><li>recognition ratio={self.hit_ratio}"
652
            self.setToolTip(tooltip)
653

    
654
            return True
655
        except Exception as ex:
656
            from App import App
657

    
658
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
659
                                                          sys.exc_info()[-1].tb_lineno)
660
            App.mainWnd().addMessage.emit(MessageType.Error, message)
661

    
662
            return False
663

    
664
    '''
665
        @brief  return bounding box of symbol
666
        @author humkyung
667
        @date   2018.04.08
668
    '''
669

    
670
    def rect(self):
671
        return self.sceneBoundingRect()
672

    
673
    '''
674
        @brief  return true if line is able to connect symbol
675
        @author humkyung
676
        @date   2018.04.13
677
    '''
678

    
679
    def is_connectable(self, item, toler=10):
680
        # from EngineeringLineItem import QEngineeringLineItem
681

    
682
        '''
683
        if False:#type(item) is QEngineeringLineItem:
684
            line = item
685
            start_pt = line.startPoint()
686
            end_pt = line.endPoint()
687
            for connector in self.connectors:
688
                dx = connector.sceneConnectPoint[0] - (start_pt[0])
689
                dy = connector.sceneConnectPoint[1] - (start_pt[1])
690
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
691
                dx = connector.sceneConnectPoint[0] - (end_pt[0])
692
                dy = connector.sceneConnectPoint[1] - (end_pt[1])
693
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
694
        elif True:#issubclass(type(item), SymbolSvgItem):
695
        '''
696
        for connector in self.connectors:
697
            for iConnector in item.connectors:
698
                dx = connector.sceneConnectPoint[0] - iConnector.sceneConnectPoint[0]
699
                dy = connector.sceneConnectPoint[1] - iConnector.sceneConnectPoint[1]
700
                if (math.sqrt(dx * dx + dy * dy) < toler): return True
701

    
702
        return False
703

    
704
    '''
705
        @author     humkyung
706
        @date       2018.07.03
707
    '''
708

    
709
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
710
        """ check if given item is connected to self """
711

    
712
        _connectors = [connector for connector in self.connectors if
713
                       (connector.connectedItem == item and (connector._connected_at == at if at else True))]
714
        return len(_connectors) > 0
715

    
716
    def next_connected(self, lhs, rhs):
717
        """ check given two item's are next connected(ex: 0-1, 2-3) """
718

    
719
        lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
720
        rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
721
        if lhs_matches and rhs_matches:
722
            return (lhs_matches[0] in [0, 1] and rhs_matches[0] in [0, 1]) or (
723
                    lhs_matches[0] in [2, 3] and rhs_matches[0] in [2, 3])
724

    
725
        return False
726

    
727
    def canBeSecondary(self, line):
728
        """ check given line is not connected(ex: 0-1, 2-3) """
729
        preItem = None
730

    
731
        item = [item.connectedItem for item in self.connectors if
732
                item.connectedItem is not None and item.connectedItem.owner is not None]
733
        if item:
734
            preItem = item[0]
735

    
736
        if preItem is not None and not self.next_connected(line, preItem):
737
            return True
738
        else:
739
            return False
740

    
741
    '''
742
        @brief      connect line and symbol is able to be connected and return line
743
        @author     humkyung
744
        @date       2018.04.16
745
        @history    humkyung 2018.05.08 check if symbol is possible to be connected
746
                    Jeongwoo 2018.05.15 Connect each symbol and line
747
    '''
748

    
749
    def connect_if_possible(self, obj, toler=10):
750
        """ this method not update item's position """
751

    
752
        from shapely.geometry import Point
753
        from EngineeringLineItem import QEngineeringLineItem
754

    
755
        res = []
756
        try:
757
            if type(obj) is QEngineeringLineItem:
758
                startPt = obj.startPoint()
759
                endPt = obj.endPoint()
760
                for i in range(len(self.connectors)):
761
                    if (Point(startPt[0], startPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0],
762
                                                                     self.connectors[i].sceneConnectPoint[1])) < toler):
763
                        if self.connectors[i].connectedItem is None and obj.connectors[0].connectedItem is None:
764
                            self.connectors[i].connect(obj)
765
                            obj.connectors[0].connect(self)
766
                            # line, start, end
767
                            res.append(obj)
768
                            res.append(self.connectors[i].sceneConnectPoint)
769
                            res.append(endPt)
770
                    if (Point(endPt[0], endPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0],
771
                                                                 self.connectors[i].sceneConnectPoint[1])) < toler):
772
                        if self.connectors[i].connectedItem is None and obj.connectors[1].connectedItem is None:
773
                            self.connectors[i].connect(obj)
774
                            obj.connectors[1].connect(self)
775
                            # line, start, end
776
                            res.append(obj)
777
                            res.append(startPt)
778
                            res.append(self.connectors[i].sceneConnectPoint)
779
            elif issubclass(type(obj), SymbolSvgItem):
780
                for i in range(len(self.connectors)):
781
                    if i > 3: break
782
                    for j in range(len(obj.connectors)):
783
                        if j > 3: break
784
                        _pt = Point(obj.connectors[j].sceneConnectPoint[0], obj.connectors[j].sceneConnectPoint[1])
785
                        if (_pt.distance(Point(self.connectors[i].sceneConnectPoint[0],
786
                                               self.connectors[i].sceneConnectPoint[1])) < toler):
787
                            if self.connectors[i].connectedItem is None:
788
                                self.connectors[i].connect(obj)
789
                            if obj.connectors[j].connectedItem is None:
790
                                obj.connectors[j].connect(self)
791

    
792
                            res.append(obj)
793
        except Exception as ex:
794
            from App import App
795
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
796
                                                          sys.exc_info()[-1].tb_lineno)
797
            App.mainWnd().addMessage.emit(MessageType.Error, message)
798

    
799
        return res
800

    
801
    '''
802
        @brief      disconnect connector item
803
        @author     kyouho
804
        @date       2018.08.30
805
    '''
806

    
807
    def disconnectedItemAtConnector(self, connector):
808
        for conn in self.connectors:
809
            if conn.isOverlapConnector(connector):
810
                conn.connectedItem = None
811

    
812
    '''
813
        @brief  get connection point close to given point in tolerance
814
        @author humkyung
815
        @dat
816
    '''
817

    
818
    def getConnectionPointCloseTo(self, pt, toler=10):
819
        import math
820

    
821
        for connector in self.connectors:
822
            dx = connector.sceneConnectPoint[0] - pt[0]
823
            dy = connector.sceneConnectPoint[1] - pt[1]
824
            if math.sqrt(dx * dx + dy * dy) < toler: return connPt
825

    
826
        return None
827

    
828
    '''
829
        @brief  return center of symbol
830
        @author humkyung
831
        @date   2018.04.08
832
    '''
833

    
834
    def center(self):
835
        return self.sceneBoundingRect().center()
836

    
837
    '''
838
        @brief      highlight connector and attribute
839
        @authro     humkyung
840
        @date       2018.05.02
841
    '''
842

    
843
    def hoverEnterEvent(self, event):
844
        self.highlight(True)
845

    
846
    '''
847
        @brief      unhighlight connector and attribute
848
        @author     humkyung
849
        @date       2018.05.02
850
        @history    kyouho 2018.07.18 edit ArrowCursor
851
    '''
852

    
853
    def hoverLeaveEvent(self, event):
854
        self.highlight(False)
855

    
856
    def highlight(self, flag):
857
        """ highlight/unhighlight the symbol """
858

    
859
        try:
860
            self.hover = flag
861
            self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
862
            self.update()
863

    
864
            for assoc in self.associations():
865
                assoc.highlight(flag)
866

    
867
            for connector in self.connectors:
868
                connector.highlight(flag)
869
        except Exception as ex:
870
            from App import App
871
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
872
                                                          sys.exc_info()[-1].tb_lineno)
873
            App.mainWnd().addMessage.emit(MessageType.Error, message)
874

    
875
    '''
876
        @brief      set highlight
877
        @author     kyouho
878
        @date       2018.08.27
879
    '''
880

    
881
    def setHightlight(self):
882
        self.setColor('url(#hover)')
883
        self.update()
884

    
885
    '''
886
        @brief      unset highlight
887
        @author     kyouho
888
        @date       2018.08.27
889
    '''
890

    
891
    def unsetHightlight(self):
892
        self.setColor('url(#normal)')
893
        self.update()
894

    
895
    '''
896
        @brief  change cursor to CrossCursor if mouse point is close to connection point
897
        @author humkyung
898
        @date   2018.04.28
899
    '''
900

    
901
    def hoverMoveEvent(self, event):
902
        pass
903

    
904
    '''
905
        @brief      Mouse Press Event
906
        @author     Jeongwoo
907
        @date       18.04.11
908
        @history    kyouho 2018.07.18 add isClick logic
909
    '''
910

    
911
    def mousePressEvent(self, event):
912
        if event.buttons() == Qt.LeftButton:
913
            self.clicked.emit(self)
914

    
915
    '''
916
        @brief      Mouse Release Event
917
        @author     kyouho
918
        @date       18.07.17
919
    '''
920

    
921
    def mouseReleaseEvent(self, event):
922
        if hasattr(self, '_rotating') and event.button() == Qt.RightButton:
923
            self.angle = -self._angle if self._angle > -math.pi and self._angle < 0 else 2 * math.pi - self._angle
924
            self.ungrabMouse()
925
            del self._rotating
926

    
927
        super().mouseReleaseEvent(event)
928

    
929
    def mouseMoveEvent(self, event):
930
        """ rotate symbol accroding to current mouse point """
931
        if hasattr(self, '_rotating'):
932
            # get origin point of symbol
933
            origin = self.origin  # self.sceneBoundingRect().center()
934
            # up to here
935

    
936
            dx = (event.scenePos().x() - origin[0])
937
            dy = (event.scenePos().y() - origin[1])
938
            length = math.sqrt(dx * dx + dy * dy)
939

    
940
            self._angle = 0
941
            if length > 0:
942
                self._angle = math.acos(dx / length)
943
                cross = int(np.cross([1, 0], [dx, dy]))
944
                self._angle = -self._angle if cross < 0 else self._angle
945

    
946
                self.rotate(self.getCurrentPoint(), -self._angle)
947

    
948
    def removeSelfAttr(self, attributeName):
949
        target = None
950
        for attr in self.attrs:
951
            if attr.Attribute == attributeName:
952
                target = attr
953
                break
954

    
955
        if target:
956
            del self.attrs[attr]
957

    
958
    '''
959
        @brief      Find TextItem contain Point
960
        @author     kyouho
961
        @date       18.07.17
962
    '''
963

    
964
    def findTextItemInPoint(self, point):
965
        from EngineeringTextItem import QEngineeringTextItem
966

    
967
        scene = self.scene()
968

    
969
        for item in scene.items():
970
            if type(item) is QEngineeringTextItem:
971
                if self.isOverlapItemAndPoint(item, point):
972
                    return (True, item)
973

    
974
        return (False,)
975

    
976
    '''
977
        @brief      Check Overlap
978
        @author     kyouho
979
        @date       18.07.17
980
    '''
981

    
982
    def isOverlapItemAndPoint(self, item, point):
983
        x = point.x()
984
        y = point.y()
985
        loc = item.loc
986
        size = item.size
987

    
988
        if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
989
            return True
990
        else:
991
            return False
992

    
993
    '''
994
        @brief  remove item when user press delete key
995
        @author humkyung
996
        @date   2018.04.23
997
        @history    2018.05.17  Jeongwoo    Add if-statement and move 'break'
998
                    2018.05.25  Jeongwoo    Seperate delete item method
999
    '''
1000
    def keyPressEvent(self, event):
1001
        from EngineeringErrorItem import QEngineeringErrorItem
1002
        from RotateSymbolDialog import QRotateSymbolDialog
1003

    
1004
        if not self.isSelected():
1005
            return
1006
        if event.key() == Qt.Key_B:
1007
            self.bind_close_items()
1008
        elif event.key() == Qt.Key_R and type(self) is not QEngineeringErrorItem:
1009
            self.rotateSymbol()
1010
        elif event.key() == Qt.Key_O and type(self) is not QEngineeringErrorItem:
1011
            pass
1012
        elif event.key() == Qt.Key_C and type(self) is not QEngineeringErrorItem:
1013
            self.changeConnPoint()
1014
        elif event.key() == Qt.Key_F and type(self) is not QEngineeringErrorItem:
1015
            self.flipSymbol()
1016
        elif event.key() == Qt.Key_Return:
1017
            dialog = QRotateSymbolDialog(None, self.angle, self.origin, self.zValue())
1018
            (isAccept, angle, x, y, z) = dialog.showDialog()
1019

    
1020
            if isAccept:
1021
                self.angle = angle
1022
                self.loc = [x - self.symbolOrigin[0], y - self.symbolOrigin[1]]
1023
                self.origin = [x, y]
1024
                self.rotate(self.getCurrentPoint(), self.angle)
1025
                self.setZValue(z)
1026
        elif event.key() == Qt.Key_Escape:
1027
            if hasattr(self, '_rotating'):
1028
                self.ungrabMouse()
1029

    
1030
                transform = QTransform()
1031
                transform.translate((self.loc[0] + self.symbolOrigin[0]), (self.loc[1] + self.symbolOrigin[1]))
1032
                transform.rotateRadians(self.angle)
1033
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1034
                self.setTransform(transform)
1035

    
1036
                del self._rotating
1037
        elif event.key() == Qt.Key_Up:  # translate up/down/left/right symbol
1038
            modifiers = QApplication.keyboardModifiers()
1039
            delta = 5 if modifiers == Qt.ControlModifier else 1
1040

    
1041
            self.loc[1] = self.loc[1] - delta
1042
            self.origin[1] = self.origin[1] - delta
1043
            self.rotate(self.getCurrentPoint(), self.angle)
1044
        elif event.key() == Qt.Key_Down:
1045
            modifiers = QApplication.keyboardModifiers()
1046
            delta = 5 if modifiers == Qt.ControlModifier else 1
1047

    
1048
            self.loc[1] = self.loc[1] + delta
1049
            self.origin[1] = self.origin[1] + delta
1050
            self.rotate(self.getCurrentPoint(), self.angle)
1051
        elif event.key() == Qt.Key_Left:
1052
            modifiers = QApplication.keyboardModifiers()
1053
            delta = 5 if modifiers == Qt.ControlModifier else 1
1054

    
1055
            self.loc[0] = self.loc[0] - delta
1056
            self.origin[0] = self.origin[0] - delta
1057
            self.rotate(self.getCurrentPoint(), self.angle)
1058
        elif event.key() == Qt.Key_Right:
1059
            modifiers = QApplication.keyboardModifiers()
1060
            delta = 5 if modifiers == Qt.ControlModifier else 1
1061

    
1062
            self.loc[0] = self.loc[0] + delta
1063
            self.origin[0] = self.origin[0] + delta
1064
            self.rotate(self.getCurrentPoint(), self.angle)
1065
        elif event.key() == Qt.Key_I or event.key() == Qt.Key_X:
1066
            from App import App 
1067
            App.mainWnd().keyPressEvent(event)
1068

    
1069
    def bind_close_items(self):
1070
        """ connect close item by pressing B """
1071
        from EngineeringLineItem import QEngineeringLineItem
1072

    
1073
        scene = self.scene()
1074
        if scene:
1075
            configs = AppDocData.instance().getConfigs('Line Detector', 'Length to connect line')
1076
            toler = int(configs[0].value) if configs else 20
1077
            for item in [item for item in scene.items() if hasattr(item, 'connectors')]:
1078
                if item is not self:
1079
                    res = self.connect_if_possible(item, toler)
1080
                    if res and type(item) is QEngineeringLineItem:
1081
                        item.set_line([res[1], res[2]])
1082

    
1083
    '''
1084
        @brief      connect attribute
1085
        @author     humkyung
1086
        @date       2018.05.02
1087
        @history    humkyung 2018.05.09 append only nearest size attribute
1088
    '''
1089
    def connectAttribute(self, attributes, clear=True):
1090
        import math
1091
        from EngineeringTextItem import QEngineeringTextItem
1092
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1093
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1094
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1095

    
1096
        try:
1097
            if clear:
1098
                if not self.clear_attr_and_assoc_item():
1099
                    return
1100

    
1101
            configs = AppDocData.instance().getConfigs('Range', 'Detection Ratio')
1102
            ratio = float(configs[0].value) if 1 == len(configs) else 1.5
1103

    
1104
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
1105
            center = self.sceneBoundingRect().center()
1106

    
1107
            minDist = None
1108
            selected = None
1109
            for attr in attributes:
1110
                # size text and operation code text will find onwer themselves in findowner method
1111
                if False:  # type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
1112
                    dx = attr.center().x() - center.x()
1113
                    dy = attr.center().y() - center.y()
1114
                    length = math.sqrt(dx * dx + dy * dy)
1115
                    if (length < dist) and (minDist is None or length < minDist):
1116
                        minDist = length
1117
                        selected = attr
1118
                elif False:#type(attr) is QEngineeringInstrumentItem:
1119
                    if not attr.is_connected():
1120
                        dx = attr.center().x() - center.x()
1121
                        dy = attr.center().y() - center.y()
1122
                        if math.sqrt(dx * dx + dy * dy) < dist:
1123
                            if self.add_assoc_item(attr):
1124
                                attr.owner = self
1125

    
1126
            if selected is not None:
1127
                if self.add_assoc_item(selected):
1128
                    selected.owner = self
1129

    
1130
        except Exception as ex:
1131
            from App import App
1132
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1133
                                                          sys.exc_info()[-1].tb_lineno)
1134
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1135

    
1136
    '''
1137
        @brief      start rotating
1138
        @author     euisung
1139
        @date       2019.04.16
1140
    '''
1141

    
1142
    def mouseDoubleClickEvent(self, event):
1143
        if not hasattr(self, '_rotating'):
1144
            self._rotating = True
1145
            self.grabMouse()
1146

    
1147
    '''
1148
        @brief      get attribute
1149
        @author     humkyung
1150
        @date       2018.06.14
1151
        @history    kyouho  2018.07.18  Add only attr QEngineeringTextItem
1152
    '''
1153
    '''
1154
    def getAttributes(self):
1155
        _attrs = {}
1156
        try:
1157
            from AppDocData import AppDocData
1158
            from EngineeringAbstractItem import QEngineeringAbstractItem
1159
            from EngineeringTextItem import QEngineeringTextItem
1160
            from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1161

1162
            """ get attributes of item from database """
1163
            docData = AppDocData.instance()
1164
            symbolAttrs = docData.getSymbolAttribute(self.type)
1165

1166
            _texts = self.texts()
1167
            _symbols = self.symbols()
1168
            for attr in symbolAttrs:
1169
                matches = [_attr for _attr,_ in self.attrs.items() if _attr.UID == attr.UID]
1170
                if matches:
1171
                    attr.Freeze = matches[0].Freeze         ### update freeze value
1172
                    attr.AssocItem = matches[0].AssocItem
1173
                    _attrs[attr] = self.attrs[matches[0]]   ### copy attribute value
1174
                else:
1175
                    _attrs[attr] = ''
1176
 
1177
                if attr.Freeze: continue    ### do not evalulate value if attribute is frozen
1178
                if attr.AttributeType == 'Size Text Item' or attr.AttributeType == 'Text Item' or attr.AttributeType == 'Valve Oper Code':
1179
                    at = int(attr.AttrAt)
1180
                    items = [text for text in _texts if QEngineeringAbstractItem.assoc_type(text) == attr.AttributeType]
1181
                    if not attr.AssocItem and len(items) > at:
1182
                        attr.AssocItem = items[at]
1183
                        item = attr.AssocItem
1184
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
1185
                    elif attr.AssocItem:
1186
                        item = attr.AssocItem
1187
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
1188
                    else:
1189
                        _attrs[attr] = ''
1190
                elif attr.AttributeType == 'Symbol Item':
1191
                    at = int(attr.AttrAt)
1192
                    if not attr.AssocItem and len(_symbols) > at:
1193
                        attr.AssocItem = _symbols[at]
1194
                        item = attr.AssocItem
1195
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
1196
                    elif attr.AssocItem:
1197
                        item = attr.AssocItem
1198
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
1199
                    else:
1200
                        _attrs[attr] = ''
1201
            
1202
            self.attrs = _attrs ### assign self.attrs
1203
        except Exception as ex:
1204
            from App import App 
1205
            from AppDocData import MessageType
1206

1207
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1208
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1209
        
1210
        return self.attrs
1211
    '''
1212

    
1213
    '''
1214
        @brief      generate xml code
1215
        @author     humkyung
1216
        @date       2018.04.23
1217
        @history    humkyung 2018.04.25 add angle xml node
1218
                    humkyung 2018.04.27 add originalpoint xml node
1219
                    humkyung 2018.05.02 add attribute of symbol
1220
                    Jeongwoo 2018.05.30 add attribute of symbol (parentSymbol, childSymbol, type, connectionPoint)
1221
                    yecheol  2018.07.11 add attribute of symbol (hasInstrumentLabel)
1222
                    humkyung 2018.07.20 write owner's uid to xml
1223
                    humkyung 2018.07.23 write connected item's uid to xml
1224
                    kyouho  2018.07.31 
1225
                    humkyung 2018.09.06 write area to xml
1226
    '''
1227

    
1228
    def toXml(self):
1229
        import uuid
1230
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1231
        from EngineeringAbstractItem import QEngineeringAbstractItem
1232
        from EngineeringTextItem import QEngineeringTextItem
1233
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1234
        from SymbolAttr import SymbolAttr
1235

    
1236
        try:
1237
            node = Element('SYMBOL')
1238
            uidNode = Element('UID')
1239
            uidNode.text = str(self.uid)
1240
            node.append(uidNode)
1241

    
1242
            dbUidNode = Element('DBUID')
1243
            dbUidNode.text = str(self.dbUid)
1244
            node.append(dbUidNode)
1245

    
1246
            nameNode = Element('NAME')
1247
            nameNode.text = self.name
1248
            node.append(nameNode)
1249

    
1250
            attributeValueNode = Element('ASSOCIATIONS')
1251
            for key, value in self._associations.items():
1252
                for assoc in value:
1253
                    assoc_node = Element('ASSOCIATION')
1254
                    assoc_node.attrib['TYPE'] = str(key)
1255
                    assoc_node.text = str(assoc)
1256
                    attributeValueNode.append(assoc_node)
1257
            node.append(attributeValueNode)
1258

    
1259
            typeNode = Element('TYPE')
1260
            typeNode.text = self.type
1261
            node.append(typeNode)
1262

    
1263
            # write owner's uid to xml
1264
            ownerNode = Element('OWNER')
1265
            if self.owner is not None:
1266
                ownerNode.text = str(self.owner)
1267
            else:
1268
                ownerNode.text = 'None'
1269
            node.append(ownerNode)
1270
            # up to here
1271

    
1272
            originNode = Element('ORIGINALPOINT')
1273
            originNode.text = '{},{}'.format(self.origin[0], self.origin[1])
1274
            node.append(originNode)
1275

    
1276
            connectorsNode = Element('CONNECTORS')
1277
            for connector in self.connectors:
1278
                connectorsNode.append(connector.toXml())
1279
            node.append(connectorsNode)
1280

    
1281
            connectionNode = Element('CONNECTIONPOINT')
1282
            connection_point = []
1283
            if self.connectors is not None:
1284
                for connector in self.connectors:
1285
                    connection_point.append(repr(connector))
1286
            connectionNode.text = '/'.join(connection_point)
1287
            node.append(connectionNode)
1288

    
1289
            locNode = Element('LOCATION')
1290
            locNode.text = '{},{}'.format(self.loc[0], self.loc[1])
1291
            '''
1292
            # calculate symbol's left-top corner
1293
            transform = QTransform()
1294
            transform.translate(self.scenePos().x(), self.scenePos().y())
1295
            transform.rotateRadians(-self.angle)
1296
            loc = transform.map(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
1297
            # up to here
1298
            locNode.text = '{},{}'.format(loc.x() - self.symbolOrigin[0], loc.y() - self.symbolOrigin[1])
1299
            '''
1300
            node.append(locNode)
1301

    
1302
            sizeNode = Element('SIZE')
1303
            sizeNode.text = '{},{}'.format(self.size[0], self.size[1])
1304
            node.append(sizeNode)
1305

    
1306
            angleNode = Element('ANGLE')
1307
            angleNode.text = str(self.angle)
1308
            node.append(angleNode)
1309

    
1310
            parentSymbolNode = Element('PARENT')
1311
            parentSymbolNode.text = str(self.parentSymbol)
1312
            node.append(parentSymbolNode)
1313

    
1314
            childSymbolNode = Element('CHILD')
1315
            childSymbolNode.text = str(self.childSymbol)
1316
            node.append(childSymbolNode)
1317

    
1318
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
1319
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
1320
            node.append(hasInstrumentLabelNode)
1321

    
1322
            areaNode = Element('AREA')
1323
            areaNode.text = self.area
1324
            node.append(areaNode)
1325

    
1326
            flipNode = Element('FLIP')
1327
            flipNode.text = str(self.flip)
1328
            node.append(flipNode)
1329

    
1330
            properties_node = Element('PROPERTIES')
1331
            for prop, value in self.properties.items():
1332
                prop_node = prop.toXml()
1333
                prop_node.text = str(value) if value else ''
1334
                properties_node.append(prop_node)
1335
            node.append(properties_node)
1336

    
1337
            attributesNode = Element('SYMBOLATTRIBUTES')
1338
            _attrs = self.getAttributes()
1339
            for attr in _attrs:
1340
                if type(attr) is SymbolAttr:
1341
                    _node = attr.toXml()
1342
                    if attr.AttributeType != 'Spec':
1343
                        _node.text = str(_attrs[attr])
1344
                    elif attr.AttributeType == 'Spec':
1345
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
1346
                    attributesNode.append(_node)
1347

    
1348
            node.append(attributesNode)
1349

    
1350
            currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
1351
            currentPointModeIndexNode.text = str(self.currentPointModeIndex)
1352
            node.append(currentPointModeIndexNode)
1353
        except Exception as ex:
1354
            from App import App
1355
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1356
                                                          sys.exc_info()[-1].tb_lineno)
1357
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1358

    
1359
            return None
1360

    
1361
        return node
1362

    
1363
    @staticmethod
1364
    def from_database(component):
1365
        """ create a item related to given component from database """
1366
        import uuid
1367
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1368
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1369
        from SymbolAttr import SymbolAttr
1370
        item = None
1371

    
1372
        try:
1373
            app_doc_data = AppDocData.instance()
1374

    
1375
            uid = component['UID']
1376
            pt = [float(component['X']), float(component['Y'])]
1377
            size = [float(component['Width']), float(component['Height'])]
1378

    
1379
            dbUid = int(component['Symbol_UID'])
1380
            dbData = app_doc_data.getSymbolByQuery('UID', dbUid)
1381
            name = dbData.sName
1382
            _type = dbData.sType
1383
            angle = float(component['Rotation'])
1384
            origin = [float(x) for x in component['SceneOriginPoint'].split(',')] if component['SceneOriginPoint'] is not None else pt
1385
            connPts = []
1386
            if component['ConnectionPoint']:
1387
                for conn_pt in component['ConnectionPoint'].split('/'):
1388
                    tokens = conn_pt.split(',')
1389
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
1390
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
1391
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3]))
1392

    
1393
            baseSymbol = dbData.baseSymbol
1394

    
1395
            childSymbolNode = component['AdditionalSymbol']
1396
            childSymbol = childSymbolNode if childSymbolNode is not None else ''
1397

    
1398
            owner = component['Owner'] if component['Owner'] is not None and component['Owner'] != 'None' else None
1399

    
1400
            hasInstrumentLabel = dbData.hasInstrumentLabel
1401

    
1402
            flipLabelNode = component['Flip']
1403
            flipLabel = int(flipLabelNode) if flipLabelNode is not None else 0
1404

    
1405
            project = app_doc_data.getCurrentProject()
1406
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
1407
            if os.path.isfile(svgFilePath):
1408
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
1409
                item.setVisible(False)
1410

    
1411
                # if additional symbol was changed, change symbol info
1412
                symbolInfo = None
1413
                if dbUid is None:
1414
                    symbolInfo = app_doc_data.getSymbolByQuery('name', name)
1415
                else:
1416
                    symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
1417
                if symbolInfo:
1418
                    childSymbol = symbolInfo.additionalSymbol
1419

    
1420
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
1421
                                  hasInstrumentLabel, dbUid=dbUid):
1422
                    pass
1423
                else:
1424
                    return None
1425

    
1426
                for key in item._properties.keys():
1427
                    for compo in component.keys():
1428
                        if key.Attribute == compo:
1429
                            item._properties[key] = key.parse_value(component[key.Attribute]) if component[
1430
                                key.Attribute] else ''
1431

    
1432
                ## assign area
1433
                areaNode = component['Area']
1434
                if areaNode is None:
1435
                    for area in app_doc_data.getAreaList():
1436
                        if area.contains(pt):
1437
                            item.area = area.name
1438
                            break
1439
                else:
1440
                    item.area = areaNode
1441
                ## up to here
1442

    
1443
                connectors = app_doc_data.get_component_connectors(uid)
1444
                if connectors:
1445
                    iterIndex = 0
1446
                    for connector in connectors:
1447
                        item.connectors[iterIndex].parse_record(connector)
1448
                        iterIndex += 1
1449

    
1450
                # get associations 
1451
                associations = app_doc_data.get_component_associations(uid)
1452
                if associations:
1453
                    for assoc in associations:
1454
                        _attrType = assoc['Type']
1455
                        if not _attrType in item._associations:
1456
                            item._associations[_attrType] = []
1457
                        item._associations[_attrType].append(
1458
                            uuid.UUID(assoc['Association']) if assoc['Association'] != 'None' else None)
1459
                # up to here
1460

    
1461
                attributes = app_doc_data.get_component_attributes(uid)
1462
                if attributes:
1463
                    for attr in attributes:
1464
                        _attr = SymbolAttr.from_record(attr)
1465
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
1466
                            item.attrs[_attr] = attr['Value']
1467
                        else:
1468
                            if _attr.AttributeType == 'Spec':
1469
                                item.attrs[_attr] = [attr['Value'].split(',')[0], attr['Value'].split(',')[1]]
1470
                            else:
1471
                                item.attrs[_attr] = attr['Value']
1472
                            '''
1473
                            elif _attr.Attribute == 'UpStream':
1474
                                for initKey in item.attrs.keys():
1475
                                    if initKey.Attribute == 'UpStream':
1476
                                        item.attrs[initKey] = attr['Value']
1477
                            elif _attr.Attribute == 'DownStream':
1478
                                for initKey in item.attrs.keys():
1479
                                    if initKey.Attribute == 'DownStream':
1480
                                        item.attrs[initKey] = attr['Value']
1481
                            '''
1482

    
1483
                currentPointModeIndex = component['OriginIndex']
1484
                if currentPointModeIndex is not None:
1485
                    item.currentPointModeIndex = int(currentPointModeIndex)
1486
        except Exception as ex:
1487
            from App import App
1488
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1489
                                                          sys.exc_info()[-1].tb_lineno)
1490
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1491

    
1492
            return None
1493

    
1494
        return item
1495

    
1496
    '''
1497
        @brief      parse xml code
1498
        @author     humkyung
1499
        @date       2018.07.20
1500
        @history    humkyung 2018.07.20 parse uid from xml node
1501
                    humkyung 2018.07.23 parse connected item's uid from xml node
1502
                    kyouho  2018.07.31 
1503
                    humkyung 2018.09.06 assign area to item
1504
    '''
1505

    
1506
    @staticmethod
1507
    def fromXml(node):
1508
        import uuid
1509
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1510
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1511
        from SymbolAttr import SymbolAttr
1512
        item = None
1513

    
1514
        try:
1515
            appDocData = AppDocData.instance()
1516

    
1517
            uidNode = node.find('UID')
1518
            uid = uidNode.text if uidNode is not None else uuid.uuid4()  # generate UUID
1519

    
1520
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
1521
            size = [float(x) for x in node.find('SIZE').text.split(',')]
1522

    
1523
            dbUidNode = node.find('DBUID')
1524
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
1525
            dbData = None
1526
            if dbUid:
1527
                dbData = appDocData.getSymbolByQuery('UID', dbUid)
1528
            name = node.find('NAME').text if dbData is None else dbData.sName
1529

    
1530
            angle = float(node.find('ANGLE').text)
1531
            _type = node.find('TYPE').text if dbData is None else dbData.sType
1532
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
1533
            connPts = []
1534
            if node.find('CONNECTIONPOINT').text is not None:
1535
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
1536
                    tokens = conn_pt.split(',')
1537
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
1538
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
1539
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3]))
1540
            baseSymbol = node.find('PARENT').text if dbData is None else dbData.baseSymbol
1541
            childSymbolNode = node.find('CHILD')
1542
            childSymbol = ''
1543
            if childSymbolNode is not None:
1544
                childSymbol = childSymbolNode.text
1545

    
1546
            ownerNode = node.find('OWNER')
1547
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
1548

    
1549
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
1550
            hasInstrumentLabel = hasInstrumentLabelNode.text if dbData is None else dbData.hasInstrumentLabel
1551

    
1552
            flipLabelNode = node.find('FLIP')
1553
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
1554

    
1555
            project = appDocData.getCurrentProject()
1556
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
1557
            if os.path.isfile(svgFilePath):
1558
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
1559
                item.setVisible(False)
1560

    
1561
                # if additional symbol was changed, change symbol info
1562
                symbolInfo = None
1563
                if dbUid is None:
1564
                    symbolInfo = appDocData.getSymbolByQuery('name', name)
1565
                else:
1566
                    symbolInfo = appDocData.getSymbolByQuery('UID', dbUid)
1567
                if symbolInfo:
1568
                    childSymbol = symbolInfo.additionalSymbol
1569

    
1570
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
1571
                                  hasInstrumentLabel, dbUid=dbUid):
1572
                    pass
1573
                else:
1574
                    return None
1575

    
1576
                for prop_node in node.iter('PROPERTY'):
1577
                    matches = [prop for prop in item._properties.keys() if
1578
                               prop.Attribute == prop_node.attrib['Attribute']]
1579
                    if matches:
1580
                        item._properties[matches[0]] = matches[0].parse_value(prop_node.text) if prop_node.text else ''
1581

    
1582
                ## assign area
1583
                areaNode = node.find('AREA')
1584
                if areaNode is None:
1585
                    for area in appDocData.getAreaList():
1586
                        if area.contains(pt):
1587
                            item.area = area.name
1588
                            break
1589
                else:
1590
                    item.area = areaNode.text
1591
                ## up to here
1592

    
1593
                connectors = node.find('CONNECTORS')
1594
                if connectors is not None:
1595
                    iterIndex = 0
1596
                    for connector in connectors.iter('CONNECTOR'):
1597
                        item.connectors[iterIndex].parse_xml(connector)
1598
                        iterIndex += 1
1599

    
1600
                # get associations 
1601
                attributeValue = node.find('ASSOCIATIONS')
1602
                if attributeValue is not None:
1603
                    for assoc in attributeValue.iter('ASSOCIATION'):
1604
                        _attrType = assoc.attrib['TYPE']
1605
                        if not _attrType in item._associations:
1606
                            item._associations[_attrType] = []
1607
                        item._associations[_attrType].append(uuid.UUID(assoc.text) if assoc.text != 'None' else None)
1608
                # up to here
1609

    
1610
                attributes = node.find('SYMBOLATTRIBUTES')
1611
                if attributes is not None:
1612
                    ## for old spec break item that has not uid currectly, may not necessary new data
1613
                    if _type == 'Segment Breaks':
1614
                        specBreakAttrs = appDocData.getSymbolAttribute('Segment Breaks')
1615
                    ## up to here 1
1616
                    for attr in attributes.iter('ATTRIBUTE'):
1617
                        _attr = SymbolAttr.fromXml(attr)
1618
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
1619
                            item.attrs[_attr] = attr.text
1620
                        else:
1621
                            ## for old spec break item that has not uid currectly, may not necessary new data
1622
                            matchAttr = [cAttr for cAttr in specBreakAttrs if _attr.Attribute == cAttr.Attribute]
1623
                            matchAttr = matchAttr[0] if matchAttr else None
1624
                            if matchAttr:
1625
                                _attr.UID = matchAttr.UID
1626
                                _attr.DisplayAttribute = matchAttr.DisplayAttribute
1627
                                _attr.AttributeType = matchAttr.AttributeType
1628
                                _attr.AttrAt = matchAttr.AttrAt
1629
                                _attr.Expression = matchAttr.Expression
1630
                                _attr.Length = matchAttr.Length
1631
                            ## up to here 2
1632
                            if _attr.AttributeType == 'Spec':
1633
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
1634
                            else:
1635
                                ## for old spec break item that has not uid currectly, may not necessary new data
1636
                                _attr.AssocItem = uuid.UUID(attr.text) if attr.text and attr.text != 'None' else None
1637
                                ## up to here 3
1638
                                item.attrs[_attr] = attr.text
1639
                            '''
1640
                            elif _attr.Attribute == 'UpStream':
1641
                                for initKey in item.attrs.keys():
1642
                                    if initKey.Attribute == 'UpStream':
1643
                                        item.attrs[initKey] = attr.text
1644
                            elif _attr.Attribute == 'DownStream':
1645
                                for initKey in item.attrs.keys():
1646
                                    if initKey.Attribute == 'DownStream':
1647
                                        item.attrs[initKey] = attr.text
1648
                            '''
1649

    
1650
                currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
1651
                if currentPointModeIndex is not None:
1652
                    item.currentPointModeIndex = int(currentPointModeIndex.text)
1653
        except Exception as ex:
1654
            from App import App
1655
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1656
                                                          sys.exc_info()[-1].tb_lineno)
1657
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1658

    
1659
            return None
1660

    
1661
        return item
1662

    
1663
    '''
1664
        @brief      create item corresponding to given type
1665
        @author     humkyung
1666
        @date       2018.05.02
1667
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
1668
                    humkyung 2018.05.10 change symbol's color to blue
1669
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
1670
    '''
1671

    
1672
    @staticmethod
1673
    def createItem(type, name, path, uid=None, owner=None, flip=0):
1674
        from QEngineeringOPCItem import QEngineeringOPCItem
1675
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1676
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1677
        from EngineeringNozzleItem import QEngineeringNozzleItem
1678
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1679
        from EngineeringReducerItem import QEngineeringReducerItem
1680
        from EngineeringErrorItem import QEngineeringErrorItem
1681
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1682
        from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
1683
        from AppDocData import AppDocData
1684
        import uuid
1685

    
1686
        app_doc_data = AppDocData.instance()
1687

    
1688
        item = None
1689
        try:
1690
            cateogry = app_doc_data.getSymbolCategoryByType(type)
1691
            if type == "Piping OPC's":
1692
                item = QEngineeringOPCItem(path, uid, flip=flip)
1693
            elif cateogry == 'Equipment' or cateogry == 'Equipment Components':
1694
                item = QEngineeringEquipmentItem(path, uid, flip=flip)
1695
            elif cateogry == 'Instrumentation':
1696
                item = QEngineeringInstrumentItem(path, uid, flip=flip)
1697
            elif type == 'Nozzles':
1698
                item = QEngineeringNozzleItem(path, uid, flip=flip)
1699
            elif type == 'Segment Breaks':
1700
                item = QEngineeringSpecBreakItem(path, uid, flip=flip)
1701
            elif type == 'Reducers':
1702
                item = QEngineeringReducerItem(path, uid, flip=flip)
1703
            elif type == 'Error':
1704
                item = QEngineeringErrorItem(path, uid, flip=flip)
1705
            elif type == 'End Break':
1706
                item = QEngineeringEndBreakItem(path, uid, flip=flip)
1707
            # elif type == 'Flow Mark':
1708
            #    item = QEngineeringFlowMarkItem(path, uid, flip=flip)
1709
            else:
1710
                item = SymbolSvgItem(name, path, uid, flip=flip)
1711

    
1712
            if owner is not None:
1713
                item.owner = uuid.UUID(owner)
1714

    
1715
        except Exception as ex:
1716
            from App import App
1717
            from AppDocData import MessageType
1718

    
1719
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1720
                                                           sys.exc_info()[-1].tb_lineno)
1721
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1722

    
1723
        return item
1724

    
1725
    '''
1726
        @brief      change svg's color
1727
        @author     humkyung
1728
        @date       2018.05.10
1729
        @history    2018.05.11  Jeongwoo    Override QEngineeringAbstractItem's
1730
                    humkyung 2018.05.13 update after change color
1731
    '''
1732

    
1733
    def setColor(self, color):
1734
        self.changeAttributes('fill', color)
1735
        self.changeAttributes('stroke', color)
1736
        self.renderer().load(self._document.toByteArray())
1737
        self.update()
1738

    
1739
    def getColor(self):
1740
        """ return hover color if mouse is over otherwise reutrn owner's color if owner exist else this color """
1741
        return SymbolSvgItem.HOVER_COLOR if self.hover else (
1742
            self.owner._color if self.owner and hasattr(self.owner, '_color') else self._color)
1743

    
1744
    '''
1745
        @brief  get attributes from svg file
1746
        @author humkyung
1747
        @date   2019.03.08
1748
    '''
1749

    
1750
    def get_attribute(self, attName):
1751
        root = self._document.documentElement()
1752
        node = root.firstChild()
1753
        while not node.isNull():
1754
            if node.isElement():
1755
                element = node.toElement()
1756
                if element.hasAttribute(attName):
1757
                    return element.attribute(attName)
1758

    
1759
                if element.hasChildNodes():
1760
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
1761
                    if att_val is not None: return att_val
1762

    
1763
            node = node.nextSibling()
1764

    
1765
        return None
1766

    
1767
    '''
1768
        @brief  get recursively attribute
1769
        @author humkyung
1770
        @date   2019.03.08
1771
    '''
1772

    
1773
    def recursive_get_attribute(self, node, attName):
1774
        while not node.isNull():
1775
            if node.isElement():
1776
                element = node.toElement()
1777
                if element.hasAttribute(attName):
1778
                    return element.attribute(attName)
1779

    
1780
                if node.hasChildNodes():
1781
                    att_val = self.recursive_get_attribute(node.firstChild(), attName)
1782
                    if att_val is not None: return att_val
1783

    
1784
            node = node.nextSibling()
1785

    
1786
        return None
1787

    
1788
    '''
1789
        @brief  change attributes
1790
        @author humkyung
1791
        @date   2018.05.10
1792
    '''
1793

    
1794
    def changeAttributes(self, attName, attValue):
1795
        root = self._document.documentElement()
1796
        node = root.firstChild()
1797
        while not node.isNull():
1798
            if node.isElement():
1799
                element = node.toElement()
1800
                if element.hasAttribute(attName):
1801
                    element.setAttribute(attName, attValue)
1802

    
1803
                if element.hasChildNodes():
1804
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
1805

    
1806
            node = node.nextSibling()
1807

    
1808
    '''
1809
        @brief  change attribute
1810
        @author humkyung
1811
        @date   2018.05.10
1812
    '''
1813

    
1814
    def recursiveChangeAttributes(self, node, attName, attValue):
1815
        while not node.isNull():
1816
            if node.isElement():
1817
                element = node.toElement()
1818
                if element.hasAttribute(attName):
1819
                    element.setAttribute(attName, attValue)
1820

    
1821
                if node.hasChildNodes():
1822
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
1823

    
1824
            node = node.nextSibling()
1825

    
1826
    '''
1827
        @brief  draw rect when item is selected
1828
        @author humkyung
1829
        @date   2018.07.07
1830
    '''
1831

    
1832
    def drawFocusRect(self, painter):
1833
        self.focuspen = QPen(Qt.DotLine)
1834
        self.focuspen.setColor(Qt.black)
1835
        self.focuspen.setWidthF(1.5)
1836
        hilightColor = QColor(255, 0, 0, 127)
1837
        painter.setBrush(QBrush(hilightColor))
1838
        painter.setPen(self.focuspen)
1839
        painter.drawRect(self.boundingRect())
1840

    
1841
    '''
1842
        @brief  override paint(draw connection points)
1843
        @author humkyung
1844
        @date   2018.04.21
1845
    '''
1846

    
1847
    def paint(self, painter, options=None, widget=None):
1848
        from EngineeringAbstractItem import QEngineeringAbstractItem
1849
        from EngineeringTextItem import QEngineeringTextItem
1850

    
1851
        self.setColor(self.getColor())
1852

    
1853
        painter.setClipRect(options.exposedRect)
1854
        QGraphicsSvgItem.paint(self, painter, options, widget)
1855
        for attr in self.attrs:
1856
            if issubclass(type(attr), QEngineeringTextItem):
1857
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (
1858
                    self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
1859
                attr.setColor(color)
1860
            elif issubclass(type(attr), SymbolSvgItem):
1861
                attr.setColor(self.getColor())
1862
                attr.update()
1863

    
1864
        if self.isSelected():
1865
            self.drawFocusRect(painter)
1866

    
1867
    '''
1868
        @brief      Add Svg Item into ImageViewer's Scene
1869
        @author     Jeongwoo
1870
        @date       2018.05.03
1871
        @history    add connectors which's parent is symbol
1872
                    kyouho  2018.07.30  remove connectors logic
1873
    '''
1874

    
1875
    def addSvgItemToScene(self, scene):
1876
        self.rotate(self.getCurrentPoint(), self.angle)
1877
        scene.addItem(self)
1878
        self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
1879
        for conn in self.connectors:
1880
            conn.sceneConnectPoint = (conn.sceneBoundingRect().center().x(), conn.sceneBoundingRect().center().y())
1881

    
1882
    '''
1883
        @brief      
1884
        @author     humkyung
1885
        @date       2018.07.27
1886
    '''
1887

    
1888
    def onConnectorPosChaned(self, connector):
1889
        pass
1890

    
1891
    '''
1892
        @brief      set connector
1893
        @author     kyouho
1894
        @date       2018.07.26
1895
    '''
1896

    
1897
    def setConnector(self, index):
1898
        connector = QEngineeringConnectorItem(parent=self, index=index)
1899
        connector.setParentItem(self)
1900
        self.connectors.append(connector)
1901

    
1902
    '''
1903
    '''
1904

    
1905
    def refreshConnector(self):
1906
        for connector in self.connectors:
1907
            connector.buildItem()
1908

    
1909
    '''
1910
        @brief      Delete svg item
1911
        @author     Jeongwoo
1912
        @date       2018.05.25
1913
    '''
1914

    
1915
    def deleteSvgItemFromScene(self):
1916
        ''' not used '''
1917
        for connector in self.connectors:
1918
            if connector.connectedItem is not None:
1919
                try:
1920
                    connector.connectedItem.removeSymbol(self)
1921
                except Exception as ex:
1922
                    from App import App
1923
                    from AppDocData import MessageType
1924

    
1925
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1926
                                                                  sys.exc_info()[-1].tb_lineno)
1927
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
1928
                break
1929

    
1930
        self.transfer.onRemoved.emit(self)
1931

    
1932
    '''
1933
        @brief      Return real item position
1934
        @author     Jeongwoo
1935
        @date       2018.05.25
1936
    '''
1937

    
1938
    def boundingRectOnScene(self):
1939
        rect = self.boundingRect()
1940
        rect.moveTo(self.loc[0], self.loc[1])
1941
        return rect
1942

    
1943
    def flipSymbol(self):
1944
        '''
1945
            @brief  remove item when user press delete key
1946
            @author humkyung
1947
            @date   2018.04.23
1948
        '''
1949
        if self.flip is 0:
1950
            self.flip = 1
1951
        else:
1952
            self.flip = 0
1953

    
1954
        currentPoint = self.getCurrentPoint()
1955
        self.rotate(currentPoint, self.angle)
1956

    
1957
    '''
1958
        @brief      rotate Symbol
1959
        @author     kyouho
1960
        @date       2018.07.24
1961
    '''
1962

    
1963
    def rotateSymbol(self, angle=None):
1964
        if angle is None:
1965
            # degree 0
1966
            if 0 == self.angle:
1967
                self.angle = 1.57
1968
            # degree 90
1969
            elif (1.57 == self.angle):
1970
                self.angle = 3.14
1971
            # degree 180
1972
            elif 3.14 == self.angle:
1973
                self.angle = 4.71
1974
            # degree 270
1975
            elif 4.71 == self.angle:
1976
                self.angle = 0
1977
            else:
1978
                self.angle = 0
1979
        else:
1980
            self.angle = angle
1981

    
1982
        currentPoint = self.getCurrentPoint()
1983
        self.rotate(currentPoint, self.angle)
1984

    
1985
    def get_transform(self, pt, angle):
1986
        """ return a transform """
1987
        transform = QTransform()
1988
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
1989
        transform.rotateRadians(-angle)
1990
        transform.translate(-pt[0], -pt[1])
1991

    
1992
        if self.flip is 1:
1993
            transform.scale(-1.0, 1.0)
1994
            transform.translate(-2 * self.symbolOrigin[0], 0)
1995

    
1996
        return transform
1997

    
1998
    '''
1999
        @brief      resetting rotate Symbol
2000
        @author     kyouho
2001
        @date       2018.07.24
2002
    '''
2003

    
2004
    def rotate(self, standardPoint, angle):
2005
        self.setTransform(self.get_transform(standardPoint, angle))
2006
        self.setRotation(0)  # force to emit itemChange signal
2007

    
2008
    '''
2009
        @brief      change Conn point 
2010
        @author     kyouho
2011
        @date       2018.07.25
2012
    '''
2013

    
2014
    def changeConnPoint(self):
2015
        if len(self.connectors) == 2:
2016
            conn1Item = self.connectors[0].connectedItem
2017
            conn2Item = self.connectors[1].connectedItem
2018
            self.connectors[0].connectedItem = conn2Item
2019
            self.connectors[1].connectedItem = conn1Item
2020

    
2021
            currentPoint = self.getCurrentPoint()
2022
            self.reSettingSymbol(currentPoint, self.angle)
2023

    
2024
    '''
2025
        @brief      change standard point
2026
        @author     kyouho
2027
        @date       2018.07.24
2028
    '''
2029

    
2030
    def changeStandardPoint(self):
2031
        connPtsCount = len(self.connectors)
2032

    
2033
        if self.currentPointModeIndex < connPtsCount:
2034
            self.currentPointModeIndex += 1
2035
        else:
2036
            self.currentPointModeIndex = 0
2037

    
2038
        currentPoint = self.getCurrentPoint()
2039
        self.reSettingSymbol(currentPoint, self.angle)
2040

    
2041
    '''
2042
        @brief      get standard point
2043
        @author     kyouho
2044
        @date       2018.07.25
2045
    '''
2046

    
2047
    def getCurrentPoint(self):
2048
        pointList = []
2049
        pointList.append(self.symbolOrigin)
2050
        for connector in self.connectors:
2051
            pointList.append(connector.connectPoint)
2052

    
2053
        return pointList[self.currentPointModeIndex]
2054

    
2055
    '''
2056
        @brief      심볼 회전 시 서로 다른 기준점으로 회전하기 때문에 기준점을 이후 개발한 SymbolSvgItem 기준의 회전좌표로 이동하기 위해서 만듬 (loc 기준으로 회전해야함)
2057
        @author     kyouho
2058
        @date       18.08.06
2059
    '''
2060

    
2061
    def reCalculationRotatedItem(self):
2062
        goPoint = self.get_transform(self.getCurrentPoint(), self.angle).map(
2063
            QPoint(self.symbolOrigin[0], self.symbolOrigin[1]))
2064
        self.loc = [self.loc[0] + self.origin[0] - goPoint.x(), self.loc[1] + self.origin[1] - goPoint.y()]
2065

    
2066

    
2067
def recursiveChangeAttributes(node, attName, attValue):
2068
    while not node.isNull():
2069
        if node.isElement():
2070
            element = node.toElement()
2071
            if element.hasAttribute(attName):
2072
                element.setAttribute(attName, attValue)
2073

    
2074
            if node.hasChildNodes():
2075
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
2076

    
2077
        node = node.nextSibling()
2078

    
2079

    
2080
'''
2081
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
2082
    @author     Jeongwoo
2083
    @date       2018.06.18
2084
'''
2085

    
2086

    
2087
class Transfer(QObject):
2088
    on_pos_changed = pyqtSignal(QGraphicsItem)
2089
    onRemoved = pyqtSignal(QGraphicsItem)
2090

    
2091
    def __init__(self, parent=None):
2092
        QObject.__init__(self, parent)
2093

    
2094

    
2095
if __name__ == '__main__':
2096
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
2097
    f.open(QIODevice.ReadOnly)
2098
    array = f.readAll()
2099
    document = QDomDocument()
2100
    document.setContent(array)
2101

    
2102
    root = document.documentElement()
2103
    node = root.firstChild()
2104
    while not node.isNull():
2105
        if node.isElement():
2106
            element = node.toElement()
2107
            if element.hasAttribute('fill'):
2108
                element.setAttribute('fill', '#FFFFF')
2109

    
2110
            if element.hasChildNodes():
2111
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
2112

    
2113
        node = node.nextSibling()
클립보드 이미지 추가 (최대 크기: 500 MB)