프로젝트

일반

사용자정보

통계
| 개정판:

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

이력 | 보기 | 이력해설 | 다운로드 (134 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 *
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
    DOCUMENTS = {}  # store documents and renders for symbols
30

    
31
    '''
32
        @history    18.04.11    Jeongwoo    Add Variable (Name, Type)
33
                    18.05.11    Jeongwoo    Declare variable self.color
34
                    18.05.25    Jeongwoo    Call setColor() method
35
                    18.05.30    Jeongwoo    Add self variables (parentSymbol, childSymbol)
36
    '''
37

    
38
    def __init__(self, name, path, uid=None, flip=0):
39
        import uuid
40
        from SymbolAttr import SymbolProp
41

    
42
        QGraphicsSvgItem.__init__(self)
43
        QEngineeringAbstractItem.__init__(self)
44

    
45
        self.setFlags(
46
            QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | #QGraphicsItem.ItemIsMovable |
47
            QGraphicsItem.ItemSendsGeometryChanges | QGraphicsItem.ItemClipsToShape)
48

    
49
        self.dbUid = None  # symbol UID
50
        self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid)
51
        self.name = name
52
        self.type = ''
53
        self.iType = -2
54
        self.text_area = None
55
        self.angle = 0
56
        self.origin = None
57
        self.loc = None
58
        self.size = None
59
        self._owner = None
60
        self.parentSymbol = ''
61
        self.childSymbol = ''
62
        self.hasInstrumentLabel = 0
63
        self.flip = flip
64
        self.hit_ratio = None
65
        self._converted = False  # flag indicate whether symbol is converted or not
66
        # attributeType uid
67
        self.attribute = ''
68
        self._properties = {SymbolProp(None, 'Supplied By', 'String'): None}
69

    
70
        self.setAcceptDrops(True)
71
        self.setAcceptHoverEvents(True)
72
        self.setAcceptedMouseButtons(Qt.LeftButton)
73
        self.setAcceptTouchEvents(True)
74

    
75
        self.currentCursor = 0
76
        self.transfer = Transfer()
77

    
78
        self._angle = 0
79

    
80
        self.in_out_connector = [[], []] # 0 : in, 1 : out
81
        self.break_connector = []
82
        self.conn_type = []
83

    
84
        try:
85
            app_doc_data = AppDocData.instance()
86
            svg = None
87

    
88
            if path not in SymbolSvgItem.DOCUMENTS:
89
                if self.name:
90
                    _, svg = app_doc_data.read_symbol_shape(self.name)
91

    
92
                if not svg and path and os.path.isfile(path):
93
                    f = QFile(path)
94
                    f.open(QIODevice.ReadOnly)
95
                    svg = f.readAll()
96
                    f.close()
97

    
98
                self._document = QDomDocument()
99
                self._document.setContent(svg)
100
                self._renderer = QSvgRenderer(self._document.toByteArray())
101
                SymbolSvgItem.DOCUMENTS[path] = [self._document, self._renderer]
102
            else:
103
                self._document, self._renderer = SymbolSvgItem.DOCUMENTS[path]
104

    
105
            self.setSharedRenderer(self._renderer)
106
            self.setCacheMode(QGraphicsItem.ItemCoordinateCache)
107

    
108
            #self._color = self.get_attribute('fill')
109
        except Exception as ex:
110
            from App import App
111

    
112
            message = f'error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
113
                      f'{sys.exc_info()[-1].tb_lineno}'
114
            App.mainWnd().addMessage.emit(MessageType.Error, message)
115

    
116
        self.setZValue(SymbolSvgItem.ZVALUE)
117

    
118
        app_doc_data = AppDocData.instance()
119
        configs = app_doc_data.getConfigs('Symbol Style', 'Opacity')
120
        self.setOpacity(float(configs[0].value) / 100 if configs else 0.5)
121

    
122
        self.__color = None  # svg item's color
123

    
124
    @property
125
    def converted(self):
126
        """return whethere symbol is converted or not"""
127
        return self._converted
128

    
129
    @converted.setter
130
    def converted(self, value):
131
        """set converted flag with given value"""
132
        self._converted = value
133

    
134
    def has_in_out_connector(self):
135
        """ return True if item has in or out connector """
136
        if len(self.in_out_connector[0]) > 0 and len(self.in_out_connector[1]) > 0:
137
            return True
138
        else:
139
            return False
140

    
141
    def has_break_connector(self):
142
        """ return True if item has break connector """
143
        if len(self.break_connector) > 0:
144
            return True
145
        else:
146
            return False
147

    
148
    def __str__(self):
149
        """ return string represent uuid """
150
        return str(self.uid)
151

    
152
    @property
153
    def owner(self):
154
        """get owner"""
155
        import uuid
156

    
157
        if self.scene() and self._owner and type(self._owner) is uuid.UUID:
158
            #matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
159
            matches = []
160
            for x in self.scene().items():
161
                if hasattr(x, 'uid') and str(x.uid) == str(self._owner):
162
                    matches = [x]
163
                    break
164
            if matches:
165
                self._owner = matches[0]
166

    
167
            return matches[0] if matches else None
168

    
169
        if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
170
            return self._owner
171
        else:
172
            self._owner = None
173
            return None
174

    
175
    '''
176
        @brief  setter owner
177
        @author humkyung
178
        @date   2018.05.10
179
        @history    2018.05.17  Jeongwoo    Add Calling setColor if self._owner is None or not
180
    '''
181
    @owner.setter
182
    def owner(self, value):
183
        self._owner = value
184

    
185
        if self._owner is None:
186
            self._color = self.DEFAULT_COLOR
187
        self.setColor(self._color)
188

    
189
    def validate(self, rules):
190
        """ validation check """
191

    
192
        from EngineeringLineItem import QEngineeringLineItem
193
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
194
        from EngineeringTextItem import QEngineeringTextItem
195
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
196
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
197
        from EngineeringReducerItem import QEngineeringReducerItem
198
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
199
        from ValidateCommand import SymbolValidation
200

    
201
        errors = []
202

    
203
        try:
204
            if type(self) is QEngineeringEndBreakItem or type(self) is QEngineeringEquipmentItem:
205
                return errors
206
            
207
            app_doc_data = AppDocData.instance()
208
            dataPath = app_doc_data.getErrorItemSvgPath()
209

    
210
            # validate connectors
211
            #for connector in self.connectors:
212
            #    errors.extend(connector.validate())
213

    
214
            # check if connected two lines has same direction
215
            if SymbolValidation.FlowDirectionError in rules:
216
                if len(self.connectors) is 2:
217
                    if (type(self.connectors[0].connectedItem) is QEngineeringLineItem and
218
                        self.connectors[0]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT) and \
219
                            (type(self.connectors[1].connectedItem) is QEngineeringLineItem and
220
                            self.connectors[1]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT):
221
                        indices = [0, 0]
222

    
223
                        center = self.connectors[0].center()
224
                        dx, dy = [0, 0], [0, 0]
225
                        dx[0] = center[0] - self.connectors[0].connectedItem.line().p1().x()
226
                        dy[0] = center[1] - self.connectors[0].connectedItem.line().p1().y()
227
                        dx[1] = center[0] - self.connectors[0].connectedItem.line().p2().x()
228
                        dy[1] = center[1] - self.connectors[0].connectedItem.line().p2().y()
229
                        indices[0] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
230

    
231
                        center = self.connectors[1].center()
232
                        dx[0] = center[0] - self.connectors[1].connectedItem.line().p1().x()
233
                        dy[0] = center[1] - self.connectors[1].connectedItem.line().p1().y()
234
                        dx[1] = center[0] - self.connectors[1].connectedItem.line().p2().x()
235
                        dy[1] = center[1] - self.connectors[1].connectedItem.line().p2().y()
236
                        indices[1] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
237

    
238
                        if indices[0] == indices[1]:
239
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
240
                            error.parent = self
241
                            error.msg = self.tr('Flow direction error')
242
                            error.setToolTip(error.msg)
243
                            error.area = 'Drawing'
244
                            error.name = 'Error'
245
                            errors.append(error)
246

    
247
            # check disconnected point
248
            attrs = self.getAttributes()
249
            matches = [(attr, value) for attr, value in attrs.items() if attr.Attribute == 'OWNERSYMBOL']
250
            if matches:
251
                if SymbolValidation.NotCombined in rules:
252
                    if not matches[0][1]:
253
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
254
                        error.parent = self
255
                        error.msg = 'Not combined'
256
                        error.setToolTip(error.msg)
257
                        error.area = 'Drawing'
258
                        error.name = 'Error'
259
                        errors.append(error)
260
            else:
261
                if SymbolValidation.DisconnectionError in rules:
262
                    disconnect = False
263
                    if len(self.connectors) is not 0:
264
                        disconnect = True
265
                        for connector in self.connectors:
266
                            if connector.connectedItem is not None:
267
                                disconnect = False
268
                                break
269

    
270
                    if disconnect:
271
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
272
                        error.parent = self
273
                        error.msg = 'Disconnection error'
274
                        error.setToolTip(error.msg)
275
                        error.area = 'Drawing'
276
                        error.name = 'Error'
277
                        errors.append(error)
278

    
279
            # check if symbol size if 0
280
            # no more used
281
            '''
282
            if self.size[0] == 0 or self.size[1] == 0:
283
                error = SymbolSvgItem.createItem('Error', None, dataPath)
284
                error.parent = self
285
                error.msg = self.tr('Size error')
286
                error.setToolTip(error.msg)
287
                error.area = 'Drawing'
288
                error.name = 'Error'
289
                errors.append(error)
290
            '''
291

    
292
            # check if association item's owner exists
293
            if SymbolValidation.AssociationError in rules:
294
                for assoc in self.associations():
295
                    if issubclass(type(assoc), QEngineeringTextItem) and not assoc.owner:
296
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
297
                        error.parent = self
298
                        error.msg = self.tr('Association error')
299
                        error.setToolTip(error.msg)
300
                        error.area = self.area
301
                        error.name = 'Error'
302
                        error.items = [ assoc ]
303
                        errors.append(error)
304

    
305
            if SymbolValidation.NoSizeLabelWarning in rules:
306
                if type(self) is QEngineeringReducerItem:
307
                    if not [size for size in self.associations() if type(size) is QEngineeringSizeTextItem]:
308
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
309
                        error.parent = self
310
                        error.msg = self.tr('No size label warning')
311
                        error.setToolTip(error.msg)
312
                        error.area = self.area
313
                        error.name = 'Warning'
314
                        errors.append(error)
315

    
316
            if self.iType == 34 or self.iType == 17 or self.iType == 22:
317
                if SymbolValidation.NoLabelWarning in rules:
318
                    if not self.EvaluatedLabel('OWNERSYMBOL'):
319
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
320
                        error.parent = self
321
                        error.msg = self.tr('No label warning')
322
                        error.setToolTip(error.msg)
323
                        error.area = self.area
324
                        error.name = 'Warning'
325
                        errors.append(error)
326

    
327
                if SymbolValidation.MultipleLabelError in rules:
328
                    labels = [item for item in self.scene().items() if issubclass(type(item), SymbolSvgItem) and (item.iType == 19 or item.iType == 29 or item.iType == 30)]
329
                    labels = [label for label in labels if label.EvaluatedAttribute('OWNERSYMBOL') == self or \
330
                            (type(label.EvaluatedAttribute('OWNERSYMBOL')) is str and label.EvaluatedAttribute('OWNERSYMBOL') == str(self.uid))]
331
                    if len(labels) > 1:
332
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
333
                        error.parent = self
334
                        error.msg = self.tr('Multiple label error')
335
                        error.setToolTip(error.msg)
336
                        error.area = self.area
337
                        error.name = 'Error'
338
                        error.items = labels
339
                        errors.append(error)
340

    
341
            # check overlapping
342
            if SymbolValidation.SymbolOverlappingWarning in rules:
343
                symbols = [item for item in self.scene().items() if item is not self and issubclass(type(item), SymbolSvgItem) and type(item) is not QEngineeringEquipmentItem and type(item) is not QEngineeringEndBreakItem and type(item) is not QEngineeringSpecBreakItem]
344
                for symbol in symbols:
345
                    symbolRect = symbol.sceneBoundingRect()
346
                    rect1 = QRectF(symbolRect.left() - 3, symbolRect.top() - 3, symbolRect.width() + 6, symbolRect.height() + 6)
347
                    rect2 = QRectF(symbolRect.left() + 3, symbolRect.top() + 3, symbolRect.width() - 6, symbolRect.height() - 6)
348
                    if rect1.contains(self.sceneBoundingRect()) or rect2.contains(self.mapToScene(self.transformOriginPoint())):
349
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
350
                        error.parent = self
351
                        error.msg = self.tr('Symbol overlapping warning')
352
                        error.setToolTip(error.msg)
353
                        error.area = self.area
354
                        error.name = 'Warning'
355
                        error.items = [ symbol ]
356
                        errors.append(error)
357

    
358
            # set error position
359
            for error in errors:
360
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
361

    
362
            connectedUid = []
363
            for connector in self.connectors:
364
                # for duplication check
365
                if connector.connectedItem and issubclass(type(connector.connectedItem), QEngineeringAbstractItem):
366
                    connectedUid.append(str(connector.connectedItem.uid))
367

    
368
                if (issubclass(type(connector.connectedItem), SymbolSvgItem) and \
369
                        type(connector.connectedItem) is not QEngineeringEquipmentItem) or \
370
                                type(connector.connectedItem) is QEngineeringLineItem:
371
                    matches = [conn for conn in connector.connectedItem.connectors if conn.connectedItem is self]
372
                    # check if two items are connected each other
373
                    if SymbolValidation.DisconnectedFromOppositeSide in rules:
374
                        if not matches:
375
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
376
                            error.setPosition(list(connector.center()))
377
                            error.parent = self
378
                            error.msg = self.tr('Disconnected from opposite side')
379
                            error.setToolTip(error.msg)
380
                            error.area = self.area
381
                            error.name = 'Error'
382
                            error.items = [ connector.connectedItem ]
383
                            errors.append(error)
384

    
385
            # check duplicated connection
386
            if SymbolValidation.DuplicatedConnectionError in rules:
387
                if len(connectedUid) is not len(set(connectedUid)):
388
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
389
                    error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
390
                    error.parent = self
391
                    error.msg = self.tr('Duplicated connection error')
392
                    error.setToolTip(error.msg)
393
                    error.area = self.area
394
                    error.name = 'Error'
395
                    errors.append(error)
396

    
397
            # check line type
398
            if SymbolValidation.LineTypeError in rules:
399
                if self.conn_type:
400
                    for index in range(len(self.conn_type)):
401
                        item = self.connectors[index].connectedItem
402
                        if item and type(item) is QEngineeringLineItem:
403
                            if (QEngineeringLineItem.check_piping(self.conn_type[index], True) and not item.is_piping()) or \
404
                                    (not QEngineeringLineItem.check_piping(self.conn_type[index]) and item.is_piping(True)):
405
                                error = SymbolSvgItem.createItem('Error', None, dataPath)
406
                                error.setPosition(list(self.connectors[index].center()))
407
                                error.parent = self
408
                                error.msg = self.tr('Line type error')
409
                                error.setToolTip(error.msg)
410
                                error.area = self.area
411
                                error.name = 'Error'
412
                                errors.append(error)
413
            
