프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ 6a9a247d

이력 | 보기 | 이력해설 | 다운로드 (135 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.01, self.transform().m22() + 0.01)
1393
                else:
1394
                    transform.scale(self.transform().m11() + 0.01, self.transform().m22() + 0.01)
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.01 if self.transform().m11() + 0.01 < -0.3 else -0.3, self.transform().m22() - 0.01 if self.transform().m22() - 0.01 > 0.3 else 0.3)
1404
                else:
1405
                    transform.scale(self.transform().m11() - 0.01 if self.transform().m11() - 0.01 > 0.3 else 0.3, self.transform().m22() - 0.01 if self.transform().m22() - 0.01 > 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
            scaleAction = QAction('Set Scale', None)
1440
            scaleAction.triggered.connect(self.contextScale)
1441
            menu.addAction(scaleAction)
1442

    
1443
            moveAction = QAction('Move to Nearest(N)', None)
1444
            moveAction.triggered.connect(self.contextMove)
1445
            menu.addAction(moveAction)
1446

    
1447
            freezeAction = QAction('Freeze Attributes(G)', None)
1448
            freezeAction.triggered.connect(self.freeze_attriute)
1449
            menu.addAction(freezeAction)
1450

    
1451
            replaceAction = QAction('Replace This to Selected Symbol(I, Q)', None)
1452
            replaceAction.triggered.connect(self.contextReplace)
1453
            menu.addAction(replaceAction)
1454

    
1455
            insertAction = QAction('Insert Selected Symbol to This(J)', None)
1456
            insertAction.triggered.connect(self.contextInsert)
1457
            menu.addAction(insertAction)
1458

    
1459
            if type(self) != QEngineeringSpecBreakItem:
1460
                bindAction = QAction('Bind(B)', None)
1461
                bindAction.triggered.connect(self.contextBind)
1462
                menu.addAction(bindAction)
1463
            else:
1464
                bindAction = QAction('Set Batch(Bind)', None)
1465
                bindAction.triggered.connect(self.contextBindSpec)
1466
                menu.addAction(bindAction)
1467

    
1468
            rotateAction = QAction('Rotate(R)', None)
1469
            rotateAction.triggered.connect(self.contextRotate)
1470
            menu.addAction(rotateAction)
1471

    
1472
            flipAction = QAction('Flip(F)', None)
1473
            flipAction.triggered.connect(self.contextFlip)
1474
            menu.addAction(flipAction)
1475

    
1476
            deleteAction = QAction('Delete(E)', None)
1477
            deleteAction.triggered.connect(self.contextDelete)
1478
            menu.addAction(deleteAction)
1479

    
1480
            arsAction = QAction('Show on other App', None)
1481
            arsAction.triggered.connect(self.contextShowOnOtherApp)
1482
            menu.addAction(arsAction)
1483

    
1484
            menu.exec_(event.screenPos())
1485
        elif len(items) >= 2:
1486
            menu = QMenu()
1487

    
1488
            allAction = QAction('Select All in View(A)', None)
1489
            allAction.triggered.connect(self.contextSelectAll)
1490
            menu.addAction(allAction)
1491

    
1492
            scaleAction = QAction('Set Scale', None)
1493
            scaleAction.triggered.connect(self.contextScale)
1494
            menu.addAction(scaleAction)
1495

    
1496
            batchAction = QAction('Set Batch(Insert)', None)
1497
            batchAction.triggered.connect(self.contextBatch)
1498
            menu.addAction(batchAction)
1499

    
1500
            alignAction = QAction('Align Selected Symbols', None)
1501
            alignAction.triggered.connect(self.contextAlignSymbol)
1502
            menu.addAction(alignAction)
1503

    
1504
            moveAction = QAction('Move to Nearest(N)', None)
1505
            moveAction.triggered.connect(self.contextMove)
1506
            menu.addAction(moveAction)
1507

    
1508
            mergeAction = QAction('Make Line(M)', None)
1509
            mergeAction.triggered.connect(self.contextMerge)
1510
            menu.addAction(mergeAction)
1511

    
1512
            replaceAction = QAction('Replace This to Selected Symbol(I, Q)', None)
1513
            replaceAction.triggered.connect(self.contextReplace)
1514
            menu.addAction(replaceAction)
1515

    
1516
            insertAction = QAction('Insert Selected Symbol to This(J)', None)
1517
            insertAction.triggered.connect(self.contextInsert)
1518
            menu.addAction(insertAction)
1519

    
1520
            deleteAction = QAction('Delete(E)', None)
1521
            deleteAction.triggered.connect(self.contextDelete)
1522
            menu.addAction(deleteAction)
1523

    
1524
            menu.exec_(event.screenPos())
1525

    
1526

    
1527
    def contextShow(self):
1528
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
1529

    
1530
        if type(self.owner) is QEngineeringLineNoTextItem:
1531
            self.owner.contextHighlight(self.owner)
1532

    
1533
    def contextScale(self):
1534
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1535

    
1536
        text, result = QInputDialog.getDouble(None, self.tr('Symbol Scale'), self.tr('Input : '), self.scale(), 0.30, 100, 2, Qt.WindowFlags(), 0.01)
1537
                
1538
        try:
1539
            if result:
1540
                scale = round(float(text), 3)
1541
                if 0.30 <= scale < 100:
1542
                    symbols = [item for item in self.scene().items() if issubclass(type(item), SymbolSvgItem) and type(item) is not QEngineeringEquipmentItem]
1543
                    for symbol in symbols:
1544
                        symbol.setScale(scale)
1545
        except:
1546
            return
1547

    
1548
    def contextSelectAll(self):
1549
        from App import App
1550
        
1551
        selectedSymbols = list(set([item.dbUid for item in self.scene().selectedItems() if issubclass(type(item), SymbolSvgItem)]))
1552
        rect = App.mainWnd().graphicsView.viewport().rect()
1553
        view_rect = App.mainWnd().graphicsView.mapToScene(rect).boundingRect()
1554
        #rect = App.mainWnd().graphicsView.mapToScene(App.mainWnd().graphicsView.rect()).boundingRect()
1555
        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())]
1556
        for symbol in symbols:
1557
            symbol.setSelected(True)
1558

    
1559
    def contextBatch(self):
1560
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_Insert, Qt.NoModifier)
1561
        self.keyPressEvent(event)
1562

    
1563
    def contextMerge(self):
1564
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_M, Qt.NoModifier)
1565
        self.keyPressEvent(event)
1566

    
1567
    def contextUp(self):
1568
        from App import App
1569
        import SelectAttributeCommand
1570
        _up = None
1571
        for prop, value in self.getAttributes().items():
1572
            if prop.Attribute == 'UpStream':
1573
                _up = prop
1574
                break
1575
        cmd = SelectAttributeCommand.SelectAttributeCommand(self, _up, App.mainWnd().graphicsView)
1576
        cmd.onSuccess.connect(App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute)
1577
        App.mainWnd().graphicsView.command = cmd
1578

    
1579
    def contextDown(self):
1580
        from App import App
1581
        import SelectAttributeCommand
1582
        _down = None
1583
        for prop, value in self.getAttributes().items():
1584
            if prop.Attribute == 'DownStream':
1585
                _down = prop
1586
                break
1587
        cmd = SelectAttributeCommand.SelectAttributeCommand(self, _down, App.mainWnd().graphicsView)
1588
        cmd.onSuccess.connect(App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute)
1589
        App.mainWnd().graphicsView.command = cmd
1590

    
1591
    def contextAlignSymbol(self):
1592
        scene = self.scene()
1593
        if scene:
1594
            symbolItems = [symbol for symbol in scene.selectedItems() if symbol is not self and issubclass(type(symbol), SymbolSvgItem)]
1595

    
1596
            for symbol in symbolItems:
1597
                dx = self.origin[0] - symbol.origin[0]
1598
                dy = self.origin[1] - symbol.origin[1]
1599
                if abs(dx) > abs(dy):
1600
                    if dx > 0:
1601
                        symbol.moveBy(0, dy)
1602
                    else:
1603
                        symbol.moveBy(0, dy)
1604
                else:
1605
                    if dy > 0:
1606
                        symbol.moveBy(dx, 0)
1607
                    else:
1608
                        symbol.moveBy(dx, 0)
1609

    
1610
    def contextMove(self):
1611
        self.move_near_main_line(10)
1612

    
1613
    def contextDelete(self):
