프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ 46aba3e1

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

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

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

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

    
21

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

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

    
29
    DOCUMENTS = {}  # store documents and renders for symbols
30

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

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

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

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

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

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

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

    
77
        self._angle = 0
78

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

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

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

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

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

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

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

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

    
115
        self.setZValue(SymbolSvgItem.ZVALUE)
116

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

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

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

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

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

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

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

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

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

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

    
170
    @property
171
    def stream_no(self):
172
        matches = [attr for attr in self.attrs if attr.Attribute.upper() == 'STREAM NO']
173
        if matches:
174
            return self.attrs[matches[0]]
175

    
176
        return None
177

    
178
    @stream_no.setter
179
    def stream_no(self, value):
180
        from AppDocData import AppDocData
181

    
182
        matches = [attr for attr in self.attrs if attr.Attribute.upper() == 'STREAM NO']
183
        if matches:
184
            self.attrs[matches[0]] = value
185
            """reset attributes for hmb with given stream no"""
186

    
187
            app_doc_data = AppDocData.instance()
188
            stream_data = app_doc_data.hmbTable.dataOfStreamNo(value)
189
            for attr in self.attrs:
190
                if attr.AttributeType == 'HMB':
191
                    matches = [data for data in stream_data if data.name == attr.Attribute]
192
                    if matches:
193
                        self.attrs[attr] = matches[0].value
194
                    else:
195
                        self.attrs[attr] = ''
196

    
197
    @property
198
    def Size(self):
199
        """ return symbol's size """
200
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
201

    
202
        matches = [assoc for assoc in self.associations() if type(assoc) is QEngineeringSizeTextItem]
203
        if matches:
204
            return matches[0].text()
205
        else:
206
            return None
207

    
208
    @property
209
    def EvaluatedSize(self):
210
        from EngineeringReducerItem import QEngineeringReducerItem
211
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
212

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

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

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

    
246
                return self.owner.Size
247

    
248
            return None
249
        except Exception as ex:
250
            from App import App
251
            from AppDocData import MessageType
252

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

    
257
    def EvaluatedLineNo(self, prop):
258
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
259

    
260
        if self.owner and type(self.owner) is QEngineeringLineNoTextItem:
261
            attrs = self.owner.getAttributes()
262
            for attr, value in attrs.items():
263
                if prop == attr.Attribute:
264
                    return value
265

    
266
        return None
267

    
268
    def validate(self):
269
        """validation check"""
270

    
271
        from EngineeringAbstractItem import QEngineeringAbstractItem
272
        from EngineeringLineItem import QEngineeringLineItem
273
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
274
        from EngineeringTextItem import QEngineeringTextItem
275
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
276
        errors = []
277

    
278
        try:
279
            app_doc_data = AppDocData.instance()
280
            dataPath = app_doc_data.getErrorItemSvgPath()
281

    
282
            # validate connectors
283
            for connector in self.connectors:
284
                errors.extend(connector.validate())
285

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

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

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

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

    
318
            # check disconnected point
319
            attrs = self.getAttributes()
320
            matches = [(attr, value) for attr, value in attrs.items() if attr.Attribute == 'OWNERSYMBOL']
321
            if matches:
322
                if not matches[0][1]:
323
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
324
                    error.parent = self
325
                    error.msg = 'not combined'
326
                    error.setToolTip(error.msg)
327
                    error.area = 'Drawing'
328
                    error.name = 'Error'
329
                    errors.append(error)
330
            else:
331
                disconnect = False
332
                if len(self.connectors) is not 0:
333
                    disconnect = True
334
                    for connector in self.connectors:
335
                        if connector.connectedItem is not None:
336
                            disconnect = False
337
                            break
338

    
339
                if disconnect:
340
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
341
                    error.parent = self
342
                    error.msg = 'disconnected'
343
                    error.setToolTip(error.msg)
344
                    error.area = 'Drawing'
345
                    error.name = 'Error'
346
                    errors.append(error)
347

    
348
            # check if symbol size if 0
349
            if self.size[0] == 0 or self.size[1] == 0:
350
                error = SymbolSvgItem.createItem('Error', None, dataPath)
351
                error.parent = self
352
                error.msg = self.tr('size error')
353
                error.setToolTip(error.msg)
354
                error.area = 'Drawing'
355
                error.name = 'Error'
356
                errors.append(error)
357

    
358
            # check if association item's owner exists
359
            for assoc in self.associations():
360
                if issubclass(type(assoc), QEngineeringTextItem) and not assoc.owner:
361
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
362
                    error.parent = self
363
                    error.msg = self.tr('association error')
364
                    error.setToolTip(error.msg)
365
                    error.area = self.area
366
                    error.name = 'Error'
367
                    errors.append(error)
368

    
369
            # set error position
370
            for error in errors:
371
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
372

    
373
            connectedUid = []
374
            for connector in self.connectors:
375
                # for duplicattion check
376
                if connector.connectedItem and issubclass(type(connector.connectedItem), QEngineeringAbstractItem):
377
                    connectedUid.append(str(connector.connectedItem.uid))
378

    
379
                if (issubclass(type(connector.connectedItem), SymbolSvgItem) and \
380
                        type(connector.connectedItem) is not QEngineeringEquipmentItem) or \
381
                                type(connector.connectedItem) is QEngineeringLineItem:
382
                    matches = [conn for conn in connector.connectedItem.connectors if conn.connectedItem is self]
383
                    # check if two items are connected each other
384
                    if not matches:
385
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
386
                        error.setPosition(connector.center())
387
                        error.parent = self
388
                        error.msg = self.tr('disconnected from opposite side')
389
                        error.setToolTip(error.msg)
390
                        error.area = self.area
391
                        error.name = 'Error'
392
                        errors.append(error)
393

    
394
            # check duplicated connection
395
            if len(connectedUid) is not len(set(connectedUid)):
396
                error = SymbolSvgItem.createItem('Error', None, dataPath)
397
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
398
                error.parent = self
399
                error.msg = self.tr('duplicated connection')
400
                error.setToolTip(error.msg)
401
                error.area = self.area
402
                error.name = 'Error'
403
                errors.append(error)
404

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

    
427
        return errors
428

    
429
    def includes(self, item, margin=0):
430
        """return True if symbol contains given point else return False"""
431
        if hasattr(item, 'sceneBoundingRect'):
432
            rect = item.sceneBoundingRect()
433
            topLeft = QPoint(rect.x(), rect.y())
434
            bottomRight = QPoint(rect.x() + rect.width(), rect.y() + rect.height())
435
            margin_rect = QRect(self.sceneBoundingRect().x() - margin, self.sceneBoundingRect().y() - margin, \
436
                            self.sceneBoundingRect().width() + 2 * margin, self.sceneBoundingRect().height() + 2 * margin)
437
            if margin_rect.contains(topLeft) and margin_rect.contains(bottomRight):
438
                return True
439
            else:
440
                return False
441

    
442
        else:
443
            pt = item
444
            rect = self.sceneBoundingRect()
445
            allowed_error = 0.1
