프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ 38eca06a

이력 | 보기 | 이력해설 | 다운로드 (128 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):
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
        errors = []
200

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

    
208
            # validate connectors
209
            for connector in self.connectors:
210
                errors.extend(connector.validate())
211

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

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

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

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

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

    
265
                if disconnect:
266
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
267
                    error.parent = self
268
                    error.msg = 'Disconnection error'
269
                    error.setToolTip(error.msg)
270
                    error.area = 'Drawing'
271
                    error.name = 'Error'
272
                    errors.append(error)
273

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

    
287
            # check if association item's owner exists
288
            for assoc in self.associations():
289
                if issubclass(type(assoc), QEngineeringTextItem) and not assoc.owner:
290
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
291
                    error.parent = self
292
                    error.msg = self.tr('Association error')
293
                    error.setToolTip(error.msg)
294
                    error.area = self.area
295
                    error.name = 'Error'
296
                    error.items = [ assoc ]
297
                    errors.append(error)
298

    
299
            if type(self) is QEngineeringReducerItem:
300
                if not [size for size in self.associations() if type(size) is QEngineeringSizeTextItem]:
301
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
302
                    error.parent = self
303
                    error.msg = self.tr('No size label warning')
304
                    error.setToolTip(error.msg)
305
                    error.area = self.area
306
                    error.name = 'Warning'
307
                    errors.append(error)
308

    
309
            if self.iType == 34 or self.iType == 17 or self.iType == 22:
310
                if not self.EvaluatedLabel('OWNERSYMBOL'):
311
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
312
                    error.parent = self
313
                    error.msg = self.tr('No label warning')
314
                    error.setToolTip(error.msg)
315
                    error.area = self.area
316
                    error.name = 'Warning'
317
                    errors.append(error)
318

    
319
                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)]
320
                labels = [label for label in labels if label.EvaluatedAttribute('OWNERSYMBOL') == self or \
321
                          (type(label.EvaluatedAttribute('OWNERSYMBOL')) is str and label.EvaluatedAttribute('OWNERSYMBOL') == str(self.uid))]
322
                if len(labels) > 1:
323
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
324
                    error.parent = self
325
                    error.msg = self.tr('Multiple label error')
326
                    error.setToolTip(error.msg)
327
                    error.area = self.area
328
                    error.name = 'Error'
329
                    error.items = labels
330
                    errors.append(error)
331

    
332
            # check overlapping
333
            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]
334
            for symbol in symbols:
335
                symbolRect = symbol.sceneBoundingRect()
336
                rect1 = QRectF(symbolRect.left() - 3, symbolRect.top() - 3, symbolRect.width() + 6, symbolRect.height() + 6)
337
                rect2 = QRectF(symbolRect.left() + 3, symbolRect.top() + 3, symbolRect.width() - 6, symbolRect.height() - 6)
338
                if rect1.contains(self.sceneBoundingRect()) or rect2.contains(self.mapToScene(self.transformOriginPoint())):
339
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
340
                    error.parent = self
341
                    error.msg = self.tr('Symbol overlapping warning')
342
                    error.setToolTip(error.msg)
343
                    error.area = self.area
344
                    error.name = 'Warning'
345
                    error.items = [ symbol ]
346
                    errors.append(error)
347

    
348
            # set error position
349
            for error in errors:
350
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
351

    
352
            connectedUid = []
353
            for connector in self.connectors:
354
                # for duplicattion check
355
                if connector.connectedItem and issubclass(type(connector.connectedItem), QEngineeringAbstractItem):
356
                    connectedUid.append(str(connector.connectedItem.uid))
357

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

    
374
            # check duplicated connection
375
            if len(connectedUid) is not len(set(connectedUid)):
376
                error = SymbolSvgItem.createItem('Error', None, dataPath)
377
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
378
                error.parent = self
379
                error.msg = self.tr('Duplicated connection error')
380
                error.setToolTip(error.msg)
381
                error.area = self.area
382
                error.name = 'Error'
383
                errors.append(error)
384

    
385
            # check line type
386
            if self.conn_type:
387
                for index in range(len(self.conn_type)):
388
                    item = self.connectors[index].connectedItem
389
                    if item and type(item) is QEngineeringLineItem:
390
                        if (QEngineeringLineItem.check_piping(self.conn_type[index], True) and not item.is_piping()) or \
391
                                (not QEngineeringLineItem.check_piping(self.conn_type[index]) and item.is_piping(True)):
392
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
393
                            error.setPosition(list(self.connectors[index].center()))
394
                            error.parent = self
395
                            error.msg = self.tr('Line type error')
396
                            error.setToolTip(error.msg)
397
                            error.area = self.area
398
                            error.name = 'Error'
399
                            errors.append(error)
400
            
401
            configs = app_doc_data.getConfigs('Project', 'Operation Code')
402
            code = configs[0].value if 1 == len(configs) else ''
403
            if code == 'nK6uurpuiw==': # Samsung
404
                if self.angle != 0.0:
405
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
406
                    error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
407
                    error.parent = self
408
                    error.msg = self.tr('X Symbol angle is not zero')
409
                    error.setToolTip(error.msg)
410
                    error.area = self.area
411
                    error.name = 'Warning'
412
                    errors.append(error)
413

    
414
                allowed_error = 0.3
415
                for conn in self.connectors:
416
                    if not conn.connectedItem:
417
                        continue
418

    
419
                    for conn2 in conn.connectedItem.connectors:
420
                        if not conn2.connectedItem or conn2.connectedItem is not self:
421
                            continue
422

    
423
                        if abs(conn.center()[0] - conn2.center()[0]) > allowed_error or abs(conn.center()[1] - conn2.center()[1]) > allowed_error:
424
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
425
                            error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
426
                            error.parent = self
427
                            error.msg = self.tr('X Mismatched connection point')
428
                            error.setToolTip(error.msg)
429
                            error.area = self.area
430
                            error.name = 'Warning'
431
                            error.items = [ conn.connectedItem ]
432
                            errors.append(error)
433
                
434
                    if issubclass(type(conn.connectedItem), SymbolSvgItem):
435
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
436
                        error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
437
                        error.parent = self
438
                        error.msg = self.tr('Symbol to symbol connection warning')
439
                        error.setToolTip(error.msg)
440
                        error.area = self.area
441
                        error.name = 'Warning'
442
                        error.items = [ conn.connectedItem ]
443
                        errors.append(error)
444
                                
445
        except Exception as ex:
446
            from App import App
447
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
448
                                                          sys.exc_info()[-1].tb_lineno)
449
            App.mainWnd().addMessage.emit(MessageType.Error, message)
450

    
451
        return errors
452

    
453
    def includes(self, item, margin=0):
454
        """return True if symbol contains given point else return False"""
455
        if hasattr(item, 'sceneBoundingRect'):
456
            rect = item.sceneBoundingRect()
457
            topLeft = QPoint(rect.x(), rect.y())
458
            bottomRight = QPoint(rect.x() + rect.width(), rect.y() + rect.height())
459
            margin_rect = QRect(self.sceneBoundingRect().x() - margin, self.sceneBoundingRect().y() - margin, \
460
                            self.sceneBoundingRect().width() + 2 * margin, self.sceneBoundingRect().height() + 2 * margin)
461
            if margin_rect.contains(topLeft) and margin_rect.contains(bottomRight):
462
                return True
463
            else:
464
                return False
465

    
466
        else:
467
            pt = item
468
            rect = self.sceneBoundingRect()
469
            allowed_error = 0.1
470

    
471
            if abs(rect.x() - 0) <= allowed_error and abs(rect.y() - 0) <= allowed_error:
472
                # when first recognition step, symbols are not in scene(not yet added) therefore cannot use scenebounding rect
473
                minX = self.loc[0] - margin
474
                minY = self.loc[1] - margin
475
                maxX = minX + self.size[0] + margin
476
                maxY = minY + self.size[1] + margin
477
            else:
478
                minX = rect.x() - margin
479
                minY = rect.y() - margin
480
                maxX = minX + rect.width() + margin
481
                maxY = minY + rect.height() + margin
482

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

    
485
    '''
486
    def associations(self):
487
        """ return associated instance """
488
        # move to abstractitem
489
        import uuid
490

491
        res = []
492
        for key in self._associations.keys():
493
            index = 0
494
            for assoc in self._associations[key]:
495
                # find owner with uid
496
                if assoc and type(assoc) is uuid.UUID:
497
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(assoc)]
498
                    if matches:
499
                        res.append(matches[0]) # TODO: need to update association with instance
500
                        self._associations[key][index] = matches[0]
501
                    index += 1
502
                # up to here
503
                elif assoc:
504
                    res.append(assoc)
505

506
        for key in self.attrs.keys():
507
            if type(key.AssocItem) is uuid.UUID:
508
                for assoc in res:
509
                    if str(key.AssocItem) == str(assoc.uid):
510
                        key.AssocItem = assoc
511

512
        return res
513

514
    def texts(self):
515
        """ return text type of associations """
516
        from EngineeringTextItem import QEngineeringTextItem
517

518
        res = []
519
        for text in [x for x in self.associations() if issubclass(type(x), QEngineeringTextItem)]:
520
            consumed = False
521
            for key in list(self.attrs.keys()):
522
                if key.AssocItem and key.AssocItem is text:
523
                    consumed = True
524
            if not consumed:
525
                res.append(text)
526

527
        return res
528

529
    def symbols(self):
530
        """ return symbol type of associations """
531
        res = []
532
        for symbol in [x for x in self.associations() if issubclass(type(x), SymbolSvgItem)]:
533
            consumed = False
534
            for key in list(self.attrs.keys()):
535
                if key.AssocItem and key.AssocItem is symbol:
536
                    consumed = True
537
            if not consumed:
538
                res.append(symbol)
539

540
        return res
541
    '''
542

    
543
    def itemChange(self, change, value):
544
        """ call signals when item's position or rotation is changed """
545
        if not self.scene(): return super().itemChange(change, value)
546

    
547
        if change == QGraphicsItem.ItemPositionHasChanged or change == QGraphicsItem.ItemRotationHasChanged or \
548
                change == QGraphicsItem.ItemScaleHasChanged:
549
            from EngineeringLineItem import QEngineeringLineItem
550
            for connector in self.connectors:
551
                if connector.connectedItem is not None and type(connector.connectedItem) is QEngineeringLineItem:
552
                    line = connector.connectedItem
553
                    line.update_shape(self, connector.center())
554
                    #line.update_arrow()
555
                    for conn in line.connectors:
556
                        conn.transfer.onPosChanged.emit(conn)
557

    
558
            self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
559
            self.loc[0], self.loc[1] = round(self.sceneBoundingRect().top()), round(self.sceneBoundingRect().left())
560

    
561
            scene_origin = self.mapToScene(self.transformOriginPoint())
562

    
563
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
564
            grid = int(configs[0].value) if 1 == len(configs) else -1
565
            if grid == 1:
566
                #dx = round(scene_origin.x()) - scene_origin.x()
567
                #dy = round(scene_origin.y()) - scene_origin.y()
568
                #if dx or dy:
569
                #    self.moveBy(dx, dy)
570

    
571
                self.origin = [round(scene_origin.x()), round(scene_origin.y())]
572
            else:
573
                self.origin = [scene_origin.x(), scene_origin.y()]
574

    
575
            if hasattr(self.scene(), 'contents_changed'):
576
                self.scene().contents_changed.emit()
577

    
578
            return value
579

    
580
        return super().itemChange(change, value)
581

    
582
    def toSql_Components(self):
583
        """ convert Components data to sql query """
584
        from AppDocData import AppDocData
585
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
586

    
587
        scale = '1.0'
588
        if type(self) is QEngineeringEquipmentItem:
589
            scaleX = round(self.transform().m11() * 100)
590
            scaleY = round(self.transform().m22() * 100)
591
            scale = str(scaleX / 100.0) + ',' + str(scaleY / 100.0)
592
        else:
593
            scale = str(round(self.scale(), 2))
594

    
595
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner',
596
                'Connected', '[Supplied By]', \
597
                'SpecialItemTypes_UID', 'OriginIndex', '[From]', '[To]', '[Freeze]', '[Connected Item]', '[Flip]',
598
                'SceneOriginPoint', 'Value'] # value is scale in symbol
599
        values = ['?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?']
600
        origin = self.mapToScene(self.transformOriginPoint())
601
        param = [(str(self.uid), str(AppDocData.instance().activeDrawing.UID), self.dbUid, self.loc[0], self.loc[1],
602
                  self.size[0], self.size[1], math.radians(self.rotation()),
603
                  self.area, str(self.owner) if self.owner else None, \
604
                  str(self.conns[0]) if self.conns else None, \
605
                  self.prop('Supplied By'), \
606
                  str(self.special_item_type) if self.special_item_type else None, \
607
                  self.currentPointModeIndex, \
608
                  None, None, self.prop('Freeze') if self.prop('Freeze') else 0,
609
                  str(self.prop('Connected Item')) if self.prop('Connected Item') else None,
610
                  self.flip, '{},{}'.format(origin.x(), origin.y()), scale)]
611
        sql = 'insert into Components({}) values({})'.format(','.join(cols), ','.join(values))
612

    
613
        return (sql, tuple(param))
614

    
615
    def toSql_return_separately(self):
616
        """ convert valve data to sql query """
617
        import uuid
618

    
619
        res = []
620
        resLater = []
