프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ 15fc16c4

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

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

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

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

    
20
class SymbolSvgItem(QGraphicsSvgItem, QEngineeringAbstractItem):
21
    """ This is symbolsvgitem class """
22

    
23
    clicked = pyqtSignal(QGraphicsSvgItem)
24
    ZVALUE = 50
25
    HOVER_COLOR = 'url(#hover)'
26

    
27
    '''
28
        @history    18.04.11    Jeongwoo    Add Variable (Name, Type)
29
                    18.05.11    Jeongwoo    Declare variable self.color
30
                    18.05.25    Jeongwoo    Call setColor() method
31
                    18.05.30    Jeongwoo    Add self variables (parentSymbol, childSymbol)
32
    '''
33
    def __init__(self, path, uid=None, flip=0):
34
        import uuid
35
        from SymbolAttr import SymbolProp
36

    
37
        QGraphicsSvgItem.__init__(self)
38
        QEngineeringAbstractItem.__init__(self)
39

    
40
        self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)#|QGraphicsItem.ItemIsMovable)
41

    
42
        self.dbUid = None
43
        self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid, version=4)
44
        self.name = ''
45
        self.type = ''
46
        self.angle = 0
47
        self.origin = None
48
        self.loc = None
49
        self.size = None
50
        self._owner = None
51
        self.parentSymbol = ''
52
        self.childSymbol = '' 
53
        self.hasInstrumentLabel = 0
54
        self.flip = flip
55
        # attributeType uid
56
        self.attribute = ''
57
        self._properties = {SymbolProp(None, 'Size', 'Size Text Item', Expression='self.EvaluatedSize'):None, SymbolProp(None, 'Supplied By', 'String'):None}
58
        
59
        self.setAcceptDrops(True)
60
        self.setAcceptHoverEvents(True)
61
        self.setAcceptedMouseButtons(Qt.LeftButton)
62
        self.setAcceptTouchEvents(True)
63
        
64
        self.currentCursor = 0
65
        self.transfer = Transfer()
66

    
67
        try:
68
            f = QFile(path)
69
            f.open(QIODevice.ReadOnly)
70
            array = f.readAll()
71
            self._document = QDomDocument()
72
            self._document.setContent(array)
73
            self._renderer = QSvgRenderer(self._document.toByteArray())
74
            self.setSharedRenderer(self._renderer)
75

    
76
            self._color = self.get_attribute('fill')
77
        except Exception as ex:
78
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
79
        finally:
80
            f.close()
81

    
82
        self.setZValue(SymbolSvgItem.ZVALUE)
83

    
84
        app_doc_data = AppDocData.instance()
85
        configs = app_doc_data.getConfigs('Symbol Style', 'Opacity')
86
        self.setOpacity(float(configs[0].value)/100 if configs else 0.5)
87

    
88
    def __str__(self):
89
        """ return string represent uuid """
90
        return str(self.uid)
91

    
92
    '''
93
        @breif  getter owner
94
        @author humkyung
95
        @date   2018.05.10
96
    '''
97
    @property
98
    def owner(self):
99
        import uuid
100

    
101
        if self._owner and type(self._owner) is uuid.UUID:
102
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
103
            if matches: self._owner = matches[0]
104

    
105
        if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
106
            return self._owner
107
        else:
108
            self._owner = None
109
            return None
110

    
111
    '''
112
        @brief  setter owner
113
        @author humkyung
114
        @date   2018.05.10
115
        @history    2018.05.17  Jeongwoo    Add Calling setColor if self._owner is None or not
116
    '''
117
    @owner.setter
118
    def owner(self, value):
119
        self._owner = value
120

    
121
        if self._owner is None:
122
            self._color = self.DEFAULT_COLOR
123
        self.setColor(self._color)
124

    
125
    @property
126
    def properties(self):
127
        """ getter of properties """
128
        import uuid
129

    
130
        for prop,value in self._properties.items():
131
            try:
132
                if prop.is_selectable and type(value) is uuid.UUID and self.scene():
133
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(value)]
134
                    if matches: self._properties[prop] = matches[0]
135
                
136
                if prop.Expression:
137
                    item = self._properties[prop]   # assign item
138
                    self._properties[prop] = eval(prop.Expression)
139
            except Exception as ex:
140
                from App import App 
141

    
142
                message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
143
                App.mainWnd().addMessage.emit(MessageType.Error, message)           
144

    
145
        return self._properties
146

    
147
    @properties.setter
148
    def properties(self, value):
149
        """ setter of properties """
150
        self._properties = value
151

    
152
    def set_property(self, property, value):
153
        """ set property with given value """
154
        if issubclass(type(value), QEngineeringAbstractItem): self.add_assoc_item(value, 0)
155
        matches = [prop for prop,_ in self._properties.items() if prop.Attribute == property]
156
        if matches: self._properties[matches[0]] = value
157

    
158
    def prop(self, name):
159
        """ return the value of given property with name """
160
        matches = [(prop,value) for prop,value in self.properties.items() if prop.Attribute == name]
161
        if matches: return matches[0][1]
162

    
163
        return None
164

    
165
    '''
166
    def attrib(self, name):
167
        """ return the value of given attribute with name """
168
        matches = [(attr,value) for attr,value in self.getAttributes().items() if attr.Attribute == name]
169
        if matches: return matches[0][1]
170

171
        return None
172

173
    def set_attrib(self, attrib, value):
174
        """ set attribute with given value """
175
        matches = [attr for attr,_ in self.attrs.items() if attr.UID == attrib.UID]
176
        if len(matches) == 1: self.attrs[matches[0]] = value
177
    '''
178

    
179
    @property
180
    def Size(self):
181
        """ return valve's size """
182
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
183

    
184
        matches = [assoc for assoc in self.associations() if type(assoc) is QEngineeringSizeTextItem]
185
        return matches[0].text() if matches else None
186
    
187
    @property
188
    def EvaluatedSize(self):
189
        from EngineeringReducerItem import QEngineeringReducerItem
190
        
191
        if self.Size: return self.Size
192
        if self.owner:
193
            matches = [run for run in self.owner.runs if self in run.items]
194
            if matches:
195
                at = matches[0].items.index(self)
196
                upstream = matches[0].items[:at]
197
                upstream.reverse()
198
                prev = self 
199
                for item in upstream:
200
                    if type(item) is QEngineeringReducerItem:
201
                        if item.connectors[0].connectedItem is prev:    ### Main Size
202
                            if item.MainSize: return item.MainSize
203
                        elif item.connectors[1].connectedItem is prev:  ### Sub Size
204
                            if item.SubSize: return item.SubSize
205
                    else:
206
                        if item.Size: return item.Size
207
                    prev = item
208

    
209
                downstream = matches[0].items[at:]
210
                prev = self
211
                for item in downstream:
212
                    if type(item) is QEngineeringReducerItem:
213
                        if item.connectors[0].connectedItem is prev:    ### Main Size
214
                            if item.MainSize: return item.MainSize
215
                        elif item.connectors[1].connectedItem is prev:  ### Sub Size
216
                            if item.SubSize: return item.SubSize
217
                    else:
218
                        if item.Size: return item.Size
219
                    prev = item
220

    
221
                if 'Drain' == matches[0].Type: return AppDocData.instance().drain_size
222
                    
223
            return self.owner.Size
224

    
225
        return None
226
        
227
    def validate(self):