446

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

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

    
461
    '''
462
    def associations(self):
463
        """ return associated instance """
464
        # move to abstractitem
465
        import uuid
466

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

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

488
        return res
489

490
    def texts(self):
491
        """ return text type of associations """
492
        from EngineeringTextItem import QEngineeringTextItem
493

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

503
        return res
504

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

516
        return res
517
    '''
518

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

    
523
        if change == QGraphicsItem.ItemPositionHasChanged or change == QGraphicsItem.ItemRotationHasChanged:
524
            from EngineeringLineItem import QEngineeringLineItem
525
            for connector in self.connectors:
526
                if connector.connectedItem is not None and type(connector.connectedItem) is QEngineeringLineItem:
527
                    line = connector.connectedItem
528
                    line.update_shape(self, connector.center())
529
                    line.update_arrow()
530

    
531
            self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), \
532
                                         round(self.sceneBoundingRect().height())
533

    
534
            scene_origin = self.mapToScene(self.transformOriginPoint())
535
            self.origin = [round(scene_origin.x(), 1), round(scene_origin.y(), 1)]
536

    
537
            if hasattr(self.scene(), 'contents_changed'):
538
                self.scene().contents_changed.emit()
539

    
540
            return value
541

    
542
        return super().itemChange(change, value)
543

    
544
    def toSql_Components(self):
545
        """ convert Components data to sql query """
546
        from AppDocData import AppDocData
547

    
548
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner',
549
                'Connected', '[Supplied By]', \
550
                'SpecialItemTypes_UID', 'OriginIndex', '[From]', '[To]', '[Freeze]', '[Connected Item]', '[Flip]',
551
                'SceneOriginPoint']
552
        values = ['?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?']
553
        origin = self.mapToScene(self.transformOriginPoint())
554
        param = [(str(self.uid), str(AppDocData.instance().activeDrawing.UID), self.dbUid, self.loc[0], self.loc[1],
555
                  self.size[0], self.size[1], math.radians(self.rotation()),
556
                  self.area, str(self.owner) if self.owner else None, \
557
                  str(self.conns[0]) if self.conns else None, \
558
                  self.prop('Supplied By'), \
559
                  str(self.special_item_type) if self.special_item_type else None, \
560
                  self.currentPointModeIndex, \
561
                  None, None, self.prop('Freeze') if self.prop('Freeze') else 0,
562
                  str(self.prop('Connected Item')) if self.prop('Connected Item') else None,
563
                  self.flip, '{},{}'.format(origin.x(), origin.y()))]
564
        sql = 'insert into Components({}) values({})'.format(','.join(cols), ','.join(values))
565

    
566
        return (sql, tuple(param))
567

    
568
    def toSql_return_separately(self):
569
        """ convert valve data to sql query """
570
        import uuid
571

    
572
        res = []
573
        resLater = []
574

    
575
        res.append(self.toSql_Components())
576

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

    
593
        if self.associations():
594
            cols = ['UID', '[Type]', 'Components_UID', 'Association']
595
            values = ['?', '?', '?', '?']
596
            params = []
597
            for assoc in self.associations():
598
                params.append(
599
                    (str(uuid.uuid4()), QEngineeringAbstractItem.assoc_type(assoc), str(self.uid), str(assoc.uid)))
600
            sql = 'insert into Associations({}) values({})'.format(','.join(cols), ','.join(values))
601
            resLater.append((sql, tuple(params)))
602

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

    
621
        return res, resLater
622

    
623
    '''
624
        @brief  build symbol item
625
        @author humkyung
626
        @date   2018.05.02
627
        @history    2018.05.09  Jeongwoo    Clear self.connectors
628
                    2018.05.30  Jeongwoo    Add parameters (parentSymbol, childSymbol)
629
    '''
630

    
631
    def buildItem(self, name, _type, angle, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel,
632
                  dbUid=None):
633
        from SpecialItemTypesDialog import SpecialItemTypes
634

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

    
654
            # setting connectors
655
            connectionPoints = symbolInfo.getConnectionPoint().split('/')
656
            for index in range(len(connectionPoints)):
657
                if connectionPoints[index] == '':
658
                    break
659
                tokens = connectionPoints[index].split(',')
660

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

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

    
699
            tooltip = f"<b>{str(self.uid)}</b><br>{self.type}={self.name}"
700
            if self.hit_ratio:
701
                tooltip += f"<br><li>recognition ratio={self.hit_ratio}"
702
            self.setToolTip(tooltip)
703

    
704
            return True
705
        except Exception as ex:
706
            from App import App
707

    
708
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
709
                                                          sys.exc_info()[-1].tb_lineno)
710
            App.mainWnd().addMessage.emit(MessageType.Error, message)
711

    
712
            return False
713

    
714
    '''
715
        @brief  return bounding box of symbol
716
        @author humkyung
717
        @date   2018.04.08
718
    '''
719

    
720
    def rect(self):
721
        return self.sceneBoundingRect()
722

    
723
    def is_connectable(self, item, toler=10):
724
        """return true if line is able to connect symbol"""
725

    
726
        for connector in self.connectors:
727
            for iConnector in item.connectors:
728
                dx = connector.center()[0] - iConnector.center()[0]
729
                dy = connector.center()[1] - iConnector.center()[1]
730
                if math.sqrt(dx * dx + dy * dy) < toler:
731
                    return True
732

    
733
        return False
734

    
735
    '''
736
        @author     humkyung
737
        @date       2018.07.03
738
    '''
739

    
740
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
741
        """ check if given item is connected to self """
742

    
743
        _connectors = [connector for connector in self.connectors if
744
                       (connector.connectedItem == item and (connector._connected_at == at if at else True))]
745
        return len(_connectors) > 0
746

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

    
750
        lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
751
        rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
752
        if lhs_matches and rhs_matches:
753
            return (lhs_matches[0] in [0, 1] and rhs_matches[0] in [0, 1]) or (
754
                    lhs_matches[0] in [2, 3] and rhs_matches[0] in [2, 3])
755

    
756
        return False
757

    
758
    def canBeSecondary(self, line):
759
        """ check given line is not connected(ex: 0-1, 2-3) """
760
        preItem = None
761

    
762
        item = [item.connectedItem for item in self.connectors if
763
                item.connectedItem is not None and item.connectedItem.owner is not None]
764
        if item:
765
            preItem = item[0]
766

    
767
        if preItem is not None and not self.next_connected(line, preItem):
768
            return True
769
        else:
770
            return False
771

    
772
    '''
773
        @brief      connect line and symbol is able to be connected and return line
774
        @author     humkyung
775
        @date       2018.04.16
776
        @history    humkyung 2018.05.08 check if symbol is possible to be connected
777
                    Jeongwoo 2018.05.15 Connect each symbol and line
778
    '''
779

    
780
    def connect_if_possible(self, obj, toler=10):
781
        """ this method not update item's position """
782

    
783
        from shapely.geometry import Point
784
        from EngineeringLineItem import QEngineeringLineItem
785

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

    
823
                            res.append(obj)
824
        except Exception as ex:
825
            from App import App
826
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
827
                                                          sys.exc_info()[-1].tb_lineno)
828
            App.mainWnd().addMessage.emit(MessageType.Error, message)
829

    
830
        return res