621

    
622
        res.append(self.toSql_Components())
623

    
624
        _attrs = self.getAttributes()
625
        if _attrs:
626
            cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value', 'Association_UID', 'Freeze']
627
            values = ['?', '?', '?', '?', '?', '?']
628
            params = []
629
            for key in _attrs.keys():
630
                if key.AttributeType != 'Spec':
631
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), str(_attrs[key]), str(key.AssocItem),
632
                                   str(key.Freeze)))
633
                elif key.AttributeType == 'Spec':
634
                    if type(_attrs[key]) is not list: continue
635
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), (str(_attrs[key][0]) + ',' + str(_attrs[key][1])), str(key.AssocItem),
636
                                   str(key.Freeze)))
637
            sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
638
            res.append((sql, tuple(params)))
639

    
640
        if self.associations():
641
            cols = ['UID', '[Type]', 'Components_UID', 'Association']
642
            values = ['?', '?', '?', '?']
643
            params = []
644
            for assoc in self.associations():
645
                params.append(
646
                    (str(uuid.uuid4()), QEngineeringAbstractItem.assoc_type(assoc), str(self.uid), str(assoc.uid)))
647
            sql = 'insert into Associations({}) values({})'.format(','.join(cols), ','.join(values))
648
            resLater.append((sql, tuple(params)))
649

    
650
        # save connectors to database
651
        if self.connectors:
652
            cols = ['Components_UID', '[Index]', 'X', 'Y', 'Connected', 'Connected_At']
653
            values = ['?', '?', '?', '?', '?', '?']
654
            params = []
655
            index = 1
656
            for connector in self.connectors:
657
                params.append( \
658
                    (  # str(connector.uid),
659
                        str(self.uid), index, connector.center()[0], connector.center()[1], \
660
                        str(connector.connectedItem.uid) if connector.connectedItem else None, \
661
                        str(connector._connected_at)) \
662
                    )
663
                index += 1
664
            sql = 'insert into Points({}) values({})'.format(','.join(cols), ','.join(values))
665
            resLater.append((sql, tuple(params)))
666
        # up to here
667

    
668
        return res, resLater
669

    
670
    @property
671
    def mainSubSize(self):
672
        """ return main sub size """
673
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
674
        from AppDocData import AppDocData
675

    
676
        matches = [assoc for assoc in self.associations() if type(assoc) is QEngineeringSizeTextItem]
677
        if matches:
678
            return matches[0].mainSubSize
679
        else:
680
            return [None, None]
681
        '''
682
        appDocData = AppDocData.instance()
683
        configs = appDocData.getConfigs('Size', 'Delimiter')
684
        delimiter = configs[0].value.upper() if 1 == len(configs) else None
685

686
        configs = appDocData.getConfigs('Project', 'Unit')
687
        sizeUnit = configs[0].value if 1 == len(configs) else None
688

689
        if matches:
690
            first, second = '', ''
691
            text = matches[0].text().replace("'", '"').upper()
692
            # for imperial
693
            if sizeUnit == 'Imperial' and text.find('"') is not -1:
694
                first = text[:text.find('"') + 1]
695

696
                text = text[text.find('"') + 1:].replace(delimiter.upper(), '',
697
                                                         1).strip() if delimiter is not None else text[text.find(
698
                    '"') + 1:].strip()
699

700
                if text and text.find('"') is len(text) - 1:
701
                    second = text
702

703
                    return [first, second]
704
            # for metric
705
            else:
706
                split_text = text.strip().split(delimiter.upper())
707
                if len(split_text) is not 2:
708
                    return [None, None]
709
                first = split_text[0]
710
                second = split_text[1]
711
                return [first, second]
712

713
        return [None, None]
714
        '''
715

    
716
    def buildItem(self, name, _type, angle: float, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel,
717
                  dbUid=None, scale=1.0):
718
        """
719
        build symbol item
720
        :param name:
721
        :param _type:
722
        :param angle:
723
        :param loc:
724
        :param size:
725
        :param origin:
726
        :param connPts:
727
        :param parentSymbol:
728
        :param childSymbol:
729
        :param hasInstrumentLabel:
730
        :param dbUid:
731
        :return:
732
        """
733
        from SpecialItemTypesDialog import SpecialItemTypes
734
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
735

    
736
        try:
737
            app_doc_data = AppDocData.instance()
738
            self.name = name
739
            self.type = _type
740
            self.angle = angle
741
            self.loc = loc
742
            self.size = size if size else [0, 0]
743
            self.origin = origin
744
            if dbUid is None:
745
                symbolInfo = app_doc_data.getSymbolByQuery('name', name)
746
            else:
747
                symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
748
            self.dbUid = symbolInfo.uid
749
            self.iType = symbolInfo.iType
750
            self.text_area = symbolInfo.text_area
751
            self._class = symbolInfo.baseSymbol
752
            originalPoint = symbolInfo.getOriginalPoint().split(',')
753
            self.symbolOrigin = [float(originalPoint[0]), float(originalPoint[1])]
754
            self.symbolConvertingOrigin = None
755
            convertOriginalPoint = symbolInfo.getConvertingPoint()
756
            if convertOriginalPoint:
757
                convertOriginalPoint = convertOriginalPoint.split(',')
758
                self.symbolConvertingOrigin = [float(convertOriginalPoint[0]), float(convertOriginalPoint[1])]
759
            if type(self) is QEngineeringEquipmentItem:
760
                transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
761
                transform.scale(scale[0], scale[1])
762
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
763
                self.setTransform(transform)
764
            else:
765
                self.setScale(scale)
766

    
767
            # setting connectors
768
            connectionPoints = symbolInfo.getConnectionPoint().split('/')
769
            for index in range(len(connectionPoints)):
770
                if connectionPoints[index] == '':
771
                    break
772
                tokens = connectionPoints[index].split(',')
773

    
774
                direction = 'AUTO'
775
                symbol_idx = '0'
776
                if len(tokens) == 2:
777
                    x = float(tokens[0])
778
                    y = float(tokens[1])
779
                elif len(tokens) >= 3:
780
                    direction = connPts[index][0] if index < len(connPts) else tokens[0]
781
                    x = float(tokens[1])
782
                    y = float(tokens[2])
783
                if len(tokens) >= 4:
784
                    symbol_idx = tokens[3]
785
                if len(tokens) >= 6:
786
                    if tokens[4] == 'In':
787
                        self.in_out_connector[0].append(index)
788
                    elif tokens[4] == 'Out':
789
                        self.in_out_connector[1].append(index)
790
                    if tokens[5] == 'O':
791
                        self.break_connector.append(index)
792
                if len(tokens) >= 7:
793
                    self.conn_type.append(tokens[6])
794

    
795
                self.setConnector(index + 1)
796
                self.connectors[index].direction = direction
797
                self.connectors[index].symbol_idx = symbol_idx
798
                self.connectors[index].setPos((x, y))
799
                self.connectors[index].connectPoint = (x, y)
800
                # recognized_pt is only valid right after symbol is recognized
801
                if index < len(connPts):
802
                    self.connectors[index].recognized_pt = (connPts[index][0], connPts[index][1]) if \
803
                        len(connPts[index]) == 2 else (connPts[index][1], connPts[index][2]) if \
804
                        len(connPts[index]) == 3 else (connPts[index][1], connPts[index][2]) if \
805
                        len(connPts[index]) == 4 else None
806
                # up to here
807
            self.parentSymbol = parentSymbol
808
            self.childSymbol = childSymbol
809
            self.hasInstrumentLabel = hasInstrumentLabel
810
            self.currentPointModeIndex = 0
811
            self.special_item_type = SpecialItemTypes.instance().find_match_exactly(self)
812

    
813
            tooltip = f"<b>{str(self.uid)}</b><br>{self.type}={self.name}"
814
            if self.hit_ratio:
815
                tooltip += f"<br><li>recognition ratio={self.hit_ratio}"
816
            self.setToolTip(tooltip)
817

    
818
            return True
819
        except Exception as ex:
820
            from App import App
821

    
822
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
823
                                                          sys.exc_info()[-1].tb_lineno)
824
            App.mainWnd().addMessage.emit(MessageType.Error, message)
825

    
826
            return False
827

    
828
    '''
829
        @brief  return bounding box of symbol
830
        @author humkyung
831
        @date   2018.04.08
832
    '''
833
    def rect(self):
834
        return self.sceneBoundingRect()
835

    
836
    def is_connectable(self, item, toler=10):
837
        """return true if line is able to connect symbol"""
838

    
839
        for connector in self.connectors:
840
            for iConnector in item.connectors:
841
                dx = connector.center()[0] - iConnector.center()[0]
842
                dy = connector.center()[1] - iConnector.center()[1]
843
                if math.sqrt(dx * dx + dy * dy) < toler:
844
                    return True
845

    
846
        return False
847

    
848
    '''
849
        @author     humkyung
850
        @date       2018.07.03
851
    '''
852
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
853
        """ check if given item is connected to self """
854

    
855
        _connectors = [connector for connector in self.connectors if
856
                       (connector.connectedItem == item and (connector._connected_at == at if at else True))]
857
        return len(_connectors) > 0
858

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

    
862
        lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
863
        rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
864
        if lhs_matches and rhs_matches and lhs is not rhs:
865
            return (lhs_matches[0] in [0, 1] and rhs_matches[0] in [0, 1]) or (
866
                    lhs_matches[0] in [2, 3] and rhs_matches[0] in [2, 3])
867

    
868
        return False
869

    
870
    def canBeSecondary(self, line):
871
        """ check given line is not connected(ex: 0-1, 2-3) """
872
        preItem = None
873

    
874
        item = [item.connectedItem for item in self.connectors if
875
                item.connectedItem is not None and item.connectedItem.owner is not None]
876
        if item:
877
            preItem = item[0]
878

    
879
        if preItem is not None and not self.next_connected(line, preItem):
880
            return True
881
        else:
882
            return False
883

    
884
    '''
885
        @brief      connect line and symbol is able to be connected and return line
886
        @author     humkyung
887
        @date       2018.04.16
888
        @history    humkyung 2018.05.08 check if symbol is possible to be connected
889
                    Jeongwoo 2018.05.15 Connect each symbol and line
890
    '''
891
    def connect_if_possible(self, obj, toler=10):
892
        """ this method not update item's position """
893

    
894
        from shapely.geometry import Point
895
        from EngineeringLineItem import QEngineeringLineItem
896

    
897
        res = []
898
        try:
899
            if type(obj) is QEngineeringLineItem:
900
                configs = AppDocData.instance().getConfigs('Data', 'Grid')
901
                grid = int(configs[0].value) if 1 == len(configs) else -1
902

    
903
                startPt = obj.start_point()
904
                endPt = obj.end_point()
905
                conns = []
906
                for i in range(len(self.connectors)):
907
                    dist = Point(startPt[0], startPt[1]).distance(Point(self.connectors[i].center()[0], self.connectors[i].center()[1]))
908
                    if (dist < toler):
909
                        if self.connectors[i].connectedItem is None and obj.connectors[0].connectedItem is None:
910
                            conns.append([dist, self.connectors[i], obj.connectors[0], self.connectors[i].center(), endPt])
911
                            #self.connectors[i].connect(obj)
912
                            #obj.connectors[0].connect(self)
913
                            ## line, start, end
914
                            #res.append(obj)
915
                            #res.append(self.connectors[i].center())
916
                            #res.append(endPt)
917
                    dist = Point(endPt[0], endPt[1]).distance(Point(self.connectors[i].center()[0], self.connectors[i].center()[1]))
918
                    if (dist < toler):
919
                        if self.connectors[i].connectedItem is None and obj.connectors[1].connectedItem is None:
920
                            conns.append([dist, self.connectors[i], obj.connectors[1], startPt, self.connectors[i].center()])
921
                            #self.connectors[i].connect(obj)
922
                            #obj.connectors[1].connect(self)
923
                            ## line, start, end
924
                            #res.append(obj)
925
                            #res.append(startPt)
926
                            #res.append(self.connectors[i].center())
927

    
928
                if conns:
929
                    conns.sort(key=lambda x: x[0])
930
                    conns[0][1].connect(obj)
931
                    conns[0][2].connect(self)
932
                    # line, start, end
933
                    res.append(obj)
934
                    if grid == 1:
935
                        res.append((round(conns[0][3][0]), round(conns[0][3][1])))
936
                        res.append((round(conns[0][4][0]), round(conns[0][4][1])))
937
                    else:
938
                        res.append(conns[0][3])
939
                        res.append(conns[0][4])
940

    
941
            elif issubclass(type(obj), SymbolSvgItem):
942
                selected = None
943
                for i in range(len(self.connectors)):
944
                    if i > 3: break
945
                    _pt1 = Point(self.connectors[i].center()[0], self.connectors[i].center()[1])
946
                    for j in range(len(obj.connectors)):
947
                        if j > 3: break
948
                        _pt2 = Point(obj.connectors[j].center()[0], obj.connectors[j].center()[1])
949
                        length = _pt1.distance(_pt2)
950
                        if (length < toler) and (selected is None or length < selected[0]):
951
                            selected = [length, self.connectors[i], obj.connectors[j]]
952

    
953
                if selected and selected[1].connectedItem is None and selected[2].connectedItem is None:
954
                    selected[1].connect(selected[2].parent)
955
                    selected[2].connect(selected[1].parent)
