프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ 4d8db4c8

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

1
#!/usr/bin/env/python3
2
# coding: utf-8
3

    
4
import sys
5
import os
6
import math
7
import numpy as np
8
from PyQt5.QtGui import *
9
from PyQt5.QtCore import *
10
from PyQt5.QtSvg import *
11
from PyQt5.QtWidgets import (QApplication, QGraphicsItem)
12
from PyQt5.QtXml import *
13

    
14
from AppDocData import *
15
from EngineeringConnectorItem import QEngineeringConnectorItem
16
from EngineeringAbstractItem import QEngineeringAbstractItem
17
from EngineeringConnectorItem import QEngineeringConnectorItem
18
from UserInputAttribute import UserInputAttribute
19
import SelectAttributeCommand
20

    
21

    
22
class SymbolSvgItem(QGraphicsSvgItem, QEngineeringAbstractItem):
23
    """ This is symbolsvgitem class """
24

    
25
    clicked = pyqtSignal(QGraphicsSvgItem)
26
    ZVALUE = 50
27
    HOVER_COLOR = 'url(#hover)'
28

    
29
    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.ItemSendsGeometryChanges)
47

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

    
68
        self.setAcceptDrops(True)
69
        self.setAcceptHoverEvents(True)
70
        self.setAcceptedMouseButtons(Qt.LeftButton)
71
        self.setAcceptTouchEvents(True)
72

    
73
        self.currentCursor = 0
74
        self.transfer = Transfer()
75

    
76
        self._angle = 0
77

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

    
82
        try:
83
            app_doc_data = AppDocData.instance()
84
            svg = None
85

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

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

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

    
103
            self.setSharedRenderer(self._renderer)
104
            self.setCacheMode(QGraphicsItem.ItemCoordinateCache)
105

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

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

    
114
        self.setZValue(SymbolSvgItem.ZVALUE)
115

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

    
120
    def has_in_out_connector(self):
121
        """ return True if item has in or out connector """
122
        if len(self.in_out_connector[0]) > 0 and len(self.in_out_connector[1]) > 0:
123
            return True
124
        else:
125
            return False
126

    
127
    def has_break_connector(self):
128
        """ return True if item has break connector """
129
        if len(self.break_connector) > 0:
130
            return True
131
        else:
132
            return False
133

    
134
    def __str__(self):
135
        """ return string represent uuid """
136
        return str(self.uid)
137

    
138
    @property
139
    def owner(self):
140
        """get owner"""
141
        import uuid
142

    
143
        if self._owner and type(self._owner) is uuid.UUID:
144
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
145
            if matches:
146
                self._owner = matches[0]
147

    
148
        if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
149
            return self._owner
150
        else:
151
            self._owner = None
152
            return None
153

    
154
    '''
155
        @brief  setter owner
156
        @author humkyung
157
        @date   2018.05.10
158
        @history    2018.05.17  Jeongwoo    Add Calling setColor if self._owner is None or not
159
    '''
160

    
161
    @owner.setter
162
    def owner(self, value):
163
        self._owner = value
164

    
165
        if self._owner is None:
166
            self._color = self.DEFAULT_COLOR
167
        self.setColor(self._color)
168

    
169
    '''
170
    @property
171
    def properties(self):
172
        """ getter of properties """
173
        import uuid
174

175
        for prop, value in self._properties.items():
176
            try:
177
                if prop.is_selectable and type(value) is uuid.UUID and self.scene():
178
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(value)]
179
                    if matches: self._properties[prop] = matches[0]
180

181
                if prop.Expression:
182
                    item = self._properties[prop]  # assign item
183
                    self._properties[prop] = eval(prop.Expression)
184
            except Exception as ex:
185
                from App import App
186

187
                message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
188
                                                              sys.exc_info()[-1].tb_lineno)
189
                App.mainWnd().addMessage.emit(MessageType.Error, message)
190

191
        return self._properties
192

193
    def set_property(self, property, value):
194
        """ set property with given value """
195
        if issubclass(type(value), QEngineeringAbstractItem): self.add_assoc_item(value, 0)
196
        matches = [prop for prop, _ in self._properties.items() if prop.Attribute == property]
197
        if matches: self._properties[matches[0]] = value
198

199
    def prop(self, name):
200
        """ return the value of given property with name """
201
        matches = [(prop, value) for prop, value in self.properties.items() if prop.Attribute == name]
202
        if matches: return matches[0][1]
203

204
        return None
205
    '''
206

    
207
    @property
208
    def Size(self):
209
        """ return valve's size """
210
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
211

    
212
        matches = [assoc for assoc in self.associations() if type(assoc) is QEngineeringSizeTextItem]
213
        if matches:
214
            return matches[0].text()
215
        else:
216
            return None
217

    
218
    @property
219
    def EvaluatedSize(self):
220
        from EngineeringReducerItem import QEngineeringReducerItem
221
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
222

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

    
242
                    downstream = matches[0].items[at:]
243
                    prev = self
244
                    for item in downstream:
245
                        if type(item) is QEngineeringReducerItem:
246
                            if item.connectors[0].connectedItem is prev:  ### Main Size
247
                                if item.MainSubSize: return item.MainSubSize[0]
248
                            elif item.connectors[1].connectedItem is prev:  ### Sub Size
249
                                if item.MainSubSize: return item.MainSubSize[1]
250
                        else:
251
                            if item.Size: return item.Size
252
                        prev = item
253

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

    
256
                return self.owner.Size
257

    
258
            return None
259
        except Exception as ex:
260
            from App import App
261
            from AppDocData import MessageType
262

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

    
267
    def EvaluatedLineNo(self, prop):
268
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
269

    
270
        if self.owner and type(self.owner) is QEngineeringLineNoTextItem:
271
            attrs = self.owner.getAttributes()
272
            for attr, value in attrs.items():
273
                if prop == attr.Attribute:
274
                    return value
275

    
276
        return None
277

    
278
    def validate(self):
279
        """validation check"""
280

    
281
        from EngineeringAbstractItem import QEngineeringAbstractItem
282
        from EngineeringLineItem import QEngineeringLineItem
283
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
284
        from EngineeringTextItem import QEngineeringTextItem
285
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
286
        errors = []
287

    
288
        try:
289
            app_doc_data = AppDocData.instance()
290
            dataPath = app_doc_data.getErrorItemSvgPath()
291

    
292
            # validate connectors
293
            for connector in self.connectors:
294
                errors.extend(connector.validate())
295

    
296
            # check if connected two lines has same direction
297
            if len(self.connectors) is 2:
298
                if (type(self.connectors[0].connectedItem) is QEngineeringLineItem and
299
                    self.connectors[0]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT) and \
300
                        (type(self.connectors[1].connectedItem) is QEngineeringLineItem and
301
                         self.connectors[1]._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT):
302
                    indices = [0, 0]
303

    
304
                    center = self.connectors[0].center()
305
                    dx, dy = [0, 0], [0, 0]
306
                    dx[0] = center[0] - self.connectors[0].connectedItem.line().p1().x()
307
                    dy[0] = center[1] - self.connectors[0].connectedItem.line().p1().y()
308
                    dx[1] = center[0] - self.connectors[0].connectedItem.line().p2().x()
309
                    dy[1] = center[1] - self.connectors[0].connectedItem.line().p2().y()
310
                    indices[0] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
311

    
312
                    center = self.connectors[1].center()
313
                    dx[0] = center[0] - self.connectors[1].connectedItem.line().p1().x()
314
                    dy[0] = center[1] - self.connectors[1].connectedItem.line().p1().y()
315
                    dx[1] = center[0] - self.connectors[1].connectedItem.line().p2().x()
316
                    dy[1] = center[1] - self.connectors[1].connectedItem.line().p2().y()
317
                    indices[1] = 1 if (dx[0] * dx[0] + dy[0] * dy[0]) < (dx[1] * dx[1]) + (dy[1] * dy[1]) else 2
318

    
319
                    if indices[0] == indices[1]:
320
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
321
                        error.parent = self
322
                        error.msg = self.tr('flow direction error')
323
                        error.setToolTip(error.msg)
324
                        error.area = 'Drawing'
325
                        error.name = 'Error'
326
                        errors.append(error)
327

    
328
            # check disconnected point
329
            attrs = self.getAttributes()
330
            matches = [(attr, value) for attr, value in attrs.items() if attr.Attribute == 'OWNERSYMBOL']
331
            if matches:
332
                if not matches[0][1]:
333
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
334
                    error.parent = self
335
                    error.msg = 'not combined'
336
                    error.setToolTip(error.msg)
337
                    error.area = 'Drawing'
338
                    error.name = 'Error'
339
                    errors.append(error)
340
            else:
341
                disconnect = False
342
                if len(self.connectors) is not 0:
343
                    disconnect = True
344
                    for connector in self.connectors:
345
                        if connector.connectedItem is not None:
346
                            disconnect = False
347
                            break
348

    
349
                if disconnect:
350
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
351
                    error.parent = self
352
                    error.msg = 'disconnected'
353
                    error.setToolTip(error.msg)
354
                    error.area = 'Drawing'
355
                    error.name = 'Error'
356
                    errors.append(error)
357

    
358
            # check if symbol size if 0
359
            if self.size[0] == 0 or self.size[1] == 0:
360
                error = SymbolSvgItem.createItem('Error', None, dataPath)
361
                error.parent = self
362
                error.msg = self.tr('size error')
363
                error.setToolTip(error.msg)
364
                error.area = 'Drawing'