1614
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_Delete, Qt.NoModifier)
1615
        self.scene().keyPressEvent(event)
1616

    
1617
    def contextReplace(self):
1618
        from App import App
1619

    
1620
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_I, Qt.NoModifier)
1621
        App.mainWnd().keyPressEvent(event)
1622

    
1623
    def contextInsert(self):
1624
        from App import App
1625

    
1626
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_J, Qt.NoModifier)
1627
        App.mainWnd().keyPressEvent(event)
1628

    
1629
    def contextShowOnOtherApp(self):
1630
        from TcpServer import TcpSocket
1631

    
1632
        app_doc_data = AppDocData.instance()
1633
        configs = app_doc_data.getConfigs('app', 'conn port')
1634
        #configs = app_doc_data.getAppConfigs('app', 'conn port')
1635
        port = 3030
1636
        if configs and 1 == len(configs):
1637
            port = int(configs[0].value)
1638
        tcpserver = TcpSocket(port)
1639
        tcpserver.sendMessage(f"{str(self.uid)},{self.origin[0]},{self.origin[1]},{self.size[0]},{self.size[1]},"
1640
                              f"{app_doc_data.imgName}")
1641
        
1642
    def contextBindSpec(self):
1643
        from App import App
1644
        if App.mainWnd().on_connect_spec_breaks([self]):
1645
            App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute(self)
1646

    
1647
    def contextBind(self):
1648
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_B, Qt.NoModifier)
1649
        self.keyPressEvent(event)
1650

    
1651
    def contextRotate(self):
1652
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_R, Qt.NoModifier)
1653
        self.scene().keyPressEvent(event)
1654

    
1655
    def contextFlip(self):
1656
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_F, Qt.NoModifier)
1657
        self.scene().keyPressEvent(event)
1658

    
1659
    def freeze_attriute(self, flag=None):
1660
        from App import App
1661

    
1662
        try:
1663
            if flag is None:
1664
                freeze = True
1665
                for _attr, _value in self.attrs.items():
1666
                    if _attr.Freeze:
1667
                        freeze = False
1668
                        break
1669
                
1670
                for _attr, _value in self.attrs.items():
1671
                    _attr.Freeze = freeze
1672

    
1673
                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)]
1674
                if labels:
1675
                    labels = [label for label in labels if label.EvaluatedAttribute('OWNERSYMBOL') == self or \
1676
                            (type(label.EvaluatedAttribute('OWNERSYMBOL')) is str and label.EvaluatedAttribute('OWNERSYMBOL') == str(self.uid))]
1677
                    for label in labels:
1678
                        label.freeze_attriute(freeze)
1679

    
1680
                App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute(self)
1681
            else:
1682
                for _attr, _value in self.attrs.items():
1683
                    _attr.Freeze = flag
1684

    
1685
                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)]
1686
                if labels:
1687
                    labels = [label for label in labels if label.EvaluatedAttribute('OWNERSYMBOL') == self or \
1688
                            (type(label.EvaluatedAttribute('OWNERSYMBOL')) is str and label.EvaluatedAttribute('OWNERSYMBOL') == str(self.uid))]
1689
                    for label in labels:
1690
                        label.freeze_attriute(flag)
1691
        except Exception as ex:
1692
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1693
                                                          sys.exc_info()[-1].tb_lineno)
1694
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1695

    
1696
    def move_near_main_line(self, length):
1697
        from EngineeringLineItem import QEngineeringLineItem
1698
        from EngineeringTextItem import QEngineeringTextItem
1699

    
1700
        scene = self.scene()
1701
        if scene:
1702
            symbolItems = [symbol for symbol in scene.selectedItems() if issubclass(type(symbol), SymbolSvgItem)]
1703
            textItems = [text for text in scene.selectedItems() if issubclass(type(text), QEngineeringTextItem)]
1704

    
1705
            symbolTextMatches = {}
1706

    
1707
            for text in textItems:
1708
                minDist = None
1709
                selected = None
1710
                center = text.sceneBoundingRect().center()
1711

    
1712
                for symbol in symbolItems:
1713
                    dx = symbol.origin[0] - center.x()
1714
                    dy = symbol.origin[1] - center.y()
1715
                    _length = math.sqrt(dx * dx + dy * dy)# - offset
1716

    
1717
                    if minDist is None or _length < minDist:
1718
                        minDist = _length
1719
                        selected = symbol
1720
                
1721
                if selected not in symbolTextMatches:
1722
                    symbolTextMatches[selected] = [text]
1723
                else:
1724
                    symbolTextMatches[selected].append(text)
1725
                    
1726
            for symbol in symbolItems:
1727
                found = False
1728

    
1729
                connectedSymbols = [_conn.connectedItem for _conn in symbol.connectors if _conn.connectedItem and issubclass(type(_conn.connectedItem), SymbolSvgItem)]
1730
                for connS in symbol.connectors:
1731
                    if found: break
1732

    
1733
                    connItemS = connS.connectedItem
1734
                    if connItemS and type(connItemS) is QEngineeringLineItem:
1735
                        for connL in connItemS.connectors:
1736
                            if found: break
1737

    
1738
                            connItemL = connL.connectedItem
1739
                            if connItemL and connItemL is not symbol and type(connItemL) is QEngineeringLineItem and connL._connected_at == QEngineeringAbstractItem.CONNECTED_AT_BODY:
1740
                                dx = connS.center()[0] - connL.center()[0]
1741
                                dy = connS.center()[1] - connL.center()[1]
1742
                                connL2 = [_conn for _conn in connItemS.connectors if _conn != connL][0]
1743
                                if connItemS.isVertical() and abs(dy) != length:
1744
                                    if dy < 0:
1745
                                        delta = - dy - length
1746
                                    else:
1747
                                        delta = - dy + length
1748
                                    symbol.moveBy(0, delta)
1749
                                    connItemS.set_line([[connL2.center()[0], connItemS.start_point()[1]], [connL2.center()[0], connItemS.end_point()[1]]])
1750
                                    for _symbol in connectedSymbols:
1751
                                        _symbol.moveBy(0, delta)
1752
                                    if symbol in symbolTextMatches:
1753
                                        for text in symbolTextMatches[symbol]:
1754
                                            text.moveText(0, 1 if delta > 0 else -1, abs(delta))
1755
                                    found = True
1756
                                    break
1757
                                elif connItemS.isHorizontal() and abs(dx) != length:
1758
                                    if dx < 0:
1759
                                        delta = - dx - length
1760
                                    else:
1761
                                        delta = - dx + length
1762
                                    symbol.moveBy(delta, 0)
1763
                                    connItemS.set_line([[connItemS.start_point()[0], connL2.center()[1]], [connItemS.end_point()[0], connL2.center()[1]]])
1764
                                    for _symbol in connectedSymbols:
1765
                                        _symbol.moveBy(delta, 0)
1766
                                    if symbol in symbolTextMatches:
1767
                                        for text in symbolTextMatches[symbol]:
1768
                                            text.moveText(1 if delta > 0 else -1, 0, abs(delta))
1769
                                    found = True
1770
                                    break
1771

    
1772
                if not found:
1773
                    matches = [conn for conn in symbol.connectors if conn.connectedItem]
1774
                    
1775
                    if len(matches) == 1:
1776
                        connS = matches[0]
1777
                        connItemS = connS.connectedItem
1778

    
1779
                        found = False
1780
                        if type(connItemS) is QEngineeringLineItem:
1781
                            for connL in connItemS.connectors:
1782
                                if found: break
1783

    
1784
                                connItemL = connL.connectedItem
1785

    
1786
                                if connItemL and connItemL is not symbol:
1787
                                    dx = symbol.center().x() - connItemL.center().x()
1788
                                    dy = symbol.center().y() - connItemL.center().y()
1789
                                    lx = connS.center()[0] - connL.center()[0]
1790
                                    ly = connS.center()[1] - connL.center()[1]
1791
                                    #connL2 = [_conn for _conn in connItemS.connectors if _conn != connL][0]
1792
                                    if abs(dy) > abs(dx) and abs(dy) != length:
1793
                                        if dy < 0:
1794
                                            delta = - ly - length
1795
                                        else:
1796
                                            delta = - ly + length
1797
                                        symbol.moveBy(-lx, delta)
1798
                                        for _symbol in connectedSymbols:
1799
                                            _symbol.moveBy(-lx, delta)