831

    
832
    '''
833
        @brief      disconnect connector item
834
        @author     kyouho
835
        @date       2018.08.30
836
    '''
837

    
838
    def disconnectedItemAtConnector(self, connector):
839
        for conn in self.connectors:
840
            if conn.isOverlapConnector(connector):
841
                conn.connectedItem = None
842

    
843
    '''
844
        @brief  get connection point close to given point in tolerance
845
        @author humkyung
846
        @dat
847
    '''
848

    
849
    def getConnectionPointCloseTo(self, pt, toler=10):
850
        import math
851

    
852
        for connector in self.connectors:
853
            dx = connector.center()[0] - pt[0]
854
            dy = connector.center()[1] - pt[1]
855

    
856
            if math.sqrt(dx * dx + dy * dy) < toler:
857
                return connPt
858

    
859
        return None
860

    
861
    '''
862
        @brief  return center of symbol
863
        @author humkyung
864
        @date   2018.04.08
865
    '''
866

    
867
    def center(self):
868
        return self.sceneBoundingRect().center()
869

    
870
    '''
871
        @brief      highlight connector and attribute
872
        @authro     humkyung
873
        @date       2018.05.02
874
    '''
875

    
876
    def hoverEnterEvent(self, event):
877
        self.highlight(True)
878

    
879
    '''
880
        @brief      unhighlight connector and attribute
881
        @author     humkyung
882
        @date       2018.05.02
883
        @history    kyouho 2018.07.18 edit ArrowCursor
884
    '''
885

    
886
    def hoverLeaveEvent(self, event):
887
        self.highlight(False)
888

    
889
    def highlight(self, flag):
890
        """ highlight/unhighlight the symbol """
891

    
892
        try:
893
            self.hover = flag
894
            self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
895
            self.update()
896

    
897
            for assoc in self.associations():
898
                assoc.highlight(flag)
899

    
900
            for connector in self.connectors:
901
                connector.highlight(flag)
902
        except Exception as ex:
903
            from App import App
904
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
905
                                                          sys.exc_info()[-1].tb_lineno)
906
            App.mainWnd().addMessage.emit(MessageType.Error, message)
907

    
908
    '''
909
        @brief      set highlight
910
        @author     kyouho
911
        @date       2018.08.27
912
    '''
913

    
914
    def setHightlight(self):
915
        self.setColor('url(#hover)')
916
        self.update()
917

    
918
    '''
919
        @brief      unset highlight
920
        @author     kyouho
921
        @date       2018.08.27
922
    '''
923

    
924
    def unsetHightlight(self):
925
        self.setColor('url(#normal)')
926
        self.update()
927

    
928
    '''
929
        @brief  change cursor to CrossCursor if mouse point is close to connection point
930
        @author humkyung
931
        @date   2018.04.28
932
    '''
933

    
934
    def hoverMoveEvent(self, event):
935
        pass
936

    
937
    '''
938
        @brief      Mouse Press Event
939
        @author     Jeongwoo
940
        @date       18.04.11
941
        @history    kyouho 2018.07.18 add isClick logic
942
    '''
943

    
944
    def mousePressEvent(self, event):
945
        if event.buttons() == Qt.LeftButton:
946
            self.clicked.emit(self)
947

    
948
    def mouseReleaseEvent(self, event):
949
        """Mouse Release Event"""
950
        if hasattr(self, '_rotating') and event.button() == Qt.RightButton:
951
            from RotateCommand import RotateCommand
952

    
953
            self.angle = -self._angle if -math.pi < self._angle < 0 else 2 * math.pi - self._angle
954

    
955
            self.scene().undo_stack.push(RotateCommand(self.scene(), [self,], self._rotating))
956

    
957
            self.ungrabMouse()
958
            del self._rotating
959

    
960
        super().mouseReleaseEvent(event)
961

    
962
    def mouseMoveEvent(self, event):
963
        """ rotate symbol around current mouse point """
964
        if hasattr(self, '_rotating'):
965
            # get origin point of symbol
966
            origin = self.mapToScene(self.transformOriginPoint())
967
            # up to here
968

    
969
            dx, dy = event.scenePos().x() - origin.x(), event.scenePos().y() - origin.y()
970
            length = math.sqrt(dx * dx + dy * dy)
971

    
972
            self._angle = 0
973
            if length > 0:
974
                self._angle = math.acos(dx / length)
975
                cross = int(np.cross([1, 0], [dx, dy]))
976
                self._angle = -self._angle if cross < 0 else self._angle
977
                self._angle = math.pi*2 + self._angle if self._angle < 0 else self._angle
978

    
979
                modifiers = QApplication.keyboardModifiers()
980
                if modifiers == Qt.ShiftModifier:
981
                    step = math.radians(30)
982
                    quotient = int(self._angle / step)
983
                    angle = quotient*step
984
                    self._angle = angle if (self._angle - angle) < step*0.5 else (quotient + 1)*step
985

    
986
                self.rotate(self._angle)
987

    
988
        modifiers = QApplication.keyboardModifiers()
989
        if modifiers == Qt.ShiftModifier:
990
            super(SymbolSvgItem, self).mouseMoveEvent(event)
991
    
992
    def removeSelfAttr(self, attributeName):
993
        target = None
994
        for attr in self.attrs:
995
            if attr.Attribute == attributeName:
996
                target = attr
997
                break
998

    
999
        if target:
1000
            del self.attrs[attr]
1001

    
1002
    '''
1003
        @brief      Find TextItem contain Point
1004
        @author     kyouho
1005
        @date       18.07.17
1006
    '''
1007

    
1008
    def findTextItemInPoint(self, point):
1009
        from EngineeringTextItem import QEngineeringTextItem
1010

    
1011
        scene = self.scene()
1012

    
1013
        for item in scene.items():
1014
            if type(item) is QEngineeringTextItem:
1015
                if self.isOverlapItemAndPoint(item, point):
1016
                    return (True, item)
1017

    
1018
        return (False,)
1019

    
1020
    '''
1021
        @brief      Check Overlap
1022
        @author     kyouho
1023
        @date       18.07.17
1024
    '''
1025

    
1026
    def isOverlapItemAndPoint(self, item, point):
1027
        x = point.x()
1028
        y = point.y()
1029
        loc = item.loc
1030
        size = item.size
1031

    
1032
        if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
1033
            return True
1034
        else:
1035
            return False
1036

    
1037
    '''
1038
        @brief  remove item when user press delete key
1039
        @author humkyung
1040
        @date   2018.04.23
1041
        @history    2018.05.17  Jeongwoo    Add if-statement and move 'break'
1042
                    2018.05.25  Jeongwoo    Seperate delete item method
1043
    '''
1044
    def keyPressEvent(self, event):
1045
        from EngineeringErrorItem import QEngineeringErrorItem
1046
        from RotateSymbolDialog import QRotateSymbolDialog
1047
        from RotateCommand import RotateCommand
1048

    
1049
        if not self.isSelected():
1050
            return
1051
        elif event.key() == Qt.Key_B:
1052
            self.bind_close_items()
1053
        elif event.key() == Qt.Key_O and type(self) is not QEngineeringErrorItem:
1054
            pass