956
                    res.append(obj)
957
        except Exception as ex:
958
            from App import App
959
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
960
                                                          sys.exc_info()[-1].tb_lineno)
961
            App.mainWnd().addMessage.emit(MessageType.Error, message)
962

    
963
        return res
964

    
965
    '''
966
        @brief      disconnect connector item
967
        @author     kyouho
968
        @date       2018.08.30
969
    '''
970
    def disconnectedItemAtConnector(self, connector):
971
        for conn in self.connectors:
972
            if conn.isOverlapConnector(connector):
973
                conn.connectedItem = None
974

    
975
    '''
976
        @brief  get connection point close to given point in tolerance
977
        @author humkyung
978
        @dat
979
    '''
980
    def getConnectionPointCloseTo(self, pt, toler=10):
981
        import math
982

    
983
        for connector in self.connectors:
984
            dx = connector.center()[0] - pt[0]
985
            dy = connector.center()[1] - pt[1]
986

    
987
            if math.sqrt(dx * dx + dy * dy) < toler:
988
                return connPt
989

    
990
        return None
991

    
992
    '''
993
        @brief  return center of symbol
994
        @author humkyung
995
        @date   2018.04.08
996
    '''
997
    def center(self):
998
        return self.sceneBoundingRect().center()
999

    
1000
    '''
1001
        @brief      highlight connector and attribute
1002
        @authro     humkyung
1003
        @date       2018.05.02
1004
    '''
1005
    def hoverEnterEvent(self, event, minimum=False):
1006
        self.highlight(True, minimum)
1007

    
1008
    '''
1009
        @brief      unhighlight connector and attribute
1010
        @author     humkyung
1011
        @date       2018.05.02
1012
        @history    kyouho 2018.07.18 edit ArrowCursor
1013
    '''
1014
    def hoverLeaveEvent(self, event, minimum=False):
1015
        self.highlight(False, minimum)
1016

    
1017
    def highlight(self, flag, minimum=False):
1018
        """ highlight/unhighlight the symbol """
1019
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1020

    
1021
        try:
1022
            self.hover = flag
1023
            if type(self) is not QEngineeringEquipmentItem:
1024
                self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
1025
            self.update()
1026

    
1027
            if not minimum:
1028
                for assoc in self.associations():
1029
                    assoc.highlight(flag)
1030

    
1031
                for connector in self.connectors:
1032
                    connector.highlight(flag)
1033
        except Exception as ex:
1034
            from App import App
1035
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1036
                                                          sys.exc_info()[-1].tb_lineno)
1037
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1038

    
1039
    '''
1040
        @brief      set highlight
1041
        @author     kyouho
1042
        @date       2018.08.27
1043
    '''
1044
    def setHightlight(self):
1045
        self.setColor('url(#hover)')
1046
        self.update()
1047

    
1048
    '''
1049
        @brief      unset highlight
1050
        @author     kyouho
1051
        @date       2018.08.27
1052
    '''
1053
    def unsetHightlight(self):
1054
        self.setColor('url(#normal)')
1055
        self.update()
1056

    
1057
    '''
1058
        @brief  change cursor to CrossCursor if mouse point is close to connection point
1059
        @author humkyung
1060
        @date   2018.04.28
1061
    '''
1062
    def hoverMoveEvent(self, event):
1063
        pass
1064

    
1065
    '''
1066
        @brief      Mouse Press Event
1067
        @author     Jeongwoo
1068
        @date       18.04.11
1069
        @history    kyouho 2018.07.18 add isClick logic
1070
    '''
1071
    def mousePressEvent(self, event):
1072
        if event.buttons() == Qt.LeftButton:
1073
            """마우스 클릭한 위치를 저장한다."""
1074
            self._mouse_pos = [event.scenePos().x(), event.scenePos().y()]
1075

    
1076
            self.clicked.emit(self)
1077
        
1078
        super().mousePressEvent(event)
1079

    
1080
    def mouseReleaseEvent(self, event):
1081
        """Mouse Release Event"""
1082
        if hasattr(self, '_rotating') and event.button() == Qt.RightButton:
1083
            from RotateCommand import RotateCommand
1084

    
1085
            self.angle = -self._angle if -math.pi < self._angle < 0 else 2 * math.pi - self._angle
1086

    
1087
            self.scene().undo_stack.push(RotateCommand(self.scene(), [self,], self._rotating))
1088

    
1089
            self.ungrabMouse()
1090
            del self._rotating
1091

    
1092
        if hasattr(self, '_scale') and event.button() == Qt.RightButton:
1093
            self.ungrabMouse()
1094
            del self._scale
1095
            del self._scale_x
1096

    
1097
        super().mouseReleaseEvent(event)
1098

    
1099
    def mouseMoveEvent(self, event):
1100
        """ rotate symbol around current mouse point """
1101

    
1102
        if hasattr(self, '_rotating'):
1103
            # get origin point of symbol
1104
            origin = self.mapToScene(self.transformOriginPoint())
1105
            # up to here
1106

    
1107
            dx, dy = event.scenePos().x() - origin.x(), event.scenePos().y() - origin.y()
1108
            length = math.sqrt(dx * dx + dy * dy)
1109

    
1110
            self._angle = 0
1111
            if length > 0:
1112
                self._angle = math.acos(dx / length)
1113
                cross = int(np.cross([1, 0], [dx, dy]))
1114
                self._angle = -self._angle if cross < 0 else self._angle
1115
                self._angle = math.pi*2 + self._angle if self._angle < 0 else self._angle
1116

    
1117
                modifiers = QApplication.keyboardModifiers()
1118
                if modifiers == Qt.ShiftModifier:
1119
                    self.rotate(self._angle)
1120
                else:
1121
                    step = math.radians(15)
1122
                    quotient = int(self._angle / step)
1123
                    angle = quotient * step
1124
                    self._angle = angle if (self._angle - angle) < step * 0.5 else (quotient + 1) * step
1125

    
1126
                    self.rotate(self._angle)
1127

    
1128
        if hasattr(self, '_scale'):
1129
            pass
1130
            '''
1131
            dx = event.scenePos().x() - self._scale_x
1132
            step = 8
1133
            quotient = int(dx / step)
1134
            scale = 0.1 * quotient
1135

1136
            self.setScale(self._scale + scale if self._scale + scale > 0.3 else 0.3)
1137
            '''
1138

    
1139
        super().mouseMoveEvent(event)
1140

    
1141
    def removeSelfAttr(self, attributeName):
1142
        target = None
1143
        for attr in self.attrs:
1144
            if attr.Attribute == attributeName:
1145
                target = attr
1146
                break
1147

    
1148
        if target:
1149
            del self.attrs[attr]
1150

    
1151
    '''
1152
        @brief      Find TextItem contain Point
1153
        @author     kyouho
1154
        @date       18.07.17
1155
        @ no more used
1156
    '''
1157
    def findTextItemInPoint(self, point):
1158
        from EngineeringTextItem import QEngineeringTextItem
1159

    
1160
        scene = self.scene()
1161

    
1162
        for item in scene.items():
1163
            if type(item) is QEngineeringTextItem:
1164
                if self.isOverlapItemAndPoint(item, point):
1165
                    return (True, item)
1166

    
1167
        return (False,)
1168

    
1169
    '''
1170
        @brief      Check Overlap
1171
        @author     kyouho
1172
        @date       18.07.17
1173
        @ no more used
1174
    '''
1175
    def isOverlapItemAndPoint(self, item, point):
1176
        x = point.x()
1177
        y = point.y()
1178
        loc = item.loc
1179
        size = item.size
1180

    
1181
        if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
1182
            return True
1183
        else:
1184
            return False
1185
        
1186
    def bind_end_break(self):
1187
        from shapely.geometry import Point
1188
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1189
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1190
        from EngineeringLineItem import QEngineeringLineItem
1191
        from AppDocData import AppDocData
1192
        from App import App
1193

    
1194
        if self.owner or self.prop('Connected Item'):
1195
            return
1196
        
1197
        app_doc_data = AppDocData.instance()
1198
        configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1199
        toler = int(configs[0].value) if configs else 20
1200

    
1201
        lines = sorted([item for item in self.scene().items() if type(item) is QEngineeringLineItem], key=lambda param: param.length(), reverse=True)
1202
        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]
1203
        end_breaks = [item for item in self.scene().items() if type(item) is QEngineeringEndBreakItem]
1204

    
1205
        usedItemPairs = []
1206
        for end_break in end_breaks:
1207
            if end_break.prop('Freeze') or end_break.owner or end_break.prop('Connected Item'):
1208
                usedItemPairs.append([end_break.owner, end_break.prop('Connected Item')])
1209
        
1210
        originPoint = Point(self.origin[0], self.origin[1])
1211
        minD = sys.maxsize
1212
        ownerItem = None
1213
        connectedItem = None
1214

    
1215
        for symbol in symbols:
1216
            for conn in symbol.connectors:
1217
                dist = originPoint.distance(Point(conn.sceneConnectPoint[0], conn.sceneConnectPoint[1]))
1218
                if not conn.connectedItem or not issubclass(type(conn.connectedItem), QEngineeringAbstractItem) or \
1219
                    [pair for pair in usedItemPairs if symbol in pair and conn.connectedItem in pair] or dist > 3 * toler or dist > minD:
1220
                    continue
1221

    
1222
                minD = dist
1223
                ownerItem = symbol
1224
                connectedItem = conn.connectedItem
1225

    
1226
        for line in lines:
1227
            for conn in line.connectors:
1228
                dist = originPoint.distance(Point(conn.sceneConnectPoint[0], conn.sceneConnectPoint[1]))
1229
                if not conn.connectedItem or not issubclass(type(conn.connectedItem), QEngineeringAbstractItem) or \
1230
                    conn._connected_at != QEngineeringAbstractItem.CONNECTED_AT_BODY  or \
1231
                    [pair for pair in usedItemPairs if line in pair and conn.connectedItem in pair] or dist > 3 * toler or dist > minD:
1232
                    continue
1233

    
1234
                minD = dist
1235
                ownerItem = line
1236
                connectedItem = conn.connectedItem
1237

    
1238
        if ownerItem and connectedItem:
1239
            self.set_property('Connected Item', connectedItem)
1240
            self.setToolTip('owner : ' + str(ownerItem))
1241
            self.owner = ownerItem
1242
            self.set_property('Freeze', True)
1243

    
1244
            App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute(self)
1245

    
1246
    '''
1247
        @brief  remove item when user press delete key
1248
        @author humkyung
1249
        @date   2018.04.23
1250
        @history    2018.05.17  Jeongwoo    Add if-statement and move 'break'
1251
                    2018.05.25  Jeongwoo    Seperate delete item method
1252
    '''
1253
    def keyPressEvent(self, event):
1254
        from EngineeringErrorItem import QEngineeringErrorItem
1255
        from RotateSymbolDialog import QRotateSymbolDialog
1256
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1257
        from RotateCommand import RotateCommand
1258

    
1259
        modifiers = QApplication.keyboardModifiers()
1260

    
1261
        if not self.isSelected():
1262
            return
1263
        elif event.key() == Qt.Key_A:
1264
            self.contextSelectAll()
1265
        elif event.key() == Qt.Key_N:
1266
            delta = 40 if modifiers == Qt.ControlModifier else 10
1267
            self.move_near_main_line(delta)
1268
        elif event.key() == Qt.Key_B:
1269
            if type(self) is not QEngineeringEndBreakItem:
1270
                self.bind_close_items()
1271
            else:
1272
                self.bind_end_break()
1273
        elif event.key() == Qt.Key_S:
1274
            self.contextUp()
1275
        elif event.key() == Qt.Key_D:
1276
            self.contextDown()
1277
        #elif event.key() == Qt.Key_O and type(self) is not QEngineeringErrorItem:
1278
        #    pass
1279
        #elif event.key() == Qt.Key_C and type(self) is not QEngineeringErrorItem:
1280
        #    self.changeConnPoint()
1281
        elif False:#event.key() == Qt.Key_Return:
1282
            dlg = QRotateSymbolDialog(None, self.rotation(), self.origin, self.zValue())
1283
            if QDialog.Accepted == dlg.showDialog():
1284
                _angle = self.rotation()
1285
                self.rotate(math.radians(dlg.angle))
1286
                self.scene().undo_stack.push(RotateCommand(self.scene(), [self, ], _angle))
1287
                self.angle = dlg.angle
1288
        elif event.key() == Qt.Key_Escape:
1289
            if hasattr(self, '_rotating'):
1290
                self.ungrabMouse()
1291

    
1292
                self.rotate(math.radians(self._rotating))
1293
                del self._rotating
1294
            if hasattr(self, '_scale'):
1295
                pass
1296
                '''
1297
                self.ungrabMouse()
1298

1299
                self.setScale(self._scale)
1300
                del self._scale
1301
                del self._scale_x
1302
                '''
1303
        elif event.key() == Qt.Key_Up:  # translate up/down/left/right symbol
1304
            delta = 10 if modifiers == Qt.ControlModifier else 1
1305

    
1306
            #self.loc[1] = self.loc[1] - delta
1307
            #self.origin[1] = self.origin[1] - delta
1308
            self.moveBy(0, -delta)
1309
        elif event.key() == Qt.Key_Down:
1310
            delta = 10 if modifiers == Qt.ControlModifier else 1
1311

    
1312
            #self.loc[1] = self.loc[1] + delta