1800
                                        if symbol in symbolTextMatches:
1801
                                            for text in symbolTextMatches[symbol]:
1802
                                                text.moveText(1 if lx < 0 else -1, 0, abs(lx))
1803
                                                text.moveText(0, 1 if delta > 0 else -1, abs(delta))
1804
                                        found = True
1805
                                        break
1806
                                    elif abs(dy) < abs(dx) and abs(dx) != length:
1807
                                        if dx < 0:
1808
                                            delta = - lx - length
1809
                                        else:
1810
                                            delta = - lx + length
1811
                                        symbol.moveBy(delta, -ly)
1812
                                        for _symbol in connectedSymbols:
1813
                                            _symbol.moveBy(delta, -ly)
1814
                                        if symbol in symbolTextMatches:
1815
                                            for text in symbolTextMatches[symbol]:
1816
                                                text.moveText(0, 1 if ly < 0 else -1, abs(ly))
1817
                                                text.moveText(1 if delta > 0 else -1, 0, abs(delta))
1818
                                        found = True
1819
                                        break                
1820

    
1821
    def bind_close_items(self):
1822
        """ connect close item by pressing B """
1823
        from EngineeringLineItem import QEngineeringLineItem
1824
        from EngineeringNozzleItem import QEngineeringNozzleItem
1825
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1826
        from shapely.geometry import Point
1827

    
1828
        scene = self.scene()
1829
        app_doc_data = AppDocData.instance()
1830
        if scene:
1831
            configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1832
            toler = int(configs[0].value) if configs else 20
1833

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

    
1836
            for s_connector in self.connectors:
1837
                if s_connector.connectedItem:
1838
                    continue
1839

    
1840
                _pt1 = Point(s_connector.center()[0], s_connector.center()[1])
1841
                dist = sys.maxsize
1842
                selected = None
1843

    
1844
                for item in items:
1845
                    for i_connector in item.connectors:
1846
                        if i_connector.connectedItem:
1847
                            continue
1848

    
1849
                        _pt2 = Point(i_connector.center()[0], i_connector.center()[1])
1850
                        length = _pt1.distance(_pt2)
1851
                        if length < toler and length < dist:
1852
                            selected = item
1853

    
1854
                if selected:
1855
                    res = self.connect_if_possible(selected, toler)
1856
                    if res and type(selected) is QEngineeringLineItem:
1857
                        selected.set_line([res[1], res[2]])
1858

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

    
1862
                for item in items:
1863
                    if item.includes(self.connectors[0], margin=40):
1864
                        self.connectors[0].connect(item)
1865
                        break
1866

    
1867
            if type(self) is QEngineeringEquipmentItem:
1868
                configs = app_doc_data.getConfigs('Symbol', 'EQ binding')
1869
                eq_binding = int(configs[0].value) if configs else -1
1870
                for item in items:
1871
                    if eq_binding == 1 and (type(item) is QEngineeringLineItem and item.is_piping(True) or issubclass(type(item), SymbolSvgItem)):
1872
                        for index in range(len(item.connectors)):
1873
                            if item.connectors[index].connectedItem:
1874
                                continue
1875
                            
1876
                            if type(item) is QEngineeringLineItem or QEngineeringLineItem.check_piping(item.conn_type[index], True):
1877
                                if self.includes(item.connectors[index], margin=100):
1878
                                    item.connectors[index].connect(self)
1879

    
1880
                    elif issubclass(type(item), QEngineeringNozzleItem) and item.has_connection:
1881
                        for index in range(len(item.connectors)):
1882
                            if item.connectors[index].connectedItem or not QEngineeringLineItem.check_piping(item.conn_type[index], True):
1883
                                continue
1884

    
1885
                            if self.includes(item.connectors[index], margin=100):
1886
                                item.connectors[index].connect(self)
1887

    
1888
    '''
1889
        @brief      connect attribute
1890
        @author     humkyung
1891
        @date       2018.05.02
1892
        @history    humkyung 2018.05.09 append only nearest size attribute
1893
    '''
1894
    def connectAttribute(self, attributes, clear=True):
1895
        import math
1896
        from EngineeringTextItem import QEngineeringTextItem
1897
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1898
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1899
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1900
        from EngineeringLineItem import QEngineeringLineItem
1901

    
1902
        try:
1903
            if clear:
1904
                if not self.clear_attr_and_assoc_item():
1905
                    return
1906

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

    
1910
            rect = self.sceneBoundingRect()
1911
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
1912
            center = self.sceneBoundingRect().center()
1913

    
1914
            minDist = None
1915
            selected = None
1916

    
1917
            contain_texts = []
1918
            for attr in attributes:
1919
                # size text and operation code text will find owner themselves in findowner method
1920
                if False:  # type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
1921
                    dx = attr.center().x() - center.x()
1922
                    dy = attr.center().y() - center.y()
1923
                    length = math.sqrt(dx * dx + dy * dy)
1924
                    if (length < dist) and (minDist is None or length < minDist):
1925
                        minDist = length
1926
                        selected = attr
1927
                elif False:#type(attr) is QEngineeringInstrumentItem:
1928
                    if not attr.is_connected():
1929
                        dx = attr.center().x() - center.x()
1930
                        dy = attr.center().y() - center.y()
1931
                        if math.sqrt(dx * dx + dy * dy) < dist:
1932
                            if self.add_assoc_item(attr):
1933
                                attr.owner = self
1934
                elif issubclass(type(attr), QEngineeringTextItem):
1935
                    if rect.contains(attr.center()):
1936
                        dx = attr.center().x() - center.x()
1937
                        dy = attr.center().y() - center.y()
1938
                        length = math.sqrt(dx * dx + dy * dy)
1939
                        contain_texts.append([length, attr])
1940
                elif type(attr) is QEngineeringLineItem:
1941
                    length = attr.distanceTo([center.x(), center.y()])
1942
                    if (length < dist) and (minDist is None or length < minDist):
1943
                        minDist = length
1944
                        selected = attr
1945

    
1946
            if contain_texts:
1947
                num = len([_attr for _attr in self.getAttributes(findOwner=True) if _attr.AttributeType == 'Text Item'])
1948
                contain_texts = sorted(contain_texts, key=lambda param: param[0])
1949
                index = 0
1950
                for _, attr in contain_texts:
1951
                    if index >= num:
1952
                        break
1953
                    if self.add_assoc_item(attr):
1954
                        attr.owner = self  # set owner of text
1955
                    index += 1
1956

    
1957
            if selected is not None:
1958
                self.add_assoc_item(selected)
1959

    
1960
        except Exception as ex:
1961
            from App import App
1962
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1963
                                                          sys.exc_info()[-1].tb_lineno)
1964
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1965

    
1966
    '''
1967
        @brief      start rotating
1968
        @author     euisung
1969
        @date       2019.04.16
1970
    '''
1971
    def mouseDoubleClickEvent(self, event):
1972
        modifiers = QApplication.keyboardModifiers()
1973
        if not hasattr(self, '_rotating') and modifiers == Qt.NoModifier:
1974
            self._rotating = self.rotation()
1975
            self.grabMouse()
1976
        elif not hasattr(self, '_scale') and modifiers == Qt.ControlModifier:
1977
            self._scale = self.scale()
1978
            self._scale_x = event.scenePos().x()
1979
            self.grabMouse()
1980
        
1981
    def toXml(self):
1982
        """
1983
        generate xml code
1984
        :return:
1985
        """
1986
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1987
        from SymbolAttr import SymbolAttr
1988
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1989

    
1990
        try:
1991
            node = Element('SYMBOL')
1992
            node.attrib['Converted'] = str(self.converted)
1993
            uidNode = Element('UID')
1994
            uidNode.text = str(self.uid)
1995
            node.append(uidNode)
1996

    
1997
            dbUidNode = Element('DBUID')
1998
            dbUidNode.text = str(self.dbUid)
1999
            node.append(dbUidNode)
2000

    
2001
            nameNode = Element('NAME')
2002
            nameNode.text = self.name
2003
            node.append(nameNode)
2004

    
2005
            attributeValueNode = Element('ASSOCIATIONS')
2006
            for key, value in self._associations.items():
2007
                for assoc in value:
2008
                    assoc_node = Element('ASSOCIATION')
2009
                    assoc_node.attrib['TYPE'] = str(key)
2010
                    assoc_node.text = str(assoc)