228
        '''
229
            @brief  validation check : flow
230
            @author euisung
231
            @date   2019.04.16
232
        '''
233
        from EngineeringLineItem import QEngineeringLineItem
234
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
235
        errors = []
236
        if type(self) is QEngineeringSpecBreakItem:
237
            return errors
238
        docdata = AppDocData.instance()
239
        dataPath = docdata.getErrorItemSvgPath()
240
        origin = [int(pt) for pt in docdata.getAppConfigs('app', 'error origin point')[0].value.split(',')]
241
        
242
        # check flow for 2 connection point symbol
243
        if len(self.connectors) is 2:
244
            if type(self.connectors[0].connectedItem) is QEngineeringLineItem and type(self.connectors[1].connectedItem) is QEngineeringLineItem:
245
                if self.includes(self.connectors[0].connectedItem.startPoint(), 5) is not self.includes(self.connectors[1].connectedItem.endPoint(), 5):
246
                    #print(self.sceneBoundingRect())
247
                    error = SymbolSvgItem.createItem('Error', dataPath)
248
                    error.parent = self
249
                    error.msg = 'flow error'
250
                    error.setToolTip(error.msg)
251
                    error.area = 'Drawing'
252
                    error.name = 'Error'
253
                    errors.append(error)
254

    
255
        # check disconnected point
256
        disconnect = False
257
        if len(self.connectors) is not 0:
258
            disconnect = True
259
            for connector in self.connectors:
260
                if connector.connectedItem is not None:
261
                    disconnect = False
262
                    break
263
        
264
        if disconnect:
265
            error = SymbolSvgItem.createItem('Error', dataPath)
266
            error.parent = self
267
            error.msg = 'disconnected'
268
            error.setToolTip(error.msg)
269
            error.area = 'Drawing'
270
            error.name = 'Error'
271
            errors.append(error)
272

    
273
        # set error position
274
        #denominator = len(errors) + 1
275
        #molecule = 1
276
        for error in errors:
277
            error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()], origin)
278
            #molecule = molecule + 1
279
        
280
        return errors
281

    
282
    def includes(self, pt, margin=0):
283
        """
284
        return True if symbol contains given point else return False
285
        """
286
        rect = self.sceneBoundingRect()
287
        allowed_error = 0.1
288

    
289
        if abs(rect.x() - 0) <= allowed_error and abs(rect.y() - 0) <= allowed_error:
290
            # when first recognition step, symbols are not in scene(not yet added) therefore cannot use scenebounding rect
291
            minX = self.loc[0] - margin
292
            minY = self.loc[1] - margin
293
            maxX = minX + self.size[0] + margin
294
            maxY = minY + self.size[1] + margin
295
        else:
296
            minX = rect.x() - margin
297
            minY = rect.y() - margin
298
            maxX = minX + rect.width() + margin
299
            maxY = minY + rect.height() + margin
300

    
301
        #print([minX, minY, maxX, maxY])
302

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

    
305
    '''
306
    def associations(self):
307
        """ return associated instance """
308
        # move to abstractitem
309
        import uuid
310

311
        res = []
312
        for key in self._associations.keys():
313
            index = 0
314
            for assoc in self._associations[key]:
315
                # find owner with uid
316
                if assoc and type(assoc) is uuid.UUID:
317
                    matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(assoc)]
318
                    if matches:
319
                        res.append(matches[0]) # TODO: need to update association with instance
320
                        self._associations[key][index] = matches[0]
321
                    index += 1
322
                # up to here
323
                elif assoc:
324
                    res.append(assoc)
325

326
        for key in self.attrs.keys():
327
            if type(key.AssocItem) is uuid.UUID:
328
                for assoc in res:
329
                    if str(key.AssocItem) == str(assoc.uid):
330
                        key.AssocItem = assoc
331

332
        return res
333

334
    def texts(self):
335
        """ return text type of associations """
336
        from EngineeringTextItem import QEngineeringTextItem
337

338
        res = []
339
        for text in [x for x in self.associations() if issubclass(type(x), QEngineeringTextItem)]:
340
            consumed = False
341
            for key in list(self.attrs.keys()):
342
                if key.AssocItem and key.AssocItem is text:
343
                    consumed = True
344
            if not consumed:
345
                res.append(text)
346

347
        return res
348

349
    def symbols(self):
350
        """ return symbol type of associations """
351
        res = []
352
        for symbol in [x for x in self.associations() if issubclass(type(x), SymbolSvgItem)]:
353
            consumed = False
354
            for key in list(self.attrs.keys()):
355
                if key.AssocItem and key.AssocItem is symbol:
356
                    consumed = True
357
            if not consumed:
358
                res.append(symbol)
359

360
        return res
361
    '''
362

    
363
    def toSql(self):
364
        """ convert valve data to sql query """
365
        import uuid
366
        from AppDocData import AppDocData
367

    
368
        res = []
369
        appDocData = AppDocData.instance()
370

    
371
        cols = ['UID', 'ITEM_NO', 'MainSize', 'SubSize', 'PNID_NO']
372
        values = ['?','?','?', '?', '?']
373
        size = self.prop('Size')
374
        param = [str(self.uid), self.name, size if size else '', size if size else '', appDocData.activeDrawing.name]
375
        sql = 'insert or replace into VALVE_DATA_LIST({}) values({})'.format(','.join(cols), ','.join(values))
376
        res.append((sql, tuple(param)))
377

    
378
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation']
379
        values = ['?','?','?', '?', '?', '?', '?', '?']
380
        param = [str(self.uid), str(appDocData.activeDrawing.UID), str(self.dbUid), self.loc[0], self.loc[1], self.size[0], self.size[1], str(self.angle)]
381
        sql = 'insert or replace into Components({}) values({})'.format(','.join(cols), ','.join(values))
382
        res.append((sql, tuple(param)))
383

    
384
        _attrs = self.getAttributes()
385
        for key in _attrs.keys():
386
            cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value']
387
            values = ['?', '?', '?', '?']
388
            param = [str(uuid.uuid4()), str(self.uid), str(key.UID), str(_attrs[key])]
389

    
390
            sql = 'insert or replace into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
391
            res.append((sql, tuple(param)))
392

    
393
        # save connectors to database
394
        for connector in self.connectors:
395
            res.append(connector.toSql())
396
        # up to here
397

    
398
        return res
399

    
400
    '''
401
        @brief  build symbol item
402
        @author humkyung
403
        @date   2018.05.02
404
        @history    2018.05.09  Jeongwoo    Clear self.connectors
405
                    2018.05.30  Jeongwoo    Add parameters (parentSymbol, childSymbol)
406
    '''
407
    def buildItem(self, name, _type, angle, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel, dbUid=None):
408
        try:
409
            docData = AppDocData.instance()
410
            self.name = name
411
            self.type = _type
412
            self.angle = angle
413
            self.loc = loc
414
            self.size = size
415
            self.origin = origin
416
            if dbUid is None:
417
                symbolInfo = docData.getSymbolByQuery('name', name)
418
            else:
419
                symbolInfo = docData.getSymbolByQuery('UID', dbUid)
420
            self.dbUid = symbolInfo.uid
421
            originalPoint = symbolInfo.getOriginalPoint().split(',')
422
            self.symbolOrigin = [float(originalPoint[0]), float(originalPoint[1])]
423
            if self.flip is 1:
424
                self.symbolOrigin[0] = self.size[0] - self.symbolOrigin[0]