1313
            #self.origin[1] = self.origin[1] + delta
1314
            self.moveBy(0, delta)
1315
        elif event.key() == Qt.Key_Left:
1316
            delta = 10 if modifiers == Qt.ControlModifier else 1
1317

    
1318
            #self.loc[0] = self.loc[0] - delta
1319
            #self.origin[0] = self.origin[0] - delta
1320
            self.moveBy(-delta, 0)
1321
        elif event.key() == Qt.Key_Right:
1322
            delta = 10 if modifiers == Qt.ControlModifier else 1
1323

    
1324
            #self.loc[0] = self.loc[0] + delta
1325
            #self.origin[0] = self.origin[0] + delta
1326
            self.moveBy(delta, 0)
1327
        elif event.key() == Qt.Key_Plus or event.key() == 61:
1328
            #self.setScale(self.scale() + 0.1)
1329
            #'''
1330
            transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1331
            if modifiers == Qt.ControlModifier:
1332
                transform.scale(self.transform().m11() + 0.1, self.transform().m22())
1333
            elif modifiers == Qt.AltModifier:
1334
                transform.scale(self.transform().m11(), self.transform().m22() + 0.1)
1335
            else:
1336
                transform.scale(self.transform().m11() + 0.1, self.transform().m22() + 0.1)
1337
            transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1338
            self.setTransform(transform)
1339
            #'''
1340
        elif event.key() == Qt.Key_Minus:
1341
            #self.setScale(self.scale() - 0.1 if self.scale() - 0.1 > 0.3 else 0.3)
1342
            #'''
1343
            transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
1344
            if modifiers == Qt.ControlModifier:
1345
                transform.scale(self.transform().m11() - 0.1 if self.transform().m11() - 0.1 > 0.3 else 0.3, self.transform().m22())
1346
            elif modifiers == Qt.AltModifier:
1347
                transform.scale(self.transform().m11(), self.transform().m22() - 0.1 if self.transform().m22() - 0.1 > 0.3 else 0.3)
1348
            else:
1349
                transform.scale(self.transform().m11() - 0.1 if self.transform().m11() - 0.1 > 0.3 else 0.3, self.transform().m22() - 0.1 if self.transform().m22() - 0.1 > 0.3 else 0.3)
1350
            transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1351
            self.setTransform(transform)
1352
            #'''
1353
        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 
1354
            from App import App 
1355

    
1356
            App.mainWnd().keyPressEvent(event)
1357

    
1358
    def contextMenuEvent(self, event):
1359
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1360

    
1361
        items = self.scene().selectedItems()
1362
        if len(items) == 1 and self in items:
1363
            menu = QMenu()
1364

    
1365
            if type(self) == QEngineeringSpecBreakItem:
1366
                upAction = QAction('Set UpStream(S)', None)
1367
                upAction.triggered.connect(self.contextUp)
1368
                menu.addAction(upAction)
1369

    
1370
                downAction = QAction('Set DownStream(D)', None)
1371
                downAction.triggered.connect(self.contextDown)
1372
                menu.addAction(downAction)
1373

    
1374
            showAction = QAction('Show Onwer', None)
1375
            showAction.triggered.connect(self.contextShow)
1376
            menu.addAction(showAction)
1377

    
1378
            allAction = QAction('Select All in View(A)', None)
1379
            allAction.triggered.connect(self.contextSelectAll)
1380
            menu.addAction(allAction)
1381

    
1382
            moveAction = QAction('Move to Nearest(N)', None)
1383
            moveAction.triggered.connect(self.contextMove)
1384
            menu.addAction(moveAction)
1385

    
1386
            replaceAction = QAction('Replace This to Selected Symbol(I, Q)', None)
1387
            replaceAction.triggered.connect(self.contextReplace)
1388
            menu.addAction(replaceAction)
1389

    
1390
            insertAction = QAction('Insert Selected Symbol to This(J)', None)
1391
            insertAction.triggered.connect(self.contextInsert)
1392
            menu.addAction(insertAction)
1393

    
1394
            if type(self) != QEngineeringSpecBreakItem:
1395
                bindAction = QAction('Bind(B)', None)
1396
                bindAction.triggered.connect(self.contextBind)
1397
                menu.addAction(bindAction)
1398
            else:
1399
                bindAction = QAction('Set Batch(Bind)', None)
1400
                bindAction.triggered.connect(self.contextBindSpec)
1401
                menu.addAction(bindAction)
1402

    
1403
            rotateAction = QAction('Rotate(R)', None)
1404
            rotateAction.triggered.connect(self.contextRotate)
1405
            menu.addAction(rotateAction)
1406

    
1407
            flipAction = QAction('Flip(F)', None)
1408
            flipAction.triggered.connect(self.contextFlip)
1409
            menu.addAction(flipAction)
1410

    
1411
            deleteAction = QAction('Delete(E)', None)
1412
            deleteAction.triggered.connect(self.contextDelete)
1413
            menu.addAction(deleteAction)
1414

    
1415
            arsAction = QAction('Show on ARS', None)
1416
            arsAction.triggered.connect(self.contextShowOnARS)
1417
            menu.addAction(arsAction)
1418

    
1419
            menu.exec_(event.screenPos())
1420
        elif len(items) >= 2:
1421
            menu = QMenu()
1422

    
1423
            allAction = QAction('Select All in View(A)', None)
1424
            allAction.triggered.connect(self.contextSelectAll)
1425
            menu.addAction(allAction)
1426

    
1427
            batchAction = QAction('Set Batch(Insert)', None)
1428
            batchAction.triggered.connect(self.contextBatch)
1429
            menu.addAction(batchAction)
1430

    
1431
            alignAction = QAction('Align Selected Symbols', None)
1432
            alignAction.triggered.connect(self.contextAlignSymbol)
1433
            menu.addAction(alignAction)
1434

    
1435
            moveAction = QAction('Move to Nearest(N)', None)
1436
            moveAction.triggered.connect(self.contextMove)
1437
            menu.addAction(moveAction)
1438

    
1439
            mergeAction = QAction('Make Line(M)', None)
1440
            mergeAction.triggered.connect(self.contextMerge)
1441
            menu.addAction(mergeAction)
1442

    
1443
            replaceAction = QAction('Replace This to Selected Symbol(I, Q)', None)
1444
            replaceAction.triggered.connect(self.contextReplace)
1445
            menu.addAction(replaceAction)
1446

    
1447
            insertAction = QAction('Insert Selected Symbol to This(J)', None)
1448
            insertAction.triggered.connect(self.contextInsert)
1449
            menu.addAction(insertAction)
1450

    
1451
            deleteAction = QAction('Delete(E)', None)
1452
            deleteAction.triggered.connect(self.contextDelete)
1453
            menu.addAction(deleteAction)
1454

    
1455
            menu.exec_(event.screenPos())
1456

    
1457

    
1458
    def contextShow(self):
1459
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
1460

    
1461
        if type(self.owner) is QEngineeringLineNoTextItem:
1462
            self.owner.contextHighlight(self.owner)
1463

    
1464
    def contextSelectAll(self):
1465
        from App import App
1466
        
1467
        selectedSymbols = list(set([item.dbUid for item in self.scene().selectedItems() if issubclass(type(item), SymbolSvgItem)]))
1468
        rect = App.mainWnd().graphicsView.viewport().rect()
1469
        view_rect = App.mainWnd().graphicsView.mapToScene(rect).boundingRect()
1470
        #rect = App.mainWnd().graphicsView.mapToScene(App.mainWnd().graphicsView.rect()).boundingRect()
1471
        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())]
1472
        for symbol in symbols:
1473
            symbol.setSelected(True)
1474

    
1475
    def contextBatch(self):
1476
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_Insert, Qt.NoModifier)
1477
        self.keyPressEvent(event)
1478

    
1479
    def contextMerge(self):
1480
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_M, Qt.NoModifier)
1481
        self.keyPressEvent(event)
1482

    
1483
    def contextUp(self):
1484
        from App import App
1485
        import SelectAttributeCommand
1486
        _up = None
1487
        for prop, value in self.getAttributes().items():
1488
            if prop.Attribute == 'UpStream':
1489
                _up = prop
1490
                break
1491
        cmd = SelectAttributeCommand.SelectAttributeCommand(self, _up, App.mainWnd().graphicsView)
1492
        cmd.onSuccess.connect(App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute)
1493
        App.mainWnd().graphicsView.command = cmd
1494

    
1495
    def contextDown(self):
1496
        from App import App
1497
        import SelectAttributeCommand
1498
        _down = None
1499
        for prop, value in self.getAttributes().items():
1500
            if prop.Attribute == 'DownStream':
1501
                _down = prop
1502
                break
1503
        cmd = SelectAttributeCommand.SelectAttributeCommand(self, _down, App.mainWnd().graphicsView)
1504
        cmd.onSuccess.connect(App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute)
1505
        App.mainWnd().graphicsView.command = cmd
1506

    
1507
    def contextAlignSymbol(self):
1508
        scene = self.scene()
1509
        if scene:
1510
            symbolItems = [symbol for symbol in scene.selectedItems() if symbol is not self and issubclass(type(symbol), SymbolSvgItem)]
1511

    
1512
            for symbol in symbolItems:
1513
                dx = self.origin[0] - symbol.origin[0]
1514
                dy = self.origin[1] - symbol.origin[1]
1515
                if abs(dx) > abs(dy):
1516
                    if dx > 0:
1517
                        symbol.moveBy(0, dy)
1518
                    else:
1519
                        symbol.moveBy(0, dy)
1520
                else:
1521
                    if dy > 0:
1522
                        symbol.moveBy(dx, 0)
1523
                    else:
1524
                        symbol.moveBy(dx, 0)
1525

    
1526
    def contextMove(self):
1527
        self.move_near_main_line(10)
1528

    
1529
    def contextDelete(self):
1530
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_Delete, Qt.NoModifier)
1531
        self.scene().keyPressEvent(event)
1532

    
1533
    def contextReplace(self):
1534
        from App import App
1535

    
1536
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_I, Qt.NoModifier)
1537
        App.mainWnd().keyPressEvent(event)
1538

    
1539
    def contextInsert(self):
1540
        from App import App
1541

    
1542
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_J, Qt.NoModifier)
1543
        App.mainWnd().keyPressEvent(event)
1544

    
1545
    def contextShowOnARS(self):
1546
        from TcpServer import TcpSocket
1547

    
1548
        app_doc_data = AppDocData.instance()
1549
        configs = app_doc_data.getConfigs('app', 'conn port')
1550
        #configs = app_doc_data.getAppConfigs('app', 'conn port')
1551
        port = 3030
1552
        if configs and 1 == len(configs):
1553
            port = int(configs[0].value)
1554
        tcpserver = TcpSocket(port)
1555
        tcpserver.sendMessage(str(self.uid))
1556
        
1557
    def contextBindSpec(self):
1558
        from App import App
1559
        if App.mainWnd().on_connect_spec_breaks([self]):
1560
            App.mainWnd().resultPropertyTableWidget.onSuccessSelectAttribute(self)
1561

    
1562
    def contextBind(self):
1563
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_B, Qt.NoModifier)
1564
        self.keyPressEvent(event)
1565

    
1566
    def contextRotate(self):
1567
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_R, Qt.NoModifier)
1568
        self.scene().keyPressEvent(event)
1569

    
1570
    def contextFlip(self):
1571
        event = QKeyEvent(QEvent.KeyPress, Qt.Key_F, Qt.NoModifier)
1572
        self.scene().keyPressEvent(event)
1573

    
1574
    def move_near_main_line(self, length):
1575
        from EngineeringLineItem import QEngineeringLineItem
1576
        from EngineeringTextItem import QEngineeringTextItem
1577

    
1578
        scene = self.scene()
1579
        if scene:
1580
            symbolItems = [symbol for symbol in scene.selectedItems() if issubclass(type(symbol), SymbolSvgItem)]
1581
            textItems = [text for text in scene.selectedItems() if issubclass(type(text), QEngineeringTextItem)]
1582

    
1583
            symbolTextMatches = {}
1584

    
1585
            for text in textItems:
1586
                minDist = None
1587
                selected = None
1588
                center = text.sceneBoundingRect().center()
1589

    
1590
                for symbol in symbolItems:
1591
                    dx = symbol.origin[0] - center.x()
1592
                    dy = symbol.origin[1] - center.y()
1593
                    _length = math.sqrt(dx * dx + dy * dy)# - offset
1594

    
1595
                    if minDist is None or _length < minDist:
1596
                        minDist = _length
1597
                        selected = symbol
1598
                
1599
                if selected not in symbolTextMatches:
1600
                    symbolTextMatches[selected] = [text]
1601
                else:
1602
                    symbolTextMatches[selected].append(text)
1603
                    
1604
            for symbol in symbolItems:
1605
                found = False
1606

    
1607
                connectedSymbols = [_conn.connectedItem for _conn in symbol.connectors if _conn.connectedItem and issubclass(type(_conn.connectedItem), SymbolSvgItem)]
1608
                for connS in symbol.connectors:
1609
                    if found: break
1610

    
1611
                    connItemS = connS.connectedItem
1612
                    if connItemS and type(connItemS) is QEngineeringLineItem:
1613
                        for connL in connItemS.connectors:
1614
                            if found: break
1615

    
1616
                            connItemL = connL.connectedItem