365
                error.name = 'Error'
366
                errors.append(error)
367

    
368
            # check if association item's owner exists
369
            for assoc in self.associations():
370
                if issubclass(type(assoc), QEngineeringTextItem) and not assoc.owner:
371
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
372
                    error.parent = self
373
                    error.msg = self.tr('association error')
374
                    error.setToolTip(error.msg)
375
                    error.area = self.area
376
                    error.name = 'Error'
377
                    errors.append(error)
378

    
379
            # set error position
380
            for error in errors:
381
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
382

    
383
            connectedUid = []
384
            for connector in self.connectors:
385
                # for duplicattion check
386
                if connector.connectedItem and issubclass(type(connector.connectedItem), QEngineeringAbstractItem):
387
                    connectedUid.append(str(connector.connectedItem.uid))
388

    
389
                if (issubclass(type(connector.connectedItem), SymbolSvgItem) and \
390
                        type(connector.connectedItem) is not QEngineeringEquipmentItem) or \
391
                                type(connector.connectedItem) is QEngineeringLineItem:
392
                    matches = [conn for conn in connector.connectedItem.connectors if conn.connectedItem is self]
393
                    # check if two items are connected each other
394
                    if not matches:
395
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
396
                        error.setPosition(connector.center())
397
                        error.parent = self
398
                        error.msg = self.tr('disconnected from opposite side')
399
                        error.setToolTip(error.msg)
400
                        error.area = self.area
401
                        error.name = 'Error'
402
                        errors.append(error)
403

    
404
            # check duplicated connection
405
            if len(connectedUid) is not len(set(connectedUid)):
406
                error = SymbolSvgItem.createItem('Error', None, dataPath)
407
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
408
                error.parent = self
409
                error.msg = self.tr('duplicated connection')
410
                error.setToolTip(error.msg)
411
                error.area = self.area
412
                error.name = 'Error'
413
                errors.append(error)
414

    
415
            # check line type
416
            if self.conn_type:
417
                for index in range(len(self.conn_type)):
418
                    item = self.connectors[index].connectedItem
419
                    if item and type(item) is QEngineeringLineItem:
420
                        if ((self.conn_type[index] == 'Primary' or self.conn_type[index] == 'Secondary') and not item.is_piping()) or \
421
                                (not (self.conn_type[index] == 'Primary' or self.conn_type[index] == 'Secondary') and item.is_piping(True)):
422
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
423
                            error.setPosition(self.connectors[index].center())
424
                            error.parent = self
425
                            error.msg = self.tr('line type error')
426
                            error.setToolTip(error.msg)
427
                            error.area = self.area
428
                            error.name = 'Error'
429
                            errors.append(error)
430
                            
431
        except Exception as ex:
432
            from App import App
433
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
434
                                                          sys.exc_info()[-1].tb_lineno)
435
            App.mainWnd().addMessage.emit(MessageType.Error, message)
436

    
437
        return errors
438

    
439
    def includes(self, pt, margin=0):
440
        """return True if symbol contains given point else return False"""
441
        rect = self.sceneBoundingRect()
442
        allowed_error = 0.1
443

    
444
        if abs(rect.x() - 0) <= allowed_error and abs(rect.y() - 0) <= allowed_error:
445
            # when first recognition step, symbols are not in scene(not yet added) therefore cannot use scenebounding rect
446
            minX = self.loc[0] - margin
447
            minY = self.loc[1] - margin
448
            maxX = minX + self.size[0] + margin
449
            maxY = minY + self.size[1] + margin
450
        else:
451
            minX = rect.x() - margin
452
            minY = rect.y() - margin
453
            maxX = minX + rect.width() + margin
454
            maxY = minY + rect.height() + margin
455

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

    
458
    '''
459
    def associations(self):
460
        """ return associated instance """
461
        # move to abstractitem
462
        import uuid
463

464
        res = []
465
        for key in self._associations.keys():
466
            index = 0
467
            for assoc in self._associations[key]:
468
                # find owner with uid
469
                if assoc and type(assoc) is uuid.UUID:
470
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(assoc)]
471
                    if matches:
472
                        res.append(matches[0]) # TODO: need to update association with instance
473
                        self._associations[key][index] = matches[0]
474
                    index += 1
475
                # up to here
476
                elif assoc:
477
                    res.append(assoc)
478

479
        for key in self.attrs.keys():
480
            if type(key.AssocItem) is uuid.UUID:
481
                for assoc in res:
482
                    if str(key.AssocItem) == str(assoc.uid):
483
                        key.AssocItem = assoc
484

485
        return res
486

487
    def texts(self):
488
        """ return text type of associations """
489
        from EngineeringTextItem import QEngineeringTextItem
490

491
        res = []
492
        for text in [x for x in self.associations() if issubclass(type(x), QEngineeringTextItem)]:
493
            consumed = False
494
            for key in list(self.attrs.keys()):
495
                if key.AssocItem and key.AssocItem is text:
496
                    consumed = True
497
            if not consumed:
498
                res.append(text)
499

500
        return res
501

502
    def symbols(self):
503
        """ return symbol type of associations """
504
        res = []
505
        for symbol in [x for x in self.associations() if issubclass(type(x), SymbolSvgItem)]:
506
            consumed = False
507
            for key in list(self.attrs.keys()):
508
                if key.AssocItem and key.AssocItem is symbol:
509
                    consumed = True
510
            if not consumed:
511
                res.append(symbol)
512

513
        return res
514
    '''
515

    
516
    def itemChange(self, change, value):
517
        """ call signals when item's position or rotation is changed """
518
        if not self.scene(): return super().itemChange(change, value)
519

    
520
        if change == QGraphicsItem.ItemPositionHasChanged or change == QGraphicsItem.ItemRotationChange:
521
            from EngineeringLineItem import QEngineeringLineItem
522
            for connector in self.connectors:
523
                if connector.connectedItem is not None and type(connector.connectedItem) == QEngineeringLineItem:
524
                    line = connector.connectedItem
525
                    line.reDrawLine(self, connector.center())
526
                    line.update_arrow()
527

    
528
            self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(
529
                self.sceneBoundingRect().height())
530

    
531
            self.scene().contents_changed.emit()
532

    
533
            return value
534

    
535
        return super().itemChange(change, value)
536

    
537
    def toSql_Components(self):
538
        """ convert Components data to sql query """
539
        from AppDocData import AppDocData
540

    
541
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner',
542
                'Connected', '[Supplied By]', \
543
                'SpecialItemTypes_UID', 'OriginIndex', '[From]', '[To]', '[Freeze]', '[Connected Item]', '[Flip]', 'SceneOriginPoint']
544
        values = ['?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?']
545
        param = [(str(self.uid), str(AppDocData.instance().activeDrawing.UID), self.dbUid, self.loc[0], self.loc[1],
546
                  self.size[0], self.size[1], self.angle,
547
                  self.area, str(self.owner) if self.owner else None, \
548
                  str(self.conns[0]) if self.conns else None, \
549
                  self.prop('Supplied By'), \
550
                  str(self.special_item_type) if self.special_item_type else None, \
551
                  self.currentPointModeIndex, \
552
                  None, \
553
                  None, \
554
                  self.prop('Freeze') if self.prop('Freeze') else 0, \
555
                  str(self.prop('Connected Item')) if self.prop('Connected Item') else None, \
556
                  self.flip, \
557
                  '{},{}'.format(self.origin[0], self.origin[1]))]
558
        sql = 'insert into Components({}) values({})'.format(','.join(cols), ','.join(values))
559

    
560
        return (sql, tuple(param))
561

    
562
    def toSql_return_separately(self):
563
        """ convert valve data to sql query """
564
        import uuid
565

    
566
        res = []
567
        resLater = []
568

    
569
        res.append(self.toSql_Components())
570

    
571
        _attrs = self.getAttributes()
572
        if _attrs:
573
            cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value', 'Association_UID', 'Freeze']
574
            values = ['?', '?', '?', '?', '?', '?']
575
            params = []
576
            for key in _attrs.keys():
577
                if key.AttributeType != 'Spec':
578
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), str(_attrs[key]), str(key.AssocItem),
579
                                   str(key.Freeze)))
580
                elif key.AttributeType == 'Spec':
581
                    if type(_attrs[key]) is not list: continue
582
                    params.append((str(uuid.uuid4()), str(self.uid), str(key.UID), (str(_attrs[key][0]) + ',' + str(_attrs[key][1])), str(key.AssocItem),
583
                                   str(key.Freeze)))
584
            sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
585
            res.append((sql, tuple(params)))
586

    
587
        if self.associations():
588
            cols = ['UID', '[Type]', 'Components_UID', 'Association']
589
            values = ['?', '?', '?', '?']
590
            params = []
591
            for assoc in self.associations():
592
                params.append(
593
                    (str(uuid.uuid4()), QEngineeringAbstractItem.assoc_type(assoc), str(self.uid), str(assoc.uid)))
594
            sql = 'insert into Associations({}) values({})'.format(','.join(cols), ','.join(values))
595
            resLater.append((sql, tuple(params)))
596

    
597
        # save connectors to database
598
        if self.connectors:
599
            cols = ['Components_UID', '[Index]', 'X', 'Y', 'Connected', 'Connected_At']
600
            values = ['?', '?', '?', '?', '?', '?']
601
            params = []
602
            index = 1
603
            for connector in self.connectors:
604
                params.append( \
605
                    (  # str(connector.uid),
606
                        str(self.uid), index, connector.center()[0], connector.center()[1], \
607
                        str(connector.connectedItem.uid) if connector.connectedItem else None, \
608
                        str(connector._connected_at)) \
609
                    )
610
                index += 1
611
            sql = 'insert into Points({}) values({})'.format(','.join(cols), ','.join(values))
612
            resLater.append((sql, tuple(params)))
613
        # up to here
614

    
615
        return res, resLater
616

    
617
    '''
618
        @brief  build symbol item
619
        @author humkyung
620
        @date   2018.05.02
621
        @history    2018.05.09  Jeongwoo    Clear self.connectors
622
                    2018.05.30  Jeongwoo    Add parameters (parentSymbol, childSymbol)
623
    '''
624

    
625
    def buildItem(self, name, _type, angle, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel,
626
                  dbUid=None):
627
        from SpecialItemTypesDialog import SpecialItemTypes
628

    
629
        try:
630
            app_doc_data = AppDocData.instance()
631
            self.name = name
632
            self.type = _type
633
            self.angle = angle
634
            self.loc = loc
635
            self.size = size if size else [0, 0]
636
            self.origin = origin
637
            if dbUid is None:
638
                symbolInfo = app_doc_data.getSymbolByQuery('name', name)
639
            else:
640
                symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
641
            self.dbUid = symbolInfo.uid
642
            self.iType = symbolInfo.iType
643
            self.text_area = symbolInfo.text_area
644
            self._class = symbolInfo.baseSymbol
645
            originalPoint = symbolInfo.getOriginalPoint().split(',')
646
            self.symbolOrigin = [float(originalPoint[0]), float(originalPoint[1])]
647

    
648
            # setting connectors
649
            connectionPoints = symbolInfo.getConnectionPoint().split('/')
650
            for index in range(len(connectionPoints)):
651
                if connectionPoints[index] == '':
652
                    break
653
                tokens = connectionPoints[index].split(',')
654

    
655
                direction = 'AUTO'
656
                symbol_idx = '0'
657
                if len(tokens) == 2:
658
                    x = float(tokens[0])
659
                    y = float(tokens[1])
660
                elif len(tokens) >= 3:
661
                    direction = connPts[index][0] if index < len(connPts) else tokens[0]
662
                    x = float(tokens[1])
663
                    y = float(tokens[2])
664
                if len(tokens) >= 4:
665
                    symbol_idx = tokens[3]
666
                if len(tokens) >= 6:
667
                    if tokens[4] == 'In':
668
                        self.in_out_connector[0].append(index)
669
                    elif tokens[4] == 'Out':
670
                        self.in_out_connector[1].append(index)
671
                    if tokens[5] == 'O':
672
                        self.break_connector.append(index)
673
                if len(tokens) >= 7:
674
                    self.conn_type.append(tokens[6])
675

    
676
                self.setConnector(index + 1)
677
                self.connectors[index].direction = direction
678
                self.connectors[index].symbol_idx = symbol_idx
679
                self.connectors[index].setPos((x, y))
680
                self.connectors[index].connectPoint = (x, y)
681
                # recognized_pt is only valid right after symbol is recognized
682
                self.connectors[index].recognized_pt = (connPts[index][0], connPts[index][1]) if \
683
                    len(connPts[index]) == 2 else (connPts[index][1], connPts[index][2]) if \
684
                    len(connPts[index]) == 3 else (connPts[index][1], connPts[index][2]) if \
685
                    len(connPts[index]) == 4 else None
686
                # up to here
687
            self.parentSymbol = parentSymbol
688
            self.childSymbol = childSymbol
689
            self.hasInstrumentLabel = hasInstrumentLabel
690
            self.currentPointModeIndex = 0
691
            self.special_item_type = SpecialItemTypes.instance().find_match_exactly(self)
692

    
693
            tooltip = f"<b>{str(self.uid)}</b><br>{self.type}={self.name}"
694
            if self.hit_ratio:
695
                tooltip += f"<br><li>recognition ratio={self.hit_ratio}"
696
            self.setToolTip(tooltip)
697

    
698
            return True
699
        except Exception as ex:
700
            from App import App
701

    
702
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
703
                                                          sys.exc_info()[-1].tb_lineno)
704
            App.mainWnd().addMessage.emit(MessageType.Error, message)
705

    
706
            return False
707

    
708
    '''
709
        @brief  return bounding box of symbol
710
        @author humkyung
711
        @date   2018.04.08
712
    '''
713

    
714
    def rect(self):
715
        return self.sceneBoundingRect()
716

    
717
    def is_connectable(self, item, toler=10):
718
        """return true if line is able to connect symbol"""
719

    
720
        for connector in self.connectors:
721
            for iConnector in item.connectors:
722
                dx = connector.center()[0] - iConnector.center()[0]
723
                dy = connector.center()[1] - iConnector.center()[1]
724
                if math.sqrt(dx * dx + dy * dy) < toler:
725
                    return True
726

    
727
        return False
728

    
729
    '''
730
        @author     humkyung
731
        @date       2018.07.03
732
    '''
733

    
734
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
735
        """ check if given item is connected to self """
736

    
737
        _connectors = [connector for connector in self.connectors if
738
                       (connector.connectedItem == item and (connector._connected_at == at if at else True))]
739
        return len(_connectors) > 0
740

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

    
744
        lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
745
        rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
746
        if lhs_matches and rhs_matches:
747
            return (lhs_matches[0] in [0, 1] and rhs_matches[0] in [0, 1]) or (
748
                    lhs_matches[0] in [2, 3] and rhs_matches[0] in [2, 3])
749

    
750
        return False
751

    
752
    def canBeSecondary(self, line):
753
        """ check given line is not connected(ex: 0-1, 2-3) """
754
        preItem = None
755

    
756
        item = [item.connectedItem for item in self.connectors if
757
                item.connectedItem is not None and item.connectedItem.owner is not None]
758
        if item:
759
            preItem = item[0]
760

    
761
        if preItem is not None and not self.next_connected(line, preItem):
762
            return True
763
        else:
764
            return False
765

    
766
    '''
767
        @brief      connect line and symbol is able to be connected and return line
768
        @author     humkyung
769
        @date       2018.04.16
770
        @history    humkyung 2018.05.08 check if symbol is possible to be connected
771
                    Jeongwoo 2018.05.15 Connect each symbol and line
772
    '''
773

    
774
    def connect_if_possible(self, obj, toler=10):
775
        """ this method not update item's position """
776

    
777
        from shapely.geometry import Point
778
        from EngineeringLineItem import QEngineeringLineItem
779

    
780
        res = []
781
        try:
782
            if type(obj) is QEngineeringLineItem:
783
                startPt = obj.start_point()
784
                endPt = obj.end_point()
785
                for i in range(len(self.connectors)):
786
                    if (Point(startPt[0], startPt[1]).distance(Point(self.connectors[i].center()[0],
787
                                                                     self.connectors[i].center()[1])) < toler):
788
                        if self.connectors[i].connectedItem is None and obj.connectors[0].connectedItem is None:
789
                            self.connectors[i].connect(obj)
790
                            obj.connectors[0].connect(self)
791
                            # line, start, end
792
                            res.append(obj)
793
                            res.append(self.connectors[i].center())
794
                            res.append(endPt)
795
                    if (Point(endPt[0], endPt[1]).distance(Point(self.connectors[i].center()[0],
796
                                                                 self.connectors[i].center()[1])) < toler):
797
                        if self.connectors[i].connectedItem is None and obj.connectors[1].connectedItem is None:
798
                            self.connectors[i].connect(obj)
799
                            obj.connectors[1].connect(self)
800
                            # line, start, end
801
                            res.append(obj)
802
                            res.append(startPt)
803
                            res.append(self.connectors[i].center())
804
            elif issubclass(type(obj), SymbolSvgItem):
805
                for i in range(len(self.connectors)):
806
                    if i > 3: break
807
                    for j in range(len(obj.connectors)):
808
                        if j > 3: break
809
                        _pt = Point(obj.connectors[j].center()[0], obj.connectors[j].center()[1])
810
                        if (_pt.distance(Point(self.connectors[i].center()[0],
811
                                               self.connectors[i].center()[1])) < toler):
812
                            if self.connectors[i].connectedItem is None:
813
                                self.connectors[i].connect(obj)
814
                            if obj.connectors[j].connectedItem is None:
815
                                obj.connectors[j].connect(self)
816

    
817
                            res.append(obj)
818
        except Exception as ex:
819
            from App import App
820
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
821
                                                          sys.exc_info()[-1].tb_lineno)
822
            App.mainWnd().addMessage.emit(MessageType.Error, message)
823

    
824
        return res
825

    
826
    '''
827
        @brief      disconnect connector item
828
        @author     kyouho
829
        @date       2018.08.30
830
    '''
831

    
832
    def disconnectedItemAtConnector(self, connector):
833
        for conn in self.connectors:
834
            if conn.isOverlapConnector(connector):
835
                conn.connectedItem = None
836

    
837
    '''
838
        @brief  get connection point close to given point in tolerance
839
        @author humkyung
840
        @dat
841
    '''
842

    
843
    def getConnectionPointCloseTo(self, pt, toler=10):
844
        import math
845

    
846
        for connector in self.connectors:
847
            dx = connector.center()[0] - pt[0]
848
            dy = connector.center()[1] - pt[1]
849

    
850
            if math.sqrt(dx * dx + dy * dy) < toler:
851
                return connPt
852

    
853
        return None