414
            configs = app_doc_data.getConfigs('Project', 'Operation Code')
415
            code = configs[0].value if 1 == len(configs) else ''
416
            if code == 'nK6uurpuiw==': # Samsung
417
                if self.angle != 0.0:
418
                    '''
419
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
420
                    error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
421
                    error.parent = self
422
                    error.msg = self.tr('X Symbol angle is not zero')
423
                    error.setToolTip(error.msg)
424
                    error.area = self.area
425
                    error.name = 'Warning'
426
                    errors.append(error)
427
                    '''
428

    
429
                allowed_error = 0.3
430
                for conn in self.connectors:
431
                    if not conn.connectedItem:
432
                        continue
433

    
434
                    if SymbolValidation.XMismatchedConnectionPoint in rules:
435
                        for conn2 in conn.connectedItem.connectors:
436
                            if not conn2.connectedItem or conn2.connectedItem is not self:
437
                                continue
438

    
439
                            if abs(conn.center()[0] - conn2.center()[0]) > allowed_error or abs(conn.center()[1] - conn2.center()[1]) > allowed_error:
440
                                error = SymbolSvgItem.createItem('Error', None, dataPath)
441
                                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
442
                                error.parent = self
443
                                error.msg = self.tr('X Mismatched connection point')
444
                                error.setToolTip(error.msg)
445
                                error.area = self.area
446
                                error.name = 'Warning'
447
                                error.items = [ conn.connectedItem ]
448
                                errors.append(error)
449
                
450
                    if SymbolValidation.SymbolToSymbolConnectionWarning in rules:
451
                        if issubclass(type(conn.connectedItem), SymbolSvgItem):
452
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
453
                            error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
454
                            error.parent = self
455
                            error.msg = self.tr('Symbol to symbol connection warning')
456
                            error.setToolTip(error.msg)
457
                            error.area = self.area
458
                            error.name = 'Warning'
459
                            error.items = [ conn.connectedItem ]
460
                            errors.append(error)
461
                                
462
        except Exception as ex:
463
            from App import App
464
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
465
                                                          sys.exc_info()[-1].tb_lineno)
466
            App.mainWnd().addMessage.emit(MessageType.Error, message)
467

    
468
        return errors
469

    
470
    def includes(self, item, margin=0):
471
        """return True if symbol contains given point else return False"""
472
        if hasattr(item, 'sceneBoundingRect'):
473
            rect = item.sceneBoundingRect()
474
            topLeft = QPoint(rect.x(), rect.y())
475
            bottomRight = QPoint(rect.x() + rect.width(), rect.y() + rect.height())
476
            margin_rect = QRect(self.sceneBoundingRect().x() - margin, self.sceneBoundingRect().y() - margin, \
477
                            self.sceneBoundingRect().width() + 2 * margin, self.sceneBoundingRect().height() + 2 * margin)
478
            if margin_rect.contains(topLeft) and margin_rect.contains(bottomRight):
479
                return True
480
            else:
481
                return False
482

    
483
        else:
484
            pt = item
485
            rect = self.sceneBoundingRect()
486
            allowed_error = 0.1
487

    
488
            if abs(rect.x() - 0) <= allowed_error and abs(rect.y() - 0) <= allowed_error:
489
                # when first recognition step, symbols are not in scene(not yet added) therefore cannot use scenebounding rect
490
                minX = self.loc[0] - margin
491
                minY = self.loc[1] - margin
492
                maxX = minX + self.size[0] + margin
493
                maxY = minY + self.size[1] + margin
494
            else:
495
                minX = rect.x() - margin
496
                minY = rect.y() - margin
497
                maxX = minX + rect.width() + margin
498
                maxY = minY + rect.height() + margin
499

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

    
502
    '''
503
    def associations(self):
504
        """ return associated instance """
505
        # move to abstractitem
506
        import uuid
507

508
        res = []
509
        for key in self._associations.keys():
510
            index = 0
511
            for assoc in self._associations[key]:
512
                # find owner with uid
513
                if assoc and type(assoc) is uuid.UUID:
514
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(assoc)]
515
                    if matches:
516
                        res.append(matches[0]) # TODO: need to update association with instance
517
                        self._associations[key][index] = matches[0]
518
                    index += 1
519
                # up to here
520
                elif assoc:
521
                    res.append(assoc)
522

523
        for key in self.attrs.keys():
524
            if type(key.AssocItem) is uuid.UUID:
525
                for assoc in res:
526
                    if str(key.AssocItem) == str(assoc.uid):
527
                        key.AssocItem = assoc
528

529
        return res
530

531
    def texts(self):
532
        """ return text type of associations """
533
        from EngineeringTextItem import QEngineeringTextItem
534

535
        res = []
536
        for text in [x for x in self.associations() if issubclass(type(x), QEngineeringTextItem)]:
537
            consumed = False
538
            for key in list(self.attrs.keys()):
539
                if key.AssocItem and key.AssocItem is text:
540
                    consumed = True
541
            if not consumed:
542
                res.append(text)
543

544
        return res
545

546
    def symbols(self):
547
        """ return symbol type of associations """
548
        res = []
549
        for symbol in [x for x in self.associations() if issubclass(type(x), SymbolSvgItem)]:
550
            consumed = False
551
            for key in list(self.attrs.keys()):
552
                if key.AssocItem and key.AssocItem is symbol:
553
                    consumed = True
554
            if not consumed:
555
                res.append(symbol)
556

557
        return res
558
    '''
559

    
560
    def itemChange(self, change, value):
561
        """ call signals when item's position or rotation is changed """
562
        if not self.scene(): return super().itemChange(change, value)
563

    
564
        if change == QGraphicsItem.ItemPositionHasChanged or change == QGraphicsItem.ItemRotationHasChanged or \
565
                change == QGraphicsItem.ItemScaleHasChanged:
566
            from EngineeringLineItem import QEngineeringLineItem
567
            for connector in self.connectors:
568
                if connector.connectedItem is not None and type(connector.connectedItem) is QEngineeringLineItem:
569
                    line = connector.connectedItem
570
                    line.update_shape(self, connector.center())
571
                    #line.update_arrow()
572
                    for conn in line.connectors:
573
                        conn.transfer.onPosChanged.emit(conn)
574

    
575
            self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
576
            self.loc[0], self.loc[1] = round(self.sceneBoundingRect().top()), round(self.sceneBoundingRect().left())
577

    
578
            scene_origin = self.mapToScene(self.transformOriginPoint())
579

    
580
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
581
            grid = int(configs[0].value) if 1 == len(configs) else -1
582
            if grid == 1:
583
                #dx = round(scene_origin.x()) - scene_origin.x()
584
                #dy = round(scene_origin.y()) - scene_origin.y()
585
                #if dx or dy:
586
                #    self.moveBy(dx, dy)
587

    
588
                self.origin = [round(scene_origin.x()), round(scene_origin.y())]
589
            else:
590
                self.origin = [scene_origin.x(), scene_origin.y()]
591

    
592
            if hasattr(self.scene(), 'contents_changed'):
593
                self.scene().contents_changed.emit()
594

    
595
            return value
596

    
597
        return super().itemChange(change, value)
598

    
599
    def toSql_Components(self):
600
        """ convert Components data to sql query """
601
        from AppDocData import AppDocData
602
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
603

    
604
        scale = '1.0'
605
        if type(self) is QEngineeringEquipmentItem:
606
            scaleX = abs(round(self.transform().m11() * 100))
607
            scaleY = abs(round(self.transform().m22() * 100))
608
            scale = str(scaleX / 100.0) + ',' + str(scaleY / 100.0)
609
        else:
610
            _scale = str(round(self.scale(), 2))
611
            scale = _scale + ',' + _scale
612

    
613
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner',
614
                'Connected', '[Supplied By]', \
615
                'SpecialItemTypes_UID', 'OriginIndex', '[From]', '[To]', '[Freeze]', '[Connected Item]', '[Flip]',
616
                'SceneOriginPoint', 'Value'] # value is scale in symbol
617
        values = ['?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?']
618
        origin = self.mapToScene(self.transformOriginPoint())
619
        param = [(str(self.uid), str(AppDocData.instance().activeDrawing.UID), self.dbUid, self.loc[0], self.loc[1],
620
                  self.size[0], self.size[1], math.radians(self.rotation()),
621
                  self.area, str(self.owner) if self.owner else None, \
622
                  str(self.conns[0]) if self.conns else None, \
623
                  self.prop('Supplied By'), \
624
                  str(self.special_item_type) if self.special_item_type else None, \
625
                  self.currentPointModeIndex, \
626
                  None, None, self.prop('Freeze') if self.prop('Freeze') else 0,
627
                  str(self.prop('Connected Item')) if self.prop('Connected Item') else None,
628
                  self.flip, '{},{}'.format(origin.x(), origin.y()), scale)]
629
        sql = 'insert into Components({}) values({})'.format(','.join(cols), ','.join(values))
630

    
631
        return (sql, tuple(param))
632

    
633
    def toSql_return_separately(self):
634
        """ convert valve data to sql query """
635
        import uuid
636

    
637
        res = []
638
        resLater = []
639

    
640
        res.append(self.toSql_Components())
641

    
642
        _attrs = self.getAttributes()
643
        if _attrs:
644
            cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value', 'Association_UID', 'Freeze']
645
            values = ['?', '?', '?', '?', '?', '?']
646
            params = []
647
            for key in _attrs.keys():
648
                if key.AttributeType != 'Spec':
649
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), str(_attrs[key]), str(key.AssocItem),
650
                                   str(key.Freeze)))
651
                elif key.AttributeType == 'Spec':
652
                    if type(_attrs[key]) is not list: continue
653
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), (str(_attrs[key][0]) + ',' + str(_attrs[key][1])), str(key.AssocItem),
654
                                   str(key.Freeze)))
655
            sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
656
            res.append((sql, tuple(params)))
657

    
658
        if self.associations():
659
            cols = ['UID', '[Type]', 'Components_UID', 'Association']
660
            values = ['?', '?', '?', '?']
661
            params = []
662
            for assoc in self.associations():
663
                params.append(
664
                    (str(uuid.uuid4()), QEngineeringAbstractItem.assoc_type(assoc), str(self.uid), str(assoc.uid)))
665
            sql = 'insert into Associations({}) values({})'.format(','.join(cols), ','.join(values))
666
            resLater.append((sql, tuple(params)))
667

    
668
        # save connectors to database
669
        if self.connectors:
670
            cols = ['Components_UID', '[Index]', 'X', 'Y', 'Connected', 'Connected_At']
671
            values = ['?', '?', '?', '?', '?', '?']
672
            params = []
673
            index = 1
674
            for connector in self.connectors:
675
                params.append( \
676
                    (  # str(connector.uid),
677
                        str(self.uid), index, connector.center()[0], connector.center()[1], \
678
                        str(connector.connectedItem.uid) if connector.connectedItem else None, \
679
                        str(connector._connected_at)) \
680
                    )
681
                index += 1
682
            sql = 'insert into Points({}) values({})'.format(','.join(cols), ','.join(values))
683
            resLater.append((sql, tuple(params)))
684
        # up to here
685

    
686
        return res, resLater
687

    
688
    @property
689
    def mainSubSize(self):
690
        """ return main sub size """
691
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
692
        from AppDocData import AppDocData
693

    
694
        matches = [assoc for assoc in self.associations() if type(assoc) is QEngineeringSizeTextItem]
695
        if matches:
696
            return matches[0].mainSubSize
697
        else:
698
            return [None, None]
699
        '''
700
        appDocData = AppDocData.instance()
701
        configs = appDocData.getConfigs('Size', 'Delimiter')
702
        delimiter = configs[0].value.upper() if 1 == len(configs) else None
703

704
        configs = appDocData.getConfigs('Project', 'Unit')
705
        sizeUnit = configs[0].value if 1 == len(configs) else None
706

707
        if matches:
708
            first, second = '', ''
709
            text = matches[0].text().replace("'", '"').upper()
710
            # for imperial
711
            if sizeUnit == 'Imperial' and text.find('"') is not -1:
712
                first = text[:text.find('"') + 1]
713

714
                text = text[text.find('"') + 1:].replace(delimiter.upper(), '',
715
                                                         1).strip() if delimiter is not None else text[text.find(
716
                    '"') + 1:].strip()
717

718
                if text and text.find('"') is len(text) - 1:
719
                    second = text
720

721
                    return [first, second]
722
            # for metric
723
            else:
724
                split_text = text.strip().split(delimiter.upper())
725
                if len(split_text) is not 2:
726
                    return [None, None]
727
                first = split_text[0]
728
                second = split_text[1]
729
                return [first, second]
730

731
        return [None, None]
732
        '''
733

    
734
    def buildItem(self, name, _type, angle: float, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel,
735
                  dbUid=None, scale=[1.0, 1.0]):
736
        """
737
        build symbol item
738
        :param name:
739
        :param _type:
740
        :param angle:
741
        :param loc:
742
        :param size:
743
        :param origin:
744
        :param connPts:
745
        :param parentSymbol:
746
        :param childSymbol:
747
        :param hasInstrumentLabel:
748
        :param dbUid:
749
        :return:
750
        """
751
        from SpecialItemTypesDialog import SpecialItemTypes
752
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
753

    
754
        try:
755
            app_doc_data = AppDocData.instance()
756
            self.name = name
757
            self.type = _type
758
            self.angle = angle
759
            self.loc = loc
760
            self.size = size if size else [0, 0]
761
            self.origin = origin
762
            if dbUid is None:
763
                symbolInfo = app_doc_data.getSymbolByQuery('name', name)
764
            else:
765
                symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
766
            self.dbUid = symbolInfo.uid
767
            self.iType = symbolInfo.iType
768
            self.text_area = symbolInfo.text_area
769
            self._class = symbolInfo.baseSymbol
770
            originalPoint = symbolInfo.getOriginalPoint().split(',')
771
            self.symbolOrigin = [float(originalPoint[0]), float(originalPoint[1])]
772
            self.symbolConvertingOrigin = None
773
            convertOriginalPoint = symbolInfo.getConvertingPoint()
774
            if convertOriginalPoint:
775
                convertOriginalPoint = convertOriginalPoint.split(',')
776
                self.symbolConvertingOrigin = [float(convertOriginalPoint[0]), float(convertOriginalPoint[1])]
777
            if type(self) is QEngineeringEquipmentItem:
778
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
779
                transform.scale(scale[0], scale[1])
780
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
781
                if self.flip == 1:
782
                    transform.scale(-1.0, 1.0)
783
                    transform.translate(-2 * self.symbolOrigin[0], 0)
784
                self.setTransform(transform)
785
            else:
786
                self.setScale(scale[0])
787
                if self.flip == 1:
788
                    transform = QTransform()
789
                    transform.scale(-1.0, 1.0)
790
                    transform.translate(-2 * self.symbolOrigin[0], 0)
791
                    self.setTransform(transform)
792

    
793
            # setting connectors
794
            connectionPoints = symbolInfo.getConnectionPoint().split('/')
795
            for index in range(len(connectionPoints)):
796
                if connectionPoints[index] == '':
797
                    break
798
                tokens = connectionPoints[index].split(',')
799

    
800
                direction = 'AUTO'
801
                symbol_idx = '0'
802
                if len(tokens) == 2:
803
                    x = float(tokens[0])
804
                    y = float(tokens[1])