1617
                            if connItemL and connItemL is not symbol and type(connItemL) is QEngineeringLineItem and connL._connected_at == QEngineeringAbstractItem.CONNECTED_AT_BODY:
1618
                                dx = connS.center()[0] - connL.center()[0]
1619
                                dy = connS.center()[1] - connL.center()[1]
1620
                                connL2 = [_conn for _conn in connItemS.connectors if _conn != connL][0]
1621
                                if connItemS.isVertical() and abs(dy) != length:
1622
                                    if dy < 0:
1623
                                        delta = - dy - length
1624
                                    else:
1625
                                        delta = - dy + length
1626
                                    symbol.moveBy(0, delta)
1627
                                    connItemS.set_line([[connL2.center()[0], connItemS.start_point()[1]], [connL2.center()[0], connItemS.end_point()[1]]])
1628
                                    for _symbol in connectedSymbols:
1629
                                        _symbol.moveBy(0, delta)
1630
                                    if symbol in symbolTextMatches:
1631
                                        for text in symbolTextMatches[symbol]:
1632
                                            text.moveText(0, 1 if delta > 0 else -1, abs(delta))
1633
                                    found = True
1634
                                    break
1635
                                elif connItemS.isHorizontal() and abs(dx) != length:
1636
                                    if dx < 0:
1637
                                        delta = - dx - length
1638
                                    else:
1639
                                        delta = - dx + length
1640
                                    symbol.moveBy(delta, 0)
1641
                                    connItemS.set_line([[connItemS.start_point()[0], connL2.center()[1]], [connItemS.end_point()[0], connL2.center()[1]]])
1642
                                    for _symbol in connectedSymbols:
1643
                                        _symbol.moveBy(delta, 0)
1644
                                    if symbol in symbolTextMatches:
1645
                                        for text in symbolTextMatches[symbol]:
1646
                                            text.moveText(1 if delta > 0 else -1, 0, abs(delta))
1647
                                    found = True
1648
                                    break
1649

    
1650
                if not found:
1651
                    matches = [conn for conn in symbol.connectors if conn.connectedItem]
1652
                    
1653
                    if len(matches) == 1:
1654
                        connS = matches[0]
1655
                        connItemS = connS.connectedItem
1656

    
1657
                        found = False
1658
                        if type(connItemS) is QEngineeringLineItem:
1659
                            for connL in connItemS.connectors:
1660
                                if found: break
1661

    
1662
                                connItemL = connL.connectedItem
1663

    
1664
                                if connItemL and connItemL is not symbol:
1665
                                    dx = symbol.center().x() - connItemL.center().x()
1666
                                    dy = symbol.center().y() - connItemL.center().y()
1667
                                    lx = connS.center()[0] - connL.center()[0]
1668
                                    ly = connS.center()[1] - connL.center()[1]
1669
                                    #connL2 = [_conn for _conn in connItemS.connectors if _conn != connL][0]
1670
                                    if abs(dy) > abs(dx) and abs(dy) != length:
1671
                                        if dy < 0:
1672
                                            delta = - ly - length
1673
                                        else:
1674
                                            delta = - ly + length
1675
                                        symbol.moveBy(-lx, delta)
1676
                                        for _symbol in connectedSymbols:
1677
                                            _symbol.moveBy(-lx, delta)
1678
                                        if symbol in symbolTextMatches:
1679
                                            for text in symbolTextMatches[symbol]:
1680
                                                text.moveText(1 if lx < 0 else -1, 0, abs(lx))
1681
                                                text.moveText(0, 1 if delta > 0 else -1, abs(delta))
1682
                                        found = True
1683
                                        break
1684
                                    elif abs(dy) < abs(dx) and abs(dx) != length:
1685
                                        if dx < 0:
1686
                                            delta = - lx - length
1687
                                        else:
1688
                                            delta = - lx + length
1689
                                        symbol.moveBy(delta, -ly)
1690
                                        for _symbol in connectedSymbols:
1691
                                            _symbol.moveBy(delta, -ly)
1692
                                        if symbol in symbolTextMatches:
1693
                                            for text in symbolTextMatches[symbol]:
1694
                                                text.moveText(0, 1 if ly < 0 else -1, abs(ly))
1695
                                                text.moveText(1 if delta > 0 else -1, 0, abs(delta))
1696
                                        found = True
1697
                                        break                
1698

    
1699
    def bind_close_items(self):
1700
        """ connect close item by pressing B """
1701
        from EngineeringLineItem import QEngineeringLineItem
1702
        from EngineeringNozzleItem import QEngineeringNozzleItem
1703
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1704
        from shapely.geometry import Point
1705

    
1706
        scene = self.scene()
1707
        app_doc_data = AppDocData.instance()
1708
        if scene:
1709
            configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1710
            toler = int(configs[0].value) if configs else 20
1711

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

    
1714
            for s_connector in self.connectors:
1715
                if s_connector.connectedItem:
1716
                    continue
1717

    
1718
                _pt1 = Point(s_connector.center()[0], s_connector.center()[1])
1719
                dist = sys.maxsize
1720
                selected = None
1721

    
1722
                for item in items:
1723
                    for i_connector in item.connectors:
1724
                        if i_connector.connectedItem:
1725
                            continue
1726

    
1727
                        _pt2 = Point(i_connector.center()[0], i_connector.center()[1])
1728
                        length = _pt1.distance(_pt2)
1729
                        if length < toler and length < dist:
1730
                            selected = item
1731

    
1732
                if selected:
1733
                    res = self.connect_if_possible(selected, toler)
1734
                    if res and type(selected) is QEngineeringLineItem:
1735
                        selected.set_line([res[1], res[2]])
1736

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

    
1740
                for item in items:
1741
                    if item.includes(self.connectors[0], margin=40):
1742
                        self.connectors[0].connect(item)
1743
                        break
1744

    
1745
            if type(self) is QEngineeringEquipmentItem:
1746
                configs = app_doc_data.getConfigs('Symbol', 'EQ binding')
1747
                eq_binding = int(configs[0].value) if configs else -1
1748
                for item in items:
1749
                    if eq_binding == 1 and (type(item) is QEngineeringLineItem and item.is_piping(True) or issubclass(type(item), SymbolSvgItem)):
1750
                        for index in range(len(item.connectors)):
1751
                            if item.connectors[index].connectedItem:
1752
                                continue
1753
                            
1754
                            if type(item) is QEngineeringLineItem or QEngineeringLineItem.check_piping(item.conn_type[index], True):
1755
                                if self.includes(item.connectors[index], margin=100):
1756
                                    item.connectors[index].connect(self)
1757

    
1758
                    elif issubclass(type(item), QEngineeringNozzleItem) and item.has_connection:
1759
                        for index in range(len(item.connectors)):
1760
                            if item.connectors[index].connectedItem or not QEngineeringLineItem.check_piping(item.conn_type[index], True):
1761
                                continue
1762

    
1763
                            if self.includes(item.connectors[index], margin=100):
1764
                                item.connectors[index].connect(self)
1765

    
1766
    '''
1767
        @brief      connect attribute
1768
        @author     humkyung
1769
        @date       2018.05.02
1770
        @history    humkyung 2018.05.09 append only nearest size attribute
1771
    '''
1772
    def connectAttribute(self, attributes, clear=True):
1773
        import math
1774
        from EngineeringTextItem import QEngineeringTextItem
1775
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1776
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1777
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1778
        from EngineeringLineItem import QEngineeringLineItem
1779

    
1780
        try:
1781
            if clear:
1782
                if not self.clear_attr_and_assoc_item():
1783
                    return
1784

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

    
1788
            rect = self.sceneBoundingRect()
1789
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
1790
            center = self.sceneBoundingRect().center()
1791

    
1792
            minDist = None
1793
            selected = None
1794

    
1795
            contain_texts = []
1796
            for attr in attributes:
1797
                # size text and operation code text will find owner themselves in findowner method
1798
                if False:  # type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
1799
                    dx = attr.center().x() - center.x()
1800
                    dy = attr.center().y() - center.y()
1801
                    length = math.sqrt(dx * dx + dy * dy)
1802
                    if (length < dist) and (minDist is None or length < minDist):
1803
                        minDist = length
1804
                        selected = attr
1805
                elif False:#type(attr) is QEngineeringInstrumentItem:
1806
                    if not attr.is_connected():
1807
                        dx = attr.center().x() - center.x()
1808
                        dy = attr.center().y() - center.y()
1809
                        if math.sqrt(dx * dx + dy * dy) < dist:
1810
                            if self.add_assoc_item(attr):
1811
                                attr.owner = self
1812
                elif issubclass(type(attr), QEngineeringTextItem):
1813
                    if rect.contains(attr.center()):
1814
                        dx = attr.center().x() - center.x()
1815
                        dy = attr.center().y() - center.y()
1816
                        length = math.sqrt(dx * dx + dy * dy)
1817
                        contain_texts.append([length, attr])
1818
                elif type(attr) is QEngineeringLineItem:
1819
                    length = attr.distanceTo([center.x(), center.y()])
1820
                    if (length < dist) and (minDist is None or length < minDist):
1821
                        minDist = length
1822
                        selected = attr
1823

    
1824
            if contain_texts:
1825
                num = len([_attr for _attr in self.getAttributes(findOwner=True) if _attr.AttributeType == 'Text Item'])
1826
                contain_texts = sorted(contain_texts, key=lambda param: param[0])
1827
                index = 0
1828
                for _, attr in contain_texts:
1829
                    if index >= num:
1830
                        break
1831
                    if self.add_assoc_item(attr):
1832
                        attr.owner = self  # set owner of text
1833
                    index += 1
1834

    
1835
            if selected is not None:
1836
                self.add_assoc_item(selected)
1837

    
1838
        except Exception as ex:
1839
            from App import App
1840
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1841
                                                          sys.exc_info()[-1].tb_lineno)
1842
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1843

    
1844
    '''
1845
        @brief      start rotating
1846
        @author     euisung
1847
        @date       2019.04.16
1848
    '''
1849
    def mouseDoubleClickEvent(self, event):
1850
        modifiers = QApplication.keyboardModifiers()
1851
        if not hasattr(self, '_rotating') and modifiers == Qt.NoModifier:
1852
            self._rotating = self.rotation()
1853
            self.grabMouse()
1854
        elif not hasattr(self, '_scale') and modifiers == Qt.ControlModifier:
1855
            self._scale = self.scale()
1856
            self._scale_x = event.scenePos().x()
1857
            self.grabMouse()
1858
        
1859
    def toXml(self):
1860
        """
1861
        generate xml code
1862
        :return:
1863
        """
1864
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1865
        from SymbolAttr import SymbolAttr
1866
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1867

    
1868
        try:
1869
            node = Element('SYMBOL')
1870
            node.attrib['Converted'] = str(self.converted)
1871
            uidNode = Element('UID')
1872
            uidNode.text = str(self.uid)
1873
            node.append(uidNode)
1874

    
1875
            dbUidNode = Element('DBUID')
1876
            dbUidNode.text = str(self.dbUid)
1877
            node.append(dbUidNode)
1878

    
1879
            nameNode = Element('NAME')
1880
            nameNode.text = self.name
1881
            node.append(nameNode)
1882

    
1883
            attributeValueNode = Element('ASSOCIATIONS')
1884
            for key, value in self._associations.items():
1885
                for assoc in value:
1886
                    assoc_node = Element('ASSOCIATION')
1887
                    assoc_node.attrib['TYPE'] = str(key)
1888
                    assoc_node.text = str(assoc)
1889
                    attributeValueNode.append(assoc_node)
1890
            node.append(attributeValueNode)
1891

    
1892
            typeNode = Element('TYPE')
1893
            typeNode.text = self.type
1894
            node.append(typeNode)
1895

    
1896
            # write owner's uid to xml
1897
            ownerNode = Element('OWNER')
1898
            if self.owner is not None:
1899
                ownerNode.text = str(self.owner)
1900
            else:
1901
                ownerNode.text = 'None'
1902
            node.append(ownerNode)
1903
            # up to here
1904

    
1905
            originNode = Element('ORIGINALPOINT')
1906
            if not self.transformOriginPoint().isNull():
1907
                origin = self.mapToScene(self.transformOriginPoint())
1908
                originNode.text = f"{origin.x()},{origin.y()}"
1909
            else:
1910
                origin = self.origin
1911
                originNode.text = f"{origin[0]},{origin[1]}"
1912
            node.append(originNode)
1913

    
1914
            connectorsNode = Element('CONNECTORS')
1915
            for connector in self.connectors:
1916
                connectorsNode.append(connector.toXml())
1917
            node.append(connectorsNode)
1918

    
1919
            connectionNode = Element('CONNECTIONPOINT')
1920
            connection_point = []
1921
            if self.connectors is not None:
1922
                for connector in self.connectors:
1923
                    connection_point.append(repr(connector))
1924
            connectionNode.text = '/'.join(connection_point)
1925
            node.append(connectionNode)
1926

    
1927
            locNode = Element('LOCATION')
1928
            locNode.text = f'{self.loc[0]},{self.loc[1]}'
1929
            '''
1930
            # calculate symbol's left-top corner
1931
            transform = QTransform()
1932
            transform.translate(self.scenePos().x(), self.scenePos().y())
1933
            transform.rotateRadians(-self.angle)
1934
            loc = transform.map(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
1935
            # up to here
1936
            locNode.text = '{},{}'.format(loc.x() - self.symbolOrigin[0], loc.y() - self.symbolOrigin[1])
1937
            '''