425

    
426
            # setting connectors
427
            connectionPoints = symbolInfo.getConnectionPoint().split('/')
428
            #print(connectionPoints)
429
            for index in range(len(connectionPoints)):
430
                if connectionPoints[index] == '':
431
                    break
432
                tokens = connectionPoints[index].split(',')
433

    
434
                direction = 'AUTO'
435
                symbol_idx = '0'
436
                if len(tokens) == 2:
437
                    x = float(tokens[0])
438
                    y = float(tokens[1])
439
                elif len(tokens) == 3:
440
                    direction = tokens[0]
441
                    x = float(tokens[1])
442
                    y = float(tokens[2])
443
                elif len(tokens) == 4:
444
                    direction = tokens[0]
445
                    x = float(tokens[1])
446
                    y = float(tokens[2])
447
                    symbol_idx = tokens[3]
448

    
449
                self.setConnector(index+1)
450
                self.connectors[index].direction = direction
451
                self.connectors[index].symbol_idx = symbol_idx
452
                self.connectors[index].setPos((x, y))
453
                self.connectors[index].connectPoint = (x, y)
454
                self.connectors[index].sceneConnectPoint = (connPts[index][0], connPts[index][1]) if len(connPts[index]) == 2 else \
455
                                                           (connPts[index][1], connPts[index][2]) if len(connPts[index]) == 3 else \
456
                                                           (connPts[index][1], connPts[index][2]) if len(connPts[index]) == 4 else None
457
                #print((x, y))
458
                #print(self.connectors[index].sceneConnectPoint)
459
                
460
            self.parentSymbol = parentSymbol
461
            self.childSymbol = childSymbol
462
            self.hasInstrumentLabel = hasInstrumentLabel
463
            self.currentPointModeIndex = 0
464

    
465
            tooltip = '<b>{}</b><br>{}={}'.format(str(self.uid), self.type, self.name)
466
            self.setToolTip(tooltip)
467
        except Exception as ex:
468
            from App import App 
469

    
470
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
471
            App.mainWnd().addMessage.emit(MessageType.Error, message)
472

    
473
    '''
474
        @brief  return bounding box of symbol
475
        @author humkyung
476
        @date   2018.04.08
477
    '''
478
    def rect(self):
479
        return self.sceneBoundingRect()
480
        
481
    '''
482
        @brief  return true if line is able to connect symbol
483
        @author humkyung
484
        @date   2018.04.13
485
    '''
486
    def is_connectable(self, item, toler=10):
487
        #from EngineeringLineItem import QEngineeringLineItem
488

    
489
        '''
490
        if False:#type(item) is QEngineeringLineItem:
491
            line = item
492
            start_pt = line.startPoint()
493
            end_pt = line.endPoint()
494
            for connector in self.connectors:
495
                dx = connector.sceneConnectPoint[0] - (start_pt[0])
496
                dy = connector.sceneConnectPoint[1] - (start_pt[1])
497
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
498
                dx = connector.sceneConnectPoint[0] - (end_pt[0])
499
                dy = connector.sceneConnectPoint[1] - (end_pt[1])
500
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
501
        elif True:#issubclass(type(item), SymbolSvgItem):
502
        '''
503
        for connector in self.connectors:
504
            for iConnector in item.connectors:
505
                dx = connector.sceneConnectPoint[0] - iConnector.sceneConnectPoint[0]
506
                dy = connector.sceneConnectPoint[1] - iConnector.sceneConnectPoint[1]
507
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
508

    
509
        return False
510
 
511
    '''
512
        @author     humkyung
513
        @date       2018.07.03
514
    '''
515
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
516
        """ check if given item is connected to self """
517

    
518
        _connectors = [connector for connector in self.connectors if (connector.connectedItem == item and (connector._connected_at == at if at else True))]
519
        return len(_connectors) > 0
520

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

    
524
        lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
525
        rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
526
        if lhs_matches and rhs_matches:
527
            return (lhs_matches[0] in [0,1] and rhs_matches[0] in [0,1]) or (lhs_matches[0] in [2,3] and rhs_matches[0] in [2,3])
528

    
529
        return False
530

    
531
    def canBeSecondary(self, line):
532
        """ check given line is not connected(ex: 0-1, 2-3) """
533
        preItem = None
534

    
535
        item = [item.connectedItem for item in self.connectors if item.connectedItem is not None and item.connectedItem.owner is not None]
536
        if item:
537
            preItem = item[0]
538

    
539
        if preItem is not None and not self.next_connected(line, preItem):
540
            return True
541
        else:
542
            return False
543

    
544
    '''
545
        @brief      connect line and symbol is able to be connected and return line
546
        @author     humkyung
547
        @date       2018.04.16
548
        @history    humkyung 2018.05.08 check if symbol is possible to be connected
549
                    Jeongwoo 2018.05.15 Connect each symbol and line
550
    '''
551
    def connect_if_possible(self, obj, toler=10):
552
        from shapely.geometry import Point
553
        from EngineeringLineItem import QEngineeringLineItem
554

    
555
        res = []
556
        try:
557
            if type(obj) is QEngineeringLineItem:
558
                startPt = obj.startPoint()
559
                endPt = obj.endPoint()
560
                for i in range(len(self.connectors)):
561
                    if (Point(startPt[0], startPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0], self.connectors[i].sceneConnectPoint[1])) < toler):
562
                        if self.connectors[i].connectedItem is None:
563
                            self.connectors[i].connect(obj)
564
                        if obj.connectors[0].connectedItem is None:
565
                            obj.connectors[0].connect(self)
566
                        
567
                        res.append(obj)
568
                    if (Point(endPt[0], endPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0], self.connectors[i].sceneConnectPoint[1])) < toler):
569
                        if self.connectors[i].connectedItem is None:
570
                            self.connectors[i].connect(obj)
571
                        if obj.connectors[1].connectedItem is None:
572
                            obj.connectors[1].connect(self)
573
                        
574
                        res.append(obj)
575
            elif issubclass(type(obj), SymbolSvgItem):
576
                for i in range(len(self.connectors)):
577
                    for j in range(len(obj.connectors)):
578
                        _pt = Point(obj.connectors[j].sceneConnectPoint[0], obj.connectors[j].sceneConnectPoint[1])
579
                        if (_pt.distance(Point(self.connectors[i].sceneConnectPoint[0], self.connectors[i].sceneConnectPoint[1])) < toler):
580
                            if self.connectors[i].connectedItem is None:
581
                                self.connectors[i].connect(obj)
582
                            if obj.connectors[j].connectedItem is None:
583
                                obj.connectors[j].connect(self)
584

    
585
                            res.append(obj)
586
        except Exception as ex:
587
            from App import App 
588
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
589
            App.mainWnd().addMessage.emit(MessageType.Error, message)
590

    
591
        return res
592

    
593
    '''
594
        @brief      disconnect connector item
595
        @author     kyouho
596
        @date       2018.08.30
597
    '''
598
    def disconnectedItemAtConnector(self, connector):
599
        for conn in self.connectors:
600
            if conn.isOverlapConnector(connector):
601
                conn.connectedItem = None
602

    
603
    '''
604
        @brief  get connection point close to given point in tolerance
605
        @author humkyung
606
        @dat
607
    '''
608
    def getConnectionPointCloseTo(self, pt, toler=10):
609
        import math
610

    
611
        for connector in self.connectors:
612
            dx = connector.sceneConnectPoint[0] - pt[0]
613
            dy = connector.sceneConnectPoint[1] - pt[1]
614
            if math.sqrt(dx*dx + dy*dy) < toler: return connPt
615
            
616
        return None
617

    
618
    '''
619
        @brief  return center of symbol
620
        @author humkyung
621
        @date   2018.04.08
622
    '''
623
    def center(self):
624
        return self.sceneBoundingRect().center()
625
        
626
    '''
627
        @brief      highlight connector and attribute
628
        @authro     humkyung
629
        @date       2018.05.02
630
    '''
631
    def hoverEnterEvent(self, event):
632
        self.highlight(True)
633

    
634
    '''
635
        @brief      unhighlight connector and attribute
636
        @author     humkyung
637
        @date       2018.05.02
638
        @history    kyouho 2018.07.18 edit ArrowCursor
639
    '''
640
    def hoverLeaveEvent(self, event):
641
        self.highlight(False)
642

    
643
    def highlight(self, flag):
644
        """ highlight/unhighlight the symbol """
645

    
646
        try:
647
            self.hover = flag 
648
            self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
649
            self.update()
650

    
651
            for assoc in self.associations():
652
                assoc.highlight(flag)
653

    
654
            for connector in self.connectors:
655
                connector.highlight(flag)
656
        except Exception as ex: 
657
            from App import App 
658
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
659
            App.mainWnd().addMessage.emit(MessageType.Error, message)
660

    
661
    '''
662
        @brief      set highlight
663
        @author     kyouho
664
        @date       2018.08.27
665
    '''
666
    def setHightlight(self):
667
        self.setColor('url(#hover)')
668
        self.update()
669

    
670
    '''
671
        @brief      unset highlight
672
        @author     kyouho
673
        @date       2018.08.27
674
    '''
675
    def unsetHightlight(self):
676
        self.setColor('url(#normal)')
677
        self.update()
678

    
679
    '''
680
        @brief  change cursor to CrossCursor if mouse point is close to connection point
681
        @author humkyung
682
        @date   2018.04.28
683
    '''
684
    def hoverMoveEvent(self, event):
685
        pass
686

    
687
    '''
688
        @brief      Mouse Press Event
689
        @author     Jeongwoo
690
        @date       18.04.11
691
        @history    kyouho 2018.07.18 add isClick logic
692
    '''
693
    def mousePressEvent(self, event):
694
        if event.buttons() == Qt.LeftButton:
695
            self.clicked.emit(self)
696

    
697
    '''
698
        @brief      Mouse Release Event
699
        @author     kyouho
700
        @date       18.07.17
701
    '''
702
    def mouseReleaseEvent(self, event):
703
        super().mouseReleaseEvent(event)
704

    
705
    def removeSelfAttr(self, attributeName):
706
        target = None
707
        for attr in self.attrs:
708
            if attr.Attribute == attributeName:
709
                target = attr
710
                break
711
                
712
        if target:
713
            del self.attrs[attr]
714

    
715
    '''
716
        @brief      Find TextItem contain Point
717
        @author     kyouho
718
        @date       18.07.17
719
    '''
720
    def findTextItemInPoint(self, point):
721
        from EngineeringTextItem import QEngineeringTextItem
722
        
723
        scene = self.scene()
724
 
725
        for item in scene.items():
726
            if type(item) is QEngineeringTextItem:
727
                if self.isOverlapItemAndPoint(item, point):
728
                    return (True, item)
729

    
730
        return (False,)
731

    
732
    '''
733
        @brief      Check Overlap
734
        @author     kyouho
735
        @date       18.07.17
736
    '''
737
    def isOverlapItemAndPoint(self, item, point):
738
        x = point.x()
739
        y = point.y()
740
        loc = item.loc
741
        size = item.size
742

    
743
        if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
744
            return True
745
        else:
746
            return False
747

    
748
    '''
749
        @brief  remove item when user press delete key
750
        @author humkyung
751
        @date   2018.04.23
752
        @history    2018.05.17  Jeongwoo    Add if-statement and move 'break'
753
                    2018.05.25  Jeongwoo    Seperate delete item method
754
    '''
755
    def keyPressEvent(self, event):
756
        if not self.isSelected():
757
            return
758
        if event.key() == Qt.Key_Delete:
759
            self.deleteSvgItemFromScene()
760
        elif event.key() == Qt.Key_R:
761
            self.rotateSymbol()
762
        elif event.key() == Qt.Key_O:
763
            self.changeStandardPoint()
764
        elif event.key() == Qt.Key_C:
765
            self.changeConnPoint()
766
        elif event.key() == Qt.Key_F:
767
            self.flipSymbol()
768
           
769
    '''
770
        @brief      connect attribute
771
        @author     humkyung
772
        @date       2018.05.02
773
        @history    humkyung 2018.05.09 append only nearest size attribute
774
    '''
775
    def connectAttribute(self, attributes, clear=True):
776
        import math
777
        from EngineeringTextItem import QEngineeringTextItem
778
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
779
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
780
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
781

    
782
        try:
783
            if clear:
784
                self.clear_attr_and_assoc_item()
785

    
786
            configs = AppDocData.instance().getConfigs('Range', 'Detection Ratio')
787
            ratio = float(configs[0].value) if 1 == len(configs) else 1.5
788
            
789
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
790
            center = self.sceneBoundingRect().center()
791

    
792
            minDist = None
793
            selected = None
794
            for attr in attributes:
795
                # size text and operation code text will find onwer themselves in findowner method
796
                if False:# type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
797
                    dx = attr.center().x() - center.x()
798
                    dy = attr.center().y() - center.y()
799
                    length = math.sqrt(dx*dx + dy*dy)
800
                    if (length < dist) and (minDist is None or length < minDist):
801
                        minDist = length
802
                        selected = attr
803
                elif type(attr) is QEngineeringInstrumentItem:
804
                    if not attr.is_connected:
805
                        dx = attr.center().x() - center.x()
806
                        dy = attr.center().y() - center.y()
807
                        if math.sqrt(dx*dx + dy*dy) < dist:
808
                            if self.add_assoc_item(attr):
809
                                attr.owner = self
810

    
811
            if selected is not None:
812
                if self.add_assoc_item(selected):
813
                    selected.owner = self
814

    
815
        except Exception as ex:
816
            from App import App 
817
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
818
            App.mainWnd().addMessage.emit(MessageType.Error, message)
819

    
820
    '''
821
        @brief      Double click event, Show rotate symbol dialog
822
        @author     euisung
823
        @date       2019.04.16
824
    '''
825
    def mouseDoubleClickEvent(self, event):
826
        from RotateSymbolDialog import QRotateSymbolDialog
827
        dialog = QRotateSymbolDialog(None, self.angle, self.origin, self.zValue())
828
        (isAccept, angle, x, y, z) = dialog.showDialog()
829
  
830
        if isAccept:
831
            self.angle = angle
832
            self.loc = [x - self.symbolOrigin[0], y - self.symbolOrigin[1]]
833
            self.origin = [x, y]
834
            scene = self.scene()
835
            scene.removeItem(self)
836
            self.addSvgItemToScene(scene)
837
            self.setZValue(z)
838

    
839
            self.reSettingConnetors()
840

    
841
    '''
842
        @brief      get attribute
843
        @author     humkyung
844
        @date       2018.06.14
845
        @history    kyouho  2018.07.18  Add only attr QEngineeringTextItem
846
    '''