854

    
855
    '''
856
        @brief  return center of symbol
857
        @author humkyung
858
        @date   2018.04.08
859
    '''
860

    
861
    def center(self):
862
        return self.sceneBoundingRect().center()
863

    
864
    '''
865
        @brief      highlight connector and attribute
866
        @authro     humkyung
867
        @date       2018.05.02
868
    '''
869

    
870
    def hoverEnterEvent(self, event):
871
        self.highlight(True)
872

    
873
    '''
874
        @brief      unhighlight connector and attribute
875
        @author     humkyung
876
        @date       2018.05.02
877
        @history    kyouho 2018.07.18 edit ArrowCursor
878
    '''
879

    
880
    def hoverLeaveEvent(self, event):
881
        self.highlight(False)
882

    
883
    def highlight(self, flag):
884
        """ highlight/unhighlight the symbol """
885

    
886
        try:
887
            self.hover = flag
888
            self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
889
            self.update()
890

    
891
            for assoc in self.associations():
892
                assoc.highlight(flag)
893

    
894
            for connector in self.connectors:
895
                connector.highlight(flag)
896
        except Exception as ex:
897
            from App import App
898
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
899
                                                          sys.exc_info()[-1].tb_lineno)
900
            App.mainWnd().addMessage.emit(MessageType.Error, message)
901

    
902
    '''
903
        @brief      set highlight
904
        @author     kyouho
905
        @date       2018.08.27
906
    '''
907

    
908
    def setHightlight(self):
909
        self.setColor('url(#hover)')
910
        self.update()
911

    
912
    '''
913
        @brief      unset highlight
914
        @author     kyouho
915
        @date       2018.08.27
916
    '''
917

    
918
    def unsetHightlight(self):
919
        self.setColor('url(#normal)')
920
        self.update()
921

    
922
    '''
923
        @brief  change cursor to CrossCursor if mouse point is close to connection point
924
        @author humkyung
925
        @date   2018.04.28
926
    '''
927

    
928
    def hoverMoveEvent(self, event):
929
        pass
930

    
931
    '''
932
        @brief      Mouse Press Event
933
        @author     Jeongwoo
934
        @date       18.04.11
935
        @history    kyouho 2018.07.18 add isClick logic
936
    '''
937

    
938
    def mousePressEvent(self, event):
939
        if event.buttons() == Qt.LeftButton:
940
            self.clicked.emit(self)
941

    
942
    '''
943
        @brief      Mouse Release Event
944
        @author     kyouho
945
        @date       18.07.17
946
    '''
947

    
948
    def mouseReleaseEvent(self, event):
949
        if hasattr(self, '_rotating') and event.button() == Qt.RightButton:
950
            self.angle = -self._angle if self._angle > -math.pi and self._angle < 0 else 2 * math.pi - self._angle
951
            self.ungrabMouse()
952
            del self._rotating
953

    
954
        super().mouseReleaseEvent(event)
955

    
956
    def mouseMoveEvent(self, event):
957
        """ rotate symbol accroding to current mouse point """
958
        if hasattr(self, '_rotating'):
959
            # get origin point of symbol
960
            origin = self.origin  # self.sceneBoundingRect().center()
961
            # up to here
962

    
963
            dx = (event.scenePos().x() - origin[0])
964
            dy = (event.scenePos().y() - origin[1])
965
            length = math.sqrt(dx * dx + dy * dy)
966

    
967
            self._angle = 0
968
            if length > 0:
969
                self._angle = math.acos(dx / length)
970
                cross = int(np.cross([1, 0], [dx, dy]))
971
                self._angle = -self._angle if cross < 0 else self._angle
972

    
973
                self.rotate(self.getCurrentPoint(), -self._angle)
974

    
975
    def removeSelfAttr(self, attributeName):
976
        target = None
977
        for attr in self.attrs:
978
            if attr.Attribute == attributeName:
979
                target = attr
980
                break
981

    
982
        if target:
983
            del self.attrs[attr]
984

    
985
    '''
986
        @brief      Find TextItem contain Point
987
        @author     kyouho
988
        @date       18.07.17
989
    '''
990

    
991
    def findTextItemInPoint(self, point):
992
        from EngineeringTextItem import QEngineeringTextItem
993

    
994
        scene = self.scene()
995

    
996
        for item in scene.items():
997
            if type(item) is QEngineeringTextItem:
998
                if self.isOverlapItemAndPoint(item, point):
999
                    return (True, item)
1000

    
1001
        return (False,)
1002

    
1003
    '''
1004
        @brief      Check Overlap
1005
        @author     kyouho
1006
        @date       18.07.17
1007
    '''
1008

    
1009
    def isOverlapItemAndPoint(self, item, point):
1010
        x = point.x()
1011
        y = point.y()
1012
        loc = item.loc
1013
        size = item.size
1014

    
1015
        if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
1016
            return True
1017
        else:
1018
            return False
1019

    
1020
    '''
1021
        @brief  remove item when user press delete key
1022
        @author humkyung
1023
        @date   2018.04.23
1024
        @history    2018.05.17  Jeongwoo    Add if-statement and move 'break'
1025
                    2018.05.25  Jeongwoo    Seperate delete item method
1026
    '''
1027
    def keyPressEvent(self, event):
1028
        from EngineeringErrorItem import QEngineeringErrorItem
1029
        from RotateSymbolDialog import QRotateSymbolDialog
1030

    
1031
        if not self.isSelected():
1032
            return
1033
        if event.key() == Qt.Key_B:
1034
            self.bind_close_items()
1035
        elif event.key() == Qt.Key_R and type(self) is not QEngineeringErrorItem:
1036
            self.rotateSymbol()
1037
        elif event.key() == Qt.Key_O and type(self) is not QEngineeringErrorItem:
1038
            pass
1039
        elif event.key() == Qt.Key_C and type(self) is not QEngineeringErrorItem:
1040
            self.changeConnPoint()
1041
        elif event.key() == Qt.Key_F and type(self) is not QEngineeringErrorItem:
1042
            self.flipSymbol()
1043
        elif event.key() == Qt.Key_Return:
1044
            dialog = QRotateSymbolDialog(None, self.angle, self.origin, self.zValue())
1045
            (isAccept, angle, x, y, z) = dialog.showDialog()
1046

    
1047
            if isAccept:
1048
                self.angle = angle
1049
                self.loc = [x - self.symbolOrigin[0], y - self.symbolOrigin[1]]
1050
                self.origin = [x, y]
1051
                self.rotate(self.getCurrentPoint(), self.angle)
1052
                self.setZValue(z)
1053
        elif event.key() == Qt.Key_Escape:
1054
            if hasattr(self, '_rotating'):
1055
                self.ungrabMouse()
1056

    
1057
                transform = QTransform()
1058
                transform.translate((self.loc[0] + self.symbolOrigin[0]), (self.loc[1] + self.symbolOrigin[1]))
1059
                transform.rotateRadians(self.angle)
1060
                transform.translate(-self.symbolOrigin[0], -self.symbolOrigin[1])
1061
                self.setTransform(transform)
1062

    
1063
                del self._rotating
1064
        elif event.key() == Qt.Key_Up:  # translate up/down/left/right symbol
1065
            modifiers = QApplication.keyboardModifiers()
1066
            delta = 10 if modifiers == Qt.ControlModifier else 1
1067

    
1068
            self.loc[1] = self.loc[1] - delta
1069
            self.origin[1] = self.origin[1] - delta
1070
            self.rotate(self.getCurrentPoint(), self.angle)
1071
        elif event.key() == Qt.Key_Down:
1072
            modifiers = QApplication.keyboardModifiers()
1073
            delta = 10 if modifiers == Qt.ControlModifier else 1
1074

    
1075
            self.loc[1] = self.loc[1] + delta
1076
            self.origin[1] = self.origin[1] + delta
1077
            self.rotate(self.getCurrentPoint(), self.angle)
1078
        elif event.key() == Qt.Key_Left:
1079
            modifiers = QApplication.keyboardModifiers()
1080
            delta = 10 if modifiers == Qt.ControlModifier else 1
1081

    
1082
            self.loc[0] = self.loc[0] - delta
1083
            self.origin[0] = self.origin[0] - delta
1084
            self.rotate(self.getCurrentPoint(), self.angle)
1085
        elif event.key() == Qt.Key_Right:
1086
            modifiers = QApplication.keyboardModifiers()
1087
            delta = 10 if modifiers == Qt.ControlModifier else 1
1088

    
1089
            self.loc[0] = self.loc[0] + delta
1090
            self.origin[0] = self.origin[0] + delta
1091
            self.rotate(self.getCurrentPoint(), self.angle)
1092
        elif event.key() == Qt.Key_I or event.key() == Qt.Key_X:
1093
            from App import App 
1094
            App.mainWnd().keyPressEvent(event)
1095

    
1096
    def bind_close_items(self):
1097
        """ connect close item by pressing B """
1098
        from EngineeringLineItem import QEngineeringLineItem
1099

    
1100
        scene = self.scene()
1101
        if scene:
1102
            configs = AppDocData.instance().getConfigs('Line Detector', 'Length to connect line')
1103
            toler = int(configs[0].value) if configs else 20
1104
            for item in [item for item in scene.items() if hasattr(item, 'connectors')]:
1105
                if item is not self:
1106
                    res = self.connect_if_possible(item, toler)
1107
                    if res and type(item) is QEngineeringLineItem:
1108
                        item.set_line([res[1], res[2]])