805
                elif len(tokens) >= 3:
806
                    direction = connPts[index][0] if index < len(connPts) else tokens[0]
807
                    x = float(tokens[1])
808
                    y = float(tokens[2])
809
                if len(tokens) >= 4:
810
                    symbol_idx = tokens[3]
811
                if len(tokens) >= 6:
812
                    if tokens[4] == 'In':
813
                        self.in_out_connector[0].append(index)
814
                    elif tokens[4] == 'Out':
815
                        self.in_out_connector[1].append(index)
816
                    if tokens[5] == 'O':
817
                        self.break_connector.append(index)
818
                if len(tokens) >= 7:
819
                    self.conn_type.append(tokens[6])
820

    
821
                self.setConnector(index + 1)
822
                self.connectors[index].direction = direction
823
                self.connectors[index].symbol_idx = symbol_idx
824
                self.connectors[index].setPos((x, y))
825
                self.connectors[index].connectPoint = (x, y)
826
                # recognized_pt is only valid right after symbol is recognized
827
                if index < len(connPts):
828
                    self.connectors[index].recognized_pt = (connPts[index][0], connPts[index][1]) if \
829
                        len(connPts[index]) == 2 else (connPts[index][1], connPts[index][2]) if \
830
                        len(connPts[index]) == 3 else (connPts[index][1], connPts[index][2]) if \
831
                        len(connPts[index]) == 4 else None
832
                # up to here
833
            self.parentSymbol = parentSymbol
834
            self.childSymbol = childSymbol
835
            self.hasInstrumentLabel = hasInstrumentLabel
836
            self.currentPointModeIndex = 0
837
            self.special_item_type = SpecialItemTypes.instance().find_match_exactly(self)
838

    
839
            tooltip = f"<b>{str(self.uid)}</b><br>{self.type}={self.name}"
840
            if self.hit_ratio:
841
                tooltip += f"<br><li>recognition ratio={self.hit_ratio}"
842
            self.setToolTip(tooltip)
843

    
844
            return True
845
        except Exception as ex:
846
            from App import App
847

    
848
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
849
                                                          sys.exc_info()[-1].tb_lineno)
850
            App.mainWnd().addMessage.emit(MessageType.Error, message)
851

    
852
            return False
853

    
854
    '''
855
        @brief  return bounding box of symbol
856
        @author humkyung
857
        @date   2018.04.08
858
    '''
859
    def rect(self):
860
        return self.sceneBoundingRect()
861

    
862
    def is_connectable(self, item, toler=10):
863
        """return true if line is able to connect symbol"""
864

    
865
        for connector in self.connectors:
866
            for iConnector in item.connectors:
867
                dx = connector.center()[0] - iConnector.center()[0]
868
                dy = connector.center()[1] - iConnector.center()[1]
869
                if math.sqrt(dx * dx + dy * dy) < toler:
870
                    return True
871

    
872
        return False
873

    
874
    '''
875
        @author     humkyung
876
        @date       2018.07.03
877
    '''
878
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
879
        """ check if given item is connected to self """
880

    
881
        _connectors = [connector for connector in self.connectors if
882
                       (connector.connectedItem == item and (connector._connected_at == at if at else True))]
883
        return len(_connectors) > 0
884

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

    
888
        lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
889
        rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
890
        if lhs_matches and rhs_matches and lhs is not rhs:
891
            return (lhs_matches[0] in [0, 1] and rhs_matches[0] in [0, 1]) or (
892
                    lhs_matches[0] in [2, 3] and rhs_matches[0] in [2, 3])
893

    
894
        return False
895

    
896
    def canBeSecondary(self, line):
897
        """ check given line is not connected(ex: 0-1, 2-3) """
898
        preItem = None
899

    
900
        item = [item.connectedItem for item in self.connectors if
901
                item.connectedItem is not None and item.connectedItem.owner is not None]
902
        if item:
903
            preItem = item[0]
904

    
905
        if preItem is not None and not self.next_connected(line, preItem):
906
            return True
907
        else:
908
            return False
909

    
910
    '''
911
        @brief      connect line and symbol is able to be connected and return line
912
        @author     humkyung
913
        @date       2018.04.16
914
        @history    humkyung 2018.05.08 check if symbol is possible to be connected
915
                    Jeongwoo 2018.05.15 Connect each symbol and line
916
    '''
917
    def connect_if_possible(self, obj, toler=10):
918
        """ this method not update item's position """
919

    
920
        from shapely.geometry import Point
921
        from EngineeringLineItem import QEngineeringLineItem
922

    
923
        res = []
924
        try:
925
            if type(obj) is QEngineeringLineItem:
926
                configs = AppDocData.instance().getConfigs('Data', 'Grid')
927
                grid = int(configs[0].value) if 1 == len(configs) else -1
928

    
929
                startPt = obj.start_point()
930
                endPt = obj.end_point()
931
                conns = []
932
                for i in range(len(self.connectors)):
933
                    dist = Point(startPt[0], startPt[1]).distance(Point(self.connectors[i].center()[0], self.connectors[i].center()[1]))
934
                    if (dist < toler):
935
                        if self.connectors[i].connectedItem is None and obj.connectors[0].connectedItem is None:
936
                            conns.append([dist, self.connectors[i], obj.connectors[0], self.connectors[i].center(), endPt])
937
                            #self.connectors[i].connect(obj)
938
                            #obj.connectors[0].connect(self)
939
                            ## line, start, end
940
                            #res.append(obj)
941
                            #res.append(self.connectors[i].center())
942
                            #res.append(endPt)
943
                    dist = Point(endPt[0], endPt[1]).distance(Point(self.connectors[i].center()[0], self.connectors[i].center()[1]))
944
                    if (dist < toler):
945
                        if self.connectors[i].connectedItem is None and obj.connectors[1].connectedItem is None:
946
                            conns.append([dist, self.connectors[i], obj.connectors[1], startPt, self.connectors[i].center()])
947
                            #self.connectors[i].connect(obj)
948
                            #obj.connectors[1].connect(self)
949
                            ## line, start, end
950
                            #res.append(obj)
951
                            #res.append(startPt)
952
                            #res.append(self.connectors[i].center())
953

    
954
                if conns:
955
                    conns.sort(key=lambda x: x[0])
956
                    conns[0][1].connect(obj)
957
                    conns[0][2].connect(self)
958
                    # line, start, end
959
                    res.append(obj)
960
                    if grid == 1:
961
                        res.append((round(conns[0][3][0]), round(conns[0][3][1])))
962
                        res.append((round(conns[0][4][0]), round(conns[0][4][1])))
963
                    else:
964
                        res.append(conns[0][3])
965
                        res.append(conns[0][4])
966

    
967
            elif issubclass(type(obj), SymbolSvgItem):
968
                selected = None
969
                for i in range(len(self.connectors)):
970
                    if i > 3: break
971
                    _pt1 = Point(self.connectors[i].center()[0], self.connectors[i].center()[1])
972
                    for j in range(len(obj.connectors)):
973
                        if j > 3: break
974
                        _pt2 = Point(obj.connectors[j].center()[0], obj.connectors[j].center()[1])
975
                        length = _pt1.distance(_pt2)
976
                        if (length < toler) and (selected is None or length < selected[0]):
977
                            selected = [length, self.connectors[i], obj.connectors[j]]
978

    
979
                if selected and selected[1].connectedItem is None and selected[2].connectedItem is None:
980
                    selected[1].connect(selected[2].parent)
981
                    selected[2].connect(selected[1].parent)
982
                    res.append(obj)
983
        except Exception as ex:
984
            from App import App
985
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
986
                                                          sys.exc_info()[-1].tb_lineno)
987
            App.mainWnd().addMessage.emit(MessageType.Error, message)
988

    
989
        return res
990

    
991
    '''
992
        @brief      disconnect connector item
993
        @author     kyouho
994
        @date       2018.08.30
995
    '''
996
    def disconnectedItemAtConnector(self, connector):
997
        for conn in self.connectors:
998
            if conn.isOverlapConnector(connector):
999
                conn.connectedItem = None
1000

    
1001
    '''
1002
        @brief  get connection point close to given point in tolerance
1003
        @author humkyung
1004
        @dat
1005
    '''
1006
    def getConnectionPointCloseTo(self, pt, toler=10):
1007
        import math
1008

    
1009
        for connector in self.connectors:
1010
            dx = connector.center()[0] - pt[0]
1011
            dy = connector.center()[1] - pt[1]
1012

    
1013
            if math.sqrt(dx * dx + dy * dy) < toler:
1014
                return connPt
1015

    
1016
        return None
1017

    
1018
    '''
1019
        @brief  return center of symbol
1020
        @author humkyung
1021
        @date   2018.04.08
1022
    '''
1023
    def center(self):
1024
        return self.sceneBoundingRect().center()
1025

    
1026
    '''
1027
        @brief      highlight connector and attribute
1028
        @authro     humkyung
1029
        @date       2018.05.02
1030
    '''
1031
    def hoverEnterEvent(self, event, minimum=False):
1032
        self.highlight(True, minimum)
1033

    
1034
    '''
1035
        @brief      unhighlight connector and attribute
1036
        @author     humkyung
1037
        @date       2018.05.02
1038
        @history    kyouho 2018.07.18 edit ArrowCursor
1039
    '''
1040
    def hoverLeaveEvent(self, event, minimum=False):
1041
        self.highlight(False, minimum)
1042

    
1043
    def highlight(self, flag, minimum=False):
1044
        """ highlight/unhighlight the symbol """
1045
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1046

    
1047
        try:
1048
            self.hover = flag
1049
            if type(self) is not QEngineeringEquipmentItem:
1050
                self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
1051
            self.update()
1052

    
1053
            if not minimum:
1054
                for assoc in self.associations():
1055
                    assoc.highlight(flag)
1056

    
1057
                for connector in self.connectors:
1058
                    connector.highlight(flag)
1059
        except Exception as ex:
1060
            from App import App
1061
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1062
                                                          sys.exc_info()[-1].tb_lineno)
1063
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1064

    
1065
    '''
1066
        @brief      set highlight
1067
        @author     kyouho
1068
        @date       2018.08.27
1069
    '''
1070
    def setHightlight(self):
1071
        """no more used"""
1072
        self.setColor('url(#hover)')
1073
        self.update()
1074

    
1075
    '''
1076
        @brief      unset highlight
1077
        @author     kyouho
1078
        @date       2018.08.27
1079
    '''
1080
    def unsetHightlight(self):
1081
        """no more used"""
1082
        self.setColor('url(#normal)')
1083
        self.update()
1084

    
1085
    '''
1086
        @brief  change cursor to CrossCursor if mouse point is close to connection point
1087
        @author humkyung
1088
        @date   2018.04.28
1089
    '''
1090
    def hoverMoveEvent(self, event):
1091
        pass
1092

    
1093
    '''
1094
        @brief      Mouse Press Event
1095
        @author     Jeongwoo
1096
        @date       18.04.11
1097
        @history    kyouho 2018.07.18 add isClick logic
1098
    '''
1099
    def mousePressEvent(self, event):
1100
        if event.buttons() == Qt.LeftButton:
1101
            """마우스 클릭한 위치를 저장한다."""
1102
            self._mouse_pos = [event.scenePos().x(), event.scenePos().y()]
1103

    
1104
            self.clicked.emit(self)
1105
        
1106
        super().mousePressEvent(event)
1107

    
1108
    def mouseReleaseEvent(self, event):
1109
        """Mouse Release Event"""
1110
        if hasattr(self, '_rotating') and event.button() == Qt.RightButton:
1111
            from RotateCommand import RotateCommand
1112

    
1113
            self.angle = -self._angle if -math.pi < self._angle < 0 else 2 * math.pi - self._angle
1114

    
1115
            self.scene().undo_stack.push(RotateCommand(self.scene(), [self,], self._rotating))
1116

    
1117
            self.ungrabMouse()
1118
            del self._rotating
1119

    
1120
        if hasattr(self, '_scale') and event.button() == Qt.RightButton:
1121
            self.ungrabMouse()
1122
            del self._scale
1123
            del self._scale_x
1124

    
1125
        super().mouseReleaseEvent(event)
1126

    
1127
    def mouseMoveEvent(self, event):
1128
        """ rotate symbol around current mouse point """
1129

    
1130
        if hasattr(self, '_rotating'):
1131
            # get origin point of symbol
1132
            origin = self.mapToScene(self.transformOriginPoint())
1133
            # up to here
1134

    
1135
            dx, dy = event.scenePos().x() - origin.x(), event.scenePos().y() - origin.y()
1136
            length = math.sqrt(dx * dx + dy * dy)
1137

    
1138
            self._angle = 0
1139
            if length > 0:
1140
                self._angle = math.acos(dx / length)
1141
                cross = int(np.cross([1, 0], [dx, dy]))
1142
                self._angle = -self._angle if cross < 0 else self._angle
1143
                self._angle = math.pi*2 + self._angle if self._angle < 0 else self._angle
1144

    
1145
                modifiers = QApplication.keyboardModifiers()
1146
                if modifiers == Qt.ShiftModifier:
1147
                    self.rotate(self._angle)
1148
                else:
1149
                    step = math.radians(15)
1150
                    quotient = int(self._angle / step)
1151
                    angle = quotient * step
1152
                    self._angle = angle if (self._angle - angle) < step * 0.5 else (quotient + 1) * step
1153

    
1154
                    self.rotate(self._angle)
1155

    
1156
        if hasattr(self, '_scale'):
1157
            pass
1158
            '''
1159
            dx = event.scenePos().x() - self._scale_x
1160
            step = 8
1161
            quotient = int(dx / step)
1162
            scale = 0.1 * quotient
1163

1164
            self.setScale(self._scale + scale if self._scale + scale > 0.3 else 0.3)
1165
            '''
1166

    
1167
        super().mouseMoveEvent(event)
1168

    
1169
    def removeSelfAttr(self, attributeName):
1170
        target = None
1171
        for attr in self.attrs:
1172
            if attr.Attribute == attributeName:
1173
                target = attr
1174
                break
1175

    
1176
        if target:
1177
            del self.attrs[attr]
1178

    
1179
    '''
1180
        @brief      Find TextItem contain Point
1181
        @author     kyouho
1182
        @date       18.07.17
1183
        @ no more used
1184
    '''
1185
    def findTextItemInPoint(self, point):
1186
        from EngineeringTextItem import QEngineeringTextItem
1187

    
1188
        scene = self.scene()
1189

    
1190
        for item in scene.items():
1191
            if type(item) is QEngineeringTextItem:
1192
                if self.isOverlapItemAndPoint(item, point):
1193
                    return (True, item)
1194

    
1195
        return (False,)
1196

    
1197
    '''
1198
        @brief      Check Overlap
1199
        @author     kyouho
1200
        @date       18.07.17
1201
        @ no more used
1202
    '''
1203
    def isOverlapItemAndPoint(self, item, point):
1204
        x = point.x()
1205
        y = point.y()
1206
        loc = item.loc
1207
        size = item.size
1208

    
1209
        if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
1210
            return True
1211
        else:
1212
            return False
1213
        
1214
    def bind_end_break(self):
1215
        from shapely.geometry import Point
1216
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1217
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1218
        from EngineeringLineItem import QEngineeringLineItem
1219
        from AppDocData import AppDocData
1220
        from App import App