1938
            node.append(locNode)
1939

    
1940
            sizeNode = Element('SIZE')
1941
            sizeNode.text = f'{self.size[0]},{self.size[1]}'
1942
            node.append(sizeNode)
1943

    
1944
            angleNode = Element('ANGLE')
1945
            angleNode.text = str(math.radians(self.rotation())) if self.scene() else str(self.angle)
1946
            node.append(angleNode)
1947

    
1948
            scaleNode = Element('SCALE')
1949
            if type(self) is QEngineeringEquipmentItem:
1950
                scaleX = round(self.transform().m11() * 100)
1951
                scaleY = round(self.transform().m22() * 100)
1952
                scaleNode.text = str(scaleX / 100.0) + ',' + str(scaleY / 100.0)
1953
            else:
1954
                scaleNode.text = str(round(self.scale(), 2))
1955
            node.append(scaleNode)
1956

    
1957
            parentSymbolNode = Element('PARENT')
1958
            parentSymbolNode.text = str(self.parentSymbol)
1959
            node.append(parentSymbolNode)
1960

    
1961
            childSymbolNode = Element('CHILD')
1962
            childSymbolNode.text = str(self.childSymbol)
1963
            node.append(childSymbolNode)
1964

    
1965
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
1966
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
1967
            node.append(hasInstrumentLabelNode)
1968

    
1969
            areaNode = Element('AREA')
1970
            areaNode.text = self.area
1971
            node.append(areaNode)
1972

    
1973
            flipNode = Element('FLIP')
1974
            flipNode.text = str(self.flip)
1975
            node.append(flipNode)
1976

    
1977
            if self.hit_ratio:
1978
                ratioNode = Element('RATIO')
1979
                ratioNode.text = str(self.hit_ratio)
1980
                node.append(ratioNode)
1981

    
1982
            properties_node = Element('PROPERTIES')
1983
            for prop, value in self.properties.items():
1984
                prop_node = prop.toXml()
1985
                prop_node.text = str(value) if value else ''
1986
                properties_node.append(prop_node)
1987
            node.append(properties_node)
1988

    
1989
            attributesNode = Element('SYMBOLATTRIBUTES')
1990
            _attrs = self.getAttributes()
1991
            for attr in _attrs:
1992
                if type(attr) is SymbolAttr:
1993
                    _node = attr.toXml()
1994
                    if attr.AttributeType != 'Spec':
1995
                        _node.text = str(_attrs[attr])
1996
                    elif attr.AttributeType == 'Spec':
1997
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
1998
                    attributesNode.append(_node)
1999

    
2000
            node.append(attributesNode)
2001

    
2002
            if hasattr(self, 'currentPointModeIndex'):
2003
                currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
2004
                currentPointModeIndexNode.text = str(self.currentPointModeIndex) if self.currentPointModeIndex else ''
2005
                node.append(currentPointModeIndexNode)
2006
        except Exception as ex:
2007
            from App import App
2008
            message = f'error occurred({ex}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:' \
2009
                      f'{sys.exc_info()[-1].tb_lineno}'
2010
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2011

    
2012
            return None
2013

    
2014
        return node
2015

    
2016
    @staticmethod
2017
    def from_database(component):
2018
        """ create a item related to given component from database """
2019
        import uuid
2020
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2021
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2022
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2023
        from SymbolAttr import SymbolAttr
2024
        item = None
2025

    
2026
        try:
2027
            app_doc_data = AppDocData.instance()
2028

    
2029
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
2030
            grid = int(configs[0].value) if 1 == len(configs) else -1
2031

    
2032
            uid = component['UID']
2033

    
2034
            pt = [float(component['X']), float(component['Y'])]
2035
            if grid == 1:
2036
                pt = [round(pt[0]), round(pt[1])]
2037
                
2038
            size = [float(component['Width']), float(component['Height'])]
2039

    
2040
            dbUid = int(component['Symbol_UID'])
2041
            dbData = app_doc_data.getSymbolByQuery('UID', dbUid)
2042
            name = dbData.sName
2043
            _type = dbData.sType
2044
            angle = float(component['Rotation'])
2045

    
2046
            origin = [float(x) for x in component['SceneOriginPoint'].split(',')] if component['SceneOriginPoint'] is not None else pt
2047
            if grid == 1:
2048
                origin = [round(origin[0]), round(origin[1])]
2049

    
2050
            connPts = []
2051
            if component['ConnectionPoint']:
2052
                if dbData:
2053
                    db_conn = dbData.connectionPoint.split('/')
2054
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
2055
                    db_point_direction = [conn.split(',')[0] for conn in db_conn]
2056
                index = 0
2057
                for conn_pt in component['ConnectionPoint'].split('/'):
2058
                    if index >= len(db_conn):
2059
                        break
2060
                    tokens = conn_pt.split(',')
2061
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
2062
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
2063
                                   (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]))
2064
                    index += 1
2065

    
2066
            baseSymbol = dbData.baseSymbol
2067

    
2068
            childSymbolNode = component['AdditionalSymbol']
2069
            childSymbol = childSymbolNode if childSymbolNode is not None else ''
2070

    
2071
            owner = component['Owner'] if component['Owner'] is not None and component['Owner'] != 'None' else None
2072

    
2073
            hasInstrumentLabel = dbData.hasInstrumentLabel
2074

    
2075
            flipLabelNode = component['Flip']
2076
            flipLabel = int(flipLabelNode) if flipLabelNode is not None else 0
2077

    
2078
            project = app_doc_data.getCurrentProject()
2079
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
2080
            if os.path.isfile(svgFilePath):
2081
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
2082
                item.setVisible(False)
2083

    
2084
                scale = None
2085
                if type(item) is QEngineeringEquipmentItem:
2086
                    scale = [1.0, 1.0]
2087
                    if component['Value']:
2088
                        if ',' in component['Value']:
2089
                            scale = [float(component['Value'].split(',')[0]), float(component['Value'].split(',')[1])]
2090
                        else:
2091
                            scale = [float(component['Value']), float(component['Value'])]
2092
                else:
2093
                    scale = float(component['Value']) if component['Value'] else 1.0
2094

    
2095
                # if additional symbol was changed, change symbol info
2096
                symbolInfo = None
2097
                if dbUid is None:
2098
                    symbolInfo = app_doc_data.getSymbolByQuery('name', name)
2099
                else:
2100
                    symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
2101
                if symbolInfo:
2102
                    childSymbol = symbolInfo.additionalSymbol
2103

    
2104
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
2105
                                  hasInstrumentLabel, dbUid=dbUid, scale=scale):
2106
                    pass
2107
                else:
2108
                    return None
2109

    
2110
                for key in item._properties.keys():
2111
                    for compo in component.keys():
2112
                        if key.Attribute == compo:
2113
                            item._properties[key] = key.parse_value(component[key.Attribute])# if component[key.Attribute] else ''
2114

    
2115
                ## assign area
2116
                areaNode = component['Area']
2117
                if areaNode is None:
2118
                    for area in app_doc_data.getAreaList():
2119
                        if area.contains(pt):
2120
                            item.area = area.name
2121
                            break
2122
                else:
2123
                    item.area = areaNode
2124
                ## up to here
2125

    
2126
                connectors = app_doc_data.get_component_connectors(uid)
2127
                if connectors:
2128
                    iterIndex = 0
2129
                    for connector in connectors:
2130
                        if iterIndex >= len(db_conn):
2131
                            break
2132
                        item.connectors[iterIndex].parse_record(connector)
2133
                        iterIndex += 1
2134

    
2135
                # get associations 
2136
                associations = app_doc_data.get_component_associations(uid)
2137
                if associations:
2138
                    for assoc in associations:
2139
                        _attrType = assoc['Type']
2140
                        if not _attrType in item._associations:
2141
                            item._associations[_attrType] = []
2142
                        item._associations[_attrType].append(
2143
                            uuid.UUID(assoc['Association']) if assoc['Association'] != 'None' else None)
2144
                # up to here
2145

    
2146
                attributes = app_doc_data.get_component_attributes(uid)
2147
                if attributes:
2148
                    for attr in attributes:
2149
                        _attr = SymbolAttr.from_record(attr)
2150
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
2151
                            item.attrs[_attr] = attr['Value']
2152
                        else:
2153
                            if _attr.AttributeType == 'Spec':
2154
                                item.attrs[_attr] = [attr['Value'].split(',')[0], attr['Value'].split(',')[1]]
2155
                            else:
2156
                                item.attrs[_attr] = attr['Value']
2157
                            '''
2158
                            elif _attr.Attribute == 'UpStream':
2159
                                for initKey in item.attrs.keys():
2160
                                    if initKey.Attribute == 'UpStream':
2161
                                        item.attrs[initKey] = attr['Value']
2162
                            elif _attr.Attribute == 'DownStream':
2163
                                for initKey in item.attrs.keys():
2164
                                    if initKey.Attribute == 'DownStream':
2165
                                        item.attrs[initKey] = attr['Value']
2166
                            '''
2167

    
2168
                currentPointModeIndex = component['OriginIndex']
2169
                if currentPointModeIndex is not None:
2170
                    item.currentPointModeIndex = int(currentPointModeIndex)
2171
        except Exception as ex:
2172
            from App import App
2173
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2174
                                                          sys.exc_info()[-1].tb_lineno)
2175
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2176

    
2177
            return None
2178

    
2179
        return item
2180

    
2181
    '''
2182
        @brief      parse xml code
2183
        @author     humkyung
2184
        @date       2018.07.20
2185
        @history    humkyung 2018.07.20 parse uid from xml node
2186
                    humkyung 2018.07.23 parse connected item's uid from xml node
2187
                    kyouho  2018.07.31 
2188
                    humkyung 2018.09.06 assign area to item
2189
    '''
2190

    
2191
    @staticmethod
2192
    def fromXml(node):
2193
        import uuid
2194
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2195
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2196
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2197
        from SymbolAttr import SymbolAttr
2198
        item = None
2199

    
2200
        try:
2201
            appDocData = AppDocData.instance()
2202

    
2203
            uidNode = node.find('UID')
2204
            uid = uidNode.text if uidNode is not None else uuid.uuid4()  # generate UUID
2205

    
2206
            configs = AppDocData.instance().getConfigs('Data', 'Grid')
2207
            grid = int(configs[0].value) if 1 == len(configs) else -1
2208

    
2209
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
2210
            if grid == 1:
2211
                pt = [round(pt[0]), round(pt[1])]
2212
            size = [float(x) for x in node.find('SIZE').text.split(',')]
2213

    
2214
            dbUidNode = node.find('DBUID')
2215
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
2216
            dbData = None
2217
            if dbUid:
2218
                dbData = appDocData.getSymbolByQuery('UID', dbUid)
2219
            name = node.find('NAME').text if dbData is None else dbData.sName
2220

    
2221
            angle = float(node.find('ANGLE').text)
2222
            _type = node.find('TYPE').text if dbData is None else dbData.sType
2223
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
2224
            if grid == 1:
2225
                origin = [round(origin[0]), round(origin[1])]
2226
            connPts = []
2227
            if node.find('CONNECTIONPOINT').text is not None:
2228
                if dbData:
2229
                    db_conn = dbData.connectionPoint.split('/')
2230
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
2231
                    db_point_direction = [conn.split(',')[0] for conn in db_conn]
2232
                index = 0
2233
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
2234
                    if index >= len(db_conn):
2235
                        break
2236
                    tokens = conn_pt.split(',')
2237
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
2238
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
2239
                                   (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]))
2240
                    index += 1
2241
            baseSymbol = node.find('PARENT').text if dbData is None else dbData.baseSymbol
2242
            childSymbolNode = node.find('CHILD')
2243
            childSymbol = ''
2244
            if childSymbolNode is not None:
2245
                childSymbol = childSymbolNode.text
2246

    
2247
            ownerNode = node.find('OWNER')
2248
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
2249

    
2250
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
2251
            hasInstrumentLabel = hasInstrumentLabelNode.text if dbData is None else dbData.hasInstrumentLabel
2252

    
2253
            flipLabelNode = node.find('FLIP')
2254
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
2255

    
2256
            ratioNode = node.find('RATIO')
2257
            hit_ratio = float(ratioNode.text) if ratioNode is not None else None
2258

    
2259
            project = appDocData.getCurrentProject()
2260
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
2261
            if os.path.isfile(svgFilePath):
2262
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
2263
                item.setVisible(False)
2264

    
2265
                scaleNode = node.find('SCALE')
2266
                scale = None
2267
                if type(item) is QEngineeringEquipmentItem:
2268
                    scale = [1.0, 1.0]
2269
                    if scaleNode is not None and scaleNode.text:
2270
                        if ',' in scaleNode.text:
2271
                            scale = [float(scaleNode.text.split(',')[0]), float(scaleNode.text.split(',')[1])]
2272
                        else:
2273
                            scale = [float(scaleNode.text), float(scaleNode.text)]
2274
                else:
2275
                    scale = float(scaleNode.text) if scaleNode is not None else 1.0
2276

    
2277
                # if additional symbol was changed, change symbol info
2278
                symbolInfo = None
2279
                if dbUid is None:
2280
                    symbolInfo = appDocData.getSymbolByQuery('name', name)
2281
                else:
2282
                    symbolInfo = appDocData.getSymbolByQuery('UID', dbUid)