1109

    
1110
    '''
1111
        @brief      connect attribute
1112
        @author     humkyung
1113
        @date       2018.05.02
1114
        @history    humkyung 2018.05.09 append only nearest size attribute
1115
    '''
1116
    def connectAttribute(self, attributes, clear=True):
1117
        import math
1118
        from EngineeringTextItem import QEngineeringTextItem
1119
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1120
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1121
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1122

    
1123
        try:
1124
            if clear:
1125
                if not self.clear_attr_and_assoc_item():
1126
                    return
1127

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

    
1131
            rect = self.sceneBoundingRect()
1132
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
1133
            center = self.sceneBoundingRect().center()
1134

    
1135
            minDist = None
1136
            selected = None
1137
            for attr in attributes:
1138
                # size text and operation code text will find owner themselves in findowner method
1139
                if False:  # type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
1140
                    dx = attr.center().x() - center.x()
1141
                    dy = attr.center().y() - center.y()
1142
                    length = math.sqrt(dx * dx + dy * dy)
1143
                    if (length < dist) and (minDist is None or length < minDist):
1144
                        minDist = length
1145
                        selected = attr
1146
                elif False:#type(attr) is QEngineeringInstrumentItem:
1147
                    if not attr.is_connected():
1148
                        dx = attr.center().x() - center.x()
1149
                        dy = attr.center().y() - center.y()
1150
                        if math.sqrt(dx * dx + dy * dy) < dist:
1151
                            if self.add_assoc_item(attr):
1152
                                attr.owner = self
1153
                elif issubclass(type(attr), QEngineeringTextItem):
1154
                    if rect.contains(attr.center()):
1155
                        if self.add_assoc_item(attr):
1156
                            attr.owner = self  # set owner of text
1157

    
1158
            if selected is not None:
1159
                if self.add_assoc_item(selected):
1160
                    selected.owner = self
1161

    
1162
        except Exception as ex:
1163
            from App import App
1164
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1165
                                                          sys.exc_info()[-1].tb_lineno)
1166
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1167

    
1168
    '''
1169
        @brief      start rotating
1170
        @author     euisung
1171
        @date       2019.04.16
1172
    '''
1173

    
1174
    def mouseDoubleClickEvent(self, event):
1175
        if not hasattr(self, '_rotating'):
1176
            self._rotating = True
1177
            self.grabMouse()
1178

    
1179
    '''
1180
        @brief      generate xml code
1181
        @author     humkyung
1182
        @date       2018.04.23
1183
        @history    humkyung 2018.04.25 add angle xml node
1184
                    humkyung 2018.04.27 add originalpoint xml node
1185
                    humkyung 2018.05.02 add attribute of symbol
1186
                    Jeongwoo 2018.05.30 add attribute of symbol (parentSymbol, childSymbol, type, connectionPoint)
1187
                    yecheol  2018.07.11 add attribute of symbol (hasInstrumentLabel)
1188
                    humkyung 2018.07.20 write owner's uid to xml
1189
                    humkyung 2018.07.23 write connected item's uid to xml
1190
                    kyouho  2018.07.31 
1191
                    humkyung 2018.09.06 write area to xml
1192
    '''
1193

    
1194
    def toXml(self):
1195
        import uuid
1196
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1197
        from EngineeringAbstractItem import QEngineeringAbstractItem
1198
        from EngineeringTextItem import QEngineeringTextItem
1199
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1200
        from SymbolAttr import SymbolAttr
1201

    
1202
        try:
1203
            node = Element('SYMBOL')
1204
            uidNode = Element('UID')
1205
            uidNode.text = str(self.uid)
1206
            node.append(uidNode)
1207

    
1208
            dbUidNode = Element('DBUID')
1209
            dbUidNode.text = str(self.dbUid)
1210
            node.append(dbUidNode)
1211

    
1212
            nameNode = Element('NAME')
1213
            nameNode.text = self.name
1214
            node.append(nameNode)
1215

    
1216
            attributeValueNode = Element('ASSOCIATIONS')
1217
            for key, value in self._associations.items():
1218
                for assoc in value:
1219
                    assoc_node = Element('ASSOCIATION')
1220
                    assoc_node.attrib['TYPE'] = str(key)
1221
                    assoc_node.text = str(assoc)
1222
                    attributeValueNode.append(assoc_node)
1223
            node.append(attributeValueNode)
1224

    
1225
            typeNode = Element('TYPE')
1226
            typeNode.text = self.type
1227
            node.append(typeNode)
1228

    
1229
            # write owner's uid to xml
1230
            ownerNode = Element('OWNER')
1231
            if self.owner is not None:
1232
                ownerNode.text = str(self.owner)
1233
            else:
1234
                ownerNode.text = 'None'
1235
            node.append(ownerNode)
1236
            # up to here
1237

    
1238
            originNode = Element('ORIGINALPOINT')
1239
            originNode.text = '{},{}'.format(self.origin[0], self.origin[1])
1240
            node.append(originNode)
1241

    
1242
            connectorsNode = Element('CONNECTORS')
1243
            for connector in self.connectors:
1244
                connectorsNode.append(connector.toXml())
1245
            node.append(connectorsNode)
1246

    
1247
            connectionNode = Element('CONNECTIONPOINT')
1248
            connection_point = []
1249
            if self.connectors is not None:
1250
                for connector in self.connectors:
1251
                    connection_point.append(repr(connector))
1252
            connectionNode.text = '/'.join(connection_point)
1253
            node.append(connectionNode)
1254

    
1255
            locNode = Element('LOCATION')
1256
            locNode.text = '{},{}'.format(self.loc[0], self.loc[1])
1257
            '''
1258
            # calculate symbol's left-top corner
1259
            transform = QTransform()
1260
            transform.translate(self.scenePos().x(), self.scenePos().y())
1261
            transform.rotateRadians(-self.angle)
1262
            loc = transform.map(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
1263
            # up to here
1264
            locNode.text = '{},{}'.format(loc.x() - self.symbolOrigin[0], loc.y() - self.symbolOrigin[1])
1265
            '''
1266
            node.append(locNode)
1267

    
1268
            sizeNode = Element('SIZE')
1269
            sizeNode.text = '{},{}'.format(self.size[0], self.size[1])
1270
            node.append(sizeNode)
1271

    
1272
            angleNode = Element('ANGLE')
1273
            angleNode.text = str(self.angle)
1274
            node.append(angleNode)
1275

    
1276
            parentSymbolNode = Element('PARENT')
1277
            parentSymbolNode.text = str(self.parentSymbol)
1278
            node.append(parentSymbolNode)
1279

    
1280
            childSymbolNode = Element('CHILD')
1281
            childSymbolNode.text = str(self.childSymbol)
1282
            node.append(childSymbolNode)
1283

    
1284
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
1285
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
1286
            node.append(hasInstrumentLabelNode)
1287

    
1288
            areaNode = Element('AREA')
1289
            areaNode.text = self.area
1290
            node.append(areaNode)
1291

    
1292
            flipNode = Element('FLIP')
1293
            flipNode.text = str(self.flip)
1294
            node.append(flipNode)
1295

    
1296
            properties_node = Element('PROPERTIES')
1297
            for prop, value in self.properties.items():
1298
                prop_node = prop.toXml()
1299
                prop_node.text = str(value) if value else ''
1300
                properties_node.append(prop_node)
1301
            node.append(properties_node)
1302

    
1303
            attributesNode = Element('SYMBOLATTRIBUTES')
1304
            _attrs = self.getAttributes()
1305
            for attr in _attrs:
1306
                if type(attr) is SymbolAttr:
1307
                    _node = attr.toXml()
1308
                    if attr.AttributeType != 'Spec':
1309
                        _node.text = str(_attrs[attr])
1310
                    elif attr.AttributeType == 'Spec':
1311
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
1312
                    attributesNode.append(_node)
1313

    
1314
            node.append(attributesNode)
1315

    
1316
            currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
1317
            currentPointModeIndexNode.text = str(self.currentPointModeIndex)
1318
            node.append(currentPointModeIndexNode)
1319
        except Exception as ex:
1320
            from App import App
1321
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1322
                                                          sys.exc_info()[-1].tb_lineno)
1323
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1324

    
1325
            return None
1326

    
1327
        return node
1328

    
1329
    @staticmethod
1330
    def from_database(component):
1331
        """ create a item related to given component from database """
1332
        import uuid
1333
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1334
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1335
        from SymbolAttr import SymbolAttr
1336
        item = None
1337

    
1338
        try:
1339
            app_doc_data = AppDocData.instance()
1340

    
1341
            uid = component['UID']
1342
            pt = [float(component['X']), float(component['Y'])]
1343
            size = [float(component['Width']), float(component['Height'])]
1344

    
1345
            dbUid = int(component['Symbol_UID'])
1346
            dbData = app_doc_data.getSymbolByQuery('UID', dbUid)
1347
            name = dbData.sName
1348
            _type = dbData.sType
1349
            angle = float(component['Rotation'])
1350
            origin = [float(x) for x in component['SceneOriginPoint'].split(',')] if component['SceneOriginPoint'] is not None else pt
1351
            connPts = []
1352
            if component['ConnectionPoint']:
1353
                if dbData:
1354
                    db_conn = dbData.connectionPoint.split('/')
1355
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
1356
                index = 0
1357
                for conn_pt in component['ConnectionPoint'].split('/'):
1358
                    tokens = conn_pt.split(',')
1359
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
1360
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
1361
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3] if dbData is None else db_symbol_num[index]))
1362
                    index += 1