1221

    
1222
        if self.owner or self.prop('Connected Item'):
1223
            return
1224
        
1225
        app_doc_data = AppDocData.instance()
1226
        configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1227
        toler = int(configs[0].value) if configs else 20
1228

    
1229
        lines = sorted([item for item in self.scene().items() if type(item) is QEngineeringLineItem], key=lambda param: param.length(), reverse=True)
1230
        symbols = [item for item in self.scene().items() if issubclass(type(item), SymbolSvgItem) and type(item) is not QEngineeringEndBreakItem and  type(item) is not QEngineeringSpecBreakItem]
1231
        end_breaks = [item for item in self.scene().items() if type(item) is QEngineeringEndBreakItem]
1232

    
1233
        usedItemPairs = []
1234
        for end_break in end_breaks:
1235
            if end_break.prop('Freeze') or end_break.owner or end_break.prop('Connected Item'):
1236
                usedItemPairs.append([end_break.owner, end_break.prop('Connected Item')])
1237
        
1238
        originPoint = Point(self.origin[0], self.origin[1])
1239
        minD = sys.maxsize
1240
        ownerItem = None
1241
        connectedItem = None
1242

    
1243
        for symbol in symbols:
1244
            for conn in symbol.connectors:
1245
                dist = originPoint.distance(Point(conn.sceneConnectPoint[0], conn.sceneConnectPoint[1]))
1246
                if not conn.connectedItem or not issubclass(type(conn.connectedItem), QEngineeringAbstractItem) or \
1247
                    [pair for pair in usedItemPairs if symbol in pair and conn.connectedItem in pair] or dist > 3 * toler or dist > minD:
1248
                    continue
1249

    
1250
                minD = dist
1251
                ownerItem = symbol
1252
                connectedItem = conn.connectedItem
1253

    
1254
        for line in lines:
1255
            for conn in line.connectors:
1256
                dist = originPoint.distance(Point(conn.sceneConnectPoint[0], conn.sceneConnectPoint[1]))
1257
                if not conn.connectedItem or not issubclass(type(conn.connectedItem), QEngineeringAbstractItem) or \
1258
                    conn._connected_at != QEngineeringAbstractItem.CONNECTED_AT_BODY  or \
1259
                    [pair for pair in usedItemPairs if line in pair and conn.connectedItem in pair] or dist > 3 * toler or dist > minD:
1260
                    continue
1261

    
1262
                minD = dist
1263
                ownerItem = line
1264
                connectedItem = conn.connectedItem
1265

    
1266
        if ownerItem and connectedItem:
1267
            self.set_property('Connected Item', connectedItem)
1268
            self.setToolTip('owner : ' + str(ownerItem))
1269
            self.owner = ownerItem
1270
            self.set_property('Freeze', True)
1271

    
1272
            App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute(self)
1273

    
1274
    '''
1275
        @brief  remove item when user press delete key
1276
        @author humkyung
1277
        @date   2018.04.23
1278
        @history    2018.05.17  Jeongwoo    Add if-statement and move 'break'
1279
                    2018.05.25  Jeongwoo    Seperate delete item method
1280
    '''
1281
    def keyPressEvent(self, event):
1282
        from EngineeringErrorItem import QEngineeringErrorItem
1283
        from RotateSymbolDialog import QRotateSymbolDialog
1284
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1285
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1286
        from RotateCommand import RotateCommand
1287

    
1288
        modifiers = QApplication.keyboardModifiers()
1289

    
1290
        if not self.isSelected():
1291
            return
1292
        elif event.key() == Qt.Key_A:
1293
            self.contextSelectAll()
1294
        elif event.key() == Qt.Key_N:
1295
            delta = 40 if modifiers == Qt.ControlModifier else 10
1296
            self.move_near_main_line(delta)
1297
        elif event.key() == Qt.Key_G:
1298
            self.freeze_attriute()
1299
        elif event.key() == Qt.Key_B:
1300
            if type(self) is not QEngineeringEndBreakItem:
1301
                self.bind_close_items()
1302
            else:
1303
                self.bind_end_break()
1304
        elif event.key() == Qt.Key_S:
1305
            self.contextUp()
1306
        elif event.key() == Qt.Key_D:
1307
            self.contextDown()
1308
        #elif event.key() == Qt.Key_O and type(self) is not QEngineeringErrorItem:
1309
        #    pass
1310
        #elif event.key() == Qt.Key_C and type(self) is not QEngineeringErrorItem:
1311
        #    self.changeConnPoint()
1312
        elif False:#event.key() == Qt.Key_Return:
1313
            dlg = QRotateSymbolDialog(None, self.rotation(), self.origin, self.zValue())
1314
            if QDialog.Accepted == dlg.showDialog():
1315
                _angle = self.rotation()
1316
                self.rotate(math.radians(dlg.angle))
1317
                self.scene().undo_stack.push(RotateCommand(self.scene(), [self, ], _angle))
1318
                self.angle = dlg.angle
1319
        elif event.key() == Qt.Key_Escape:
1320
            if hasattr(self, '_rotating'):
1321
                self.ungrabMouse()
1322

    
1323
                self.rotate(math.radians(self._rotating))
1324
                del self._rotating
1325
            if hasattr(self, '_scale'):
1326
                pass
1327
                '''
1328
                self.ungrabMouse()
1329

1330
                self.setScale(self._scale)
1331
                del self._scale
1332
                del self._scale_x
1333
                '''
1334
        elif event.key() == Qt.Key_Up:  # translate up/down/left/right symbol
1335
            if modifiers == Qt.AltModifier and type(self) is QEngineeringEquipmentItem:
1336
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1337
                transform.scale(self.transform().m11(), self.transform().m22() + 0.05)
1338
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1339
                self.setTransform(transform)
1340
            else:
1341
                delta = 10 if modifiers == Qt.ControlModifier else 1
1342

    
1343
                #self.loc[1] = self.loc[1] - delta
1344
                #self.origin[1] = self.origin[1] - delta
1345
                self.moveBy(0, -delta)
1346
        elif event.key() == Qt.Key_Down:
1347
            if modifiers == Qt.AltModifier and type(self) is QEngineeringEquipmentItem:
1348
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1349
                transform.scale(self.transform().m11(), self.transform().m22() - 0.05 if self.transform().m22() - 0.05 > 0.3 else 0.3)
1350
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1351
                self.setTransform(transform)
1352
            else:
1353
                delta = 10 if modifiers == Qt.ControlModifier else 1
1354

    
1355
                #self.loc[1] = self.loc[1] + delta
1356
                #self.origin[1] = self.origin[1] + delta
1357
                self.moveBy(0, delta)
1358
        elif event.key() == Qt.Key_Left:
1359
            if modifiers == Qt.AltModifier and type(self) is QEngineeringEquipmentItem:
1360
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1361
                if self.flip == 1:
1362
                    transform.scale(self.transform().m11() + 0.05 if self.transform().m11() + 0.05 < -0.3 else -0.3, self.transform().m22())
1363
                else:
1364
                    transform.scale(self.transform().m11() - 0.05 if self.transform().m11() - 0.05 > 0.3 else 0.3, self.transform().m22())
1365
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1366
                self.setTransform(transform)
1367
            else:
1368
                delta = 10 if modifiers == Qt.ControlModifier else 1
1369

    
1370
                #self.loc[0] = self.loc[0] - delta
1371
                #self.origin[0] = self.origin[0] - delta
1372
                self.moveBy(-delta, 0)
1373
        elif event.key() == Qt.Key_Right:
1374
            if modifiers == Qt.AltModifier and type(self) is QEngineeringEquipmentItem:
1375
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1376
                if self.flip == 1:
1377
                    transform.scale(self.transform().m11() - 0.05, self.transform().m22())
1378
                else:
1379
                    transform.scale(self.transform().m11() + 0.05, self.transform().m22())
1380
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1381
                self.setTransform(transform)
1382
            else:
1383
                delta = 10 if modifiers == Qt.ControlModifier else 1
1384

    
1385
                #self.loc[0] = self.loc[0] + delta
1386
                #self.origin[0] = self.origin[0] + delta
1387
                self.moveBy(delta, 0)
1388
        elif event.key() == Qt.Key_Plus or event.key() == 61:
1389
            if type(self) is QEngineeringEquipmentItem:
1390
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1391
                if self.flip == 1:
1392
                    transform.scale(self.transform().m11() - 0.1, self.transform().m22() + 0.1)
1393
                else:
1394
                    transform.scale(self.transform().m11() + 0.1, self.transform().m22() + 0.1)
1395
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1396
                self.setTransform(transform)
1397
            else:
1398
                self.setScale(self.scale() + 0.1)
1399
        elif event.key() == Qt.Key_Minus:
1400
            if type(self) is QEngineeringEquipmentItem:
1401
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1402
                if self.flip == 1:
1403
                    transform.scale(self.transform().m11() + 0.1 if self.transform().m11() + 0.1 < -0.3 else -0.3, self.transform().m22() - 0.1 if self.transform().m22() - 0.1 > 0.3 else 0.3)
1404
                else:
1405
                    transform.scale(self.transform().m11() - 0.1 if self.transform().m11() - 0.1 > 0.3 else 0.3, self.transform().m22() - 0.1 if self.transform().m22() - 0.1 > 0.3 else 0.3)
1406
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1407
                self.setTransform(transform)
1408
            else:
1409
                self.setScale(self.scale() - 0.1 if self.scale() - 0.1 > 0.3 else 0.3)
1410
        elif event.key() == Qt.Key_I or event.key() == Qt.Key_J or event.key() == Qt.Key_Q or event.key() == Qt.Key_Insert or event.key() == Qt.Key_M: #or event.key() == Qt.Key_X 
1411
            from App import App 
1412

    
1413
            App.mainWnd().keyPressEvent(event)
1414

    
1415
    def contextMenuEvent(self, event):
1416
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1417

    
1418
        items = self.scene().selectedItems()
1419
        if len(items) == 1 and self in items:
1420
            menu = QMenu()
1421

    
1422
            if type(self) == QEngineeringSpecBreakItem:
1423
                upAction = QAction('Set UpStream(S)', None)
1424
                upAction.triggered.connect(self.contextUp)
1425
                menu.addAction(upAction)
1426

    
1427
                downAction = QAction('Set DownStream(D)', None)
1428
                downAction.triggered.connect(self.contextDown)
1429
                menu.addAction(downAction)
1430

    
1431
            showAction = QAction('Show Onwer', None)
1432
            showAction.triggered.connect(self.contextShow)
1433
            menu.addAction(showAction)
1434

    
1435
            allAction = QAction('Select All in View(A)', None)
1436
            allAction.triggered.connect(self.contextSelectAll)
1437
            menu.addAction(allAction)
1438

    
1439
            moveAction = QAction('Move to Nearest(N)', None)
1440
            moveAction.triggered.connect(self.contextMove)
1441
            menu.addAction(moveAction)
1442

    
1443
            freezeAction = QAction('Freeze Attributes(G)', None)
1444
            freezeAction.triggered.connect(self.freeze_attriute)
1445
            menu.addAction(freezeAction)
1446

    
1447
            replaceAction = QAction('Replace This to Selected Symbol(I, Q)', None)
1448
            replaceAction.triggered.connect(self.contextReplace)
1449
            menu.addAction(replaceAction)
1450

    
1451
            insertAction = QAction('Insert Selected Symbol to This(J)', None)
1452
            insertAction.triggered.connect(self.contextInsert)
1453
            menu.addAction(insertAction)
1454

    
1455
            if type(self) != QEngineeringSpecBreakItem:
1456
                bindAction = QAction('Bind(B)', None)
1457
                bindAction.triggered.connect(self.contextBind)
1458
                menu.addAction(bindAction)
1459
            else:
1460
                bindAction = QAction('Set Batch(Bind)', None)
1461
                bindAction.triggered.connect(self.contextBindSpec)
1462
                menu.addAction(bindAction)
1463

    
1464
            rotateAction = QAction('Rotate(R)', None)
1465
            rotateAction.triggered.connect(self.contextRotate)
1466
            menu.addAction(rotateAction)
1467

    
1468
            flipAction = QAction('Flip(F)', None)
1469
            flipAction.triggered.connect(self.contextFlip)
1470
            menu.addAction(flipAction)
1471

    
1472
            deleteAction = QAction('Delete(E)', None)
1473
            deleteAction.triggered.connect(self.contextDelete)
1474
            menu.addAction(deleteAction)
1475

    
1476
            arsAction = QAction('Show on other App', None)
1477
            arsAction.triggered.connect(self.contextShowOnOtherApp)
1478
            menu.addAction(arsAction)
1479

    
1480
            menu.exec_(event.screenPos())
1481
        elif len(items) >= 2:
1482
            menu = QMenu()
1483

    
1484
            allAction = QAction('Select All in View(A)', None)
1485
            allAction.triggered.connect(self.contextSelectAll)
1486
            menu.addAction(allAction)
1487

    
1488
            batchAction = QAction('Set Batch(Insert)', None)
1489
            batchAction.triggered.connect(self.contextBatch)
1490
            menu.addAction(batchAction)
1491

    
1492
            alignAction = QAction('Align Selected Symbols', None)
1493
            alignAction.triggered.connect(self.contextAlignSymbol)
1494
            menu.addAction(alignAction)
1495

    
1496
            moveAction = QAction('Move to Nearest(N)', None)
1497
            moveAction.triggered.connect(self.contextMove)
1498
            menu.addAction(moveAction)
1499

    
1500
            mergeAction = QAction('Make Line(M)', None)
1501
            mergeAction.triggered.connect(self.contextMerge)
1502
            menu.addAction(mergeAction)
1503

    
1504
            replaceAction = QAction('Replace This to Selected Symbol(I, Q)', None)
1505
            replaceAction.triggered.connect(self.contextReplace)
1506
            menu.addAction(replaceAction)
1507

    
1508
            insertAction = QAction('Insert Selected Symbol to This(J)', None)
1509
            insertAction.triggered.connect(self.contextInsert)
1510
            menu.addAction(insertAction)
1511

    
1512
            deleteAction = QAction('Delete(E)', None)
1513
            deleteAction.triggered.connect(self.contextDelete)
1514
            menu.addAction(deleteAction)
1515

    
1516
            menu.exec_(event.screenPos())
1517

    
1518

    
1519
    def contextShow(self):
1520
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
1521

    
1522
        if type(self.owner) is QEngineeringLineNoTextItem:
1523
            self.owner.contextHighlight(self.owner)
1524

    
1525
    def contextSelectAll(self):
1526
        from App import App
1527
        
1528
        selectedSymbols = list(set([item.dbUid for item in self.scene().selectedItems() if issubclass(type(item), SymbolSvgItem)]))
1529
        rect = App.mainWnd().graphicsView.viewport().rect()
1530
        view_rect = App.mainWnd().graphicsView.mapToScene(rect).boundingRect()
1531
        #rect = App.mainWnd().graphicsView.mapToScene(App.mainWnd().graphicsView.rect()).boundingRect()
1532
        symbols = [item for item in self.scene().items() if issubclass(type(item), SymbolSvgItem) and item.dbUid in selectedSymbols and view_rect.contains(item.sceneBoundingRect().center())]
1533
        for symbol in symbols:
1534
            symbol.setSelected(True)
1535

    
1536
    def contextBatch(self):
1537
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_Insert, Qt.NoModifier)
1538
        self.keyPressEvent(event)