1055
        elif event.key() == Qt.Key_C and type(self) is not QEngineeringErrorItem:
1056
            self.changeConnPoint()
1057
        elif event.key() == Qt.Key_Return:
1058
            dlg = QRotateSymbolDialog(None, self.rotation(), self.origin, self.zValue())
1059
            if QDialog.Accepted == dlg.showDialog():
1060
                _angle = self.rotation()
1061
                self.rotate(math.radians(dlg.angle))
1062
                self.scene().undo_stack.push(RotateCommand(self.scene(), [self, ], _angle))
1063
                self.angle = dlg.angle
1064
        elif event.key() == Qt.Key_Escape:
1065
            if hasattr(self, '_rotating'):
1066
                self.ungrabMouse()
1067

    
1068
                self.rotate(math.radians(self._rotating))
1069
                del self._rotating
1070
        elif event.key() == Qt.Key_Up:  # translate up/down/left/right symbol
1071
            modifiers = QApplication.keyboardModifiers()
1072
            delta = 10 if modifiers == Qt.ControlModifier else 1
1073

    
1074
            self.loc[1] = self.loc[1] - delta
1075
            #self.origin[1] = self.origin[1] - delta
1076
            self.moveBy(0, -delta)
1077
        elif event.key() == Qt.Key_Down:
1078
            modifiers = QApplication.keyboardModifiers()
1079
            delta = 10 if modifiers == Qt.ControlModifier else 1
1080

    
1081
            self.loc[1] = self.loc[1] + delta
1082
            #self.origin[1] = self.origin[1] + delta
1083
            self.moveBy(0, delta)
1084
        elif event.key() == Qt.Key_Left:
1085
            modifiers = QApplication.keyboardModifiers()
1086
            delta = 10 if modifiers == Qt.ControlModifier else 1
1087

    
1088
            self.loc[0] = self.loc[0] - delta
1089
            #self.origin[0] = self.origin[0] - delta
1090
            self.moveBy(-delta, 0)
1091
        elif event.key() == Qt.Key_Right:
1092
            modifiers = QApplication.keyboardModifiers()
1093
            delta = 10 if modifiers == Qt.ControlModifier else 1
1094

    
1095
            self.loc[0] = self.loc[0] + delta
1096
            #self.origin[0] = self.origin[0] + delta
1097
            self.moveBy(delta, 0)
1098
        elif event.key() == Qt.Key_I or event.key() == Qt.Key_X or event.key() == Qt.Key_J:
1099
            from App import App 
1100
            App.mainWnd().keyPressEvent(event)
1101

    
1102
    def bind_close_items(self):
1103
        """ connect close item by pressing B """
1104
        from EngineeringLineItem import QEngineeringLineItem
1105

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

    
1116
    '''
1117
        @brief      connect attribute
1118
        @author     humkyung
1119
        @date       2018.05.02
1120
        @history    humkyung 2018.05.09 append only nearest size attribute
1121
    '''
1122
    def connectAttribute(self, attributes, clear=True):
1123
        import math
1124
        from EngineeringTextItem import QEngineeringTextItem
1125
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1126
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1127
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1128
        from EngineeringLineItem import QEngineeringLineItem
1129

    
1130
        try:
1131
            if clear:
1132
                if not self.clear_attr_and_assoc_item():
1133
                    return
1134

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

    
1138
            rect = self.sceneBoundingRect()
1139
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
1140
            center = self.sceneBoundingRect().center()
1141

    
1142
            minDist = None
1143
            selected = None
1144
            for attr in attributes:
1145
                # size text and operation code text will find owner themselves in findowner method
1146
                if False:  # type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
1147
                    dx = attr.center().x() - center.x()
1148
                    dy = attr.center().y() - center.y()
1149
                    length = math.sqrt(dx * dx + dy * dy)
1150
                    if (length < dist) and (minDist is None or length < minDist):
1151
                        minDist = length
1152
                        selected = attr
1153
                elif False:#type(attr) is QEngineeringInstrumentItem:
1154
                    if not attr.is_connected():
1155
                        dx = attr.center().x() - center.x()
1156
                        dy = attr.center().y() - center.y()
1157
                        if math.sqrt(dx * dx + dy * dy) < dist:
1158
                            if self.add_assoc_item(attr):
1159
                                attr.owner = self
1160
                elif issubclass(type(attr), QEngineeringTextItem):
1161
                    if rect.contains(attr.center()):
1162
                        if self.add_assoc_item(attr):
1163
                            attr.owner = self  # set owner of text
1164
                elif type(attr) is QEngineeringLineItem:
1165
                    length = attr.distanceTo([center.x(), center.y()])
1166
                    if (length < dist) and (minDist is None or length < minDist):
1167
                        minDist = length
1168
                        selected = attr
1169

    
1170
            if selected is not None:
1171
                self.add_assoc_item(selected)
1172

    
1173
        except Exception as ex:
1174
            from App import App
1175
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1176
                                                          sys.exc_info()[-1].tb_lineno)
1177
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1178

    
1179
    '''
1180
        @brief      start rotating
1181
        @author     euisung
1182
        @date       2019.04.16
1183
    '''
1184

    
1185
    def mouseDoubleClickEvent(self, event):
1186
        if not hasattr(self, '_rotating'):
1187
            self._rotating = self.rotation()
1188
            self.grabMouse()
1189

    
1190
    '''
1191
        @brief      generate xml code
1192
        @author     humkyung
1193
        @date       2018.04.23
1194
        @history    humkyung 2018.04.25 add angle xml node
1195
                    humkyung 2018.04.27 add originalpoint xml node
1196
                    humkyung 2018.05.02 add attribute of symbol
1197
                    Jeongwoo 2018.05.30 add attribute of symbol (parentSymbol, childSymbol, type, connectionPoint)
1198
                    yecheol  2018.07.11 add attribute of symbol (hasInstrumentLabel)
1199
                    humkyung 2018.07.20 write owner's uid to xml
1200
                    humkyung 2018.07.23 write connected item's uid to xml
1201
                    kyouho  2018.07.31 
1202
                    humkyung 2018.09.06 write area to xml
1203
    '''
1204

    
1205
    def toXml(self):
1206
        import uuid
1207
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1208
        from EngineeringAbstractItem import QEngineeringAbstractItem
1209
        from EngineeringTextItem import QEngineeringTextItem
1210
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1211
        from SymbolAttr import SymbolAttr
1212

    
1213
        try:
1214
            node = Element('SYMBOL')
1215
            uidNode = Element('UID')
1216
            uidNode.text = str(self.uid)
1217
            node.append(uidNode)
1218

    
1219
            dbUidNode = Element('DBUID')
1220
            dbUidNode.text = str(self.dbUid)
1221
            node.append(dbUidNode)
1222

    
1223
            nameNode = Element('NAME')
1224
            nameNode.text = self.name
1225
            node.append(nameNode)
1226

    
1227
            attributeValueNode = Element('ASSOCIATIONS')
1228
            for key, value in self._associations.items():
1229
                for assoc in value:
1230
                    assoc_node = Element('ASSOCIATION')
1231
                    assoc_node.attrib['TYPE'] = str(key)
1232
                    assoc_node.text = str(assoc)