2011
                    attributeValueNode.append(assoc_node)
2012
            node.append(attributeValueNode)
2013

    
2014
            typeNode = Element('TYPE')
2015
            typeNode.text = self.type
2016
            node.append(typeNode)
2017

    
2018
            # write owner's uid to xml
2019
            ownerNode = Element('OWNER')
2020
            if self.owner is not None:
2021
                ownerNode.text = str(self.owner)
2022
            else:
2023
                ownerNode.text = 'None'
2024
            node.append(ownerNode)
2025
            # up to here
2026

    
2027
            originNode = Element('ORIGINALPOINT')
2028
            if not self.transformOriginPoint().isNull():
2029
                origin = self.mapToScene(self.transformOriginPoint())
2030
                originNode.text = f"{origin.x()},{origin.y()}"
2031
            else:
2032
                origin = self.origin
2033
                originNode.text = f"{origin[0]},{origin[1]}"
2034
            node.append(originNode)
2035

    
2036
            connectorsNode = Element('CONNECTORS')
2037
            for connector in self.connectors:
2038
                connectorsNode.append(connector.toXml())
2039
            node.append(connectorsNode)
2040

    
2041
            connectionNode = Element('CONNECTIONPOINT')
2042
            connection_point = []
2043
            if self.connectors is not None:
2044
                for connector in self.connectors:
2045
                    connection_point.append(repr(connector))
2046
            connectionNode.text = '/'.join(connection_point)
2047
            node.append(connectionNode)
2048

    
2049
            locNode = Element('LOCATION')
2050
            locNode.text = f'{self.loc[0]},{self.loc[1]}'
2051
            '''
2052
            # calculate symbol's left-top corner
2053
            transform = QTransform()
2054
            transform.translate(self.scenePos().x(), self.scenePos().y())
2055
            transform.rotateRadians(-self.angle)
2056
            loc = transform.map(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2057
            # up to here
2058
            locNode.text = '{},{}'.format(loc.x() - self.symbolOrigin[0], loc.y() - self.symbolOrigin[1])
2059
            '''
2060
            node.append(locNode)
2061

    
2062
            sizeNode = Element('SIZE')
2063
            sizeNode.text = f'{self.size[0]},{self.size[1]}'
2064
            node.append(sizeNode)
2065

    
2066
            angleNode = Element('ANGLE')
2067
            angleNode.text = str(math.radians(self.rotation())) if self.scene() else str(self.angle)
2068
            node.append(angleNode)
2069

    
2070
            scaleNode = Element('SCALE')
2071
            if type(self) is QEngineeringEquipmentItem:
2072
                scaleX = abs(round(self.transform().m11() * 100))
2073
                scaleY = abs(round(self.transform().m22() * 100))
2074
                scaleNode.text = str(scaleX / 100.0) + ',' + str(scaleY / 100.0)
2075
            else:
2076
                _scale = str(round(self.scale(), 2))
2077
                scaleNode.text = _scale + ',' + _scale
2078
            node.append(scaleNode)
2079

    
2080
            parentSymbolNode = Element('PARENT')
2081
            parentSymbolNode.text = str(self.parentSymbol)
2082
            node.append(parentSymbolNode)
2083

    
2084
            childSymbolNode = Element('CHILD')
2085
            childSymbolNode.text = str(self.childSymbol)
2086
            node.append(childSymbolNode)
2087

    
2088
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
2089
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
2090
            node.append(hasInstrumentLabelNode)
2091

    
2092
            areaNode = Element('AREA')
2093
            areaNode.text = self.area
2094
            node.append(areaNode)
2095

    
2096
            flipNode = Element('FLIP')
2097
            flipNode.text = str(self.flip)
2098
            node.append(flipNode)
2099

    
2100
            if self.hit_ratio:
2101
                ratioNode = Element('RATIO')
2102
                ratioNode.text = str(self.hit_ratio)
2103
                node.append(ratioNode)
2104

    
2105
            properties_node = Element('PROPERTIES')
2106
            for prop, value in self.properties.items():
2107
                prop_node = prop.toXml()
2108
                prop_node.text = str(value) if value else ''
2109
                properties_node.append(prop_node)
2110
            node.append(properties_node)
2111

    
2112
            attributesNode = Element('SYMBOLATTRIBUTES')
2113
            _attrs = self.getAttributes()
2114
            for attr in _attrs:
2115
                if type(attr) is SymbolAttr:
2116
                    _node = attr.toXml()
2117
                    if attr.AttributeType != 'Spec':
2118
                        _node.text = str(_attrs[attr])
2119
                    elif attr.AttributeType == 'Spec':
2120
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
2121
                    attributesNode.append(_node)
2122

    
2123
            node.append(attributesNode)
2124

    
2125
            if hasattr(self, 'currentPointModeIndex'):
2126
                currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
2127
                currentPointModeIndexNode.text = str(self.currentPointModeIndex) if self.currentPointModeIndex else ''
2128
                node.append(currentPointModeIndexNode)
2129
        except Exception as ex:
2130
            from App import App
2131
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
2132
                      f'{sys.exc_info()[-1].tb_lineno}'
2133
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2134

    
2135
            return None
2136

    
2137
        return node
2138

    
2139
    @staticmethod
2140
    def from_database(component):
2141
        """ create a item related to given component from database """
2142
        import uuid
2143
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2144
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2145
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2146
        from SymbolAttr import SymbolAttr
2147
        item = None
2148

    
2149
        try:
2150
            app_doc_data = AppDocData.instance()
2151

    
2152
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
2153
            grid = int(configs[0].value) if 1 == len(configs) else -1
2154

    
2155
            uid = component['UID']
2156

    
2157
            pt = [float(component['X']), float(component['Y'])]
2158
            if grid == 1:
2159
                pt = [round(pt[0]), round(pt[1])]
2160
                
2161
            size = [float(component['Width']), float(component['Height'])]
2162

    
2163
            dbUid = int(component['Symbol_UID'])
2164
            dbData = app_doc_data.getSymbolByQuery('UID', dbUid)
2165
            name = dbData.sName
2166
            _type = dbData.sType
2167
            angle = float(component['Rotation'])
2168

    
2169
            origin = [float(x) for x in component['SceneOriginPoint'].split(',')] if component['SceneOriginPoint'] is not None else pt
2170
            if grid == 1:
2171
                origin = [round(origin[0]), round(origin[1])]
2172

    
2173
            connPts = []
2174
            if component['ConnectionPoint']:
2175
                if dbData:
2176
                    db_conn = dbData.connectionPoint.split('/')
2177
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
2178
                    db_point_direction = [conn.split(',')[0] for conn in db_conn]
2179
                index = 0
2180
                for conn_pt in component['ConnectionPoint'].split('/'):
2181
                    if index >= len(db_conn):
2182
                        break
2183
                    tokens = conn_pt.split(',')
2184
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
2185
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
2186
                                   (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]))
2187
                    index += 1
2188

    
2189
            baseSymbol = dbData.baseSymbol
2190

    
2191
            childSymbolNode = component['AdditionalSymbol']
2192
            childSymbol = childSymbolNode if childSymbolNode is not None else ''
2193

    
2194
            owner = component['Owner'] if component['Owner'] is not None and component['Owner'] != 'None' else None
2195

    
2196
            hasInstrumentLabel = dbData.hasInstrumentLabel
2197

    
2198
            flipLabelNode = component['Flip']
2199
            flipLabel = int(flipLabelNode) if flipLabelNode is not None else 0
2200

    
2201
            project = app_doc_data.getCurrentProject()
2202
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
2203
            if os.path.isfile(svgFilePath):
2204
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
2205
                item.setVisible(False)
2206

    
2207
                scale = [1.0, 1.0]
2208
                if component['Value']:
2209
                    if ',' in component['Value']:
2210
                        scale = [float(component['Value'].split(',')[0]), float(component['Value'].split(',')[1])]
2211
                    else:
2212
                        scale = [float(component['Value']), float(component['Value'])]
2213

    
2214
                # if additional symbol was changed, change symbol info
2215
                symbolInfo = None
2216
                if dbUid is None:
2217
                    symbolInfo = app_doc_data.getSymbolByQuery('name', name)
2218
                else:
2219
                    symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
2220
                if symbolInfo:
2221
                    childSymbol = symbolInfo.additionalSymbol
2222

    
2223
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
2224
                                  hasInstrumentLabel, dbUid=dbUid, scale=scale):