847
    '''
848
    def getAttributes(self):
849
        _attrs = {}
850
        try:
851
            from AppDocData import AppDocData
852
            from EngineeringAbstractItem import QEngineeringAbstractItem
853
            from EngineeringTextItem import QEngineeringTextItem
854
            from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
855

856
            """ get attributes of item from database """
857
            docData = AppDocData.instance()
858
            symbolAttrs = docData.getSymbolAttribute(self.type)
859

860
            _texts = self.texts()
861
            _symbols = self.symbols()
862
            for attr in symbolAttrs:
863
                matches = [_attr for _attr,_ in self.attrs.items() if _attr.UID == attr.UID]
864
                if matches:
865
                    attr.Freeze = matches[0].Freeze         ### update freeze value
866
                    attr.AssocItem = matches[0].AssocItem
867
                    _attrs[attr] = self.attrs[matches[0]]   ### copy attribute value
868
                else:
869
                    _attrs[attr] = ''
870
 
871
                if attr.Freeze: continue    ### do not evalulate value if attribute is frozen
872
                if attr.AttributeType == 'Size Text Item' or attr.AttributeType == 'Text Item' or attr.AttributeType == 'Valve Oper Code':
873
                    at = int(attr.AttrAt)
874
                    items = [text for text in _texts if QEngineeringAbstractItem.assoc_type(text) == attr.AttributeType]
875
                    if not attr.AssocItem and len(items) > at:
876
                        attr.AssocItem = items[at]
877
                        item = attr.AssocItem
878
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
879
                    elif attr.AssocItem:
880
                        item = attr.AssocItem
881
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
882
                    else:
883
                        _attrs[attr] = ''
884
                elif attr.AttributeType == 'Symbol Item':
885
                    at = int(attr.AttrAt)
886
                    if not attr.AssocItem and len(_symbols) > at:
887
                        attr.AssocItem = _symbols[at]
888
                        item = attr.AssocItem
889
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
890
                    elif attr.AssocItem:
891
                        item = attr.AssocItem
892
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
893
                    else:
894
                        _attrs[attr] = ''
895
            
896
            self.attrs = _attrs ### assign self.attrs
897
        except Exception as ex:
898
            from App import App 
899
            from AppDocData import MessageType
900

901
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
902
            App.mainWnd().addMessage.emit(MessageType.Error, message)
903
        
904
        return self.attrs
905
    '''
906

    
907
    '''
908
        @brief      generate xml code
909
        @author     humkyung
910
        @date       2018.04.23
911
        @history    humkyung 2018.04.25 add angle xml node
912
                    humkyung 2018.04.27 add originalpoint xml node
913
                    humkyung 2018.05.02 add attribute of symbol
914
                    Jeongwoo 2018.05.30 add attribute of symbol (parentSymbol, childSymbol, type, connectionPoint)
915
                    yecheol  2018.07.11 add attribute of symbol (hasInstrumentLabel)
916
                    humkyung 2018.07.20 write owner's uid to xml
917
                    humkyung 2018.07.23 write connected item's uid to xml
918
                    kyouho  2018.07.31 
919
                    humkyung 2018.09.06 write area to xml
920
    '''
921
    def toXml(self):
922
        import uuid
923
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
924
        from EngineeringAbstractItem import QEngineeringAbstractItem
925
        from EngineeringTextItem import QEngineeringTextItem
926
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
927
        from SymbolAttr import SymbolAttr
928

    
929
        try:
930
            node = Element('SYMBOL')
931
            uidNode = Element('UID')
932
            uidNode.text = str(self.uid)
933
            node.append(uidNode)
934

    
935
            dbUidNode = Element('DBUID')
936
            dbUidNode.text = str(self.dbUid)
937
            node.append(dbUidNode)
938

    
939
            nameNode = Element('NAME')
940
            nameNode.text = self.name
941
            node.append(nameNode)
942

    
943
            attributeValueNode = Element('ASSOCIATIONS')
944
            for assoc in self.associations():
945
                assoc_node = Element('ASSOCIATION')
946
                assoc_node.attrib['TYPE'] = QEngineeringAbstractItem.assoc_type(assoc)
947
                assoc_node.text = str(assoc.uid)
948
                attributeValueNode.append(assoc_node)
949
            node.append(attributeValueNode)
950

    
951
            typeNode = Element('TYPE')
952
            typeNode.text = self.type
953
            node.append(typeNode)
954

    
955
            # write owner's uid to xml
956
            ownerNode = Element('OWNER')
957
            if self.owner is not None:
958
                ownerNode.text = str(self.owner)
959
            else:
960
                ownerNode.text = 'None'
961
            node.append(ownerNode)
962
            # up to here
963

    
964
            originNode = Element('ORIGINALPOINT')
965
            originNode.text = '{},{}'.format(self.origin[0], self.origin[1])
966
            node.append(originNode)
967

    
968
            connectorsNode = Element('CONNECTORS')
969
            for connector in self.connectors:
970
                connectorsNode.append(connector.toXml())
971
            node.append(connectorsNode)
972

    
973
            connectionNode = Element('CONNECTIONPOINT')
974
            connection_point = []
975
            if self.connectors is not None:
976
                for connector in self.connectors:
977
                    connection_point.append(repr(connector))
978
            connectionNode.text = '/'.join(connection_point)
979
            node.append(connectionNode)
980

    
981
           #rect = self.sceneBoundingRect()
982
            locNode = Element('LOCATION')
983
            locNode.text = '{},{}'.format(self.loc[0], self.loc[1])
984
            node.append(locNode)
985

    
986
            sizeNode = Element('SIZE')
987
            #sizeNode.text = '{},{}'.format(rect.width(), rect.height())
988
            sizeNode.text = '{},{}'.format(self.size[0], self.size[1])
989
            node.append(sizeNode)
990

    
991
            angleNode = Element('ANGLE')
992
            angleNode.text = str(self.angle)
993
            node.append(angleNode)
994

    
995
            parentSymbolNode = Element('PARENT')
996
            parentSymbolNode.text = str(self.parentSymbol)
997
            node.append(parentSymbolNode)
998

    
999
            childSymbolNode = Element('CHILD')
1000
            childSymbolNode.text = str(self.childSymbol)
1001
            node.append(childSymbolNode)
1002

    
1003
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
1004
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
1005
            node.append(hasInstrumentLabelNode)
1006

    
1007
            areaNode = Element('AREA')
1008
            areaNode.text = self.area
1009
            node.append(areaNode)
1010

    
1011
            flipNode = Element('FLIP')
1012
            flipNode.text = str(self.flip)
1013
            node.append(flipNode)
1014
            
1015
            properties_node = Element('PROPERTIES')
1016
            for prop,value in self.properties.items():
1017
                prop_node = prop.toXml()
1018
                prop_node.text = str(value) if value else ''
1019
                properties_node.append(prop_node)
1020
            node.append(properties_node)
1021

    
1022
            attributesNode = Element('SYMBOLATTRIBUTES')
1023
            _attrs = self.getAttributes()
1024
            for attr in _attrs:
1025
                if type(attr) is SymbolAttr:
1026
                    _node = attr.toXml()
1027
                    if attr.AttributeType != 'Spec':
1028
                        _node.text = str(_attrs[attr])
1029
                    elif attr.AttributeType == 'Spec':