1233
                    attributeValueNode.append(assoc_node)
1234
            node.append(attributeValueNode)
1235

    
1236
            typeNode = Element('TYPE')
1237
            typeNode.text = self.type
1238
            node.append(typeNode)
1239

    
1240
            # write owner's uid to xml
1241
            ownerNode = Element('OWNER')
1242
            if self.owner is not None:
1243
                ownerNode.text = str(self.owner)
1244
            else:
1245
                ownerNode.text = 'None'
1246
            node.append(ownerNode)
1247
            # up to here
1248

    
1249
            originNode = Element('ORIGINALPOINT')
1250
            origin = self.mapToScene(self.transformOriginPoint())
1251
            originNode.text = f"{origin.x()},{origin.y()}"
1252
            node.append(originNode)
1253

    
1254
            connectorsNode = Element('CONNECTORS')
1255
            for connector in self.connectors:
1256
                connectorsNode.append(connector.toXml())
1257
            node.append(connectorsNode)
1258

    
1259
            connectionNode = Element('CONNECTIONPOINT')
1260
            connection_point = []
1261
            if self.connectors is not None:
1262
                for connector in self.connectors:
1263
                    connection_point.append(repr(connector))
1264
            connectionNode.text = '/'.join(connection_point)
1265
            node.append(connectionNode)
1266

    
1267
            locNode = Element('LOCATION')
1268
            locNode.text = '{},{}'.format(self.loc[0], self.loc[1])
1269
            '''
1270
            # calculate symbol's left-top corner
1271
            transform = QTransform()
1272
            transform.translate(self.scenePos().x(), self.scenePos().y())
1273
            transform.rotateRadians(-self.angle)
1274
            loc = transform.map(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
1275
            # up to here
1276
            locNode.text = '{},{}'.format(loc.x() - self.symbolOrigin[0], loc.y() - self.symbolOrigin[1])
1277
            '''
1278
            node.append(locNode)
1279

    
1280
            sizeNode = Element('SIZE')
1281
            sizeNode.text = '{},{}'.format(self.size[0], self.size[1])
1282
            node.append(sizeNode)
1283

    
1284
            angleNode = Element('ANGLE')
1285
            angleNode.text = str(math.radians(self.rotation()))
1286
            node.append(angleNode)
1287

    
1288
            parentSymbolNode = Element('PARENT')
1289
            parentSymbolNode.text = str(self.parentSymbol)
1290
            node.append(parentSymbolNode)
1291

    
1292
            childSymbolNode = Element('CHILD')
1293
            childSymbolNode.text = str(self.childSymbol)
1294
            node.append(childSymbolNode)
1295

    
1296
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
1297
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
1298
            node.append(hasInstrumentLabelNode)
1299

    
1300
            areaNode = Element('AREA')
1301
            areaNode.text = self.area
1302
            node.append(areaNode)
1303

    
1304
            flipNode = Element('FLIP')
1305
            flipNode.text = str(self.flip)
1306
            node.append(flipNode)
1307

    
1308
            if self.hit_ratio:
1309
                ratioNode = Element('RATIO')
1310
                ratioNode.text = str(self.hit_ratio)
1311
                node.append(ratioNode)
1312

    
1313
            properties_node = Element('PROPERTIES')
1314
            for prop, value in self.properties.items():
1315
                prop_node = prop.toXml()
1316
                prop_node.text = str(value) if value else ''
1317
                properties_node.append(prop_node)
1318
            node.append(properties_node)
1319

    
1320
            attributesNode = Element('SYMBOLATTRIBUTES')
1321
            _attrs = self.getAttributes()
1322
            for attr in _attrs:
1323
                if type(attr) is SymbolAttr:
1324
                    _node = attr.toXml()
1325
                    if attr.AttributeType != 'Spec':
1326
                        _node.text = str(_attrs[attr])
1327
                    elif attr.AttributeType == 'Spec':
1328
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
1329
                    attributesNode.append(_node)
1330

    
1331
            node.append(attributesNode)
1332

    
1333
            currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
1334
            currentPointModeIndexNode.text = str(self.currentPointModeIndex)
1335
            node.append(currentPointModeIndexNode)
1336
        except Exception as ex:
1337
            from App import App
1338
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1339
                                                          sys.exc_info()[-1].tb_lineno)
1340
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1341

    
1342
            return None
1343

    
1344
        return node
1345

    
1346
    @staticmethod
1347
    def from_database(component):
1348
        """ create a item related to given component from database """
1349
        import uuid
1350
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1351
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1352
        from SymbolAttr import SymbolAttr
1353
        item = None
1354

    
1355
        try:
1356
            app_doc_data = AppDocData.instance()
1357

    
1358
            uid = component['UID']
1359
            pt = [float(component['X']), float(component['Y'])]
1360
            size = [float(component['Width']), float(component['Height'])]
1361

    
1362
            dbUid = int(component['Symbol_UID'])
1363
            dbData = app_doc_data.getSymbolByQuery('UID', dbUid)
1364
            name = dbData.sName
1365
            _type = dbData.sType
1366
            angle = float(component['Rotation'])
1367
            origin = [float(x) for x in component['SceneOriginPoint'].split(',')] if component['SceneOriginPoint'] is not None else pt
1368
            connPts = []
1369
            if component['ConnectionPoint']:
1370
                if dbData:
1371
                    db_conn = dbData.connectionPoint.split('/')
1372
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
1373
                index = 0
1374
                for conn_pt in component['ConnectionPoint'].split('/'):
1375
                    tokens = conn_pt.split(',')
1376
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
1377
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
1378
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3] if dbData is None else db_symbol_num[index]))
1379
                    index += 1
1380

    
1381
            baseSymbol = dbData.baseSymbol
1382

    
1383
            childSymbolNode = component['AdditionalSymbol']
1384
            childSymbol = childSymbolNode if childSymbolNode is not None else ''
1385

    
1386
            owner = component['Owner'] if component['Owner'] is not None and component['Owner'] != 'None' else None
1387

    
1388
            hasInstrumentLabel = dbData.hasInstrumentLabel
1389

    
1390
            flipLabelNode = component['Flip']
1391
            flipLabel = int(flipLabelNode) if flipLabelNode is not None else 0
1392

    
1393
            project = app_doc_data.getCurrentProject()
1394
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
1395
            if os.path.isfile(svgFilePath):
1396
                item = SymbolSvgItem.createItem(_type, name, svgFilePath, uid, owner=owner, flip=flipLabel)
1397
                item.setVisible(False)
1398

    
1399
                # if additional symbol was changed, change symbol info
1400
                symbolInfo = None
1401
                if dbUid is None:
1402
                    symbolInfo = app_doc_data.getSymbolByQuery('name', name)
1403
                else:
1404
                    symbolInfo = app_doc_data.getSymbolByQuery('UID', dbUid)
1405
                if symbolInfo:
1406
                    childSymbol = symbolInfo.additionalSymbol
1407

    
1408
                if item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol,
1409
                                  hasInstrumentLabel, dbUid=dbUid):
1410
                    pass
1411
                else:
1412
                    return None
1413

    
1414
                for key in item._properties.keys():