2283
                if symbolInfo:
2284
                    childSymbol = symbolInfo.additionalSymbol
2285

    
2286
                if hit_ratio:
2287
                    item.hit_ratio = hit_ratio
2288

    
2289
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
2290
                                  hasInstrumentLabel, dbUid=dbUid, scale=scale):
2291
                    pass
2292
                else:
2293
                    return None
2294

    
2295
                for prop_node in node.iter('PROPERTY'):
2296
                    matches = [prop for prop in item._properties.keys() if
2297
                               prop.Attribute == prop_node.attrib['Attribute']]
2298
                    if matches:
2299
                        item._properties[matches[0]] = matches[0].parse_value(prop_node.text)# if prop_node.text else ''
2300

    
2301
                # assign area
2302
                areaNode = node.find('AREA')
2303
                if areaNode is None:
2304
                    for area in appDocData.getAreaList():
2305
                        if area.contains(pt):
2306
                            item.area = area.name
2307
                            break
2308
                else:
2309
                    item.area = areaNode.text
2310
                # up to here
2311

    
2312
                connectors = node.find('CONNECTORS')
2313
                if connectors is not None:
2314
                    iterIndex = 0
2315
                    for connector in connectors.iter('CONNECTOR'):
2316
                        if iterIndex >= len(db_conn):
2317
                            break
2318
                        item.connectors[iterIndex].parse_xml(connector)
2319
                        iterIndex += 1
2320

    
2321
                # get associations 
2322
                attributeValue = node.find('ASSOCIATIONS')
2323
                if attributeValue is not None:
2324
                    for assoc in attributeValue.iter('ASSOCIATION'):
2325
                        _attrType = assoc.attrib['TYPE']
2326
                        if not _attrType in item._associations:
2327
                            item._associations[_attrType] = []
2328
                        item._associations[_attrType].append(uuid.UUID(assoc.text) if assoc.text != 'None' else None)
2329
                # up to here
2330

    
2331
                attributes = node.find('SYMBOLATTRIBUTES')
2332
                if attributes is not None:
2333
                    '''
2334
                    ## for old spec break item that has not uid currectly, may not necessary new data
2335
                    if _type == 'Segment Breaks':
2336
                        specBreakAttrs = appDocData.getSymbolAttribute('Segment Breaks')
2337
                    ## up to here 1
2338
                    '''
2339
                    for attr in attributes.iter('ATTRIBUTE'):
2340
                        _attr = SymbolAttr.fromXml(attr)
2341
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
2342
                            item.attrs[_attr] = attr.text
2343
                        else:
2344
                            '''
2345
                            ## for old spec break item that has not uid currectly, may not necessary new data
2346
                            matchAttr = [cAttr for cAttr in specBreakAttrs if _attr.Attribute == cAttr.Attribute]
2347
                            matchAttr = matchAttr[0] if matchAttr else None
2348
                            if matchAttr:
2349
                                _attr.UID = matchAttr.UID
2350
                                _attr.DisplayAttribute = matchAttr.DisplayAttribute
2351
                                _attr.AttributeType = matchAttr.AttributeType
2352
                                _attr.AttrAt = matchAttr.AttrAt
2353
                                _attr.Expression = matchAttr.Expression
2354
                                _attr.Length = matchAttr.Length
2355
                            # up to here 2
2356
                            '''
2357
                            if _attr.AttributeType == 'Spec':
2358
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
2359
                            else:
2360
                                '''
2361
                                # for old spec break item that has not uid currectly, may not necessary new data
2362
                                _attr.AssocItem = uuid.UUID(attr.text) if attr.text and attr.text != 'None' else None
2363
                                # up to here 3
2364
                                '''
2365
                                item.attrs[_attr] = attr.text
2366
                            '''
2367
                            elif _attr.Attribute == 'UpStream':
2368
                                for initKey in item.attrs.keys():
2369
                                    if initKey.Attribute == 'UpStream':
2370
                                        item.attrs[initKey] = attr.text
2371
                            elif _attr.Attribute == 'DownStream':
2372
                                for initKey in item.attrs.keys():
2373
                                    if initKey.Attribute == 'DownStream':
2374
                                        item.attrs[initKey] = attr.text
2375
                            '''
2376

    
2377
                currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
2378
                if currentPointModeIndex is not None:
2379
                    item.currentPointModeIndex = int(currentPointModeIndex.text) if currentPointModeIndex.text else 0
2380
        except Exception as ex:
2381
            from App import App
2382
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2383
                                                          sys.exc_info()[-1].tb_lineno)
2384
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2385

    
2386
            return None
2387

    
2388
        return item
2389

    
2390
    def to_svg(self, parent) -> list:
2391
        """convert symbol svg item to svg"""
2392
        import re
2393
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
2394
        from App import App
2395

    
2396
        res = []
2397
        try:
2398
            app_doc_data = AppDocData.instance()
2399
            prj = app_doc_data.getCurrentProject()
2400

    
2401
            node = Element('g')
2402
            node.attrib['id'] = str(self.uid)
2403
            node.attrib['class'] = self._class
2404

    
2405
            except_pattern = re.compile('[^a-zA-Z0-9-_]')
2406
            for attr, value in self.getAttributes().items():
2407
                node.attrib[re.sub(except_pattern, '_', attr.Attribute)] = str(value) if value else ''
2408
            trans = self.sceneTransform()
2409
            node.attrib['transform'] = f"matrix(" \
2410
                                       f"{trans.m11()},{trans.m12()}," \
2411
                                       f"{trans.m21()},{trans.m22()}," \
2412
                                       f"{trans.m31()},{trans.m32()}" \
2413
                                       f")"
2414

    
2415
            node_list = self._document.elementsByTagName('path')
2416
            for at in range(node_list.count()):
2417
                path = Element('path')
2418
                path.attrib['d'] = node_list.item(at).attributes().namedItem('d').nodeValue()
2419
                path.attrib['transform'] = self._document.elementsByTagName('g').item(0).attributes().namedItem('transform').nodeValue()
2420
                node.append(path)
2421

    
2422
            for assoc in self.associations():
2423
                assoc_node = assoc.to_svg(parent=self)
2424
                node.extend(assoc_node)
2425

    
2426
            res.append(node)
2427
        except Exception as ex:
2428
            from App import App
2429
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2430
                                                          sys.exc_info()[-1].tb_lineno)
2431
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2432

    
2433
        return res
2434

    
2435
    '''
2436
        @brief      create item corresponding to given type
2437
        @author     humkyung
2438
        @date       2018.05.02
2439
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
2440
                    humkyung 2018.05.10 change symbol's color to blue
2441
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
2442
    '''
2443

    
2444
    @staticmethod
2445
    def createItem(type: str, name: str, path: str, uid=None, owner=None, flip=0):
2446
        from QEngineeringOPCItem import QEngineeringOPCItem
2447
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
2448
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
2449
        from EngineeringNozzleItem import QEngineeringNozzleItem
2450
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
2451
        from EngineeringReducerItem import QEngineeringReducerItem
2452
        from EngineeringErrorItem import QEngineeringErrorItem
2453
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
2454
        from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
2455
        from AppDocData import AppDocData
2456
        import uuid
2457

    
2458
        app_doc_data = AppDocData.instance()
2459

    
2460
        item = None
2461
        try:
2462
            cateogry = app_doc_data.getSymbolCategoryByType(type)
2463
            if type == "Piping OPC's" or type == "Instrument OPC's":
2464
                item = QEngineeringOPCItem(path, uid, flip=flip)
2465
            elif type == 'Nozzles':
2466
                item = QEngineeringNozzleItem(path, uid, flip=flip)
2467
            elif cateogry == 'Equipment' or cateogry == 'Equipment Components' or cateogry == 'Package':
2468
                item = QEngineeringEquipmentItem(path, uid, flip=flip)
2469
            elif cateogry == 'Instrumentation':
2470
                item = QEngineeringInstrumentItem(path, uid, flip=flip)
2471
            elif type == 'Segment Breaks':
2472
                item = QEngineeringSpecBreakItem(path, uid, flip=flip)
2473
            elif type == 'Reducers':
2474
                item = QEngineeringReducerItem(path, uid, flip=flip)
2475
            elif type == 'Error':
2476
                item = QEngineeringErrorItem(path, uid, flip=flip)
2477
            elif type == 'End Break':
2478
                item = QEngineeringEndBreakItem(path, uid, flip=flip)
2479
            # elif type == 'Flow Mark':
2480
            #    item = QEngineeringFlowMarkItem(path, uid, flip=flip)
2481
            else:
2482
                item = SymbolSvgItem(name, path, uid, flip=flip)
2483

    
2484
            if owner is not None:
2485
                item.owner = uuid.UUID(owner)
2486

    
2487
        except Exception as ex:
2488
            from App import App
2489
            from AppDocData import MessageType
2490

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

    
2495
        return item
2496

    
2497
    def setColor(self, color):
2498
        """change svg item's color"""
2499
        if self._document:# and color != self.__color:
2500
            self.changeAttributes('fill', color)
2501
            self.changeAttributes('stroke', color)
2502
            self.renderer().load(self._document.toByteArray())
2503
            self.__color = color
2504
            #self.update()
2505

    
2506
    '''
2507
        @brief  get attributes from svg file
2508
        @author humkyung
2509
        @date   2019.03.08
2510
    '''
2511

    
2512
    def get_attribute(self, attName):
2513
        root = self._document.documentElement()
2514
        node = root.firstChild()
2515
        while not node.isNull():
2516
            if node.isElement():
2517
                element = node.toElement()
2518
                if element.hasAttribute(attName):
2519
                    return element.attribute(attName)
2520

    
2521
                if element.hasChildNodes():
2522
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
2523
                    if att_val is not None: return att_val
2524

    
2525
            node = node.nextSibling()
2526

    
2527
        return None
2528

    
2529
    '''
2530
        @brief  get recursively attribute
2531
        @author humkyung
2532
        @date   2019.03.08
2533
    '''
2534

    
2535
    def recursive_get_attribute(self, node, attName):
2536
        while not node.isNull():
2537
            if node.isElement():
2538
                element = node.toElement()
2539
                if element.hasAttribute(attName):
2540
                    return element.attribute(attName)
2541

    
2542
                if node.hasChildNodes():
2543
                    att_val = self.recursive_get_attribute(node.firstChild(), attName)
2544
                    if att_val is not None: return att_val
2545

    
2546
            node = node.nextSibling()
2547

    
2548
        return None
2549

    
2550
    '''
2551
        @brief  change attributes
2552
        @author humkyung
2553
        @date   2018.05.10
2554
    '''
2555

    
2556
    def changeAttributes(self, attName, attValue):
2557
        root = self._document.documentElement()
2558
        node = root.firstChild()
2559
        while not node.isNull():
2560
            if node.isElement():
2561
                element = node.toElement()
2562
                if element.hasAttribute(attName):
2563
                    element.setAttribute(attName, attValue)
2564

    
2565
                if element.hasChildNodes():
2566
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
2567

    
2568
            node = node.nextSibling()
2569

    
2570
    '''
2571
        @brief  change attribute
2572
        @author humkyung
2573
        @date   2018.05.10
2574
    '''
2575

    
2576
    def recursiveChangeAttributes(self, node, attName, attValue):
2577
        while not node.isNull():
2578
            if node.isElement():
2579
                element = node.toElement()
2580
                if element.hasAttribute(attName):
2581
                    element.setAttribute(attName, attValue)
2582

    
2583
                if node.hasChildNodes():
2584
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
2585

    
2586
            node = node.nextSibling()
2587

    
2588
    '''
2589
        @brief  draw rect when item is selected
2590
        @author humkyung
2591
        @date   2018.07.07
2592
    '''
2593

    
2594
    def drawFocusRect(self, painter):
2595
        self.focuspen = QPen(Qt.DotLine)
2596
        self.focuspen.setColor(Qt.black)
2597
        self.focuspen.setWidthF(1.5)
2598
        hilightColor = QColor(255, 0, 0, 127)
2599
        painter.setBrush(QBrush(hilightColor))
2600
        painter.setPen(self.focuspen)
2601
        painter.drawRect(self.boundingRect())
2602

    
2603
    '''
2604
        @brief  override paint(draw connection points)
2605
        @author humkyung
2606
        @date   2018.04.21
2607
    '''
2608

    
2609
    def paint(self, painter, options=None, widget=None):
2610
        #from EngineeringTextItem import QEngineeringTextItem
2611

    
2612
        self.setColor(self.getColor())
2613

    
2614
        painter.setClipRect(options.exposedRect)
2615
        QGraphicsSvgItem.paint(self, painter, options, widget)
2616
        '''
2617
        # not used
2618
        for attr in self.attrs:
2619
            if issubclass(type(attr), QEngineeringTextItem):
2620
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (
2621
                    self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
2622
                attr.setColor(color)
2623
            elif issubclass(type(attr), SymbolSvgItem):
2624
                attr.setColor(self.getColor())
2625
                attr.update()
2626
        '''
2627

    
2628
        if self.isSelected():
2629
            self.drawFocusRect(painter)
2630

    
2631
    def resetPosition(self):
2632
        if self.flip:
2633
            self.flip_symbol()
2634

    
2635
        self.moveBy(-self.origin[0], -self.origin[1])
2636
        self.setRotation(math.degrees(-self.angle))