1030
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
1031
                    attributesNode.append(_node)
1032

    
1033
            node.append(attributesNode)
1034

    
1035
            currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
1036
            currentPointModeIndexNode.text = str(self.currentPointModeIndex)
1037
            node.append(currentPointModeIndexNode)
1038
        except Exception as ex:
1039
            from App import App 
1040
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1041
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1042

    
1043
            return None
1044

    
1045
        return node 
1046

    
1047
    '''
1048
        @brief      parse xml code
1049
        @author     humkyung
1050
        @date       2018.07.20
1051
        @history    humkyung 2018.07.20 parse uid from xml node
1052
                    humkyung 2018.07.23 parse connected item's uid from xml node
1053
                    kyouho  2018.07.31 
1054
                    humkyung 2018.09.06 assign area to item
1055
    '''
1056
    @staticmethod 
1057
    def fromXml(node):
1058
        import uuid
1059
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1060
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1061
        from SymbolAttr import SymbolAttr
1062
        item = None
1063

    
1064
        try:
1065
            uidNode = node.find('UID')
1066
            uid = uidNode.text if uidNode is not None else uuid.uuid4() # generate UUID
1067

    
1068
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
1069
            size = [float(x) for x in node.find('SIZE').text.split(',')]
1070

    
1071
            dbUidNode = node.find('DBUID')
1072
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
1073
            name = node.find('NAME').text if dbUidNode is None else AppDocData.instance().getSymbolByQuery('UID', dbUid).sName
1074

    
1075
            angle = float(node.find('ANGLE').text)
1076
            _type = node.find('TYPE').text
1077
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
1078
            connPts = []
1079
            if node.find('CONNECTIONPOINT').text is not None:
1080
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
1081
                    tokens = conn_pt.split(',')
1082
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else \
1083
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else \
1084
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3]))
1085
            baseSymbol = node.find('PARENT').text
1086
            childSymbolNode = node.find('CHILD')
1087
            childSymbol = ''
1088
            if childSymbolNode is not None:
1089
                childSymbol = childSymbolNode.text
1090
            
1091
            ownerNode = node.find('OWNER')
1092
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
1093

    
1094
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
1095
            hasInstrumentLabel = hasInstrumentLabelNode.text if hasInstrumentLabelNode is not None else 'False'
1096

    
1097
            flipLabelNode = node.find('FLIP')
1098
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
1099

    
1100
            appDocData = AppDocData.instance()
1101
            project = appDocData.getCurrentProject()
1102
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
1103
            if os.path.isfile(svgFilePath):
1104
                item = SymbolSvgItem.createItem(_type, svgFilePath, uid, owner=owner, flip=flipLabel)
1105
                item.setVisible(False)
1106
                item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol, hasInstrumentLabel, dbUid=dbUid)
1107

    
1108
                for prop_node in node.iter('PROPERTY'):
1109
                    matches = [prop for prop in item._properties.keys() if prop.Attribute == prop_node.attrib['Attribute']]
1110
                    if matches:
1111
                        #matches[0].parse_xml(prop_node)
1112
                        item._properties[matches[0]] = matches[0].parse_value(prop_node.text) if prop_node.text else ''
1113

    
1114
                ## assign area
1115
                areaNode = node.find('AREA')
1116
                if areaNode is None:
1117
                    for area in appDocData.getAreaList():
1118
                        if area.contains(pt):
1119
                            item.area = area.name
1120
                            break
1121
                else:
1122
                    item.area = areaNode.text
1123
                ## up to here
1124
                
1125
                connectors = node.find('CONNECTORS')
1126
                if connectors is not None:
1127
                    iterIndex = 0
1128
                    for connector in connectors.iter('CONNECTOR'):
1129
                        item.connectors[iterIndex].parse_xml(connector)
1130
                        iterIndex += 1
1131
                
1132
                # get associations 
1133
                attributeValue = node.find('ASSOCIATIONS')
1134
                if attributeValue is not None:
1135
                    for assoc in attributeValue.iter('ASSOCIATION'):
1136
                        _type = assoc.attrib['TYPE']
1137
                        if not _type in item._associations:
1138
                            item._associations[_type] = []
1139
                        item._associations[_type].append(uuid.UUID(assoc.text))
1140
                # up to here
1141

    
1142
                attributes = node.find('SYMBOLATTRIBUTES')
1143
                if attributes is not None:
1144
                    for attr in attributes.iter('ATTRIBUTE'):
1145
                        _attr = SymbolAttr.fromXml(attr)
1146
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
1147
                            item.attrs[_attr] = attr.text
1148
                        else:
1149
                            if _attr.AttributeType == 'Spec':
1150
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
1151
                            elif _attr.Attribute == 'UpStream':
1152
                                for initKey in item.attrs.keys():
1153
                                    if initKey.Attribute == 'UpStream':
1154
                                        item.attrs[initKey] = attr.text
1155
                            elif _attr.Attribute == 'DownStream':
1156
                                for initKey in item.attrs.keys():
1157
                                    if initKey.Attribute == 'DownStream':
1158
                                        item.attrs[initKey] = attr.text
1159
                            
1160
                currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
1161
                if currentPointModeIndex is not None:
1162
                    item.currentPointModeIndex = int(currentPointModeIndex.text)
1163

    
1164
                #if type(item) is QEngineeringEndBreakItem:
1165
                #    item.freeze_item.update_freeze(item.prop('Freeze'))
1166
                
1167
                #if type(item) is QEngineeringSpecBreakItem:
1168
                #    item.connectors[0].setPos((item.connectors[0].connectPoint[0], item.connectors[0].connectPoint[1]))
1169
        except Exception as ex:
1170
            from App import App 
1171
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1172
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1173

    
1174
        return item
1175

    
1176
    '''
1177
        @brief      create item corresponding to given type
1178
        @author     humkyung
1179
        @date       2018.05.02
1180
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
1181
                    humkyung 2018.05.10 change symbol's color to blue
1182
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
1183
    '''
1184
    @staticmethod
1185
    def createItem(type, path, uid=None, owner=None, flip=0):
1186
        from QEngineeringOPCItem import QEngineeringOPCItem
1187
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1188
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1189
        from EngineeringNozzleItem import QEngineeringNozzleItem
1190
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1191
        from EngineeringReducerItem import QEngineeringReducerItem
1192
        from EngineeringErrorItem import QEngineeringErrorItem
1193
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1194
        from AppDocData import AppDocData
1195
        import uuid
1196

    
1197
        docData = AppDocData.instance()
1198

    
1199
        item = None
1200
        cateogry = docData.getSymbolCategoryByType(type)
1201
        if type == "Piping OPC's":
1202
            item = QEngineeringOPCItem(path, uid, flip=flip)
1203
        elif cateogry == 'Equipment':
1204
            item = QEngineeringEquipmentItem(path, uid, flip=flip)
1205
        elif cateogry == 'Instrumentation':
1206
            item = QEngineeringInstrumentItem(path, uid, flip=flip)
1207
        #elif type == 'Nozzles':
1208
        #    item = QEngineeringNozzleItem(path, uid, flip=flip)
1209
        elif type == 'Segment Breaks':
1210
            item = QEngineeringSpecBreakItem(path, uid, flip=flip)
1211
        elif type == 'Reducers':
1212
            item = QEngineeringReducerItem(path, uid, flip=flip)
1213
        elif type == 'Error':