2225
                    pass
2226
                else:
2227
                    return None
2228

    
2229
                for key in item._properties.keys():
2230
                    for compo in component.keys():
2231
                        if key.Attribute == compo:
2232
                            item._properties[key] = key.parse_value(component[key.Attribute])# if component[key.Attribute] else ''
2233

    
2234
                ## assign area
2235
                areaNode = component['Area']
2236
                if areaNode is None:
2237
                    for area in app_doc_data.getAreaList():
2238
                        if area.contains(pt):
2239
                            item.area = area.name
2240
                            break
2241
                else:
2242
                    item.area = areaNode
2243
                ## up to here
2244

    
2245
                connectors = app_doc_data.get_component_connectors(uid)
2246
                if connectors:
2247
                    iterIndex = 0
2248
                    for connector in connectors:
2249
                        if iterIndex >= len(db_conn):
2250
                            break
2251
                        item.connectors[iterIndex].parse_record(connector)
2252
                        iterIndex += 1
2253

    
2254
                # get associations 
2255
                associations = app_doc_data.get_component_associations(uid)
2256
                if associations:
2257
                    for assoc in associations:
2258
                        _attrType = assoc['Type']
2259
                        if not _attrType in item._associations:
2260
                            item._associations[_attrType] = []
2261
                        item._associations[_attrType].append(
2262
                            uuid.UUID(assoc['Association']) if assoc['Association'] != 'None' else None)
2263
                # up to here
2264

    
2265
                attributes = app_doc_data.get_component_attributes(uid)
2266
                if attributes:
2267
                    for attr in attributes:
2268
                        _attr = SymbolAttr.from_record(attr)
2269
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
2270
                            item.attrs[_attr] = attr['Value']
2271
                        else:
2272
                            if _attr.AttributeType == 'Spec':
2273
                                item.attrs[_attr] = [attr['Value'].split(',')[0], attr['Value'].split(',')[1]]
2274
                            else:
2275
                                item.attrs[_attr] = attr['Value']
2276
                            '''
2277
                            elif _attr.Attribute == 'UpStream':
2278
                                for initKey in item.attrs.keys():
2279
                                    if initKey.Attribute == 'UpStream':
2280
                                        item.attrs[initKey] = attr['Value']
2281
                            elif _attr.Attribute == 'DownStream':
2282
                                for initKey in item.attrs.keys():
2283
                                    if initKey.Attribute == 'DownStream':
2284
                                        item.attrs[initKey] = attr['Value']
2285
                            '''
2286

    
2287
                currentPointModeIndex = component['OriginIndex']
2288
                if currentPointModeIndex is not None:
2289
                    item.currentPointModeIndex = int(currentPointModeIndex)
2290
        except Exception as ex:
2291
            from App import App
2292
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2293
                                                          sys.exc_info()[-1].tb_lineno)
2294
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2295

    
2296
            return None
2297

    
2298
        return item
2299

    
2300
    '''
2301
        @brief      parse xml code
2302
        @author     humkyung
2303
        @date       2018.07.20
2304
        @history    humkyung 2018.07.20 parse uid from xml node
2305
                    humkyung 2018.07.23 parse connected item's uid from xml node
2306
                    kyouho  2018.07.31 
2307
                    humkyung 2018.09.06 assign area to item
2308
    '''
2309

    
2310
    @staticmethod
2311
    def fromXml(node):
2312
        import uuid
2313
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2314
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2315
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2316
        from SymbolAttr import SymbolAttr
2317
        item = None
2318

    
2319
        try:
2320
            appDocData = AppDocData.instance()
2321

    
2322
            uidNode = node.find('UID')
2323
            uid = uidNode.text if uidNode is not None else uuid.uuid4()  # generate UUID
2324

    
2325
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
2326
            grid = int(configs[0].value) if 1 == len(configs) else -1
2327

    
2328
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
2329
            if grid == 1:
2330
                pt = [round(pt[0]), round(pt[1])]
2331
            size = [float(x) for x in node.find('SIZE').text.split(',')]
2332

    
2333
            dbUidNode = node.find('DBUID')
2334
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
2335
            dbData = None
2336
            if dbUid:
2337
                dbData = appDocData.getSymbolByQuery('UID', dbUid)
2338
            name = node.find('NAME').text if dbData is None else dbData.sName
2339

    
2340
            angle = float(node.find('ANGLE').text)
2341
            _type = node.find('TYPE').text if dbData is None else dbData.sType
2342
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
2343
            if grid == 1:
2344
                origin = [round(origin[0]), round(origin[1])]
2345
            connPts = []
2346
            if node.find('CONNECTIONPOINT').text is not None:
2347
                if dbData:
2348
                    db_conn = dbData.connectionPoint.split('/')
2349
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
2350
                    db_point_direction = [conn.split(',')[0] for conn in db_conn]
2351
                index = 0
2352
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
2353
                    if index >= len(db_conn):
2354
                        break
2355
                    tokens = conn_pt.split(',')
2356
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
2357
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
2358
                                   (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]))
2359
                    index += 1
2360
            baseSymbol = node.find('PARENT').text if dbData is None else dbData.baseSymbol
2361
            childSymbolNode = node.find('CHILD')
2362
            childSymbol = ''
2363
            if childSymbolNode is not None:
2364
                childSymbol = childSymbolNode.text
2365

    
2366
            ownerNode = node.find('OWNER')
2367
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
2368

    
2369
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
2370
            hasInstrumentLabel = hasInstrumentLabelNode.text if dbData is None else dbData.hasInstrumentLabel
2371

    
2372
            flipLabelNode = node.find('FLIP')
2373
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
2374

    
2375
            ratioNode = node.find('RATIO')
2376
            hit_ratio = float(ratioNode.text) if ratioNode is not None else None
2377

    
2378
            project = appDocData.getCurrentProject()
2379
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
2380
            if os.path.isfile(svgFilePath):
2381
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
2382
                item.setVisible(False)
2383

    
2384
                scaleNode = node.find('SCALE')
2385
                scale = [1.0, 1.0]
2386
                if scaleNode is not None and scaleNode.text:
2387
                    if ',' in scaleNode.text:
2388
                        scale = [float(scaleNode.text.split(',')[0]), float(scaleNode.text.split(',')[1])]
2389
                    else:
2390
                        scale = [float(scaleNode.text), float(scaleNode.text)]
2391
                
2392
                # if additional symbol was changed, change symbol info
2393
                symbolInfo = None
2394
                if dbUid is None:
2395
                    symbolInfo = appDocData.getSymbolByQuery('name', name)
2396
                else:
2397
                    symbolInfo = appDocData.getSymbolByQuery('UID', dbUid)
2398
                if symbolInfo:
2399
                    childSymbol = symbolInfo.additionalSymbol
2400

    
2401
                if hit_ratio:
2402
                    item.hit_ratio = hit_ratio
2403

    
2404
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
2405
                                  hasInstrumentLabel, dbUid=dbUid, scale=scale):
2406
                    pass
2407
                else:
2408
                    return None
2409

    
2410
                for prop_node in node.iter('PROPERTY'):
2411
                    matches = [prop for prop in item._properties.keys() if
2412
                               prop.Attribute == prop_node.attrib['Attribute']]
2413
                    if matches:
2414
                        item._properties[matches[0]] = matches[0].parse_value(prop_node.text)# if prop_node.text else ''
2415

    
2416
                # assign area
2417
                areaNode = node.find('AREA')
2418
                if areaNode is None:
2419
                    for area in appDocData.getAreaList():
2420
                        if area.contains(pt):
2421
                            item.area = area.name
2422
                            break
2423
                else:
2424
                    item.area = areaNode.text
2425
                # up to here
2426

    
2427
                connectors = node.find('CONNECTORS')
2428
                if connectors is not None:
2429
                    iterIndex = 0
2430
                    for connector in connectors.iter('CONNECTOR'):
2431
                        if iterIndex >= len(db_conn):
2432
                            break
2433
                        item.connectors[iterIndex].parse_xml(connector)
2434
                        iterIndex += 1
2435

    
2436
                # get associations 
2437
                attributeValue = node.find('ASSOCIATIONS')
2438
                if attributeValue is not None:
2439
                    for assoc in attributeValue.iter('ASSOCIATION'):
2440
                        _attrType = assoc.attrib['TYPE']
2441
                        if not _attrType in item._associations:
2442
                            item._associations[_attrType] = []
2443
                        item._associations[_attrType].append(uuid.UUID(assoc.text) if assoc.text != 'None' else None)
2444
                # up to here
2445

    
2446
                attributes = node.find('SYMBOLATTRIBUTES')
2447
                if attributes is not None:
2448
                    '''
2449
                    ## for old spec break item that has not uid currectly, may not necessary new data
2450
                    if _type == 'Segment Breaks':
2451
                        specBreakAttrs = appDocData.getSymbolAttribute('Segment Breaks')
2452
                    ## up to here 1
2453
                    '''
2454
                    for attr in attributes.iter('ATTRIBUTE'):
2455
                        _attr = SymbolAttr.fromXml(attr)
2456
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
2457
                            item.attrs[_attr] = attr.text
2458
                        else:
2459
                            '''
2460
                            ## for old spec break item that has not uid currectly, may not necessary new data
2461
                            matchAttr = [cAttr for cAttr in specBreakAttrs if _attr.Attribute == cAttr.Attribute]
2462
                            matchAttr = matchAttr[0] if matchAttr else None
2463
                            if matchAttr:
2464
                                _attr.UID = matchAttr.UID
2465
                                _attr.DisplayAttribute = matchAttr.DisplayAttribute
2466
                                _attr.AttributeType = matchAttr.AttributeType
2467
                                _attr.AttrAt = matchAttr.AttrAt
2468
                                _attr.Expression = matchAttr.Expression
2469
                                _attr.Length = matchAttr.Length
2470
                            # up to here 2
2471
                            '''
2472
                            if _attr.AttributeType == 'Spec':
2473
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
2474
                            else:
2475
                                '''
2476
                                # for old spec break item that has not uid currectly, may not necessary new data
2477
                                _attr.AssocItem = uuid.UUID(attr.text) if attr.text and attr.text != 'None' else None
2478
                                # up to here 3
2479
                                '''
2480
                                item.attrs[_attr] = attr.text
2481
                            '''
2482
                            elif _attr.Attribute == 'UpStream':
2483
                                for initKey in item.attrs.keys():
2484
                                    if initKey.Attribute == 'UpStream':
2485
                                        item.attrs[initKey] = attr.text
2486
                            elif _attr.Attribute == 'DownStream':
2487
                                for initKey in item.attrs.keys():
2488
                                    if initKey.Attribute == 'DownStream':
2489
                                        item.attrs[initKey] = attr.text
2490
                            '''
2491

    
2492
                currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
2493
                if currentPointModeIndex is not None:
2494
                    item.currentPointModeIndex = int(currentPointModeIndex.text) if currentPointModeIndex.text else 0
2495
        except Exception as ex:
2496
            from App import App
2497
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2498
                                                          sys.exc_info()[-1].tb_lineno)
2499
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2500

    
2501
            return None
2502

    
2503
        return item
2504

    
2505
    def to_svg(self, parent) -> list:
2506
        """convert symbol svg item to svg"""
2507
        import re
2508
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
2509
        from App import App
2510

    
2511
        res = []
2512
        try:
2513
            app_doc_data = AppDocData.instance()
2514
            prj = app_doc_data.getCurrentProject()
2515

    
2516
            node = Element('g')
2517
            node.attrib['id'] = str(self.uid)
2518
            node.attrib['class'] = self._class
2519

    
2520
            except_pattern = re.compile('[^a-zA-Z0-9-_]')
2521
            for attr, value in self.getAttributes().items():
2522
                node.attrib[re.sub(except_pattern, '_', attr.Attribute)] = str(value) if value else ''
2523
            trans = self.sceneTransform()
2524
            node.attrib['transform'] = f"matrix(" \
2525
                                       f"{trans.m11()},{trans.m12()}," \
2526
                                       f"{trans.m21()},{trans.m22()}," \
2527
                                       f"{trans.m31()},{trans.m32()}" \
2528
                                       f")"
2529

    
2530
            node_list = self._document.elementsByTagName('path')
2531
            for at in range(node_list.count()):
2532
                path = Element('path')
2533
                path.attrib['d'] = node_list.item(at).attributes().namedItem('d').nodeValue()
2534
                path.attrib['transform'] = self._document.elementsByTagName('g').item(0).attributes().namedItem('transform').nodeValue()
2535
                node.append(path)
2536

    
2537
            for assoc in self.associations():
2538
                assoc_node = assoc.to_svg(parent=self)
2539
                node.extend(assoc_node)
2540

    
2541
            res.append(node)
2542
        except Exception as ex:
2543
            from App import App
2544
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2545
                                                          sys.exc_info()[-1].tb_lineno)
2546
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2547

    
2548
        return res
2549

    
2550
    '''
2551
        @brief      create item corresponding to given type
2552
        @author     humkyung
2553
        @date       2018.05.02
2554
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
2555
                    humkyung 2018.05.10 change symbol's color to blue
2556
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
2557
    '''
2558

    
2559
    @staticmethod
2560
    def createItem(type: str, name: str, path: str, uid=None, owner=None, flip=0):
2561
        from QEngineeringOPCItem import QEngineeringOPCItem
2562
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2563
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
2564
        from EngineeringNozzleItem import QEngineeringNozzleItem
2565
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2566
        from EngineeringReducerItem import QEngineeringReducerItem
2567
        from EngineeringErrorItem import QEngineeringErrorItem
2568
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2569
        from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
2570
        from AppDocData import AppDocData
2571
        import uuid
2572

    
2573
        app_doc_data = AppDocData.instance()
2574

    
2575
        item = None
2576
        try:
2577
            cateogry = app_doc_data.getSymbolCategoryByType(type)
2578
            if type == "Piping OPC's" or type == "Instrument OPC's":
2579
                item = QEngineeringOPCItem(path, uid, flip=flip)
2580
            elif type == 'Nozzles':
2581
                item = QEngineeringNozzleItem(path, uid, flip=flip)
2582
            elif cateogry == 'Equipment' or cateogry == 'Equipment Components' or cateogry == 'Package':
2583
                item = QEngineeringEquipmentItem(path, uid, flip=flip)
2584
            elif cateogry == 'Instrumentation':
2585
                item = QEngineeringInstrumentItem(path, uid, flip=flip)
2586
            elif type == 'Segment Breaks':
2587
                item = QEngineeringSpecBreakItem(path, uid, flip=flip)
2588
            elif type == 'Reducers':
2589
                item = QEngineeringReducerItem(path, uid, flip=flip)
2590
            elif type == 'Error':
2591
                item = QEngineeringErrorItem(path, uid, flip=flip)
2592
            elif type == 'End Break':
2593
                item = QEngineeringEndBreakItem(path, uid, flip=flip)
2594
            # elif type == 'Flow Mark':
2595
            #    item = QEngineeringFlowMarkItem(path, uid, flip=flip)
2596
            else:
2597
                item = SymbolSvgItem(name, path, uid, flip=flip)
2598

    
2599
            if owner is not None:
2600
                item.owner = uuid.UUID(owner)
2601

    
2602
        except Exception as ex:
2603
            from App import App
2604
            from AppDocData import MessageType
2605

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

    
2610
        return item
2611

    
2612
    def setColor(self, color):
2613
        """change svg item's color"""
2614
        if self._document:# and color != self.__color:
2615
            self.changeAttributes('fill', color)
2616
            self.changeAttributes('stroke', color)
2617
            self.renderer().load(self._document.toByteArray())
2618
            self.__color = color
2619
            #self.update()
2620

    
2621
    '''
2622
        @brief  get attributes from svg file
2623
        @author humkyung
2624
        @date   2019.03.08
2625
    '''
2626

    
2627
    def get_attribute(self, attName):
2628
        root = self._document.documentElement()
2629
        node = root.firstChild()
2630
        while not node.isNull():
2631
            if node.isElement():
2632
                element = node.toElement()
2633
                if element.hasAttribute(attName):
2634
                    return element.attribute(attName)
2635

    
2636
                if element.hasChildNodes():
2637
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
2638
                    if att_val is not None: return att_val
2639

    
2640
            node = node.nextSibling()
2641

    
2642
        return None
2643

    
2644
    '''
2645
        @brief  get recursively attribute
2646
        @author humkyung
2647
        @date   2019.03.08
2648
    '''
2649

    
2650
    def recursive_get_attribute(self, node, attName):
2651
        while not node.isNull():