1539

    
1540
    def contextMerge(self):
1541
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_M, Qt.NoModifier)
1542
        self.keyPressEvent(event)
1543

    
1544
    def contextUp(self):
1545
        from App import App
1546
        import SelectAttributeCommand
1547
        _up = None
1548
        for prop, value in self.getAttributes().items():
1549
            if prop.Attribute == 'UpStream':
1550
                _up = prop
1551
                break
1552
        cmd = SelectAttributeCommand.SelectAttributeCommand(self, _up, App.mainWnd().graphicsView)
1553
        cmd.onSuccess.connect(App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute)
1554
        App.mainWnd().graphicsView.command = cmd
1555

    
1556
    def contextDown(self):
1557
        from App import App
1558
        import SelectAttributeCommand
1559
        _down = None
1560
        for prop, value in self.getAttributes().items():
1561
            if prop.Attribute == 'DownStream':
1562
                _down = prop
1563
                break
1564
        cmd = SelectAttributeCommand.SelectAttributeCommand(self, _down, App.mainWnd().graphicsView)
1565
        cmd.onSuccess.connect(App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute)
1566
        App.mainWnd().graphicsView.command = cmd
1567

    
1568
    def contextAlignSymbol(self):
1569
        scene = self.scene()
1570
        if scene:
1571
            symbolItems = [symbol for symbol in scene.selectedItems() if symbol is not self and issubclass(type(symbol), SymbolSvgItem)]
1572

    
1573
            for symbol in symbolItems:
1574
                dx = self.origin[0] - symbol.origin[0]
1575
                dy = self.origin[1] - symbol.origin[1]
1576
                if abs(dx) > abs(dy):
1577
                    if dx > 0:
1578
                        symbol.moveBy(0, dy)
1579
                    else:
1580
                        symbol.moveBy(0, dy)
1581
                else:
1582
                    if dy > 0:
1583
                        symbol.moveBy(dx, 0)
1584
                    else:
1585
                        symbol.moveBy(dx, 0)
1586

    
1587
    def contextMove(self):
1588
        self.move_near_main_line(10)
1589

    
1590
    def contextDelete(self):
1591
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_Delete, Qt.NoModifier)
1592
        self.scene().keyPressEvent(event)
1593

    
1594
    def contextReplace(self):
1595
        from App import App
1596

    
1597
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_I, Qt.NoModifier)
1598
        App.mainWnd().keyPressEvent(event)
1599

    
1600
    def contextInsert(self):
1601
        from App import App
1602

    
1603
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_J, Qt.NoModifier)
1604
        App.mainWnd().keyPressEvent(event)
1605

    
1606
    def contextShowOnOtherApp(self):
1607
        from TcpServer import TcpSocket
1608

    
1609
        app_doc_data = AppDocData.instance()
1610
        configs = app_doc_data.getConfigs('app', 'conn port')
1611
        #configs = app_doc_data.getAppConfigs('app', 'conn port')
1612
        port = 3030
1613
        if configs and 1 == len(configs):
1614
            port = int(configs[0].value)
1615
        tcpserver = TcpSocket(port)
1616
        tcpserver.sendMessage(f"{str(self.uid)},{self.origin[0]},{self.origin[1]},{self.size[0]},{self.size[1]},"
1617
                              f"{app_doc_data.imgName}")
1618
        
1619
    def contextBindSpec(self):
1620
        from App import App
1621
        if App.mainWnd().on_connect_spec_breaks([self]):
1622
            App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute(self)
1623

    
1624
    def contextBind(self):
1625
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_B, Qt.NoModifier)
1626
        self.keyPressEvent(event)
1627

    
1628
    def contextRotate(self):
1629
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_R, Qt.NoModifier)
1630
        self.scene().keyPressEvent(event)
1631

    
1632
    def contextFlip(self):
1633
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_F, Qt.NoModifier)
1634
        self.scene().keyPressEvent(event)
1635

    
1636
    def freeze_attriute(self, flag=None):
1637
        from App import App
1638

    
1639
        try:
1640
            if flag is None:
1641
                freeze = True
1642
                for _attr, _value in self.attrs.items():
1643
                    if _attr.Freeze:
1644
                        freeze = False
1645
                        break
1646
                
1647
                for _attr, _value in self.attrs.items():
1648
                    _attr.Freeze = freeze
1649

    
1650
                labels = [item for item in self.scene().items() if issubclass(type(item), SymbolSvgItem) and (item.iType == 19 or item.iType == 29 or item.iType == 30)]
1651
                if labels:
1652
                    labels = [label for label in labels if label.EvaluatedAttribute('OWNERSYMBOL') == self or \
1653
                            (type(label.EvaluatedAttribute('OWNERSYMBOL')) is str and label.EvaluatedAttribute('OWNERSYMBOL') == str(self.uid))]
1654
                    for label in labels:
1655
                        label.freeze_attriute(freeze)
1656

    
1657
                App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute(self)
1658
            else:
1659
                for _attr, _value in self.attrs.items():
1660
                    _attr.Freeze = flag
1661

    
1662
                labels = [item for item in self.scene().items() if issubclass(type(item), SymbolSvgItem) and (item.iType == 19 or item.iType == 29 or item.iType == 30)]
1663
                if labels:
1664
                    labels = [label for label in labels if label.EvaluatedAttribute('OWNERSYMBOL') == self or \
1665
                            (type(label.EvaluatedAttribute('OWNERSYMBOL')) is str and label.EvaluatedAttribute('OWNERSYMBOL') == str(self.uid))]
1666
                    for label in labels:
1667
                        label.freeze_attriute(flag)
1668
        except Exception as ex:
1669
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1670
                                                          sys.exc_info()[-1].tb_lineno)
1671
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1672

    
1673
    def move_near_main_line(self, length):
1674
        from EngineeringLineItem import QEngineeringLineItem
1675
        from EngineeringTextItem import QEngineeringTextItem
1676

    
1677
        scene = self.scene()
1678
        if scene:
1679
            symbolItems = [symbol for symbol in scene.selectedItems() if issubclass(type(symbol), SymbolSvgItem)]
1680
            textItems = [text for text in scene.selectedItems() if issubclass(type(text), QEngineeringTextItem)]
1681

    
1682
            symbolTextMatches = {}
1683

    
1684
            for text in textItems:
1685
                minDist = None
1686
                selected = None
1687
                center = text.sceneBoundingRect().center()
1688

    
1689
                for symbol in symbolItems:
1690
                    dx = symbol.origin[0] - center.x()
1691
                    dy = symbol.origin[1] - center.y()
1692
                    _length = math.sqrt(dx * dx + dy * dy)# - offset
1693

    
1694
                    if minDist is None or _length < minDist:
1695
                        minDist = _length
1696
                        selected = symbol
1697
                
1698
                if selected not in symbolTextMatches:
1699
                    symbolTextMatches[selected] = [text]
1700
                else:
1701
                    symbolTextMatches[selected].append(text)
1702
                    
1703
            for symbol in symbolItems:
1704
                found = False
1705

    
1706
                connectedSymbols = [_conn.connectedItem for _conn in symbol.connectors if _conn.connectedItem and issubclass(type(_conn.connectedItem), SymbolSvgItem)]
1707
                for connS in symbol.connectors:
1708
                    if found: break
1709

    
1710
                    connItemS = connS.connectedItem
1711
                    if connItemS and type(connItemS) is QEngineeringLineItem:
1712
                        for connL in connItemS.connectors:
1713
                            if found: break
1714

    
1715
                            connItemL = connL.connectedItem
1716
                            if connItemL and connItemL is not symbol and type(connItemL) is QEngineeringLineItem and connL._connected_at == QEngineeringAbstractItem.CONNECTED_AT_BODY:
1717
                                dx = connS.center()[0] - connL.center()[0]
1718
                                dy = connS.center()[1] - connL.center()[1]
1719
                                connL2 = [_conn for _conn in connItemS.connectors if _conn != connL][0]
1720
                                if connItemS.isVertical() and abs(dy) != length:
1721
                                    if dy < 0:
1722
                                        delta = - dy - length
1723
                                    else:
1724
                                        delta = - dy + length
1725
                                    symbol.moveBy(0, delta)
1726
                                    connItemS.set_line([[connL2.center()[0], connItemS.start_point()[1]], [connL2.center()[0], connItemS.end_point()[1]]])
1727
                                    for _symbol in connectedSymbols:
1728
                                        _symbol.moveBy(0, delta)
1729
                                    if symbol in symbolTextMatches:
1730
                                        for text in symbolTextMatches[symbol]:
1731
                                            text.moveText(0, 1 if delta > 0 else -1, abs(delta))
1732
                                    found = True
1733
                                    break
1734
                                elif connItemS.isHorizontal() and abs(dx) != length:
1735
                                    if dx < 0:
1736
                                        delta = - dx - length
1737
                                    else:
1738
                                        delta = - dx + length
1739
                                    symbol.moveBy(delta, 0)
1740
                                    connItemS.set_line([[connItemS.start_point()[0], connL2.center()[1]], [connItemS.end_point()[0], connL2.center()[1]]])
1741
                                    for _symbol in connectedSymbols:
1742
                                        _symbol.moveBy(delta, 0)
1743
                                    if symbol in symbolTextMatches:
1744
                                        for text in symbolTextMatches[symbol]:
1745
                                            text.moveText(1 if delta > 0 else -1, 0, abs(delta))
1746
                                    found = True
1747
                                    break
1748

    
1749
                if not found:
1750
                    matches = [conn for conn in symbol.connectors if conn.connectedItem]
1751
                    
1752
                    if len(matches) == 1:
1753
                        connS = matches[0]
1754
                        connItemS = connS.connectedItem
1755

    
1756
                        found = False
1757
                        if type(connItemS) is QEngineeringLineItem:
1758
                            for connL in connItemS.connectors:
1759
                                if found: break
1760

    
1761
                                connItemL = connL.connectedItem
1762

    
1763
                                if connItemL and connItemL is not symbol:
1764
                                    dx = symbol.center().x() - connItemL.center().x()
1765
                                    dy = symbol.center().y() - connItemL.center().y()
1766
                                    lx = connS.center()[0] - connL.center()[0]
1767
                                    ly = connS.center()[1] - connL.center()[1]
1768
                                    #connL2 = [_conn for _conn in connItemS.connectors if _conn != connL][0]
1769
                                    if abs(dy) > abs(dx) and abs(dy) != length:
1770
                                        if dy < 0:
1771
                                            delta = - ly - length
1772
                                        else:
1773
                                            delta = - ly + length
1774
                                        symbol.moveBy(-lx, delta)
1775
                                        for _symbol in connectedSymbols:
1776
                                            _symbol.moveBy(-lx, delta)
1777
                                        if symbol in symbolTextMatches:
1778
                                            for text in symbolTextMatches[symbol]:
1779
                                                text.moveText(1 if lx < 0 else -1, 0, abs(lx))
1780
                                                text.moveText(0, 1 if delta > 0 else -1, abs(delta))
1781
                                        found = True
1782
                                        break
1783
                                    elif abs(dy) < abs(dx) and abs(dx) != length:
1784
                                        if dx < 0:
1785
                                            delta = - lx - length
1786
                                        else:
1787
                                            delta = - lx + length
1788
                                        symbol.moveBy(delta, -ly)
1789
                                        for _symbol in connectedSymbols:
1790
                                            _symbol.moveBy(delta, -ly)
1791
                                        if symbol in symbolTextMatches:
1792
                                            for text in symbolTextMatches[symbol]:
1793
                                                text.moveText(0, 1 if ly < 0 else -1, abs(ly))
1794
                                                text.moveText(1 if delta > 0 else -1, 0, abs(delta))
1795
                                        found = True
1796
                                        break                
1797

    
1798
    def bind_close_items(self):
1799
        """ connect close item by pressing B """
1800
        from EngineeringLineItem import QEngineeringLineItem
1801
        from EngineeringNozzleItem import QEngineeringNozzleItem
1802
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1803
        from shapely.geometry import Point
1804

    
1805
        scene = self.scene()
1806
        app_doc_data = AppDocData.instance()
1807
        if scene:
1808
            configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1809
            toler = int(configs[0].value) if configs else 20
1810

    
1811
            items = [item for item in scene.items() if hasattr(item, 'connectors') and item is not self]
1812

    
1813
            for s_connector in self.connectors:
1814
                if s_connector.connectedItem:
1815
                    continue
1816

    
1817
                _pt1 = Point(s_connector.center()[0], s_connector.center()[1])
1818
                dist = sys.maxsize
1819
                selected = None
1820

    
1821
                for item in items:
1822
                    for i_connector in item.connectors:
1823
                        if i_connector.connectedItem:
1824
                            continue
1825

    
1826
                        _pt2 = Point(i_connector.center()[0], i_connector.center()[1])
1827
                        length = _pt1.distance(_pt2)
1828
                        if length < toler and length < dist:
1829
                            selected = item
1830

    
1831
                if selected:
1832
                    res = self.connect_if_possible(selected, toler)
1833
                    if res and type(selected) is QEngineeringLineItem:
1834
                        selected.set_line([res[1], res[2]])
1835

    
1836
            if type(self) is QEngineeringNozzleItem and self.connectors and not self.connectors[0].connectedItem:
1837
                items = [item for item in scene.items() if issubclass(type(item), QEngineeringEquipmentItem)]
1838

    
1839
                for item in items:
1840
                    if item.includes(self.connectors[0], margin=40):
1841
                        self.connectors[0].connect(item)
1842
                        break
1843

    
1844
            if type(self) is QEngineeringEquipmentItem:
1845
                configs = app_doc_data.getConfigs('Symbol', 'EQ binding')
1846
                eq_binding = int(configs[0].value) if configs else -1
1847
                for item in items:
1848
                    if eq_binding == 1 and (type(item) is QEngineeringLineItem and item.is_piping(True) or issubclass(type(item), SymbolSvgItem)):
1849
                        for index in range(len(item.connectors)):
1850
                            if item.connectors[index].connectedItem:
1851
                                continue
1852
                            
1853
                            if type(item) is QEngineeringLineItem or QEngineeringLineItem.check_piping(item.conn_type[index], True):
1854
                                if self.includes(item.connectors[index], margin=100):
1855
                                    item.connectors[index].connect(self)
1856

    
1857
                    elif issubclass(type(item), QEngineeringNozzleItem) and item.has_connection:
1858
                        for index in range(len(item.connectors)):
1859
                            if item.connectors[index].connectedItem or not QEngineeringLineItem.check_piping(item.conn_type[index], True):
1860
                                continue
1861

    
1862
                            if self.includes(item.connectors[index], margin=100):
1863
                                item.connectors[index].connect(self)
1864

    
1865
    '''
1866
        @brief      connect attribute
1867
        @author     humkyung
1868
        @date       2018.05.02
1869
        @history    humkyung 2018.05.09 append only nearest size attribute
1870
    '''
1871
    def connectAttribute(self, attributes, clear=True):
1872
        import math
1873
        from EngineeringTextItem import QEngineeringTextItem
1874
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1875
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1876
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1877
        from EngineeringLineItem import QEngineeringLineItem
1878

    
1879
        try:
1880
            if clear:
1881
                if not self.clear_attr_and_assoc_item():
1882
                    return
1883

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

    
1887
            rect = self.sceneBoundingRect()