2637
        self.moveBy(self.symbolOrigin[0], self.symbolOrigin[1])
2638
        self.setTransformOriginPoint(QPointF(0, 0))
2639

    
2640
    def addSvgItemToScene(self, scene, undoable: bool = False, manual=False) -> None:
2641
        """Add Svg Item into ImageViewer's Scene"""
2642
        if self.flip:
2643
            self.flip_symbol()
2644

    
2645
        if (hasattr(self, 'symbolConvertingOrigin') and not self.symbolConvertingOrigin) or not manual:
2646
            self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2647
            self.moveBy(-self.symbolOrigin[0], -self.symbolOrigin[1])
2648
            self.setRotation(math.degrees(self.angle))
2649
            self.moveBy(self.origin[0], self.origin[1])
2650
        else:
2651
            self.setTransformOriginPoint(QPointF(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1]))
2652
            self.moveBy(-self.symbolConvertingOrigin[0], -self.symbolConvertingOrigin[1])
2653
            self.setRotation(math.degrees(self.angle))
2654
            self.moveBy(self.origin[0], self.origin[1])
2655
            self.setTransformOriginPoint(QPointF(0, 0))
2656
            self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2657

    
2658
            #self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
2659
            #self.moveBy(-self.symbolOrigin[0], -self.symbolOrigin[1])
2660
            #self.setRotation(math.degrees(self.angle))
2661
            #self.moveBy(self.origin[0], self.origin[1])
2662

    
2663
            dx = self.symbolOrigin[0] - self.symbolConvertingOrigin[0]
2664
            dy = self.symbolOrigin[1] - self.symbolConvertingOrigin[1]
2665
            nx = round(math.cos(self.angle) * dx - math.sin(self.angle) * dy)
2666
            ny = round(math.sin(self.angle) * dx + math.cos(self.angle) * dy)
2667
            self.moveBy(nx - dx, ny - dy)
2668

    
2669
            #transform = QTransform()
2670
            #transform.translate(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1])
2671
            ##transform.map(self.symbolConvertingOrigin[0], self.symbolConvertingOrigin[1])
2672
            #transform.rotate(math.degrees(self.angle))
2673
            #transform.translate(-self.symbolConvertingOrigin[0], -self.symbolConvertingOrigin[1])
2674
            #self.setTransform(transform)
2675

    
2676
        scene.addItem(self)
2677

    
2678
        self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
2679
        self.loc[0], self.loc[1] = round(self.sceneBoundingRect().top()), round(self.sceneBoundingRect().left())
2680

    
2681
        scene_origin = self.mapToScene(self.transformOriginPoint())
2682

    
2683
        configs = AppDocData.instance().getConfigs('Data', 'Grid')
2684
        grid = int(configs[0].value) if 1 == len(configs) else -1
2685
        if grid == 1:
2686
            self.origin = [round(scene_origin.x()), round(scene_origin.y())]
2687
        else:
2688
            self.origin = [scene_origin.x(), scene_origin.y()]
2689

    
2690
        if undoable:
2691
            from CreateCommand import CreateCommand
2692
            self.scene().undo_stack.push(CreateCommand(self.scene(), [self,]))
2693

    
2694
    '''
2695
        @brief      
2696
        @author     humkyung
2697
        @date       2018.07.27
2698
    '''
2699
    def onConnectorPosChaned(self, connector):
2700
        pass
2701

    
2702
    '''
2703
        @brief      set connector
2704
        @author     kyouho
2705
        @date       2018.07.26
2706
    '''
2707
    def setConnector(self, index):
2708
        connector = QEngineeringConnectorItem(parent=self, index=index)
2709
        connector.setParentItem(self)
2710
        self.connectors.append(connector)
2711

    
2712
    def refreshConnector(self):
2713
        for connector in self.connectors:
2714
            connector.buildItem()
2715

    
2716
    '''
2717
        @brief      Delete svg item
2718
        @author     Jeongwoo
2719
        @date       2018.05.25
2720
    '''
2721
    def deleteSvgItemFromScene(self):
2722
        ''' not used '''
2723
        for connector in self.connectors:
2724
            if connector.connectedItem is not None:
2725
                try:
2726
                    connector.connectedItem.removeSymbol(self)
2727
                except Exception as ex:
2728
                    from App import App
2729
                    from AppDocData import MessageType
2730

    
2731
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2732
                                                                  sys.exc_info()[-1].tb_lineno)
2733
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
2734
                break
2735

    
2736
        self.transfer.onRemoved.emit(self)
2737

    
2738
    def flip_symbol(self) -> None:
2739
        """flip symbol"""
2740
        transform = QTransform()
2741
        if self.flip is 1:
2742
            rect = self.boundingRect()
2743
            transform.scale(-1.0, 1.0)
2744
            transform.translate(-2 * self.symbolOrigin[0], 0)
2745

    
2746
        self.setTransform(transform)
2747

    
2748
    def rotate(self, angle: float) -> None:
2749
        """rotate symbol by given angle in radian"""
2750
        import math
2751
        self.setRotation(math.degrees(angle))
2752
        '''
2753
        transform = QTransform().translate(self.symbolOrigin[0], self.symbolOrigin[1])
2754
        transform.rotate(math.degrees(angle))
2755
        transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
2756
        self.setTransform(transform)
2757
        '''
2758

    
2759
    '''
2760
        @brief      change Conn point 
2761
        @author     kyouho
2762
        @date       2018.07.25
2763
    '''
2764
    def changeConnPoint(self):
2765
        if len(self.connectors) == 2:
2766
            conn1Item = self.connectors[0].connectedItem
2767
            conn2Item = self.connectors[1].connectedItem
2768
            self.connectors[0].connectedItem = conn2Item
2769
            self.connectors[1].connectedItem = conn1Item
2770

    
2771
            currentPoint = self.getCurrentPoint()
2772
            #self.reSettingSymbol(currentPoint, self.angle)
2773

    
2774
    '''
2775
        @brief      change standard point
2776
        @author     kyouho
2777
        @date       2018.07.24
2778
    '''
2779
    def changeStandardPoint(self):
2780
        connPtsCount = len(self.connectors)
2781

    
2782
        if self.currentPointModeIndex < connPtsCount:
2783
            self.currentPointModeIndex += 1
2784
        else:
2785
            self.currentPointModeIndex = 0
2786

    
2787
        currentPoint = self.getCurrentPoint()
2788
        #self.reSettingSymbol(currentPoint, self.angle)
2789

    
2790
    def getCurrentPoint(self):
2791
        """return transform origin point"""
2792
        pointList = []
2793
        pointList.append(self.symbolOrigin)
2794
        for connector in self.connectors:
2795
            pointList.append(connector.connectPoint)
2796

    
2797
        return pointList[self.currentPointModeIndex]
2798

    
2799
    def EvaluatedCode(self, old_code, code_name):
2800
        """ return new attribute code """
2801
        from AppDocData import AppDocData
2802
        from CodeTables import CodeTable
2803
        from LineTypeConditions import LineTypeConditions
2804
        from EngineeringLineItem import QEngineeringLineItem
2805
        import re
2806

    
2807
        try:
2808
            code = old_code
2809
            start_item = None
2810
            if self.iType == 19:    # Labels - Symbol
2811
                matches = [assoc for assoc in self.associations() if issubclass(type(assoc), SymbolSvgItem)]
2812
                start_item = matches[0] if matches else self
2813
            else:
2814
                start_item = self
2815

    
2816
            #if not start_item:
2817
            #    return ''
2818

    
2819
            app_doc_data = AppDocData.instance()
2820
            connected_items_lists = app_doc_data._connected_items_lists
2821
            items = [connected_items_list.items for connected_items_list in connected_items_lists.runs if start_item in connected_items_list.items]
2822
            if len(items) != 1:
2823
                return ''
2824
            else:
2825
                items = [item for item in items[0] if issubclass(type(item), SymbolSvgItem)]
2826

    
2827
            table = CodeTable.instance(code_name, inst=True)
2828
            line_types = [condition.name for condition in LineTypeConditions.items()]
2829

    
2830
            new_codes = []
2831
            for value in table.values: # uid, old code, symbol, attribute, new code, expression, priority
2832
                # symbol, line type
2833
                if len(value[3]) == 1 and not value[3][0]:
2834
                    # symbol
2835
                    if not [line_type for line_type in line_types if line_type in value[2]]:
2836
                        for item in items:
2837
                            match = re.search(value[1][0], code, re.DOTALL)
2838
                            if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) \
2839
                                and item.name in value[2]:
2840
                                dx = item.origin[0] - self.origin[0]
2841
                                dy = item.origin[1] - self.origin[1]
2842
                                length = math.sqrt(dx*dx + dy*dy)
2843
                                if not value[5]:
2844
                                    new_codes.append([length if not value[6] else value[6], '"' + value[4] + '"', item, length])
2845
                                else:
2846
                                    new_codes.append([length if not value[6] else value[6], value[5], item, length])
2847
                    # line
2848
                    else:
2849
                        match = re.search(value[1][0], code, re.DOTALL)
2850
                        types = [conn.connectedItem.lineType for conn in self.connectors if conn.connectedItem and type(conn.connectedItem) is QEngineeringLineItem]
2851
                        if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) and \
2852
                            [line_type for line_type in value[2] if line_type in types]:
2853
                            if not value[5]:
2854
                                new_codes.append([0 if not value[6] else value[6], '"' + value[4] + '"', None, sys.maxsize])
2855
                            else:
2856
                                new_codes.append([0 if not value[6] else value[6], value[5], None, sys.maxsize])
2857

    
2858
                # self attribute
2859
                elif len(value[2]) == 1 and not value[2][0] and value[3][0]:
2860
                    for key, _value in self.attrs.items():
2861
                        if _value in value[3]:
2862
                            if not value[5]:
2863
                                new_codes.append([0 if not value[6] else value[6], '"' + value[4] + '"', self, sys.maxsize])
2864
                            else:
2865
                                new_codes.append([0 if not value[6] else value[6], value[5], self, sys.maxsize])
2866

    
2867
                # symbol + attribute
2868
                elif value[2][0] and value[3][0]:
2869
                    for item in items:
2870
                        match = re.search(value[1][0], code, re.DOTALL)
2871
                        if (code in value[1] or (match and match.start() is 0 and match.end() is len(code))) \
2872
                            and item.name in value[2]:
2873
                            dx = item.origin[0] - self.origin[0]
2874
                            dy = item.origin[1] - self.origin[1]
2875
                            length = math.sqrt(dx*dx + dy*dy)
2876

    
2877
                            for key, _value in item.attrs.items():
2878
                                if _value in value[3]:
2879
                                    if not value[5]:
2880
                                        new_codes.append([length if not value[6] else value[6], '"' + value[4] + '"', item, length])
2881
                                    else:
2882
                                        new_codes.append([length if not value[6] else value[6], value[5], item, length])
2883
                        
2884
            # default
2885
            for value in table.values:
2886
                match = re.search(value[1][0], code, re.DOTALL)
2887
                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] == '':
2888
                    if not value[5]:
2889
                        new_codes.append([sys.maxsize if not value[6] else value[6], '"' + value[4] + '"', None, sys.maxsize])
2890
                    else:
2891
                        new_codes.append([sys.maxsize if not value[6] else value[6], value[5], None, sys.maxsize])
2892

    
2893
            if new_codes:
2894
                new_codes = sorted(new_codes, key=lambda param: param[0])
2895
                new_codes = [_code for _code in new_codes if _code[0] == new_codes[0][0]]
2896
                new_codes = sorted(new_codes, key=lambda param: param[-1])
2897
                item = new_codes[0][-2]
2898
                new_code = eval(new_codes[0][1])
2899
                return new_code
2900
            else:
2901
                return code
2902
        except Exception as ex:
2903
            from App import App
2904
            from AppDocData import MessageType
2905

    
2906
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2907
                                                          sys.exc_info()[-1].tb_lineno)
2908
            #App.mainWnd().addMessage.emit(MessageType.Error, str(self.uid) + self.name + message)
2909
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2910

    
2911
def recursiveChangeAttributes(node, attName, attValue):
2912
    while not node.isNull():
2913
        if node.isElement():
2914
            element = node.toElement()
2915
            if element.hasAttribute(attName):
2916
                element.setAttribute(attName, attValue)
2917

    
2918
            if node.hasChildNodes():
2919
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
2920

    
2921
        node = node.nextSibling()
2922

    
2923

    
2924
'''
2925
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
2926
    @author     Jeongwoo
2927
    @date       2018.06.18
2928
'''
2929
class Transfer(QObject):
2930
    on_pos_changed = pyqtSignal(QGraphicsItem)
2931
    onRemoved = pyqtSignal(QGraphicsItem)
2932

    
2933
    def __init__(self, parent=None):
2934
        QObject.__init__(self, parent)
2935

    
2936

    
2937
if __name__ == '__main__':
2938
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
2939
    f.open(QIODevice.ReadOnly)
2940
    array = f.readAll()
2941
    document = QDomDocument()
2942
    document.setContent(array)
2943

    
2944
    root = document.documentElement()
2945
    node = root.firstChild()
2946
    while not node.isNull():
2947
        if node.isElement():
2948
            element = node.toElement()
2949
            if element.hasAttribute('fill'):
2950
                element.setAttribute('fill', '#FFFFF')
2951

    
2952
            if element.hasChildNodes():
2953
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
2954

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