1363

    
1364
            baseSymbol = dbData.baseSymbol
1365

    
1366
            childSymbolNode = component['AdditionalSymbol']
1367
            childSymbol = childSymbolNode if childSymbolNode is not None else ''
1368

    
1369
            owner = component['Owner'] if component['Owner'] is not None and component['Owner'] != 'None' else None
1370

    
1371
            hasInstrumentLabel = dbData.hasInstrumentLabel
1372

    
1373
            flipLabelNode = component['Flip']
1374
            flipLabel = int(flipLabelNode) if flipLabelNode is not None else 0
1375

    
1376
            project = app_doc_data.getCurrentProject()
1377
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
1378
            if os.path.isfile(svgFilePath):
1379
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
1380
                item.setVisible(False)
1381

    
1382
                # if additional symbol was changed, change symbol info
1383
                symbolInfo = None
1384
                if dbUid is None:
1385
                    symbolInfo = app_doc_data.getSymbolByQuery('name', name)
1386
                else:
1387
                    symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
1388
                if symbolInfo:
1389
                    childSymbol = symbolInfo.additionalSymbol
1390

    
1391
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
1392
                                  hasInstrumentLabel, dbUid=dbUid):
1393
                    pass
1394
                else:
1395
                    return None
1396

    
1397
                for key in item._properties.keys():
1398
                    for compo in component.keys():
1399
                        if key.Attribute == compo:
1400
                            item._properties[key] = key.parse_value(component[key.Attribute]) if component[
1401
                                key.Attribute] else ''
1402

    
1403
                ## assign area
1404
                areaNode = component['Area']
1405
                if areaNode is None:
1406
                    for area in app_doc_data.getAreaList():
1407
                        if area.contains(pt):
1408
                            item.area = area.name
1409
                            break
1410
                else:
1411
                    item.area = areaNode
1412
                ## up to here
1413

    
1414
                connectors = app_doc_data.get_component_connectors(uid)
1415
                if connectors:
1416
                    iterIndex = 0
1417
                    for connector in connectors:
1418
                        item.connectors[iterIndex].parse_record(connector)
1419
                        iterIndex += 1
1420

    
1421
                # get associations 
1422
                associations = app_doc_data.get_component_associations(uid)
1423
                if associations:
1424
                    for assoc in associations:
1425
                        _attrType = assoc['Type']
1426
                        if not _attrType in item._associations:
1427
                            item._associations[_attrType] = []
1428
                        item._associations[_attrType].append(
1429
                            uuid.UUID(assoc['Association']) if assoc['Association'] != 'None' else None)
1430
                # up to here
1431

    
1432
                attributes = app_doc_data.get_component_attributes(uid)
1433
                if attributes:
1434
                    for attr in attributes:
1435
                        _attr = SymbolAttr.from_record(attr)
1436
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
1437
                            item.attrs[_attr] = attr['Value']
1438
                        else:
1439
                            if _attr.AttributeType == 'Spec':
1440
                                item.attrs[_attr] = [attr['Value'].split(',')[0], attr['Value'].split(',')[1]]
1441
                            else:
1442
                                item.attrs[_attr] = attr['Value']
1443
                            '''
1444
                            elif _attr.Attribute == 'UpStream':
1445
                                for initKey in item.attrs.keys():
1446
                                    if initKey.Attribute == 'UpStream':
1447
                                        item.attrs[initKey] = attr['Value']
1448
                            elif _attr.Attribute == 'DownStream':
1449
                                for initKey in item.attrs.keys():
1450
                                    if initKey.Attribute == 'DownStream':
1451
                                        item.attrs[initKey] = attr['Value']
1452
                            '''
1453

    
1454
                currentPointModeIndex = component['OriginIndex']
1455
                if currentPointModeIndex is not None:
1456
                    item.currentPointModeIndex = int(currentPointModeIndex)
1457
        except Exception as ex:
1458
            from App import App
1459
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1460
                                                          sys.exc_info()[-1].tb_lineno)
1461
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1462

    
1463
            return None
1464

    
1465
        return item
1466

    
1467
    '''
1468
        @brief      parse xml code
1469
        @author     humkyung
1470
        @date       2018.07.20
1471
        @history    humkyung 2018.07.20 parse uid from xml node
1472
                    humkyung 2018.07.23 parse connected item's uid from xml node
1473
                    kyouho  2018.07.31 
1474
                    humkyung 2018.09.06 assign area to item
1475
    '''
1476

    
1477
    @staticmethod
1478
    def fromXml(node):
1479
        import uuid
1480
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1481
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1482
        from SymbolAttr import SymbolAttr
1483
        item = None
1484

    
1485
        try:
1486
            appDocData = AppDocData.instance()
1487

    
1488
            uidNode = node.find('UID')
1489
            uid = uidNode.text if uidNode is not None else uuid.uuid4()  # generate UUID
1490

    
1491
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
1492
            size = [float(x) for x in node.find('SIZE').text.split(',')]
1493

    
1494
            dbUidNode = node.find('DBUID')
1495
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
1496
            dbData = None
1497
            if dbUid:
1498
                dbData = appDocData.getSymbolByQuery('UID', dbUid)
1499
            name = node.find('NAME').text if dbData is None else dbData.sName
1500

    
1501
            angle = float(node.find('ANGLE').text)
1502
            _type = node.find('TYPE').text if dbData is None else dbData.sType
1503
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
1504
            connPts = []
1505
            if node.find('CONNECTIONPOINT').text is not None:
1506
                if dbData:
1507
                    db_conn = dbData.connectionPoint.split('/')
1508
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
1509
                index = 0
1510
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
1511
                    tokens = conn_pt.split(',')
1512
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
1513
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
1514
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3] if dbData is None else db_symbol_num[index]))
1515
                    index += 1
1516
            baseSymbol = node.find('PARENT').text if dbData is None else dbData.baseSymbol
1517
            childSymbolNode = node.find('CHILD')
1518
            childSymbol = ''
1519
            if childSymbolNode is not None:
1520
                childSymbol = childSymbolNode.text
1521

    
1522
            ownerNode = node.find('OWNER')
1523
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
1524

    
1525
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
1526
            hasInstrumentLabel = hasInstrumentLabelNode.text if dbData is None else dbData.hasInstrumentLabel
1527

    
1528
            flipLabelNode = node.find('FLIP')
1529
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
1530

    
1531
            project = appDocData.getCurrentProject()
1532
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
1533
            if os.path.isfile(svgFilePath):
1534
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
1535
                item.setVisible(False)
1536

    
1537
                # if additional symbol was changed, change symbol info
1538
                symbolInfo = None
1539
                if dbUid is None:
1540
                    symbolInfo = appDocData.getSymbolByQuery('name', name)
1541
                else:
1542
                    symbolInfo = appDocData.getSymbolByQuery('UID', dbUid)
1543
                if symbolInfo:
1544
                    childSymbol = symbolInfo.additionalSymbol
1545

    
1546
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
1547
                                  hasInstrumentLabel, dbUid=dbUid):
1548
                    pass
1549
                else:
1550
                    return None
1551

    
1552
                for prop_node in node.iter('PROPERTY'):
1553
                    matches = [prop for prop in item._properties.keys() if
1554
                               prop.Attribute == prop_node.attrib['Attribute']]
1555
                    if matches:
1556
                        item._properties[matches[0]] = matches[0].parse_value(prop_node.text) if prop_node.text else ''
1557

    
1558
                ## assign area
1559
                areaNode = node.find('AREA')
1560
                if areaNode is None:
1561
                    for area in appDocData.getAreaList():
1562
                        if area.contains(pt):
1563
                            item.area = area.name
1564
                            break
1565
                else:
1566
                    item.area = areaNode.text
1567
                ## up to here
1568

    
1569
                connectors = node.find('CONNECTORS')
1570
                if connectors is not None:
1571
                    iterIndex = 0
1572
                    for connector in connectors.iter('CONNECTOR'):
1573
                        item.connectors[iterIndex].parse_xml(connector)
1574
                        iterIndex += 1
1575

    
1576
                # get associations 
1577
                attributeValue = node.find('ASSOCIATIONS')
1578
                if attributeValue is not None:
1579
                    for assoc in attributeValue.iter('ASSOCIATION'):
1580
                        _attrType = assoc.attrib['TYPE']
1581
                        if not _attrType in item._associations:
1582
                            item._associations[_attrType] = []
1583
                        item._associations[_attrType].append(uuid.UUID(assoc.text) if assoc.text != 'None' else None)
1584
                # up to here
1585

    
1586
                attributes = node.find('SYMBOLATTRIBUTES')
1587
                if attributes is not None:
1588
                    '''
1589
                    ## for old spec break item that has not uid currectly, may not necessary new data
1590
                    if _type == 'Segment Breaks':
1591
                        specBreakAttrs = appDocData.getSymbolAttribute('Segment Breaks')
1592
                    ## up to here 1
1593
                    '''
1594
                    for attr in attributes.iter('ATTRIBUTE'):
1595
                        _attr = SymbolAttr.fromXml(attr)
1596
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
1597
                            item.attrs[_attr] = attr.text
1598
                        else:
1599
                            '''
1600
                            ## for old spec break item that has not uid currectly, may not necessary new data
1601
                            matchAttr = [cAttr for cAttr in specBreakAttrs if _attr.Attribute == cAttr.Attribute]
1602
                            matchAttr = matchAttr[0] if matchAttr else None
1603
                            if matchAttr:
1604
                                _attr.UID = matchAttr.UID
1605
                                _attr.DisplayAttribute = matchAttr.DisplayAttribute
1606
                                _attr.AttributeType = matchAttr.AttributeType
1607
                                _attr.AttrAt = matchAttr.AttrAt
1608
                                _attr.Expression = matchAttr.Expression
1609
                                _attr.Length = matchAttr.Length
1610
                            # up to here 2
1611
                            '''
1612
                            if _attr.AttributeType == 'Spec':
1613
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
1614
                            else:
1615
                                '''
1616
                                # for old spec break item that has not uid currectly, may not necessary new data
1617
                                _attr.AssocItem = uuid.UUID(attr.text) if attr.text and attr.text != 'None' else None
1618
                                # up to here 3
1619
                                '''
1620
                                item.attrs[_attr] = attr.text
1621
                            '''
1622
                            elif _attr.Attribute == 'UpStream':
1623
                                for initKey in item.attrs.keys():
1624
                                    if initKey.Attribute == 'UpStream':
1625
                                        item.attrs[initKey] = attr.text
1626
                            elif _attr.Attribute == 'DownStream':
1627
                                for initKey in item.attrs.keys():
1628
                                    if initKey.Attribute == 'DownStream':
1629
                                        item.attrs[initKey] = attr.text
1630
                            '''
1631

    
1632
                currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
1633
                if currentPointModeIndex is not None:
1634
                    item.currentPointModeIndex = int(currentPointModeIndex.text)
1635
        except Exception as ex:
1636
            from App import App
1637
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1638
                                                          sys.exc_info()[-1].tb_lineno)
1639
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1640

    
1641
            return None
1642

    
1643
        return item
1644

    
1645
    def to_svg(self, parent) -> list:
1646
        """convert symbol svg item to svg"""
1647
        import re
1648
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1649
        from App import App
1650

    
1651
        res = []
1652
        try:
1653
            app_doc_data = AppDocData.instance()
1654
            prj = app_doc_data.getCurrentProject()
1655

    
1656
            node = Element('g')
1657
            node.attrib['id'] = str(self.uid)
1658
            node.attrib['class'] = self._class
1659

    
1660
            except_pattern = re.compile('[^a-zA-Z0-9-_]')
1661
            for attr, value in self.getAttributes().items():
1662
                node.attrib[re.sub(except_pattern, '_', attr.Attribute)] = str(value) if value else ''
1663
            trans = self.sceneTransform()
1664
            node.attrib['transform'] = f"matrix(" \
1665
                                       f"{trans.m11()},{trans.m12()}," \
1666
                                       f"{trans.m21()},{trans.m22()}," \
1667
                                       f"{trans.m31()},{trans.m32()}" \
1668
                                       f")"
1669

    
1670
            node_list = self._document.elementsByTagName('path')
1671
            for at in range(node_list.count()):
1672
                path = Element('path')
1673
                path.attrib['d'] = node_list.item(at).attributes().namedItem('d').nodeValue()
1674
                path.attrib['transform'] = self._document.elementsByTagName('g').item(0).attributes().namedItem('transform').nodeValue()
1675
                node.append(path)
1676

    
1677
            for assoc in self.associations():
1678
                assoc_node = assoc.to_svg(parent=self)
1679
                node.extend(assoc_node)
1680

    
1681
            res.append(node)
1682
        except Exception as ex:
1683
            from App import App
1684
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1685
                                                          sys.exc_info()[-1].tb_lineno)
1686
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1687

    
1688
        return res
1689

    
1690
    '''
1691
        @brief      create item corresponding to given type
1692
        @author     humkyung
1693
        @date       2018.05.02
1694
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
1695
                    humkyung 2018.05.10 change symbol's color to blue
1696
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
1697
    '''
1698

    
1699
    @staticmethod
1700
    def createItem(type, name, path, uid=None, owner=None, flip=0):
1701
        from QEngineeringOPCItem import QEngineeringOPCItem
1702
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1703
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1704
        from EngineeringNozzleItem import QEngineeringNozzleItem
1705
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1706
        from EngineeringReducerItem import QEngineeringReducerItem
1707
        from EngineeringErrorItem import QEngineeringErrorItem
1708
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1709
        from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
1710
        from AppDocData import AppDocData
1711
        import uuid
1712

    
1713
        app_doc_data = AppDocData.instance()
1714

    
1715
        item = None
1716
        try:
1717
            cateogry = app_doc_data.getSymbolCategoryByType(type)
1718
            if type == "Piping OPC's":
1719
                item = QEngineeringOPCItem(path, uid, flip=flip)
1720
            elif type == 'Nozzles':
1721
                item = QEngineeringNozzleItem(path, uid, flip=flip)
1722
            elif cateogry == 'Equipment' or cateogry == 'Equipment Components':
1723
                item = QEngineeringEquipmentItem(path, uid, flip=flip)
1724
            elif cateogry == 'Instrumentation':
1725
                item = QEngineeringInstrumentItem(path, uid, flip=flip)
1726
            elif type == 'Segment Breaks':
1727
                item = QEngineeringSpecBreakItem(path, uid, flip=flip)
1728
            elif type == 'Reducers':
1729
                item = QEngineeringReducerItem(path, uid, flip=flip)
1730
            elif type == 'Error':
1731
                item = QEngineeringErrorItem(path, uid, flip=flip)
1732
            elif type == 'End Break':
1733
                item = QEngineeringEndBreakItem(path, uid, flip=flip)
1734
            # elif type == 'Flow Mark':
1735
            #    item = QEngineeringFlowMarkItem(path, uid, flip=flip)
1736
            else:
1737
                item = SymbolSvgItem(name, path, uid, flip=flip)
1738

    
1739
            if owner is not None:
1740
                item.owner = uuid.UUID(owner)
1741

    
1742
        except Exception as ex:
1743
            from App import App
1744
            from AppDocData import MessageType
1745

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

    
1750
        return item
1751

    
1752
    '''
1753
        @brief      change svg's color
1754
        @author     humkyung
1755
        @date       2018.05.10
1756
        @history    2018.05.11  Jeongwoo    Override QEngineeringAbstractItem's
1757
                    humkyung 2018.05.13 update after change color
1758
    '''
1759

    
1760
    def setColor(self, color):
1761
        if self._document:
1762
            self.changeAttributes('fill', color)
1763
            self.changeAttributes('stroke', color)
1764
            self.renderer().load(self._document.toByteArray())
1765
            self.update()
1766

    
1767
    def getColor(self):
1768
        """ return hover color if mouse is over otherwise reutrn owner's color if owner exist else this color """
1769
        return SymbolSvgItem.HOVER_COLOR if self.hover else (
1770
            self.owner._color if self.owner and hasattr(self.owner, '_color') else self._color)
1771

    
1772
    '''
1773
        @brief  get attributes from svg file
1774
        @author humkyung
1775
        @date   2019.03.08
1776
    '''
1777

    
1778
    def get_attribute(self, attName):
1779
        root = self._document.documentElement()
1780
        node = root.firstChild()
1781
        while not node.isNull():
1782
            if node.isElement():
1783
                element = node.toElement()
1784
                if element.hasAttribute(attName):
1785
                    return element.attribute(attName)
1786

    
1787
                if element.hasChildNodes():
1788
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
1789
                    if att_val is not None: return att_val
1790

    
1791
            node = node.nextSibling()
1792

    
1793
        return None
1794

    
1795
    '''
1796
        @brief  get recursively attribute
1797
        @author humkyung
1798
        @date   2019.03.08
1799
    '''
1800

    
1801
    def recursive_get_attribute(self, node, attName):
1802
        while not node.isNull():
1803
            if node.isElement():
1804
                element = node.toElement()
1805
                if element.hasAttribute(attName):
1806
                    return element.attribute(attName)
1807

    
1808
                if node.hasChildNodes():
1809
                    att_val = self.recursive_get_attribute(node.firstChild(), attName)
1810
                    if att_val is not None: return att_val
1811

    
1812
            node = node.nextSibling()
1813

    
1814
        return None
1815

    
1816
    '''
1817
        @brief  change attributes
1818
        @author humkyung
1819
        @date   2018.05.10
1820
    '''
1821

    
1822
    def changeAttributes(self, attName, attValue):
1823
        root = self._document.documentElement()
1824
        node = root.firstChild()
1825
        while not node.isNull():
1826
            if node.isElement():
1827
                element = node.toElement()
1828
                if element.hasAttribute(attName):
1829
                    element.setAttribute(attName, attValue)
1830

    
1831
                if element.hasChildNodes():
1832
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
1833

    
1834
            node = node.nextSibling()
1835

    
1836
    '''
1837
        @brief  change attribute
1838
        @author humkyung
1839
        @date   2018.05.10
1840
    '''
1841

    
1842
    def recursiveChangeAttributes(self, node, attName, attValue):
1843
        while not node.isNull():
1844
            if node.isElement():
1845
                element = node.toElement()
1846
                if element.hasAttribute(attName):
1847
                    element.setAttribute(attName, attValue)
1848

    
1849
                if node.hasChildNodes():
1850
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
1851

    
1852
            node = node.nextSibling()
1853

    
1854
    '''
1855
        @brief  draw rect when item is selected
1856
        @author humkyung
1857
        @date   2018.07.07
1858
    '''
1859

    
1860
    def drawFocusRect(self, painter):
1861
        self.focuspen = QPen(Qt.DotLine)