1415
                    for compo in component.keys():
1416
                        if key.Attribute == compo:
1417
                            item._properties[key] = key.parse_value(component[key.Attribute]) if component[
1418
                                key.Attribute] else ''
1419

    
1420
                ## assign area
1421
                areaNode = component['Area']
1422
                if areaNode is None:
1423
                    for area in app_doc_data.getAreaList():
1424
                        if area.contains(pt):
1425
                            item.area = area.name
1426
                            break
1427
                else:
1428
                    item.area = areaNode
1429
                ## up to here
1430

    
1431
                connectors = app_doc_data.get_component_connectors(uid)
1432
                if connectors:
1433
                    iterIndex = 0
1434
                    for connector in connectors:
1435
                        item.connectors[iterIndex].parse_record(connector)
1436
                        iterIndex += 1
1437

    
1438
                # get associations 
1439
                associations = app_doc_data.get_component_associations(uid)
1440
                if associations:
1441
                    for assoc in associations:
1442
                        _attrType = assoc['Type']
1443
                        if not _attrType in item._associations:
1444
                            item._associations[_attrType] = []
1445
                        item._associations[_attrType].append(
1446
                            uuid.UUID(assoc['Association']) if assoc['Association'] != 'None' else None)
1447
                # up to here
1448

    
1449
                attributes = app_doc_data.get_component_attributes(uid)
1450
                if attributes:
1451
                    for attr in attributes:
1452
                        _attr = SymbolAttr.from_record(attr)
1453
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
1454
                            item.attrs[_attr] = attr['Value']
1455
                        else:
1456
                            if _attr.AttributeType == 'Spec':
1457
                                item.attrs[_attr] = [attr['Value'].split(',')[0], attr['Value'].split(',')[1]]
1458
                            else:
1459
                                item.attrs[_attr] = attr['Value']
1460
                            '''
1461
                            elif _attr.Attribute == 'UpStream':
1462
                                for initKey in item.attrs.keys():
1463
                                    if initKey.Attribute == 'UpStream':
1464
                                        item.attrs[initKey] = attr['Value']
1465
                            elif _attr.Attribute == 'DownStream':
1466
                                for initKey in item.attrs.keys():
1467
                                    if initKey.Attribute == 'DownStream':
1468
                                        item.attrs[initKey] = attr['Value']
1469
                            '''
1470

    
1471
                currentPointModeIndex = component['OriginIndex']
1472
                if currentPointModeIndex is not None:
1473
                    item.currentPointModeIndex = int(currentPointModeIndex)
1474
        except Exception as ex:
1475
            from App import App
1476
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1477
                                                          sys.exc_info()[-1].tb_lineno)
1478
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1479

    
1480
            return None
1481

    
1482
        return item
1483

    
1484
    '''
1485
        @brief      parse xml code
1486
        @author     humkyung
1487
        @date       2018.07.20
1488
        @history    humkyung 2018.07.20 parse uid from xml node
1489
                    humkyung 2018.07.23 parse connected item's uid from xml node
1490
                    kyouho  2018.07.31 
1491
                    humkyung 2018.09.06 assign area to item
1492
    '''
1493

    
1494
    @staticmethod
1495
    def fromXml(node):
1496
        import uuid
1497
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1498
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1499
        from SymbolAttr import SymbolAttr
1500
        item = None
1501

    
1502
        try:
1503
            appDocData = AppDocData.instance()
1504

    
1505
            uidNode = node.find('UID')
1506
            uid = uidNode.text if uidNode is not None else uuid.uuid4()  # generate UUID
1507

    
1508
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
1509
            size = [float(x) for x in node.find('SIZE').text.split(',')]
1510

    
1511
            dbUidNode = node.find('DBUID')
1512
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
1513
            dbData = None
1514
            if dbUid:
1515
                dbData = appDocData.getSymbolByQuery('UID', dbUid)
1516
            name = node.find('NAME').text if dbData is None else dbData.sName
1517

    
1518
            angle = float(node.find('ANGLE').text)
1519
            _type = node.find('TYPE').text if dbData is None else dbData.sType
1520
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
1521
            connPts = []
1522
            if node.find('CONNECTIONPOINT').text is not None:
1523
                if dbData:
1524
                    db_conn = dbData.connectionPoint.split('/')
1525
                    db_symbol_num = [conn.split(',')[3] for conn in db_conn]
1526
                index = 0
1527
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
1528
                    tokens = conn_pt.split(',')
1529
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else
1530
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else
1531
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3] if dbData is None else db_symbol_num[index]))
1532
                    index += 1
1533
            baseSymbol = node.find('PARENT').text if dbData is None else dbData.baseSymbol
1534
            childSymbolNode = node.find('CHILD')
1535
            childSymbol = ''
1536
            if childSymbolNode is not None:
1537
                childSymbol = childSymbolNode.text
1538

    
1539
            ownerNode = node.find('OWNER')
1540
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
1541

    
1542
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
1543
            hasInstrumentLabel = hasInstrumentLabelNode.text if dbData is None else dbData.hasInstrumentLabel
1544

    
1545
            flipLabelNode = node.find('FLIP')
1546
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
1547

    
1548
            ratioNode = node.find('RATIO')
1549
            hit_ratio = float(ratioNode.text) if ratioNode is not None else None
1550

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

    
1557
                # if additional symbol was changed, change symbol info
1558
                symbolInfo = None
1559
                if dbUid is None:
1560
                    symbolInfo = appDocData.getSymbolByQuery('name', name)
1561
                else:
1562
                    symbolInfo = appDocData.getSymbolByQuery('UID', dbUid)
1563
                if symbolInfo:
1564
                    childSymbol = symbolInfo.additionalSymbol
1565

    
1566
                if hit_ratio:
1567
                    item.hit_ratio = hit_ratio
1568

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

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

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

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

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

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

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

    
1664
            return None
1665

    
1666
        return item
1667

    
1668
    def to_svg(self, parent) -> list:
1669
        """convert symbol svg item to svg"""
1670
        import re
1671
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1672
        from App import App
1673

    
1674
        res = []
1675
        try:
1676
            app_doc_data = AppDocData.instance()
1677
            prj = app_doc_data.getCurrentProject()
1678

    
1679
            node = Element('g')
1680
            node.attrib['id'] = str(self.uid)
1681
            node.attrib['class'] = self._class
1682

    
1683
            except_pattern = re.compile('[^a-zA-Z0-9-_]')
1684
            for attr, value in self.getAttributes().items():
1685
                node.attrib[re.sub(except_pattern, '_', attr.Attribute)] = str(value) if value else ''
1686
            trans = self.sceneTransform()
1687
            node.attrib['transform'] = f"matrix(" \
1688
                                       f"{trans.m11()},{trans.m12()}," \
1689
                                       f"{trans.m21()},{trans.m22()}," \
1690
                                       f"{trans.m31()},{trans.m32()}" \
1691
                                       f")"
1692

    
1693
            node_list = self._document.elementsByTagName('path')
1694
            for at in range(node_list.count()):
1695
                path = Element('path')
1696
                path.attrib['d'] = node_list.item(at).attributes().namedItem('d').nodeValue()