1888
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
1889
            center = self.sceneBoundingRect().center()
1890

    
1891
            minDist = None
1892
            selected = None
1893

    
1894
            contain_texts = []
1895
            for attr in attributes:
1896
                # size text and operation code text will find owner themselves in findowner method
1897
                if False:  # type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
1898
                    dx = attr.center().x() - center.x()
1899
                    dy = attr.center().y() - center.y()
1900
                    length = math.sqrt(dx * dx + dy * dy)
1901
                    if (length < dist) and (minDist is None or length < minDist):
1902
                        minDist = length
1903
                        selected = attr
1904
                elif False:#type(attr) is QEngineeringInstrumentItem:
1905
                    if not attr.is_connected():
1906
                        dx = attr.center().x() - center.x()
1907
                        dy = attr.center().y() - center.y()
1908
                        if math.sqrt(dx * dx + dy * dy) < dist:
1909
                            if self.add_assoc_item(attr):
1910
                                attr.owner = self
1911
                elif issubclass(type(attr), QEngineeringTextItem):
1912
                    if rect.contains(attr.center()):
1913
                        dx = attr.center().x() - center.x()
1914
                        dy = attr.center().y() - center.y()
1915
                        length = math.sqrt(dx * dx + dy * dy)
1916
                        contain_texts.append([length, attr])
1917
                elif type(attr) is QEngineeringLineItem:
1918
                    length = attr.distanceTo([center.x(), center.y()])
1919
                    if (length < dist) and (minDist is None or length < minDist):
1920
                        minDist = length
1921
                        selected = attr
1922

    
1923
            if contain_texts:
1924
                num = len([_attr for _attr in self.getAttributes(findOwner=True) if _attr.AttributeType == 'Text Item'])
1925
                contain_texts = sorted(contain_texts, key=lambda param: param[0])
1926
                index = 0
1927
                for _, attr in contain_texts:
1928
                    if index >= num:
1929
                        break
1930
                    if self.add_assoc_item(attr):
1931
                        attr.owner = self  # set owner of text
1932
                    index += 1
1933

    
1934
            if selected is not None:
1935
                self.add_assoc_item(selected)
1936

    
1937
        except Exception as ex:
1938
            from App import App
1939
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1940
                                                          sys.exc_info()[-1].tb_lineno)
1941
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1942

    
1943
    '''
1944
        @brief      start rotating
1945
        @author     euisung
1946
        @date       2019.04.16
1947
    '''
1948
    def mouseDoubleClickEvent(self, event):
1949
        modifiers = QApplication.keyboardModifiers()
1950
        if not hasattr(self, '_rotating') and modifiers == Qt.NoModifier:
1951
            self._rotating = self.rotation()
1952
            self.grabMouse()
1953
        elif not hasattr(self, '_scale') and modifiers == Qt.ControlModifier:
1954
            self._scale = self.scale()
1955
            self._scale_x = event.scenePos().x()
1956
            self.grabMouse()
1957
        
1958
    def toXml(self):
1959
        """
1960
        generate xml code
1961
        :return:
1962
        """
1963
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1964
        from SymbolAttr import SymbolAttr
1965
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1966

    
1967
        try:
1968
            node = Element('SYMBOL')
1969
            node.attrib['Converted'] = str(self.converted)
1970
            uidNode = Element('UID')
1971
            uidNode.text = str(self.uid)
1972
            node.append(uidNode)
1973

    
1974
            dbUidNode = Element('DBUID')
1975
            dbUidNode.text = str(self.dbUid)
1976
            node.append(dbUidNode)
1977

    
1978
            nameNode = Element('NAME')
1979
            nameNode.text = self.name
1980
            node.append(nameNode)
1981

    
1982
            attributeValueNode = Element('ASSOCIATIONS')
1983
            for key, value in self._associations.items():
1984
                for assoc in value:
1985
                    assoc_node = Element('ASSOCIATION')
1986
                    assoc_node.attrib['TYPE'] = str(key)
1987
                    assoc_node.text = str(assoc)
1988
                    attributeValueNode.append(assoc_node)
1989
            node.append(attributeValueNode)
1990

    
1991
            typeNode = Element('TYPE')
1992
            typeNode.text = self.type
1993
            node.append(typeNode)
1994

    
1995
            # write owner's uid to xml
1996
            ownerNode = Element('OWNER')
1997
            if self.owner is not None:
1998
                ownerNode.text = str(self.owner)
1999
            else:
2000
                ownerNode.text = 'None'
2001
            node.append(ownerNode)
2002
            # up to here
2003

    
2004
            originNode = Element('ORIGINALPOINT')
2005
            if not self.transformOriginPoint().isNull():
2006
                origin = self.mapToScene(self.transformOriginPoint())
2007
                originNode.text = f"{origin.x()},{origin.y()}"
2008
            else:
2009
                origin = self.origin
2010
                originNode.text = f"{origin[0]},{origin[1]}"
2011
            node.append(originNode)
2012

    
2013
            connectorsNode = Element('CONNECTORS')
2014
            for connector in self.connectors:
2015
                connectorsNode.append(connector.toXml())
2016
            node.append(connectorsNode)
2017

    
2018
            connectionNode = Element('CONNECTIONPOINT')
2019
            connection_point = []
2020
            if self.connectors is not None:
2021
                for connector in self.connectors:
2022
                    connection_point.append(repr(connector))
2023
            connectionNode.text = '/'.join(connection_point)
2024
            node.append(connectionNode)
2025

    
2026
            locNode = Element('LOCATION')
2027
            locNode.text = f'{self.loc[0]},{self.loc[1]}'
2028
            '''
2029
            # calculate symbol's left-top corner
2030
            transform = QTransform()
2031
            transform.translate(self.scenePos().x(), self.scenePos().y())
2032
            transform.rotateRadians(-self.angle)
2033
            loc = transform.map(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2034
            # up to here
2035
            locNode.text = '{},{}'.format(loc.x() - self.symbolOrigin[0], loc.y() - self.symbolOrigin[1])
2036
            '''
2037
            node.append(locNode)
2038

    
2039
            sizeNode = Element('SIZE')
2040
            sizeNode.text = f'{self.size[0]},{self.size[1]}'
2041
            node.append(sizeNode)
2042

    
2043
            angleNode = Element('ANGLE')
2044
            angleNode.text = str(math.radians(self.rotation())) if self.scene() else str(self.angle)
2045
            node.append(angleNode)
2046

    
2047
            scaleNode = Element('SCALE')
2048
            if type(self) is QEngineeringEquipmentItem:
2049
                scaleX = abs(round(self.transform().m11() * 100))
2050
                scaleY = abs(round(self.transform().m22() * 100))
2051
                scaleNode.text = str(scaleX / 100.0) + ',' + str(scaleY / 100.0)
2052
            else:
2053
                _scale = str(round(self.scale(), 2))
2054
                scaleNode.text = _scale + ',' + _scale
2055
            node.append(scaleNode)
2056

    
2057
            parentSymbolNode = Element('PARENT')
2058
            parentSymbolNode.text = str(self.parentSymbol)
2059
            node.append(parentSymbolNode)
2060

    
2061
            childSymbolNode = Element('CHILD')
2062
            childSymbolNode.text = str(self.childSymbol)
2063
            node.append(childSymbolNode)
2064

    
2065
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
2066
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
2067
            node.append(hasInstrumentLabelNode)
2068

    
2069
            areaNode = Element('AREA')
2070
            areaNode.text = self.area
2071
            node.append(areaNode)
2072

    
2073
            flipNode = Element('FLIP')
2074
            flipNode.text = str(self.flip)
2075
            node.append(flipNode)
2076

    
2077
            if self.hit_ratio:
2078
                ratioNode = Element('RATIO')
2079
                ratioNode.text = str(self.hit_ratio)
2080
                node.append(ratioNode)
2081

    
2082
            properties_node = Element('PROPERTIES')
2083
            for prop, value in self.properties.items():
2084
                prop_node = prop.toXml()
2085
                prop_node.text = str(value) if value else ''
2086
                properties_node.append(prop_node)
2087
            node.append(properties_node)
2088

    
2089
            attributesNode = Element('SYMBOLATTRIBUTES')
2090
            _attrs = self.getAttributes()
2091
            for attr in _attrs:
2092
                if type(attr) is SymbolAttr:
2093
                    _node = attr.toXml()
2094
                    if attr.AttributeType != 'Spec':
2095
                        _node.text = str(_attrs[attr])
2096
                    elif attr.AttributeType == 'Spec':
2097
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
2098
                    attributesNode.append(_node)
2099

    
2100
            node.append(attributesNode)
2101

    
2102
            if hasattr(self, 'currentPointModeIndex'):
2103
                currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
2104
                currentPointModeIndexNode.text = str(self.currentPointModeIndex) if self.currentPointModeIndex else ''
2105
                node.append(currentPointModeIndexNode)
2106
        except Exception as ex:
2107
            from App import App
2108
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
2109
                      f'{sys.exc_info()[-1].tb_lineno}'
2110
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2111

    
2112
            return None
2113

    
2114
        return node
2115

    
2116
    @staticmethod
2117
    def from_database(component):
2118
        """ create a item related to given component from database """
2119
        import uuid
2120
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2121
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2122
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2123
        from SymbolAttr import SymbolAttr
2124
        item = None
2125

    
2126
        try:
2127
            app_doc_data = AppDocData.instance()
2128

    
2129
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
2130
            grid = int(configs[0].value) if 1 == len(configs) else -1
2131

    
2132
            uid = component['UID']
2133

    
2134
            pt = [float(component['X']), float(component['Y'])]
2135
            if grid == 1:
2136
                pt = [round(pt[0]), round(pt[1])]
2137
                
2138
            size = [float(component['Width']), float(component['Height'])]
2139

    
2140
            dbUid = int(component['Symbol_UID'])
2141
            dbData = app_doc_data.getSymbolByQuery('UID', dbUid)
2142
            name = dbData.sName
2143
            _type = dbData.sType
2144
            angle = float(component['Rotation'])
2145

    
2146
            origin = [float(x) for x in component['SceneOriginPoint'].split(',')] if component['SceneOriginPoint'] is not None else pt
2147
            if grid == 1:
2148
                origin = [round(origin[0]), round(origin[1])]
2149

    
2150
            connPts = []
2151
            if component['ConnectionPoint']:
2152
                if dbData:
2153
                    db_conn = dbData.connectionPoint.split('/')
2154
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
2155
                    db_point_direction = [conn.split(',')[0] for conn in db_conn]
2156
                index = 0
2157
                for conn_pt in component['ConnectionPoint'].split('/'):
2158
                    if index >= len(db_conn):
2159
                        break
2160
                    tokens = conn_pt.split(',')
2161
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
2162
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
2163
                                   (tokens[0] if dbData is None else db_point_direction[index], float(tokens[1]), float(tokens[2]), tokens[3] if dbData is None else db_symbol_num[index]))
2164
                    index += 1
2165

    
2166
            baseSymbol = dbData.baseSymbol
2167

    
2168
            childSymbolNode = component['AdditionalSymbol']
2169
            childSymbol = childSymbolNode if childSymbolNode is not None else ''
2170

    
2171
            owner = component['Owner'] if component['Owner'] is not None and component['Owner'] != 'None' else None
2172

    
2173
            hasInstrumentLabel = dbData.hasInstrumentLabel
2174

    
2175
            flipLabelNode = component['Flip']
2176
            flipLabel = int(flipLabelNode) if flipLabelNode is not None else 0
2177

    
2178
            project = app_doc_data.getCurrentProject()
2179
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
2180
            if os.path.isfile(svgFilePath):
2181
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
2182
                item.setVisible(False)
2183

    
2184
                scale = [1.0, 1.0]
2185
                if component['Value']:
2186
                    if ',' in component['Value']:
2187
                        scale = [float(component['Value'].split(',')[0]), float(component['Value'].split(',')[1])]
2188
                    else:
2189
                        scale = [float(component['Value']), float(component['Value'])]
2190

    
2191
                # if additional symbol was changed, change symbol info
2192
                symbolInfo = None
2193
                if dbUid is None:
2194
                    symbolInfo = app_doc_data.getSymbolByQuery('name', name)
2195
                else:
2196
                    symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
2197
                if symbolInfo:
2198
                    childSymbol = symbolInfo.additionalSymbol
2199

    
2200
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
2201
                                  hasInstrumentLabel, dbUid=dbUid, scale=scale):
2202
                    pass
2203
                else:
2204
                    return None
2205

    
2206
                for key in item._properties.keys():
2207
                    for compo in component.keys():
2208
                        if key.Attribute == compo:
2209
                            item._properties[key] = key.parse_value(component[key.Attribute])# if component[key.Attribute] else ''
2210

    
2211
                ## assign area
2212
                areaNode = component['Area']
2213
                if areaNode is None:
2214
                    for area in app_doc_data.getAreaList():
2215
                        if area.contains(pt):
2216
                            item.area = area.name
2217
                            break
2218
                else:
2219
                    item.area = areaNode
2220
                ## up to here
2221

    
2222
                connectors = app_doc_data.get_component_connectors(uid)
2223
                if connectors:
2224
                    iterIndex = 0
2225
                    for connector in connectors:
2226
                        if iterIndex >= len(db_conn):
2227
                            break
2228
                        item.connectors[iterIndex].parse_record(connector)
2229
                        iterIndex += 1
2230

    
2231
                # get associations 
2232
                associations = app_doc_data.get_component_associations(uid)
2233
                if associations:
2234
                    for assoc in associations:
2235
                        _attrType = assoc['Type']
2236
                        if not _attrType in item._associations:
2237
                            item._associations[_attrType] = []
2238
                        item._associations[_attrType].append(
2239
                            uuid.UUID(assoc['Association']) if assoc['Association'] != 'None' else None)
2240
                # up to here
2241

    
2242
                attributes = app_doc_data.get_component_attributes(uid)
2243
                if attributes:
2244
                    for attr in attributes:
2245
                        _attr = SymbolAttr.from_record(attr)
2246
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
2247
                            item.attrs[_attr] = attr['Value']
2248
                        else:
2249
                            if _attr.AttributeType == 'Spec':
2250
                                item.attrs[_attr] = [attr['Value'].split(',')[0], attr['Value'].split(',')[1]]
2251
                            else:
2252
                                item.attrs[_attr] = attr['Value']
2253
                            '''
2254
                            elif _attr.Attribute == 'UpStream':
2255
                                for initKey in item.attrs.keys():
2256
                                    if initKey.Attribute == 'UpStream':
2257
                                        item.attrs[initKey] = attr['Value']
2258
                            elif _attr.Attribute == 'DownStream':
2259
                                for initKey in item.attrs.keys():
2260
                                    if initKey.Attribute == 'DownStream':
2261
                                        item.attrs[initKey] = attr['Value']
2262
                            '''
2263

    
2264
                currentPointModeIndex = component['OriginIndex']
2265
                if currentPointModeIndex is not None:
2266
                    item.currentPointModeIndex = int(currentPointModeIndex)
2267
        except Exception as ex:
2268
            from App import App
2269
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2270
                                                          sys.exc_info()[-1].tb_lineno)
2271
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2272

    
2273
            return None
2274

    
2275
        return item