1862
        self.focuspen.setColor(Qt.black)
1863
        self.focuspen.setWidthF(1.5)
1864
        hilightColor = QColor(255, 0, 0, 127)
1865
        painter.setBrush(QBrush(hilightColor))
1866
        painter.setPen(self.focuspen)
1867
        painter.drawRect(self.boundingRect())
1868

    
1869
    '''
1870
        @brief  override paint(draw connection points)
1871
        @author humkyung
1872
        @date   2018.04.21
1873
    '''
1874

    
1875
    def paint(self, painter, options=None, widget=None):
1876
        from EngineeringAbstractItem import QEngineeringAbstractItem
1877
        from EngineeringTextItem import QEngineeringTextItem
1878

    
1879
        self.setColor(self.getColor())
1880

    
1881
        painter.setClipRect(options.exposedRect)
1882
        QGraphicsSvgItem.paint(self, painter, options, widget)
1883
        '''
1884
        # not used
1885
        for attr in self.attrs:
1886
            if issubclass(type(attr), QEngineeringTextItem):
1887
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (
1888
                    self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
1889
                attr.setColor(color)
1890
            elif issubclass(type(attr), SymbolSvgItem):
1891
                attr.setColor(self.getColor())
1892
                attr.update()
1893
        '''
1894

    
1895
        if self.isSelected():
1896
            self.drawFocusRect(painter)
1897

    
1898
    '''
1899
        @brief      Add Svg Item into ImageViewer's Scene
1900
        @author     Jeongwoo
1901
        @date       2018.05.03
1902
        @history    add connectors which's parent is symbol
1903
                    kyouho  2018.07.30  remove connectors logic
1904
    '''
1905

    
1906
    def addSvgItemToScene(self, scene):
1907
        self.rotate(self.getCurrentPoint(), self.angle)
1908
        scene.addItem(self)
1909
        self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
1910
        trans = self.transform()
1911

    
1912
    '''
1913
        @brief      
1914
        @author     humkyung
1915
        @date       2018.07.27
1916
    '''
1917

    
1918
    def onConnectorPosChaned(self, connector):
1919
        pass
1920

    
1921
    '''
1922
        @brief      set connector
1923
        @author     kyouho
1924
        @date       2018.07.26
1925
    '''
1926

    
1927
    def setConnector(self, index):
1928
        connector = QEngineeringConnectorItem(parent=self, index=index)
1929
        connector.setParentItem(self)
1930
        self.connectors.append(connector)
1931

    
1932
    '''
1933
    '''
1934

    
1935
    def refreshConnector(self):
1936
        for connector in self.connectors:
1937
            connector.buildItem()
1938

    
1939
    '''
1940
        @brief      Delete svg item
1941
        @author     Jeongwoo
1942
        @date       2018.05.25
1943
    '''
1944

    
1945
    def deleteSvgItemFromScene(self):
1946
        ''' not used '''
1947
        for connector in self.connectors:
1948
            if connector.connectedItem is not None:
1949
                try:
1950
                    connector.connectedItem.removeSymbol(self)
1951
                except Exception as ex:
1952
                    from App import App
1953
                    from AppDocData import MessageType
1954

    
1955
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1956
                                                                  sys.exc_info()[-1].tb_lineno)
1957
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
1958
                break
1959

    
1960
        self.transfer.onRemoved.emit(self)
1961

    
1962
    '''
1963
        @brief      Return real item position
1964
        @author     Jeongwoo
1965
        @date       2018.05.25
1966
    '''
1967

    
1968
    def boundingRectOnScene(self):
1969
        rect = self.boundingRect()
1970
        rect.moveTo(self.loc[0], self.loc[1])
1971
        return rect
1972

    
1973
    def flipSymbol(self):
1974
        '''
1975
            @brief  remove item when user press delete key
1976
            @author humkyung
1977
            @date   2018.04.23
1978
        '''
1979
        if self.flip is 0:
1980
            self.flip = 1
1981
        else:
1982
            self.flip = 0
1983

    
1984
        currentPoint = self.getCurrentPoint()
1985
        self.rotate(currentPoint, self.angle)
1986

    
1987
    '''
1988
        @brief      rotate Symbol
1989
        @author     kyouho
1990
        @date       2018.07.24
1991
    '''
1992

    
1993
    def rotateSymbol(self, angle=None):
1994
        if angle is None:
1995
            # degree 0
1996
            if 0 == self.angle:
1997
                self.angle = 1.57
1998
            # degree 90
1999
            elif (1.57 == self.angle):
2000
                self.angle = 3.14
2001
            # degree 180
2002
            elif 3.14 == self.angle:
2003
                self.angle = 4.71
2004
            # degree 270
2005
            elif 4.71 == self.angle:
2006
                self.angle = 0
2007
            else:
2008
                self.angle = 0
2009
        else:
2010
            self.angle = angle
2011

    
2012
        currentPoint = self.getCurrentPoint()
2013
        self.rotate(currentPoint, self.angle)
2014

    
2015
    def get_transform(self, pt, angle):
2016
        """ return a transform """
2017
        transform = QTransform()
2018
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
2019
        transform.rotateRadians(-angle)
2020
        transform.translate(-pt[0], -pt[1])
2021

    
2022
        if self.flip is 1:
2023
            transform.scale(-1.0, 1.0)
2024
            transform.translate(-2 * self.symbolOrigin[0], 0)
2025

    
2026
        return transform
2027

    
2028
    '''
2029
        @brief      resetting rotate Symbol
2030
        @author     kyouho
2031
        @date       2018.07.24
2032
    '''
2033

    
2034
    def rotate(self, standardPoint, angle):
2035
        self.setTransform(self.get_transform(standardPoint, angle))
2036
        self.setRotation(0)  # force to emit itemChange signal
2037

    
2038
    '''
2039
        @brief      change Conn point 
2040
        @author     kyouho
2041
        @date       2018.07.25
2042
    '''
2043

    
2044
    def changeConnPoint(self):
2045
        if len(self.connectors) == 2:
2046
            conn1Item = self.connectors[0].connectedItem
2047
            conn2Item = self.connectors[1].connectedItem
2048
            self.connectors[0].connectedItem = conn2Item
2049
            self.connectors[1].connectedItem = conn1Item
2050

    
2051
            currentPoint = self.getCurrentPoint()
2052
            #self.reSettingSymbol(currentPoint, self.angle)
2053

    
2054
    '''
2055
        @brief      change standard point
2056
        @author     kyouho
2057
        @date       2018.07.24
2058
    '''
2059

    
2060
    def changeStandardPoint(self):
2061
        connPtsCount = len(self.connectors)
2062

    
2063
        if self.currentPointModeIndex < connPtsCount:
2064
            self.currentPointModeIndex += 1
2065
        else:
2066
            self.currentPointModeIndex = 0
2067

    
2068
        currentPoint = self.getCurrentPoint()
2069
        #self.reSettingSymbol(currentPoint, self.angle)
2070

    
2071
    '''
2072
        @brief      get standard point
2073
        @author     kyouho
2074
        @date       2018.07.25
2075
    '''
2076

    
2077
    def getCurrentPoint(self):
2078
        pointList = []
2079
        pointList.append(self.symbolOrigin)
2080
        for connector in self.connectors:
2081
            pointList.append(connector.connectPoint)
2082

    
2083
        return pointList[self.currentPointModeIndex]
2084

    
2085
    '''
2086
        @brief      심볼 회전 시 서로 다른 기준점으로 회전하기 때문에 기준점을 이후 개발한 SymbolSvgItem 기준의 회전좌표로 이동하기 위해서 만듬 (loc 기준으로 회전해야함)
2087
        @author     kyouho
2088
        @date       18.08.06
2089
    '''
2090

    
2091
    def reCalculationRotatedItem(self):
2092
        goPoint = self.get_transform(self.getCurrentPoint(), self.angle).map(
2093
            QPoint(self.symbolOrigin[0], self.symbolOrigin[1]))
2094
        self.loc = [self.loc[0] + self.origin[0] - goPoint.x(), self.loc[1] + self.origin[1] - goPoint.y()]
2095

    
2096

    
2097
def recursiveChangeAttributes(node, attName, attValue):
2098
    while not node.isNull():
2099
        if node.isElement():
2100
            element = node.toElement()
2101
            if element.hasAttribute(attName):
2102
                element.setAttribute(attName, attValue)
2103

    
2104
            if node.hasChildNodes():
2105
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
2106

    
2107
        node = node.nextSibling()
2108

    
2109

    
2110
'''
2111
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
2112
    @author     Jeongwoo
2113
    @date       2018.06.18
2114
'''
2115

    
2116

    
2117
class Transfer(QObject):
2118
    on_pos_changed = pyqtSignal(QGraphicsItem)
2119
    onRemoved = pyqtSignal(QGraphicsItem)
2120

    
2121
    def __init__(self, parent=None):
2122
        QObject.__init__(self, parent)
2123

    
2124

    
2125
if __name__ == '__main__':
2126
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
2127
    f.open(QIODevice.ReadOnly)
2128
    array = f.readAll()
2129
    document = QDomDocument()
2130
    document.setContent(array)
2131

    
2132
    root = document.documentElement()
2133
    node = root.firstChild()
2134
    while not node.isNull():
2135
        if node.isElement():
2136
            element = node.toElement()
2137
            if element.hasAttribute('fill'):
2138
                element.setAttribute('fill', '#FFFFF')
2139

    
2140
            if element.hasChildNodes():
2141
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
2142

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