1697
                path.attrib['transform'] = self._document.elementsByTagName('g').item(0).attributes().namedItem('transform').nodeValue()
1698
                node.append(path)
1699

    
1700
            for assoc in self.associations():
1701
                assoc_node = assoc.to_svg(parent=self)
1702
                node.extend(assoc_node)
1703

    
1704
            res.append(node)
1705
        except Exception as ex:
1706
            from App import App
1707
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1708
                                                          sys.exc_info()[-1].tb_lineno)
1709
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1710

    
1711
        return res
1712

    
1713
    '''
1714
        @brief      create item corresponding to given type
1715
        @author     humkyung
1716
        @date       2018.05.02
1717
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
1718
                    humkyung 2018.05.10 change symbol's color to blue
1719
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
1720
    '''
1721

    
1722
    @staticmethod
1723
    def createItem(type, name, path, uid=None, owner=None, flip=0):
1724
        from QEngineeringOPCItem import QEngineeringOPCItem
1725
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1726
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1727
        from EngineeringNozzleItem import QEngineeringNozzleItem
1728
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1729
        from EngineeringReducerItem import QEngineeringReducerItem
1730
        from EngineeringErrorItem import QEngineeringErrorItem
1731
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1732
        from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
1733
        from AppDocData import AppDocData
1734
        import uuid
1735

    
1736
        app_doc_data = AppDocData.instance()
1737

    
1738
        item = None
1739
        try:
1740
            cateogry = app_doc_data.getSymbolCategoryByType(type)
1741
            if type == "Piping OPC's" or type == "Instrument OPC's":
1742
                item = QEngineeringOPCItem(path, uid, flip=flip)
1743
            elif type == 'Nozzles':
1744
                item = QEngineeringNozzleItem(path, uid, flip=flip)
1745
            elif cateogry == 'Equipment' or cateogry == 'Equipment Components':
1746
                item = QEngineeringEquipmentItem(path, uid, flip=flip)
1747
            elif cateogry == 'Instrumentation':
1748
                item = QEngineeringInstrumentItem(path, uid, flip=flip)
1749
            elif type == 'Segment Breaks':
1750
                item = QEngineeringSpecBreakItem(path, uid, flip=flip)
1751
            elif type == 'Reducers':
1752
                item = QEngineeringReducerItem(path, uid, flip=flip)
1753
            elif type == 'Error':
1754
                item = QEngineeringErrorItem(path, uid, flip=flip)
1755
            elif type == 'End Break':
1756
                item = QEngineeringEndBreakItem(path, uid, flip=flip)
1757
            # elif type == 'Flow Mark':
1758
            #    item = QEngineeringFlowMarkItem(path, uid, flip=flip)
1759
            else:
1760
                item = SymbolSvgItem(name, path, uid, flip=flip)
1761

    
1762
            if owner is not None:
1763
                item.owner = uuid.UUID(owner)
1764

    
1765
        except Exception as ex:
1766
            from App import App
1767
            from AppDocData import MessageType
1768

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

    
1773
        return item
1774

    
1775
    '''
1776
        @brief      change svg's color
1777
        @author     humkyung
1778
        @date       2018.05.10
1779
        @history    2018.05.11  Jeongwoo    Override QEngineeringAbstractItem's
1780
                    humkyung 2018.05.13 update after change color
1781
    '''
1782

    
1783
    def setColor(self, color):
1784
        if self._document:
1785
            self.changeAttributes('fill', color)
1786
            self.changeAttributes('stroke', color)
1787
            self.renderer().load(self._document.toByteArray())
1788
            self.update()
1789

    
1790
    def getColor(self):
1791
        """ return hover color if mouse is over otherwise reutrn owner's color if owner exist else this color """
1792
        return SymbolSvgItem.HOVER_COLOR if self.hover else (
1793
            self.owner._color if self.owner and hasattr(self.owner, '_color') else self._color)
1794

    
1795
    '''
1796
        @brief  get attributes from svg file
1797
        @author humkyung
1798
        @date   2019.03.08
1799
    '''
1800

    
1801
    def get_attribute(self, attName):
1802
        root = self._document.documentElement()
1803
        node = root.firstChild()
1804
        while not node.isNull():
1805
            if node.isElement():
1806
                element = node.toElement()
1807
                if element.hasAttribute(attName):
1808
                    return element.attribute(attName)
1809

    
1810
                if element.hasChildNodes():
1811
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
1812
                    if att_val is not None: return att_val
1813

    
1814
            node = node.nextSibling()
1815

    
1816
        return None
1817

    
1818
    '''
1819
        @brief  get recursively attribute
1820
        @author humkyung
1821
        @date   2019.03.08
1822
    '''
1823

    
1824
    def recursive_get_attribute(self, node, attName):
1825
        while not node.isNull():
1826
            if node.isElement():
1827
                element = node.toElement()
1828
                if element.hasAttribute(attName):
1829
                    return element.attribute(attName)
1830

    
1831
                if node.hasChildNodes():
1832
                    att_val = self.recursive_get_attribute(node.firstChild(), attName)
1833
                    if att_val is not None: return att_val
1834

    
1835
            node = node.nextSibling()
1836

    
1837
        return None
1838

    
1839
    '''
1840
        @brief  change attributes
1841
        @author humkyung
1842
        @date   2018.05.10
1843
    '''
1844

    
1845
    def changeAttributes(self, attName, attValue):
1846
        root = self._document.documentElement()
1847
        node = root.firstChild()
1848
        while not node.isNull():
1849
            if node.isElement():
1850
                element = node.toElement()
1851
                if element.hasAttribute(attName):
1852
                    element.setAttribute(attName, attValue)
1853

    
1854
                if element.hasChildNodes():
1855
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
1856

    
1857
            node = node.nextSibling()
1858

    
1859
    '''
1860
        @brief  change attribute
1861
        @author humkyung
1862
        @date   2018.05.10
1863
    '''
1864

    
1865
    def recursiveChangeAttributes(self, node, attName, attValue):
1866
        while not node.isNull():
1867
            if node.isElement():
1868
                element = node.toElement()
1869
                if element.hasAttribute(attName):
1870
                    element.setAttribute(attName, attValue)
1871

    
1872
                if node.hasChildNodes():
1873
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
1874

    
1875
            node = node.nextSibling()
1876

    
1877
    '''
1878
        @brief  draw rect when item is selected
1879
        @author humkyung
1880
        @date   2018.07.07
1881
    '''
1882

    
1883
    def drawFocusRect(self, painter):
1884
        self.focuspen = QPen(Qt.DotLine)
1885
        self.focuspen.setColor(Qt.black)
1886
        self.focuspen.setWidthF(1.5)
1887
        hilightColor = QColor(255, 0, 0, 127)
1888
        painter.setBrush(QBrush(hilightColor))
1889
        painter.setPen(self.focuspen)
1890
        painter.drawRect(self.boundingRect())
1891

    
1892
    '''
1893
        @brief  override paint(draw connection points)
1894
        @author humkyung
1895
        @date   2018.04.21
1896
    '''
1897

    
1898
    def paint(self, painter, options=None, widget=None):
1899
        from EngineeringAbstractItem import QEngineeringAbstractItem