2652
            if node.isElement():
2653
                element = node.toElement()
2654
                if element.hasAttribute(attName):
2655
                    return element.attribute(attName)
2656

    
2657
                if node.hasChildNodes():
2658
                    att_val = self.recursive_get_attribute(node.firstChild(), attName)
2659
                    if att_val is not None: return att_val
2660

    
2661
            node = node.nextSibling()
2662

    
2663
        return None
2664

    
2665
    '''
2666
        @brief  change attributes
2667
        @author humkyung
2668
        @date   2018.05.10
2669
    '''
2670

    
2671
    def changeAttributes(self, attName, attValue):
2672
        root = self._document.documentElement()
2673
        node = root.firstChild()
2674
        while not node.isNull():
2675
            if node.isElement():
2676
                element = node.toElement()
2677
                if element.hasAttribute(attName):
2678
                    element.setAttribute(attName, attValue)
2679

    
2680
                if element.hasChildNodes():
2681
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
2682

    
2683
            node = node.nextSibling()
2684

    
2685
    '''
2686
        @brief  change attribute
2687
        @author humkyung
2688
        @date   2018.05.10
2689
    '''
2690

    
2691
    def recursiveChangeAttributes(self, node, attName, attValue):
2692
        while not node.isNull():
2693
            if node.isElement():
2694
                element = node.toElement()
2695
                if element.hasAttribute(attName):
2696
                    element.setAttribute(attName, attValue)
2697

    
2698
                if node.hasChildNodes():
2699
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
2700

    
2701
            node = node.nextSibling()
2702

    
2703
    '''
2704
        @brief  draw rect when item is selected
2705
        @author humkyung
2706
        @date   2018.07.07
2707
    '''
2708

    
2709
    def drawFocusRect(self, painter):
2710
        self.focuspen = QPen(Qt.DotLine)
2711
        self.focuspen.setColor(Qt.black)
2712
        self.focuspen.setWidthF(1.5)
2713
        hilightColor = QColor(255, 0, 0, 127)
2714
        painter.setBrush(QBrush(hilightColor))
2715
        painter.setPen(self.focuspen)
2716
        painter.drawRect(self.boundingRect())
2717

    
2718
    '''
2719
        @brief  override paint(draw connection points)
2720
        @author humkyung
2721
        @date   2018.04.21
2722
    '''
2723

    
2724
    def paint(self, painter, options=None, widget=None):
2725
        #from EngineeringTextItem import QEngineeringTextItem
2726

    
2727
        self.setColor(self.getColor())
2728

    
2729
        painter.setClipRect(options.exposedRect)
2730
        QGraphicsSvgItem.paint(self, painter, options, widget)
2731
        '''
2732
        # not used
2733
        for attr in self.attrs:
2734
            if issubclass(type(attr), QEngineeringTextItem):
2735
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (
2736
                    self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
2737
                attr.setColor(color)
2738
            elif issubclass(type(attr), SymbolSvgItem):
2739
                attr.setColor(self.getColor())
2740
                attr.update()
2741
        '''
2742

    
2743
        if self.isSelected():
2744
            self.drawFocusRect(painter)
2745

    
2746
    def resetPosition(self):
2747
        if self.flip:
2748
            self.flip_symbol()
2749

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

    
2755
    def addSvgItemToScene(self, scene, undoable: bool = False, manual=False) -> None:
2756
        """Add Svg Item into ImageViewer's Scene"""
2757
        #if self.flip:
2758
        #    self.flip_symbol()
2759

    
2760
        if (hasattr(self, 'symbolConvertingOrigin') and not self.symbolConvertingOrigin) or not manual:
2761
            self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2762
            self.moveBy(-self.symbolOrigin[0], -self.symbolOrigin[1])
2763
            self.setRotation(math.degrees(self.angle))
2764
            self.moveBy(self.origin[0], self.origin[1])
2765
        else:
2766
            self.setTransformOriginPoint(QPointF(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1]))
2767
            self.moveBy(-self.symbolConvertingOrigin[0], -self.symbolConvertingOrigin[1])
2768
            self.setRotation(math.degrees(self.angle))
2769
            self.moveBy(self.origin[0], self.origin[1])
2770
            self.setTransformOriginPoint(QPointF(0, 0))
2771
            self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2772

    
2773
            #self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2774
            #self.moveBy(-self.symbolOrigin[0], -self.symbolOrigin[1])
2775
            #self.setRotation(math.degrees(self.angle))
2776
            #self.moveBy(self.origin[0], self.origin[1])
2777

    
2778
            dx = self.symbolOrigin[0] - self.symbolConvertingOrigin[0]
2779
            dy = self.symbolOrigin[1] - self.symbolConvertingOrigin[1]
2780
            nx = round(math.cos(self.angle) * dx - math.sin(self.angle) * dy)
2781
            ny = round(math.sin(self.angle) * dx + math.cos(self.angle) * dy)
2782
            self.moveBy(nx - dx, ny - dy)
2783

    
2784
            #transform = QTransform()
2785
            #transform.translate(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1])
2786
            ##transform.map(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1])
2787
            #transform.rotate(math.degrees(self.angle))
2788
            #transform.translate(-self.symbolConvertingOrigin[0], -self.symbolConvertingOrigin[1])
2789
            #self.setTransform(transform)
2790

    
2791
        scene.addItem(self)
2792

    
2793
        self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
2794
        self.loc[0], self.loc[1] = round(self.sceneBoundingRect().top()), round(self.sceneBoundingRect().left())
2795

    
2796
        scene_origin = self.mapToScene(self.transformOriginPoint())
2797

    
2798
        configs = AppDocData.instance().getConfigs('Data', 'Grid')
2799
        grid = int(configs[0].value) if 1 == len(configs) else -1
2800
        if grid == 1:
2801
            self.origin = [round(scene_origin.x()), round(scene_origin.y())]
2802
        else:
2803
            self.origin = [scene_origin.x(), scene_origin.y()]
2804

    
2805
        if undoable:
2806
            from CreateCommand import CreateCommand
2807
            self.scene().undo_stack.push(CreateCommand(self.scene(), [self,]))
2808

    
2809
    '''
2810
        @brief      
2811
        @author     humkyung
2812
        @date       2018.07.27
2813
    '''
2814
    def onConnectorPosChaned(self, connector):
2815
        pass
2816

    
2817
    '''
2818
        @brief      set connector
2819
        @author     kyouho
2820
        @date       2018.07.26
2821
    '''
2822
    def setConnector(self, index):
2823
        connector = QEngineeringConnectorItem(parent=self, index=index)
2824
        connector.setParentItem(self)
2825
        self.connectors.append(connector)
2826

    
2827
    def refreshConnector(self):
2828
        for connector in self.connectors:
2829
            connector.buildItem()
2830

    
2831
    '''
2832
        @brief      Delete svg item
2833
        @author     Jeongwoo
2834
        @date       2018.05.25
2835
    '''
2836
    def deleteSvgItemFromScene(self):
2837
        ''' not used '''
2838
        for connector in self.connectors:
2839
            if connector.connectedItem is not None:
2840
                try:
2841
                    connector.connectedItem.removeSymbol(self)
2842
                except Exception as ex:
2843
                    from App import App
2844
                    from AppDocData import MessageType
2845

    
2846
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2847
                                                                  sys.exc_info()[-1].tb_lineno)
2848
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
2849
                break
2850

    
2851
        self.transfer.onRemoved.emit(self)
2852

    
2853
    def flip_symbol(self) -> None:
2854
        """flip symbol"""
2855

    
2856
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2857

    
2858
        if type(self) is QEngineeringEquipmentItem:
2859
            transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
2860
            transform.scale(self.transform().m11(), self.transform().m22())
2861
            transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
2862
            transform.scale(-1.0, 1.0)
2863
            transform.translate(-2 * self.symbolOrigin[0], 0)
2864
        else:
2865
            transform = QTransform()
2866
            if self.flip is 1:
2867
                #rect = self.boundingRect()
2868
                transform.scale(-1.0, 1.0)
2869
                transform.translate(-2 * self.symbolOrigin[0], 0)
2870

    
2871
        self.setTransform(transform)
2872

    
2873
    def rotate(self, angle: float) -> None:
2874
        """rotate symbol by given angle in radian"""
2875
        import math
2876
        self.setRotation(math.degrees(angle))