2276

    
2277
    '''
2278
        @brief      parse xml code
2279
        @author     humkyung
2280
        @date       2018.07.20
2281
        @history    humkyung 2018.07.20 parse uid from xml node
2282
                    humkyung 2018.07.23 parse connected item's uid from xml node
2283
                    kyouho  2018.07.31 
2284
                    humkyung 2018.09.06 assign area to item
2285
    '''
2286

    
2287
    @staticmethod
2288
    def fromXml(node):
2289
        import uuid
2290
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2291
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2292
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2293
        from SymbolAttr import SymbolAttr
2294
        item = None
2295

    
2296
        try:
2297
            appDocData = AppDocData.instance()
2298

    
2299
            uidNode = node.find('UID')
2300
            uid = uidNode.text if uidNode is not None else uuid.uuid4()  # generate UUID
2301

    
2302
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
2303
            grid = int(configs[0].value) if 1 == len(configs) else -1
2304

    
2305
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
2306
            if grid == 1:
2307
                pt = [round(pt[0]), round(pt[1])]
2308
            size = [float(x) for x in node.find('SIZE').text.split(',')]
2309

    
2310
            dbUidNode = node.find('DBUID')
2311
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
2312
            dbData = None
2313
            if dbUid:
2314
                dbData = appDocData.getSymbolByQuery('UID', dbUid)
2315
            name = node.find('NAME').text if dbData is None else dbData.sName
2316

    
2317
            angle = float(node.find('ANGLE').text)
2318
            _type = node.find('TYPE').text if dbData is None else dbData.sType
2319
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
2320
            if grid == 1:
2321
                origin = [round(origin[0]), round(origin[1])]
2322
            connPts = []
2323
            if node.find('CONNECTIONPOINT').text is not None:
2324
                if dbData:
2325
                    db_conn = dbData.connectionPoint.split('/')
2326
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
2327
                    db_point_direction = [conn.split(',')[0] for conn in db_conn]
2328
                index = 0
2329
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
2330
                    if index >= len(db_conn):
2331
                        break
2332
                    tokens = conn_pt.split(',')
2333
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
2334
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
2335
                                   (tokens[0] if dbData is None else db_point_direction[index], float(tokens[1]), float(tokens[2]), tokens[3] if dbData is None else db_symbol_num[index]))
2336
                    index += 1
2337
            baseSymbol = node.find('PARENT').text if dbData is None else dbData.baseSymbol
2338
            childSymbolNode = node.find('CHILD')
2339
            childSymbol = ''
2340
            if childSymbolNode is not None:
2341
                childSymbol = childSymbolNode.text
2342

    
2343
            ownerNode = node.find('OWNER')
2344
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
2345

    
2346
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
2347
            hasInstrumentLabel = hasInstrumentLabelNode.text if dbData is None else dbData.hasInstrumentLabel
2348

    
2349
            flipLabelNode = node.find('FLIP')
2350
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
2351

    
2352
            ratioNode = node.find('RATIO')
2353
            hit_ratio = float(ratioNode.text) if ratioNode is not None else None
2354

    
2355
            project = appDocData.getCurrentProject()
2356
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
2357
            if os.path.isfile(svgFilePath):
2358
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
2359
                item.setVisible(False)
2360

    
2361
                scaleNode = node.find('SCALE')
2362
                scale = [1.0, 1.0]
2363
                if scaleNode is not None and scaleNode.text:
2364
                    if ',' in scaleNode.text:
2365
                        scale = [float(scaleNode.text.split(',')[0]), float(scaleNode.text.split(',')[1])]
2366
                    else:
2367
                        scale = [float(scaleNode.text), float(scaleNode.text)]
2368
                
2369
                # if additional symbol was changed, change symbol info
2370
                symbolInfo = None
2371
                if dbUid is None:
2372
                    symbolInfo = appDocData.getSymbolByQuery('name', name)
2373
                else:
2374
                    symbolInfo = appDocData.getSymbolByQuery('UID', dbUid)
2375
                if symbolInfo:
2376
                    childSymbol = symbolInfo.additionalSymbol
2377

    
2378
                if hit_ratio:
2379
                    item.hit_ratio = hit_ratio
2380

    
2381
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
2382
                                  hasInstrumentLabel, dbUid=dbUid, scale=scale):
2383
                    pass
2384
                else:
2385
                    return None
2386

    
2387
                for prop_node in node.iter('PROPERTY'):
2388
                    matches = [prop for prop in item._properties.keys() if
2389
                               prop.Attribute == prop_node.attrib['Attribute']]
2390
                    if matches:
2391
                        item._properties[matches[0]] = matches[0].parse_value(prop_node.text)# if prop_node.text else ''
2392

    
2393
                # assign area
2394
                areaNode = node.find('AREA')
2395
                if areaNode is None:
2396
                    for area in appDocData.getAreaList():
2397
                        if area.contains(pt):
2398
                            item.area = area.name
2399
                            break
2400
                else:
2401
                    item.area = areaNode.text
2402
                # up to here
2403

    
2404
                connectors = node.find('CONNECTORS')
2405
                if connectors is not None:
2406
                    iterIndex = 0
2407
                    for connector in connectors.iter('CONNECTOR'):
2408
                        if iterIndex >= len(db_conn):
2409
                            break
2410
                        item.connectors[iterIndex].parse_xml(connector)
2411
                        iterIndex += 1
2412

    
2413
                # get associations 
2414
                attributeValue = node.find('ASSOCIATIONS')
2415
                if attributeValue is not None:
2416
                    for assoc in attributeValue.iter('ASSOCIATION'):
2417
                        _attrType = assoc.attrib['TYPE']
2418
                        if not _attrType in item._associations:
2419
                            item._associations[_attrType] = []
2420
                        item._associations[_attrType].append(uuid.UUID(assoc.text) if assoc.text != 'None' else None)
2421
                # up to here
2422

    
2423
                attributes = node.find('SYMBOLATTRIBUTES')
2424
                if attributes is not None:
2425
                    '''
2426
                    ## for old spec break item that has not uid currectly, may not necessary new data
2427
                    if _type == 'Segment Breaks':
2428
                        specBreakAttrs = appDocData.getSymbolAttribute('Segment Breaks')
2429
                    ## up to here 1
2430
                    '''
2431
                    for attr in attributes.iter('ATTRIBUTE'):
2432
                        _attr = SymbolAttr.fromXml(attr)
2433
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
2434
                            item.attrs[_attr] = attr.text
2435
                        else:
2436
                            '''
2437
                            ## for old spec break item that has not uid currectly, may not necessary new data
2438
                            matchAttr = [cAttr for cAttr in specBreakAttrs if _attr.Attribute == cAttr.Attribute]
2439
                            matchAttr = matchAttr[0] if matchAttr else None
2440
                            if matchAttr:
2441
                                _attr.UID = matchAttr.UID
2442
                                _attr.DisplayAttribute = matchAttr.DisplayAttribute
2443
                                _attr.AttributeType = matchAttr.AttributeType
2444
                                _attr.AttrAt = matchAttr.AttrAt
2445
                                _attr.Expression = matchAttr.Expression
2446
                                _attr.Length = matchAttr.Length
2447
                            # up to here 2
2448
                            '''
2449
                            if _attr.AttributeType == 'Spec':
2450
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
2451
                            else:
2452
                                '''
2453
                                # for old spec break item that has not uid currectly, may not necessary new data
2454
                                _attr.AssocItem = uuid.UUID(attr.text) if attr.text and attr.text != 'None' else None
2455
                                # up to here 3
2456
                                '''
2457
                                item.attrs[_attr] = attr.text
2458
                            '''
2459
                            elif _attr.Attribute == 'UpStream':
2460
                                for initKey in item.attrs.keys():
2461
                                    if initKey.Attribute == 'UpStream':
2462
                                        item.attrs[initKey] = attr.text
2463
                            elif _attr.Attribute == 'DownStream':
2464
                                for initKey in item.attrs.keys():
2465
                                    if initKey.Attribute == 'DownStream':
2466
                                        item.attrs[initKey] = attr.text
2467
                            '''
2468

    
2469
                currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
2470
                if currentPointModeIndex is not None:
2471
                    item.currentPointModeIndex = int(currentPointModeIndex.text) if currentPointModeIndex.text else 0
2472
        except Exception as ex:
2473
            from App import App
2474
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2475
                                                          sys.exc_info()[-1].tb_lineno)
2476
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2477

    
2478
            return None
2479

    
2480
        return item
2481

    
2482
    def to_svg(self, parent) -> list:
2483
        """convert symbol svg item to svg"""
2484
        import re
2485
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
2486
        from App import App
2487

    
2488
        res = []
2489
        try:
2490
            app_doc_data = AppDocData.instance()
2491
            prj = app_doc_data.getCurrentProject()
2492

    
2493
            node = Element('g')
2494
            node.attrib['id'] = str(self.uid)
2495
            node.attrib['class'] = self._class
2496

    
2497
            except_pattern = re.compile('[^a-zA-Z0-9-_]')
2498
            for attr, value in self.getAttributes().items():
2499
                node.attrib[re.sub(except_pattern, '_', attr.Attribute)] = str(value) if value else ''
2500
            trans = self.sceneTransform()
2501
            node.attrib['transform'] = f"matrix(" \
2502
                                       f"{trans.m11()},{trans.m12()}," \
2503
                                       f"{trans.m21()},{trans.m22()}," \
2504
                                       f"{trans.m31()},{trans.m32()}" \
2505
                                       f")"
2506

    
2507
            node_list = self._document.elementsByTagName('path')
2508
            for at in range(node_list.count()):
2509
                path = Element('path')
2510
                path.attrib['d'] = node_list.item(at).attributes().namedItem('d').nodeValue()
2511
                path.attrib['transform'] = self._document.elementsByTagName('g').item(0).attributes().namedItem('transform').nodeValue()
2512
                node.append(path)
2513

    
2514
            for assoc in self.associations():
2515
                assoc_node = assoc.to_svg(parent=self)
2516
                node.extend(assoc_node)
2517

    
2518
            res.append(node)
2519
        except Exception as ex:
2520
            from App import App
2521
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2522
                                                          sys.exc_info()[-1].tb_lineno)
2523
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2524

    
2525
        return res
2526

    
2527
    '''
2528
        @brief      create item corresponding to given type
2529
        @author     humkyung
2530
        @date       2018.05.02
2531
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
2532
                    humkyung 2018.05.10 change symbol's color to blue
2533
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
2534
    '''
2535

    
2536
    @staticmethod
2537
    def createItem(type: str, name: str, path: str, uid=None, owner=None, flip=0):
2538
        from QEngineeringOPCItem import QEngineeringOPCItem
2539
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2540
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
2541
        from EngineeringNozzleItem import QEngineeringNozzleItem
2542
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2543
        from EngineeringReducerItem import QEngineeringReducerItem
2544
        from EngineeringErrorItem import QEngineeringErrorItem
2545
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2546
        from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
2547
        from AppDocData import AppDocData
2548
        import uuid
2549

    
2550
        app_doc_data = AppDocData.instance()
2551

    
2552
        item = None
2553
        try:
2554
            cateogry = app_doc_data.getSymbolCategoryByType(type)
2555
            if type == "Piping OPC's" or type == "Instrument OPC's":
2556
                item = QEngineeringOPCItem(path, uid, flip=flip)
2557
            elif type == 'Nozzles':
2558
                item = QEngineeringNozzleItem(path, uid, flip=flip)
2559
            elif cateogry == 'Equipment' or cateogry == 'Equipment Components' or cateogry == 'Package':
2560
                item = QEngineeringEquipmentItem(path, uid, flip=flip)
2561
            elif cateogry == 'Instrumentation':
2562
                item = QEngineeringInstrumentItem(path, uid, flip=flip)
2563
            elif type == 'Segment Breaks':
2564
                item = QEngineeringSpecBreakItem(path, uid, flip=flip)
2565
            elif type == 'Reducers':
2566
                item = QEngineeringReducerItem(path, uid, flip=flip)
2567
            elif type == 'Error':
2568
                item = QEngineeringErrorItem(path, uid, flip=flip)
2569
            elif type == 'End Break':
2570
                item = QEngineeringEndBreakItem(path, uid, flip=flip)
2571
            # elif type == 'Flow Mark':
2572
            #    item = QEngineeringFlowMarkItem(path, uid, flip=flip)
2573
            else:
2574
                item = SymbolSvgItem(name, path, uid, flip=flip)
2575

    
2576
            if owner is not None:
2577
                item.owner = uuid.UUID(owner)
2578

    
2579
        except Exception as ex:
2580
            from App import App
2581
            from AppDocData import MessageType
2582

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

    
2587
        return item
2588

    
2589
    def setColor(self, color):
2590
        """change svg item's color"""
2591
        if self._document:# and color != self.__color:
2592
            self.changeAttributes('fill', color)
2593
            self.changeAttributes('stroke', color)
2594
            self.renderer().load(self._document.toByteArray())
2595
            self.__color = color
2596
            #self.update()
2597

    
2598
    '''
2599
        @brief  get attributes from svg file
2600
        @author humkyung
2601
        @date   2019.03.08
2602
    '''
2603

    
2604
    def get_attribute(self, attName):
2605
        root = self._document.documentElement()
2606
        node = root.firstChild()
2607
        while not node.isNull():
2608
            if node.isElement():
2609
                element = node.toElement()
2610
                if element.hasAttribute(attName):
2611
                    return element.attribute(attName)
2612

    
2613
                if element.hasChildNodes():
2614
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
2615
                    if att_val is not None: return att_val
2616

    
2617
            node = node.nextSibling()
2618

    
2619
        return None
2620

    
2621
    '''
2622
        @brief  get recursively attribute
2623
        @author humkyung
2624
        @date   2019.03.08
2625
    '''
2626

    
2627
    def recursive_get_attribute(self, node, attName):
2628
        while not node.isNull():
2629
            if node.isElement():
2630
                element = node.toElement()
2631
                if element.hasAttribute(attName):
2632
                    return element.attribute(attName)
2633

    
2634
                if node.hasChildNodes():
2635
                    att_val = self.recursive_get_attribute(node.firstChild(), attName)
2636
                    if att_val is not None: return att_val
2637

    
2638
            node = node.nextSibling()
2639

    
2640
        return None
2641

    
2642
    '''
2643
        @brief  change attributes
2644
        @author humkyung
2645
        @date   2018.05.10
2646
    '''
2647

    
2648
    def changeAttributes(self, attName, attValue):
2649
        root = self._document.documentElement()
2650
        node = root.firstChild()
2651
        while not node.isNull():
2652
            if node.isElement():
2653
                element = node.toElement()
2654
                if element.hasAttribute(attName):
2655
                    element.setAttribute(attName, attValue)
2656

    
2657
                if element.hasChildNodes():
2658
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
2659

    
2660
            node = node.nextSibling()
2661

    
2662
    '''
2663
        @brief  change attribute
2664
        @author humkyung
2665
        @date   2018.05.10
2666
    '''
2667

    
2668
    def recursiveChangeAttributes(self, node, attName, attValue):
2669
        while not node.isNull():
2670
            if node.isElement():
2671
                element = node.toElement()
2672
                if element.hasAttribute(attName):
2673
                    element.setAttribute(attName, attValue)
2674

    
2675
                if node.hasChildNodes():
2676
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
2677

    
2678
            node = node.nextSibling()
2679

    
2680
    '''
2681
        @brief  draw rect when item is selected
2682
        @author humkyung
2683
        @date   2018.07.07
2684
    '''