1900
        from EngineeringTextItem import QEngineeringTextItem
1901

    
1902
        self.setColor(self.getColor())
1903

    
1904
        painter.setClipRect(options.exposedRect)
1905
        QGraphicsSvgItem.paint(self, painter, options, widget)
1906
        '''
1907
        # not used
1908
        for attr in self.attrs:
1909
            if issubclass(type(attr), QEngineeringTextItem):
1910
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (
1911
                    self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
1912
                attr.setColor(color)
1913
            elif issubclass(type(attr), SymbolSvgItem):
1914
                attr.setColor(self.getColor())
1915
                attr.update()
1916
        '''
1917

    
1918
        if self.isSelected():
1919
            self.drawFocusRect(painter)
1920

    
1921
    def addSvgItemToScene(self, scene, undoable: bool = False) -> None:
1922
        """Add Svg Item into ImageViewer's Scene"""
1923
        if self.flip:
1924
            self.flip_symbol()
1925

    
1926
        self.setTransformOriginPoint(QPointF(self.symbolOrigin[0], self.symbolOrigin[1]))
1927

    
1928
        self.moveBy(-self.symbolOrigin[0], -self.symbolOrigin[1])
1929
        self.setRotation(math.degrees(self.angle))
1930
        self.moveBy(self.origin[0], self.origin[1])
1931

    
1932
        scene.addItem(self)
1933
        self.size[0], self.size[1] = round(self.sceneBoundingRect().width()), round(self.sceneBoundingRect().height())
1934

    
1935
        if undoable:
1936
            from CreateCommand import CreateCommand
1937
            self.scene().undo_stack.push(CreateCommand(self.scene(), [self,]))
1938

    
1939
    '''
1940
        @brief      
1941
        @author     humkyung
1942
        @date       2018.07.27
1943
    '''
1944

    
1945
    def onConnectorPosChaned(self, connector):
1946
        pass
1947

    
1948
    '''
1949
        @brief      set connector
1950
        @author     kyouho
1951
        @date       2018.07.26
1952
    '''
1953

    
1954
    def setConnector(self, index):
1955
        connector = QEngineeringConnectorItem(parent=self, index=index)
1956
        connector.setParentItem(self)
1957
        self.connectors.append(connector)
1958

    
1959
    '''
1960
    '''
1961

    
1962
    def refreshConnector(self):
1963
        for connector in self.connectors:
1964
            connector.buildItem()
1965

    
1966
    '''
1967
        @brief      Delete svg item
1968
        @author     Jeongwoo
1969
        @date       2018.05.25
1970
    '''
1971

    
1972
    def deleteSvgItemFromScene(self):
1973
        ''' not used '''
1974
        for connector in self.connectors:
1975
            if connector.connectedItem is not None:
1976
                try:
1977
                    connector.connectedItem.removeSymbol(self)
1978
                except Exception as ex:
1979
                    from App import App
1980
                    from AppDocData import MessageType
1981

    
1982
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1983
                                                                  sys.exc_info()[-1].tb_lineno)
1984
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
1985
                break
1986

    
1987
        self.transfer.onRemoved.emit(self)
1988

    
1989
    def flip_symbol(self) -> None:
1990
        """flip symbol"""
1991

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

    
1998
        self.setTransform(transform)
1999

    
2000
    def rotate(self, angle: float) -> None:
2001
        """rotate symbol by given angle in radian"""
2002
        import math
2003
        self.setRotation(math.degrees(angle))
2004

    
2005
    '''
2006
        @brief      change Conn point 
2007
        @author     kyouho
2008
        @date       2018.07.25
2009
    '''
2010

    
2011
    def changeConnPoint(self):
2012
        if len(self.connectors) == 2:
2013
            conn1Item = self.connectors[0].connectedItem
2014
            conn2Item = self.connectors[1].connectedItem
2015
            self.connectors[0].connectedItem = conn2Item
2016
            self.connectors[1].connectedItem = conn1Item
2017

    
2018
            currentPoint = self.getCurrentPoint()
2019
            #self.reSettingSymbol(currentPoint, self.angle)
2020

    
2021
    '''
2022
        @brief      change standard point
2023
        @author     kyouho
2024
        @date       2018.07.24
2025
    '''
2026

    
2027
    def changeStandardPoint(self):
2028
        connPtsCount = len(self.connectors)
2029

    
2030
        if self.currentPointModeIndex < connPtsCount:
2031
            self.currentPointModeIndex += 1
2032
        else:
2033
            self.currentPointModeIndex = 0
2034

    
2035
        currentPoint = self.getCurrentPoint()
2036
        #self.reSettingSymbol(currentPoint, self.angle)
2037

    
2038
    def getCurrentPoint(self):
2039
        """return transform origin point"""
2040
        pointList = []
2041
        pointList.append(self.symbolOrigin)
2042
        for connector in self.connectors:
2043
            pointList.append(connector.connectPoint)
2044

    
2045
        return pointList[self.currentPointModeIndex]
2046

    
2047
    '''
2048
        @brief      심볼 회전 시 서로 다른 기준점으로 회전하기 때문에 기준점을 이후 개발한 SymbolSvgItem 기준의 회전좌표로 이동하기 위해서 만듬 (loc 기준으로 회전해야함)
2049
        @author     kyouho
2050
        @date       18.08.06
2051
    '''
2052

    
2053
    """
2054
    def reCalculationRotatedItem(self):
2055
        goPoint = self.get_transform(self.getCurrentPoint(), self.angle).map(
2056
            QPoint(self.symbolOrigin[0], self.symbolOrigin[1]))
2057
        self.loc = [self.loc[0] + self.origin[0] - goPoint.x(), self.loc[1] + self.origin[1] - goPoint.y()]
2058
    """
2059

    
2060

    
2061
def recursiveChangeAttributes(node, attName, attValue):
2062
    while not node.isNull():
2063
        if node.isElement():
2064
            element = node.toElement()
2065
            if element.hasAttribute(attName):
2066
                element.setAttribute(attName, attValue)
2067

    
2068
            if node.hasChildNodes():
2069
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
2070

    
2071
        node = node.nextSibling()
2072

    
2073

    
2074
'''
2075
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
2076
    @author     Jeongwoo
2077
    @date       2018.06.18
2078
'''
2079

    
2080

    
2081
class Transfer(QObject):
2082
    on_pos_changed = pyqtSignal(QGraphicsItem)
2083
    onRemoved = pyqtSignal(QGraphicsItem)
2084

    
2085
    def __init__(self, parent=None):
2086
        QObject.__init__(self, parent)
2087

    
2088

    
2089
if __name__ == '__main__':
2090
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
2091
    f.open(QIODevice.ReadOnly)
2092
    array = f.readAll()
2093
    document = QDomDocument()
2094
    document.setContent(array)
2095

    
2096
    root = document.documentElement()
2097
    node = root.firstChild()
2098
    while not node.isNull():
2099
        if node.isElement():
2100
            element = node.toElement()
2101
            if element.hasAttribute('fill'):
2102
                element.setAttribute('fill', '#FFFFF')
2103

    
2104
            if element.hasChildNodes():
2105
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
2106

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