1214
            item = QEngineeringErrorItem(path, uid, flip=flip)
1215
        elif type == 'End Break':
1216
            item = QEngineeringEndBreakItem(path, uid, flip=flip)
1217
        else:
1218
            item = SymbolSvgItem(path, uid, flip=flip)
1219

    
1220
        if owner is not None:
1221
            item.owner = uuid.UUID(owner, version=4)
1222

    
1223
        return item
1224

    
1225
    '''
1226
        @brief      change svg's color
1227
        @author     humkyung
1228
        @date       2018.05.10
1229
        @history    2018.05.11  Jeongwoo    Override QEngineeringAbstractItem's
1230
                    humkyung 2018.05.13 update after change color
1231
    '''
1232
    def setColor(self, color):
1233
        self.changeAttributes('fill', color)
1234
        self.changeAttributes('stroke', color)
1235
        self.renderer().load(self._document.toByteArray())
1236
        self.update()
1237

    
1238
    def getColor(self):
1239
        """ return hover color if mouse is over otherwise reutrn owner's color if owner exist else this color """
1240
        return SymbolSvgItem.HOVER_COLOR if self.hover else (self.owner._color if self.owner and hasattr(self.owner, '_color') else self._color)
1241

    
1242
    '''
1243
        @brief  get attributes from svg file
1244
        @author humkyung
1245
        @date   2019.03.08
1246
    '''
1247
    def get_attribute(self, attName):
1248
        root = self._document.documentElement()
1249
        node = root.firstChild()
1250
        while not node.isNull():
1251
            if node.isElement():
1252
                element = node.toElement()
1253
                if element.hasAttribute(attName):
1254
                    return element.attribute(attName)
1255

    
1256
                if element.hasChildNodes():
1257
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
1258
                    if att_val is not None: return att_val
1259

    
1260
            node = node.nextSibling()
1261

    
1262
        return None
1263

    
1264
    '''
1265
        @brief  get recursively attribute
1266
        @author humkyung
1267
        @date   2019.03.08
1268
    '''
1269
    def recursive_get_attribute(self, node, attName):
1270
        while not node.isNull():
1271
            if node.isElement():
1272
                element = node.toElement()
1273
                if element.hasAttribute(attName):
1274
                    return element.attribute(attName)
1275

    
1276
                if node.hasChildNodes():
1277
                    att_val = self.recursive_get_attribute(node.firstChild(), attName)
1278
                    if att_val is not None: return att_val
1279
            
1280
            node = node.nextSibling()
1281

    
1282
        return None
1283

    
1284
    '''
1285
        @brief  change attributes
1286
        @author humkyung
1287
        @date   2018.05.10
1288
    '''
1289
    def changeAttributes(self, attName, attValue):
1290
        root = self._document.documentElement()
1291
        node = root.firstChild()
1292
        while not node.isNull():
1293
            if node.isElement():
1294
                element = node.toElement()
1295
                if element.hasAttribute(attName):
1296
                    element.setAttribute(attName, attValue)
1297

    
1298
                if element.hasChildNodes():
1299
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
1300

    
1301
            node = node.nextSibling()
1302

    
1303
    '''
1304
        @brief  change attribute
1305
        @author humkyung
1306
        @date   2018.05.10
1307
    '''
1308
    def recursiveChangeAttributes(self, node, attName, attValue):
1309
        while not node.isNull():
1310
            if node.isElement():
1311
                element = node.toElement()
1312
                if element.hasAttribute(attName):
1313
                    element.setAttribute(attName, attValue)
1314

    
1315
                if node.hasChildNodes():
1316
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
1317
            
1318
            node = node.nextSibling()
1319

    
1320
    '''
1321
        @brief  draw rect when item is selected
1322
        @author humkyung
1323
        @date   2018.07.07
1324
    '''
1325
    def drawFocusRect(self, painter):
1326
        self.focuspen = QPen(Qt.DotLine)
1327
        self.focuspen.setColor(Qt.black)
1328
        self.focuspen.setWidthF(1.5)
1329
        hilightColor = QColor(255, 0, 0, 127)
1330
        painter.setBrush(QBrush(hilightColor))
1331
        painter.setPen(self.focuspen)
1332
        painter.drawRect(self.boundingRect())
1333

    
1334
    '''
1335
        @brief  override paint(draw connection points)
1336
        @author humkyung
1337
        @date   2018.04.21
1338
    '''
1339
    def paint(self, painter, options=None, widget=None):
1340
        from EngineeringAbstractItem import QEngineeringAbstractItem
1341
        from EngineeringTextItem import QEngineeringTextItem
1342

    
1343
        self.setColor(self.getColor())
1344

    
1345
        painter.setClipRect(options.exposedRect)
1346
        QGraphicsSvgItem.paint(self, painter, options, widget)
1347
        for attr in self.attrs:
1348
            if issubclass(type(attr), QEngineeringTextItem):
1349
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
1350
                attr.setColor(color)
1351
            elif issubclass(type(attr), SymbolSvgItem):
1352
                attr.setColor(self.getColor())
1353
                attr.update()
1354

    
1355
        if self.isSelected():
1356
            self.drawFocusRect(painter)
1357

    
1358
    '''
1359
        @brief      Add Svg Item into ImageViewer's Scene
1360
        @author     Jeongwoo
1361
        @date       2018.05.03
1362
        @history    add connectors which's parent is symbol
1363
                    kyouho  2018.07.30  remove connectors logic
1364
    '''
1365
    def addSvgItemToScene(self, scene):
1366
        transform = QTransform()
1367
        #print(self.symbolOrigin)
1368

    
1369
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
1370
        transform.rotateRadians(-self.angle)
1371
        currentPoint = self.getCurrentPoint()
1372
        transform.translate(-currentPoint[0], -currentPoint[1])
1373

    
1374
        if self.flip is 1:
1375
            transform.scale(-1.0, 1.0)
1376
            transform.translate(-self.size[0], 0)
1377
            #allowed_error = 0.01
1378
            #if abs(self.angle - 0) <= allowed_error or abs(self.angle - 3.14) <= allowed_error:
1379
            #    transform.translate(-self.size[0], 0)
1380
            #elif abs(self.angle - 1.57) <= allowed_error:
1381
            #    transform.translate(0, 0)
1382
            #else:
1383
            #    transform.translate(0, 0)
1384

    
1385
        self.setTransform(transform)
1386
        scene.addItem(self)
1387

    
1388
    '''
1389
        @brief      
1390
        @author     humkyung
1391
        @date       2018.07.27
1392
    '''
1393
    def onConnectorPosChaned(self, connector):
1394
        pass
1395

    
1396
    '''
1397
        @brief      set connector
1398
        @author     kyouho
1399
        @date       2018.07.26
1400
    '''
1401
    def setConnector(self, index):
1402
        connector = QEngineeringConnectorItem(parent=self, index=index)
1403
        connector.setParentItem(self)
1404
        self.connectors.append(connector)
1405

    
1406
    '''
1407
    '''
1408
    def refreshConnector(self):
1409
        for connector in self.connectors:
1410
            connector.buildItem()
1411

    
1412
    '''
1413
        @brief      Delete svg item
1414
        @author     Jeongwoo
1415
        @date       2018.05.25
1416
    '''
1417
    def deleteSvgItemFromScene(self):
1418
        for connector in self.connectors:
1419
            if connector.connectedItem is not None:
1420
                try:
1421
                    connector.connectedItem.removeSymbol(self)
1422
                except Exception as ex:
1423
                    from App import App
1424
                    from AppDocData import MessageType
1425

    
1426
                    message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1427
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
1428
                break
1429

    
1430
        self.transfer.onRemoved.emit(self)
1431
        
1432
    '''
1433
        @brief      Return real item position
1434
        @author     Jeongwoo
1435
        @date       2018.05.25
1436
    '''
1437
    def boundingRectOnScene(self):
1438
        rect = self.boundingRect()
1439
        rect.moveTo(self.loc[0], self.loc[1])
1440
        return rect
1441

    
1442
    def flipSymbol(self):
1443
        '''
1444
            @brief  remove item when user press delete key
1445
            @author humkyung
1446
            @date   2018.04.23
1447
        '''
1448
        if self.flip is 0:
1449
            self.flip = 1
1450
        else:
1451
            self.flip = 0
1452

    
1453
        currentPoint = self.getCurrentPoint()
1454
        self.reSettingSymbol(currentPoint, self.angle)
1455

    
1456
    '''
1457
        @brief      rotate Symbol
1458
        @author     kyouho
1459
        @date       2018.07.24
1460
    '''
1461
    def rotateSymbol(self, angle=None):
1462
        if angle is None:
1463
            #degree 0
1464
            if 0 == self.angle:
1465
                self.angle = 1.57
1466
            #degree 90
1467
            elif (1.57 == self.angle):
1468
                self.angle = 3.14
1469
            #degree 180
1470
            elif 3.14 == self.angle:
1471
                self.angle = 4.71
1472
            #degree 270
1473
            elif 4.71 == self.angle :
1474
                self.angle = 0
1475
            else:
1476
                self.angle = 0
1477
                
1478
            self.size[0], self.size[1] = self.size[1], self.size[0]
1479
        else:
1480
            self.angle = angle
1481
        
1482
        #scene = self.scene()
1483
        #self.scene().removeItem(self)
1484
        #self.addSvgItemToScene(scene)
1485
        currentPoint = self.getCurrentPoint()
1486
        self.reSettingSymbol(currentPoint, self.angle)
1487

    
1488
    '''
1489
        @brief      resetting rotate Symbol
1490
        @author     kyouho
1491
        @date       2018.07.24
1492
    '''
1493
    def reSettingSymbol(self, standardPoint, angle):
1494
        transform = QTransform()
1495
        
1496
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
1497
        transform.rotateRadians(-angle)
1498
        transform.translate(-standardPoint[0], -standardPoint[1])
1499

    
1500
        if self.flip is 1:
1501
            transform.scale(-1.0, 1.0)
1502
            transform.translate(-self.size[0], 0)
1503

    
1504
        self.setTransform(transform)
1505
        self.reSettingConnetors()
1506

    
1507
    def reSettingConnetors(self):
1508
        for conn in self.connectors:
1509
            conn.sceneConnectPoint = (conn.sceneBoundingRect().center().x(), conn.sceneBoundingRect().center().y())
1510

    
1511
        from EngineeringLineItem import QEngineeringLineItem
1512
        for connector in self.connectors:
1513
            if connector.connectedItem is not None and type(connector.connectedItem) == QEngineeringLineItem:
1514
                line = connector.connectedItem
1515
                line.reDrawLine(self, connector.center())
1516
                line.update_arrow()
1517

    
1518
    '''
1519
        @brief      change Conn point 
1520
        @author     kyouho
1521
        @date       2018.07.25
1522
    '''
1523
    def changeConnPoint(self):
1524
        if len(self.connectors) == 2:
1525

    
1526
            conn1Item = self.connectors[0].connectedItem
1527
            conn2Item = self.connectors[1].connectedItem
1528
            self.connectors[0].connectedItem = conn2Item
1529
            self.connectors[1].connectedItem = conn1Item
1530

    
1531
            currentPoint = self.getCurrentPoint()
1532
            self.reSettingSymbol(currentPoint, self.angle)
1533

    
1534
    '''
1535
        @brief      change standard point
1536
        @author     kyouho
1537
        @date       2018.07.24
1538
    '''
1539
    def changeStandardPoint(self):
1540
        connPtsCount = len(self.connectors)
1541
        
1542
        if self.currentPointModeIndex < connPtsCount:
1543
            self.currentPointModeIndex += 1
1544
        else:
1545
            self.currentPointModeIndex = 0
1546

    
1547
        currentPoint = self.getCurrentPoint()
1548
        self.reSettingSymbol(currentPoint, self.angle)
1549
    
1550
    '''
1551
        @brief      get standard point
1552
        @author     kyouho
1553
        @date       2018.07.25
1554
    '''
1555
    def getCurrentPoint(self):
1556
        #from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1557

    
1558
        pointList = []
1559
        pointList.append(self.symbolOrigin)
1560
        for connector in self.connectors:
1561
            pointList.append(connector.connectPoint)
1562

    
1563
        #if type(self) is QEngineeringSpecBreakItem:
1564
        #    self.currentPointModeIndex = 1
1565

    
1566
        return pointList[self.currentPointModeIndex]
1567

    
1568
    '''
1569
        @brief      심볼 회전 시 서로 다른 기준점으로 회전하기 때문에 기준점을 이후 개발한 SymbolSvgItem 기준의 회전좌표로 이동하기 위해서 만듬 (loc 기준으로 회전해야함)
1570
        @author     kyouho
1571
        @date       18.08.06
1572
    '''
1573
    def reCalculationRotatedItem(self):
1574

    
1575
        transform = QTransform()
1576
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
1577
        transform.rotateRadians(-self.angle)
1578
        currentPoint = self.getCurrentPoint()
1579
        transform.translate(-currentPoint[0], -currentPoint[1])
1580
        # 시작점을 구하기 위해서
1581
        goPoint = transform.map(QPoint(self.symbolOrigin[0], self.symbolOrigin[1]))
1582
        
1583
        self.loc = [self.loc[0] + self.origin[0] - goPoint.x(), self.loc[1] + self.origin[1] - goPoint.y()]
1584

    
1585

    
1586
def recursiveChangeAttributes(node, attName, attValue):
1587
    while not node.isNull():
1588
        if node.isElement():
1589
            element = node.toElement()
1590
            if element.hasAttribute(attName):
1591
                element.setAttribute(attName, attValue)
1592

    
1593
            if node.hasChildNodes():
1594
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
1595
        
1596
        node = node.nextSibling()
1597

    
1598
'''
1599
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
1600
    @author     Jeongwoo
1601
    @date       2018.06.18
1602
'''
1603
class Transfer(QObject):
1604
    onRemoved = pyqtSignal(QGraphicsItem)
1605

    
1606
    def __init__(self, parent = None):
1607
        QObject.__init__(self, parent)
1608

    
1609

    
1610
if __name__ == '__main__':
1611
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
1612
    f.open(QIODevice.ReadOnly)
1613
    array = f.readAll()
1614
    document = QDomDocument()
1615
    document.setContent(array)
1616

    
1617
    root = document.documentElement()
1618
    node = root.firstChild()
1619
    while not node.isNull():
1620
        if node.isElement():
1621
            element = node.toElement()
1622
            if element.hasAttribute('fill'):
1623
                element.setAttribute('fill', '#FFFFF')
1624

    
1625
            if element.hasChildNodes():
1626
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
1627

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