2685

    
2686
    def drawFocusRect(self, painter):
2687
        self.focuspen = QPen(Qt.DotLine)
2688
        self.focuspen.setColor(Qt.black)
2689
        self.focuspen.setWidthF(1.5)
2690
        hilightColor = QColor(255, 0, 0, 127)
2691
        painter.setBrush(QBrush(hilightColor))
2692
        painter.setPen(self.focuspen)
2693
        painter.drawRect(self.boundingRect())
2694

    
2695
    '''
2696
        @brief  override paint(draw connection points)
2697
        @author humkyung
2698
        @date   2018.04.21
2699
    '''
2700

    
2701
    def paint(self, painter, options=None, widget=None):
2702
        #from EngineeringTextItem import QEngineeringTextItem
2703

    
2704
        self.setColor(self.getColor())
2705

    
2706
        painter.setClipRect(options.exposedRect)
2707
        QGraphicsSvgItem.paint(self, painter, options, widget)
2708
        '''
2709
        # not used
2710
        for attr in self.attrs:
2711
            if issubclass(type(attr), QEngineeringTextItem):
2712
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (
2713
                    self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
2714
                attr.setColor(color)
2715
            elif issubclass(type(attr), SymbolSvgItem):
2716
                attr.setColor(self.getColor())
2717
                attr.update()
2718
        '''
2719

    
2720
        if self.isSelected():
2721
            self.drawFocusRect(painter)
2722

    
2723
    def resetPosition(self):
2724
        if self.flip:
2725
            self.flip_symbol()
2726

    
2727
        self.moveBy(-self.origin[0], -self.origin[1])
2728
        self.setRotation(math.degrees(-self.angle))
2729
        self.moveBy(self.symbolOrigin[0], self.symbolOrigin[1])
2730
        self.setTransformOriginPoint(QPointF(0, 0))
2731

    
2732
    def addSvgItemToScene(self, scene, undoable: bool = False, manual=False) -> None:
2733
        """Add Svg Item into ImageViewer's Scene"""
2734
        #if self.flip:
2735
        #    self.flip_symbol()
2736

    
2737
        if (hasattr(self, 'symbolConvertingOrigin') and not self.symbolConvertingOrigin) or not manual:
2738
            self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2739
            self.moveBy(-self.symbolOrigin[0], -self.symbolOrigin[1])
2740
            self.setRotation(math.degrees(self.angle))
2741
            self.moveBy(self.origin[0], self.origin[1])
2742
        else:
2743
            self.setTransformOriginPoint(QPointF(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1]))
2744
            self.moveBy(-self.symbolConvertingOrigin[0], -self.symbolConvertingOrigin[1])
2745
            self.setRotation(math.degrees(self.angle))
2746
            self.moveBy(self.origin[0], self.origin[1])
2747
            self.setTransformOriginPoint(QPointF(0, 0))
2748
            self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2749

    
2750
            #self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2751
            #self.moveBy(-self.symbolOrigin[0], -self.symbolOrigin[1])
2752
            #self.setRotation(math.degrees(self.angle))
2753
            #self.moveBy(self.origin[0], self.origin[1])
2754

    
2755
            dx = self.symbolOrigin[0] - self.symbolConvertingOrigin[0]
2756
            dy = self.symbolOrigin[1] - self.symbolConvertingOrigin[1]
2757
            nx = round(math.cos(self.angle) * dx - math.sin(self.angle) * dy)
2758
            ny = round(math.sin(self.angle) * dx + math.cos(self.angle) * dy)
2759
            self.moveBy(nx - dx, ny - dy)
2760

    
2761
            #transform = QTransform()
2762
            #transform.translate(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1])
2763
            ##transform.map(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1])
2764
            #transform.rotate(math.degrees(self.angle))
2765
            #transform.translate(-self.symbolConvertingOrigin[0], -self.symbolConvertingOrigin[1])
2766
            #self.setTransform(transform)
2767

    
2768
        scene.addItem(self)
2769

    
2770
        self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
2771
        self.loc[0], self.loc[1] = round(self.sceneBoundingRect().top()), round(self.sceneBoundingRect().left())
2772

    
2773
        scene_origin = self.mapToScene(self.transformOriginPoint())
2774

    
2775
        configs = AppDocData.instance().getConfigs('Data', 'Grid')
2776
        grid = int(configs[0].value) if 1 == len(configs) else -1
2777
        if grid == 1:
2778
            self.origin = [round(scene_origin.x()), round(scene_origin.y())]
2779
        else:
2780
            self.origin = [scene_origin.x(), scene_origin.y()]
2781

    
2782
        if undoable:
2783
            from CreateCommand import CreateCommand
2784
            self.scene().undo_stack.push(CreateCommand(self.scene(), [self,]))
2785

    
2786
    '''
2787
        @brief      
2788
        @author     humkyung
2789
        @date       2018.07.27
2790
    '''
2791
    def onConnectorPosChaned(self, connector):
2792
        pass
2793

    
2794
    '''
2795
        @brief      set connector
2796
        @author     kyouho
2797
        @date       2018.07.26
2798
    '''
2799
    def setConnector(self, index):
2800
        connector = QEngineeringConnectorItem(parent=self, index=index)
2801
        connector.setParentItem(self)
2802
        self.connectors.append(connector)
2803

    
2804
    def refreshConnector(self):
2805
        for connector in self.connectors:
2806
            connector.buildItem()
2807

    
2808
    '''
2809
        @brief      Delete svg item
2810
        @author     Jeongwoo
2811
        @date       2018.05.25
2812
    '''
2813
    def deleteSvgItemFromScene(self):
2814
        ''' not used '''
2815
        for connector in self.connectors:
2816
            if connector.connectedItem is not None:
2817
                try:
2818
                    connector.connectedItem.removeSymbol(self)
2819
                except Exception as ex:
2820
                    from App import App
2821
                    from AppDocData import MessageType
2822

    
2823
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2824
                                                                  sys.exc_info()[-1].tb_lineno)
2825
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
2826
                break
2827

    
2828
        self.transfer.onRemoved.emit(self)
2829

    
2830
    def flip_symbol(self) -> None:
2831
        """flip symbol"""
2832

    
2833
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2834

    
2835
        if type(self) is QEngineeringEquipmentItem:
2836
            transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
2837
            transform.scale(self.transform().m11(), self.transform().m22())
2838
            transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
2839
            transform.scale(-1.0, 1.0)
2840
            transform.translate(-2 * self.symbolOrigin[0], 0)
2841
        else:
2842
            transform = QTransform()
2843
            if self.flip is 1:
2844
                #rect = self.boundingRect()
2845
                transform.scale(-1.0, 1.0)
2846
                transform.translate(-2 * self.symbolOrigin[0], 0)
2847

    
2848
        self.setTransform(transform)
2849

    
2850
    def rotate(self, angle: float) -> None:
2851
        """rotate symbol by given angle in radian"""
2852
        import math
2853
        self.setRotation(math.degrees(angle))
2854
        '''
2855
        transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
2856
        transform.rotate(math.degrees(angle))
2857
        transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
2858
        self.setTransform(transform)
2859
        '''
2860

    
2861
    '''
2862
        @brief      change Conn point 
2863
        @author     kyouho
2864
        @date       2018.07.25
2865
    '''
2866
    def changeConnPoint(self):
2867
        if len(self.connectors) == 2:
2868
            conn1Item = self.connectors[0].connectedItem
2869
            conn2Item = self.connectors[1].connectedItem
2870
            self.connectors[0].connectedItem = conn2Item
2871
            self.connectors[1].connectedItem = conn1Item
2872

    
2873
            currentPoint = self.getCurrentPoint()
2874
            #self.reSettingSymbol(currentPoint, self.angle)
2875

    
2876
    '''
2877
        @brief      change standard point
2878
        @author     kyouho
2879
        @date       2018.07.24
2880
    '''
2881
    def changeStandardPoint(self):
2882
        connPtsCount = len(self.connectors)
2883

    
2884
        if self.currentPointModeIndex < connPtsCount:
2885
            self.currentPointModeIndex += 1
2886
        else:
2887
            self.currentPointModeIndex = 0
2888

    
2889
        currentPoint = self.getCurrentPoint()
2890
        #self.reSettingSymbol(currentPoint, self.angle)
2891

    
2892
    def getCurrentPoint(self):
2893
        """return transform origin point"""
2894
        pointList = []
2895
        pointList.append(self.symbolOrigin)
2896
        for connector in self.connectors:
2897
            pointList.append(connector.connectPoint)
2898

    
2899
        return pointList[self.currentPointModeIndex]
2900

    
2901
    def EvaluatedCode(self, old_code, code_name):
2902
        """ return new attribute code """
2903
        from AppDocData import AppDocData
2904
        from CodeTables import CodeTable
2905
        from LineTypeConditions import LineTypeConditions
2906
        from EngineeringLineItem import QEngineeringLineItem
2907
        import re
2908

    
2909
        try:
2910
            code = old_code
2911
            start_item = None
2912
            if self.iType == 19:    # Labels - Symbol
2913
                matches = [assoc for assoc in self.associations() if issubclass(type(assoc), SymbolSvgItem)]
2914
                start_item = matches[0] if matches else self
2915
            else:
2916
                start_item = self
2917

    
2918
            #if not start_item:
2919
            #    return ''
2920

    
2921
            app_doc_data = AppDocData.instance()
2922
            connected_items_lists = app_doc_data._connected_items_lists
2923
            items = [connected_items_list.items for connected_items_list in connected_items_lists.runs if start_item in connected_items_list.items]
2924
            if len(items) != 1:
2925
                return ''
2926
            else:
2927
                items = [item for item in items[0] if issubclass(type(item), SymbolSvgItem)]
2928

    
2929
            table = CodeTable.instance(code_name, inst=True)
2930
            line_types = [condition.name for condition in LineTypeConditions.items()]
2931

    
2932
            new_codes = []
2933
            for value in table.values: # uid, old code, symbol, attribute, new code, expression, priority
2934
                # symbol, line type
2935
                if len(value[3]) == 1 and not value[3][0]:
2936
                    # symbol
2937
                    if not [line_type for line_type in line_types if line_type in value[2]]:
2938
                        for item in items:
2939
                            match = re.search(value[1][0], code, re.DOTALL)
2940
                            if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) \
2941
                                and item.name in value[2]:
2942
                                dx = item.origin[0] - self.origin[0]
2943
                                dy = item.origin[1] - self.origin[1]
2944
                                length = math.sqrt(dx*dx + dy*dy)
2945
                                if not value[5]:
2946
                                    new_codes.append([length if not value[6] else value[6], '"' + value[4] + '"', item, length])
2947
                                else:
2948
                                    new_codes.append([length if not value[6] else value[6], value[5], item, length])
2949
                    # line
2950
                    else:
2951
                        match = re.search(value[1][0], code, re.DOTALL)
2952
                        types = [conn.connectedItem.lineType for conn in self.connectors if conn.connectedItem and type(conn.connectedItem) is QEngineeringLineItem]
2953
                        if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) and \
2954
                            [line_type for line_type in value[2] if line_type in types]:
2955
                            if not value[5]:
2956
                                new_codes.append([0 if not value[6] else value[6], '"' + value[4] + '"', None, sys.maxsize])
2957
                            else:
2958
                                new_codes.append([0 if not value[6] else value[6], value[5], None, sys.maxsize])
2959

    
2960
                # self attribute
2961
                elif len(value[2]) == 1 and not value[2][0] and value[3][0]:
2962
                    for key, _value in self.attrs.items():
2963
                        if _value in value[3]:
2964
                            if not value[5]:
2965
                                new_codes.append([0 if not value[6] else value[6], '"' + value[4] + '"', self, sys.maxsize])
2966
                            else:
2967
                                new_codes.append([0 if not value[6] else value[6], value[5], self, sys.maxsize])
2968

    
2969
                # symbol + attribute
2970
                elif value[2][0] and value[3][0]:
2971
                    for item in items:
2972
                        match = re.search(value[1][0], code, re.DOTALL)
2973
                        if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) \
2974
                            and item.name in value[2]:
2975
                            dx = item.origin[0] - self.origin[0]
2976
                            dy = item.origin[1] - self.origin[1]
2977
                            length = math.sqrt(dx*dx + dy*dy)
2978

    
2979
                            for key, _value in item.attrs.items():
2980
                                if _value in value[3]:
2981
                                    if not value[5]:
2982
                                        new_codes.append([length if not value[6] else value[6], '"' + value[4] + '"', item, length])
2983
                                    else:
2984
                                        new_codes.append([length if not value[6] else value[6], value[5], item, length])
2985
                        
2986
            # default
2987
            for value in table.values:
2988
                match = re.search(value[1][0], code, re.DOTALL)
2989
                if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) and len(value[2]) == 1 and len(value[3]) == 1 and value[2][0] == '' and value[3][0] == '':
2990
                    if not value[5]:
2991
                        new_codes.append([sys.maxsize if not value[6] else value[6], '"' + value[4] + '"', None, sys.maxsize])
2992
                    else:
2993
                        new_codes.append([sys.maxsize if not value[6] else value[6], value[5], None, sys.maxsize])
2994

    
2995
            if new_codes:
2996
                new_codes = sorted(new_codes, key=lambda param: param[0])
2997
                new_codes = [_code for _code in new_codes if _code[0] == new_codes[0][0]]
2998
                new_codes = sorted(new_codes, key=lambda param: param[-1])
2999
                item = new_codes[0][-2]
3000
                new_code = eval(new_codes[0][1])
3001
                return new_code
3002
            else:
3003
                return code
3004
        except Exception as ex:
3005
            from App import App
3006
            from AppDocData import MessageType
3007

    
3008
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
3009
                                                          sys.exc_info()[-1].tb_lineno)
3010
            #App.mainWnd().addMessage.emit(MessageType.Error, str(self.uid) + self.name + message)
3011
            App.mainWnd().addMessage.emit(MessageType.Error, message)
3012

    
3013
def recursiveChangeAttributes(node, attName, attValue):
3014
    while not node.isNull():
3015
        if node.isElement():
3016
            element = node.toElement()
3017
            if element.hasAttribute(attName):
3018
                element.setAttribute(attName, attValue)
3019

    
3020
            if node.hasChildNodes():
3021
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
3022

    
3023
        node = node.nextSibling()
3024

    
3025

    
3026
'''
3027
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
3028
    @author     Jeongwoo
3029
    @date       2018.06.18
3030
'''
3031
class Transfer(QObject):
3032
    on_pos_changed = pyqtSignal(QGraphicsItem)
3033
    onRemoved = pyqtSignal(QGraphicsItem)
3034

    
3035
    def __init__(self, parent=None):
3036
        QObject.__init__(self, parent)
3037

    
3038

    
3039
if __name__ == '__main__':
3040
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
3041
    f.open(QIODevice.ReadOnly)
3042
    array = f.readAll()
3043
    document = QDomDocument()
3044
    document.setContent(array)
3045

    
3046
    root = document.documentElement()
3047
    node = root.firstChild()
3048
    while not node.isNull():
3049
        if node.isElement():
3050
            element = node.toElement()
3051
            if element.hasAttribute('fill'):
3052
                element.setAttribute('fill', '#FFFFF')
3053

    
3054
            if element.hasChildNodes():
3055
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
3056

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