2877
        '''
2878
        transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
2879
        transform.rotate(math.degrees(angle))
2880
        transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
2881
        self.setTransform(transform)
2882
        '''
2883

    
2884
    '''
2885
        @brief      change Conn point 
2886
        @author     kyouho
2887
        @date       2018.07.25
2888
    '''
2889
    def changeConnPoint(self):
2890
        if len(self.connectors) == 2:
2891
            conn1Item = self.connectors[0].connectedItem
2892
            conn2Item = self.connectors[1].connectedItem
2893
            self.connectors[0].connectedItem = conn2Item
2894
            self.connectors[1].connectedItem = conn1Item
2895

    
2896
            currentPoint = self.getCurrentPoint()
2897
            #self.reSettingSymbol(currentPoint, self.angle)
2898

    
2899
    '''
2900
        @brief      change standard point
2901
        @author     kyouho
2902
        @date       2018.07.24
2903
    '''
2904
    def changeStandardPoint(self):
2905
        connPtsCount = len(self.connectors)
2906

    
2907
        if self.currentPointModeIndex < connPtsCount:
2908
            self.currentPointModeIndex += 1
2909
        else:
2910
            self.currentPointModeIndex = 0
2911

    
2912
        currentPoint = self.getCurrentPoint()
2913
        #self.reSettingSymbol(currentPoint, self.angle)
2914

    
2915
    def getCurrentPoint(self):
2916
        """return transform origin point"""
2917
        pointList = []
2918
        pointList.append(self.symbolOrigin)
2919
        for connector in self.connectors:
2920
            pointList.append(connector.connectPoint)
2921

    
2922
        return pointList[self.currentPointModeIndex]
2923

    
2924
    def EvaluatedCode(self, old_code, code_name):
2925
        """ return new attribute code """
2926
        from AppDocData import AppDocData
2927
        from CodeTables import CodeTable
2928
        from LineTypeConditions import LineTypeConditions
2929
        from EngineeringLineItem import QEngineeringLineItem
2930
        import re
2931

    
2932
        try:
2933
            code = old_code
2934
            start_item = None
2935
            if self.iType == 19:    # Labels - Symbol
2936
                matches = [assoc for assoc in self.associations() if issubclass(type(assoc), SymbolSvgItem)]
2937
                start_item = matches[0] if matches else self
2938
            else:
2939
                start_item = self
2940

    
2941
            #if not start_item:
2942
            #    return ''
2943

    
2944
            app_doc_data = AppDocData.instance()
2945
            connected_items_lists = app_doc_data._connected_items_lists
2946
            items = [connected_items_list.items for connected_items_list in connected_items_lists.runs if start_item in connected_items_list.items]
2947
            if len(items) != 1:
2948
                return ''
2949
            else:
2950
                items = [item for item in items[0] if issubclass(type(item), SymbolSvgItem)]
2951

    
2952
            table = CodeTable.instance(code_name, inst=True)
2953
            line_types = [condition.name for condition in LineTypeConditions.items()]
2954

    
2955
            new_codes = []
2956
            for value in table.values: # uid, old code, symbol, attribute, new code, expression, priority
2957
                # symbol, line type
2958
                if len(value[3]) == 1 and not value[3][0]:
2959
                    # symbol
2960
                    if not [line_type for line_type in line_types if line_type in value[2]]:
2961
                        for item in items:
2962
                            match = re.search(value[1][0], code, re.DOTALL)
2963
                            if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) \
2964
                                and item.name in value[2]:
2965
                                dx = item.origin[0] - self.origin[0]
2966
                                dy = item.origin[1] - self.origin[1]
2967
                                length = math.sqrt(dx*dx + dy*dy)
2968
                                if not value[5]:
2969
                                    new_codes.append([length if not value[6] else value[6], '"' + value[4] + '"', item, length])
2970
                                else:
2971
                                    new_codes.append([length if not value[6] else value[6], value[5], item, length])
2972
                    # line
2973
                    else:
2974
                        match = re.search(value[1][0], code, re.DOTALL)
2975
                        types = [conn.connectedItem.lineType for conn in self.connectors if conn.connectedItem and type(conn.connectedItem) is QEngineeringLineItem]
2976
                        if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) and \
2977
                            [line_type for line_type in value[2] if line_type in types]:
2978
                            if not value[5]:
2979
                                new_codes.append([0 if not value[6] else value[6], '"' + value[4] + '"', None, sys.maxsize])
2980
                            else:
2981
                                new_codes.append([0 if not value[6] else value[6], value[5], None, sys.maxsize])
2982

    
2983
                # self attribute
2984
                elif len(value[2]) == 1 and not value[2][0] and value[3][0]:
2985
                    for key, _value in self.attrs.items():
2986
                        if _value in value[3]:
2987
                            if not value[5]:
2988
                                new_codes.append([0 if not value[6] else value[6], '"' + value[4] + '"', self, sys.maxsize])
2989
                            else:
2990
                                new_codes.append([0 if not value[6] else value[6], value[5], self, sys.maxsize])
2991

    
2992
                # symbol + attribute
2993
                elif value[2][0] and value[3][0]:
2994
                    for item in items:
2995
                        match = re.search(value[1][0], code, re.DOTALL)
2996
                        if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) \
2997
                            and item.name in value[2]:
2998
                            dx = item.origin[0] - self.origin[0]
2999
                            dy = item.origin[1] - self.origin[1]
3000
                            length = math.sqrt(dx*dx + dy*dy)
3001

    
3002
                            for key, _value in item.attrs.items():
3003
                                if _value in value[3]:
3004
                                    if not value[5]:
3005
                                        new_codes.append([length if not value[6] else value[6], '"' + value[4] + '"', item, length])
3006
                                    else:
3007
                                        new_codes.append([length if not value[6] else value[6], value[5], item, length])
3008
                        
3009
            # default
3010
            for value in table.values:
3011
                match = re.search(value[1][0], code, re.DOTALL)
3012
                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] == '':
3013
                    if not value[5]:
3014
                        new_codes.append([sys.maxsize if not value[6] else value[6], '"' + value[4] + '"', None, sys.maxsize])
3015
                    else:
3016
                        new_codes.append([sys.maxsize if not value[6] else value[6], value[5], None, sys.maxsize])
3017

    
3018
            if new_codes:
3019
                new_codes = sorted(new_codes, key=lambda param: param[0])
3020
                new_codes = [_code for _code in new_codes if _code[0] == new_codes[0][0]]
3021
                new_codes = sorted(new_codes, key=lambda param: param[-1])
3022
                item = new_codes[0][-2]
3023
                new_code = eval(new_codes[0][1])
3024
                return new_code
3025
            else:
3026
                return code
3027
        except Exception as ex:
3028
            from App import App
3029
            from AppDocData import MessageType
3030

    
3031
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
3032
                                                          sys.exc_info()[-1].tb_lineno)
3033
            #App.mainWnd().addMessage.emit(MessageType.Error, str(self.uid) + self.name + message)
3034
            App.mainWnd().addMessage.emit(MessageType.Error, message)
3035

    
3036
def recursiveChangeAttributes(node, attName, attValue):
3037
    while not node.isNull():
3038
        if node.isElement():
3039
            element = node.toElement()
3040
            if element.hasAttribute(attName):
3041
                element.setAttribute(attName, attValue)
3042

    
3043
            if node.hasChildNodes():
3044
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
3045

    
3046
        node = node.nextSibling()
3047

    
3048

    
3049
'''
3050
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
3051
    @author     Jeongwoo
3052
    @date       2018.06.18
3053
'''
3054
class Transfer(QObject):
3055
    on_pos_changed = pyqtSignal(QGraphicsItem)
3056
    onRemoved = pyqtSignal(QGraphicsItem)
3057

    
3058
    def __init__(self, parent=None):
3059
        QObject.__init__(self, parent)
3060

    
3061

    
3062
if __name__ == '__main__':
3063
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
3064
    f.open(QIODevice.ReadOnly)
3065
    array = f.readAll()
3066
    document = QDomDocument()
3067
    document.setContent(array)
3068

    
3069
    root = document.documentElement()
3070
    node = root.firstChild()
3071
    while not node.isNull():
3072
        if node.isElement():
3073
            element = node.toElement()
3074
            if element.hasAttribute('fill'):
3075
                element.setAttribute('fill', '#FFFFF')
3076

    
3077
            if element.hasChildNodes():
3078
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
3079

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