프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / Shapes / SymbolSvgItem.py @ de8b8993

이력 | 보기 | 이력해설 | 다운로드 (61.2 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
    def attrib(self, name):
166
        """ return the value of given attribute with name """
167
        matches = [(attr,value) for attr,value in self.getAttributes().items() if attr.Attribute == name]
168
        if matches: return matches[0][1]
169

    
170
        return None
171

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

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

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

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

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

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

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

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

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

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

    
299
        #print([minX, minY, maxX, maxY])
300

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

    
303
    def associations(self):
304
        """ return associated instance """
305
        import uuid
306

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

    
322
        for key in self.attrs.keys():
323
            if type(key.AssocItem) is uuid.UUID:
324
                for assoc in res:
325
                    if str(key.AssocItem) == str(assoc.uid):
326
                        key.AssocItem = assoc
327

    
328
        return res
329

    
330
    def texts(self):
331
        """ return text type of associations """
332
        from EngineeringTextItem import QEngineeringTextItem
333

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

    
343
        return res
344

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

    
356
        return res
357

    
358
    def toSql(self):
359
        """ convert valve data to sql query """
360
        import uuid
361
        from AppDocData import AppDocData
362

    
363
        res = []
364
        appDocData = AppDocData.instance()
365

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

    
373
        _attrs = self.getAttributes()
374
        for key in _attrs.keys():
375
            cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value']
376
            values = ['?', '?', '?', '?']
377
            param = [str(uuid.uuid4()), str(self.uid), str(key.UID), str(_attrs[key])]
378

    
379
            sql = 'insert or replace into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
380
            res.append((sql, tuple(param)))
381

    
382
        # save connectors to database
383
        for connector in self.connectors:
384
            res.append(connector.toSql())
385
        # up to here
386

    
387
        return res
388

    
389
    '''
390
        @brief  build symbol item
391
        @author humkyung
392
        @date   2018.05.02
393
        @history    2018.05.09  Jeongwoo    Clear self.connectors
394
                    2018.05.30  Jeongwoo    Add parameters (parentSymbol, childSymbol)
395
    '''
396
    def buildItem(self, name, _type, angle, loc, size, origin, connPts, parentSymbol, childSymbol, hasInstrumentLabel, dbUid=None):
397
        try:
398
            docData = AppDocData.instance()
399
            self.name = name
400
            self.type = _type
401
            self.angle = angle
402
            self.loc = loc
403
            self.size = size
404
            self.origin = origin
405
            if dbUid is None:
406
                symbolInfo = docData.getSymbolByQuery('name', name)
407
            else:
408
                symbolInfo = docData.getSymbolByQuery('UID', dbUid)
409
            self.dbUid = symbolInfo.uid
410
            originalPoint = symbolInfo.getOriginalPoint().split(',')
411
            self.symbolOrigin = [float(originalPoint[0]), float(originalPoint[1])]
412
            if self.flip is 1:
413
                self.symbolOrigin[0] = self.size[0] - self.symbolOrigin[0]
414

    
415
            # setting connectors
416
            connectionPoints = symbolInfo.getConnectionPoint().split('/')
417
            #print(connectionPoints)
418
            for index in range(len(connectionPoints)):
419
                if connectionPoints[index] == '':
420
                    break
421
                tokens = connectionPoints[index].split(',')
422

    
423
                direction = 'AUTO'
424
                symbol_idx = '0'
425
                if len(tokens) == 2:
426
                    x = float(tokens[0])
427
                    y = float(tokens[1])
428
                elif len(tokens) == 3:
429
                    direction = tokens[0]
430
                    x = float(tokens[1])
431
                    y = float(tokens[2])
432
                elif len(tokens) == 4:
433
                    direction = tokens[0]
434
                    x = float(tokens[1])
435
                    y = float(tokens[2])
436
                    symbol_idx = tokens[3]
437

    
438
                self.setConnector(index+1)
439
                self.connectors[index].direction = direction
440
                self.connectors[index].symbol_idx = symbol_idx
441
                self.connectors[index].setPos((x, y))
442
                self.connectors[index].connectPoint = (x, y)
443
                self.connectors[index].sceneConnectPoint = (connPts[index][0], connPts[index][1]) if len(connPts[index]) == 2 else \
444
                                                           (connPts[index][1], connPts[index][2]) if len(connPts[index]) == 3 else \
445
                                                           (connPts[index][1], connPts[index][2]) if len(connPts[index]) == 4 else None
446
                #print((x, y))
447
                #print(self.connectors[index].sceneConnectPoint)
448
                
449
            self.parentSymbol = parentSymbol
450
            self.childSymbol = childSymbol
451
            self.hasInstrumentLabel = hasInstrumentLabel
452
            self.currentPointModeIndex = 0
453

    
454
            tooltip = '<b>{}</b><br>{}={}'.format(str(self.uid), self.type, self.name)
455
            self.setToolTip(tooltip)
456
        except Exception as ex:
457
            from App import App 
458

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

    
462
    '''
463
        @brief  return bounding box of symbol
464
        @author humkyung
465
        @date   2018.04.08
466
    '''
467
    def rect(self):
468
        return self.sceneBoundingRect()
469
        
470
    '''
471
        @brief  return true if line is able to connect symbol
472
        @author humkyung
473
        @date   2018.04.13
474
    '''
475
    def is_connectable(self, item, toler=10):
476
        #from EngineeringLineItem import QEngineeringLineItem
477

    
478
        '''
479
        if False:#type(item) is QEngineeringLineItem:
480
            line = item
481
            start_pt = line.startPoint()
482
            end_pt = line.endPoint()
483
            for connector in self.connectors:
484
                dx = connector.sceneConnectPoint[0] - (start_pt[0])
485
                dy = connector.sceneConnectPoint[1] - (start_pt[1])
486
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
487
                dx = connector.sceneConnectPoint[0] - (end_pt[0])
488
                dy = connector.sceneConnectPoint[1] - (end_pt[1])
489
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
490
        elif True:#issubclass(type(item), SymbolSvgItem):
491
        '''
492
        for connector in self.connectors:
493
            for iConnector in item.connectors:
494
                dx = connector.sceneConnectPoint[0] - iConnector.sceneConnectPoint[0]
495
                dy = connector.sceneConnectPoint[1] - iConnector.sceneConnectPoint[1]
496
                if (math.sqrt(dx*dx + dy*dy) < toler): return True
497

    
498
        return False
499
 
500
    '''
501
        @author     humkyung
502
        @date       2018.07.03
503
    '''
504
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
505
        """ check if given item is connected to self """
506

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

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

    
513
        lhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == lhs]
514
        rhs_matches = [at for at in range(len(self.connectors)) if self.connectors[at].connectedItem == rhs]
515
        if lhs_matches and rhs_matches:
516
            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])
517

    
518
        return False
519

    
520
    def canBeSecondary(self, line):
521
        """ check given line is not connected(ex: 0-1, 2-3) """
522
        preItem = None
523

    
524
        item = [item.connectedItem for item in self.connectors if item.connectedItem is not None and item.connectedItem.owner is not None]
525
        if item:
526
            preItem = item[0]
527

    
528
        if preItem is not None and not self.next_connected(line, preItem):
529
            return True
530
        else:
531
            return False
532

    
533
    '''
534
        @brief      connect line and symbol is able to be connected and return line
535
        @author     humkyung
536
        @date       2018.04.16
537
        @history    humkyung 2018.05.08 check if symbol is possible to be connected
538
                    Jeongwoo 2018.05.15 Connect each symbol and line
539
    '''
540
    def connect_if_possible(self, obj, toler=10):
541
        from shapely.geometry import Point
542
        from EngineeringLineItem import QEngineeringLineItem
543

    
544
        res = []
545
        try:
546
            if type(obj) is QEngineeringLineItem:
547
                startPt = obj.startPoint()
548
                endPt = obj.endPoint()
549
                for i in range(len(self.connectors)):
550
                    if (Point(startPt[0], startPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0], self.connectors[i].sceneConnectPoint[1])) < toler):
551
                        if self.connectors[i].connectedItem is None:
552
                            self.connectors[i].connect(obj)
553
                        if obj.connectors[0].connectedItem is None:
554
                            obj.connectors[0].connect(self)
555
                        
556
                        res.append(obj)
557
                    if (Point(endPt[0], endPt[1]).distance(Point(self.connectors[i].sceneConnectPoint[0], self.connectors[i].sceneConnectPoint[1])) < toler):
558
                        if self.connectors[i].connectedItem is None:
559
                            self.connectors[i].connect(obj)
560
                        if obj.connectors[1].connectedItem is None:
561
                            obj.connectors[1].connect(self)
562
                        
563
                        res.append(obj)
564
            elif issubclass(type(obj), SymbolSvgItem):
565
                for i in range(len(self.connectors)):
566
                    for j in range(len(obj.connectors)):
567
                        _pt = Point(obj.connectors[j].sceneConnectPoint[0], obj.connectors[j].sceneConnectPoint[1])
568
                        if (_pt.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[j].connectedItem is None:
572
                                obj.connectors[j].connect(self)
573

    
574
                            res.append(obj)
575
        except Exception as ex:
576
            from App import App 
577
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
578
            App.mainWnd().addMessage.emit(MessageType.Error, message)
579

    
580
        return res
581

    
582
    '''
583
        @brief      disconnect connector item
584
        @author     kyouho
585
        @date       2018.08.30
586
    '''
587
    def disconnectedItemAtConnector(self, connector):
588
        for conn in self.connectors:
589
            if conn.isOverlapConnector(connector):
590
                conn.connectedItem = None
591

    
592
    '''
593
        @brief  get connection point close to given point in tolerance
594
        @author humkyung
595
        @dat
596
    '''
597
    def getConnectionPointCloseTo(self, pt, toler=10):
598
        import math
599

    
600
        for connector in self.connectors:
601
            dx = connector.sceneConnectPoint[0] - pt[0]
602
            dy = connector.sceneConnectPoint[1] - pt[1]
603
            if math.sqrt(dx*dx + dy*dy) < toler: return connPt
604
            
605
        return None
606

    
607
    '''
608
        @brief  return center of symbol
609
        @author humkyung
610
        @date   2018.04.08
611
    '''
612
    def center(self):
613
        return self.sceneBoundingRect().center()
614
        
615
    '''
616
        @brief      highlight connector and attribute
617
        @authro     humkyung
618
        @date       2018.05.02
619
    '''
620
    def hoverEnterEvent(self, event):
621
        self.highlight(True)
622

    
623
    '''
624
        @brief      unhighlight connector and attribute
625
        @author     humkyung
626
        @date       2018.05.02
627
        @history    kyouho 2018.07.18 edit ArrowCursor
628
    '''
629
    def hoverLeaveEvent(self, event):
630
        self.highlight(False)
631

    
632
    def highlight(self, flag):
633
        """ highlight/unhighlight the symbol """
634

    
635
        try:
636
            self.hover = flag 
637
            self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(SymbolSvgItem.ZVALUE)
638
            self.update()
639

    
640
            for assoc in self.associations():
641
                assoc.highlight(flag)
642

    
643
            for connector in self.connectors:
644
                connector.highlight(flag)
645
        except Exception as ex: 
646
            from App import App 
647
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
648
            App.mainWnd().addMessage.emit(MessageType.Error, message)
649

    
650
    '''
651
        @brief      set highlight
652
        @author     kyouho
653
        @date       2018.08.27
654
    '''
655
    def setHightlight(self):
656
        self.setColor('url(#hover)')
657
        self.update()
658

    
659
    '''
660
        @brief      unset highlight
661
        @author     kyouho
662
        @date       2018.08.27
663
    '''
664
    def unsetHightlight(self):
665
        self.setColor('url(#normal)')
666
        self.update()
667

    
668
    '''
669
        @brief  change cursor to CrossCursor if mouse point is close to connection point
670
        @author humkyung
671
        @date   2018.04.28
672
    '''
673
    def hoverMoveEvent(self, event):
674
        pass
675

    
676
    '''
677
        @brief      Mouse Press Event
678
        @author     Jeongwoo
679
        @date       18.04.11
680
        @history    kyouho 2018.07.18 add isClick logic
681
    '''
682
    def mousePressEvent(self, event):
683
        if event.buttons() == Qt.LeftButton:
684
            self.clicked.emit(self)
685

    
686
    '''
687
        @brief      Mouse Release Event
688
        @author     kyouho
689
        @date       18.07.17
690
    '''
691
    def mouseReleaseEvent(self, event):
692
        super().mouseReleaseEvent(event)
693

    
694
    def removeSelfAttr(self, attributeName):
695
        target = None
696
        for attr in self.attrs:
697
            if attr.Attribute == attributeName:
698
                target = attr
699
                break
700
                
701
        if target:
702
            del self.attrs[attr]
703

    
704
    '''
705
        @brief      Find TextItem contain Point
706
        @author     kyouho
707
        @date       18.07.17
708
    '''
709
    def findTextItemInPoint(self, point):
710
        from EngineeringTextItem import QEngineeringTextItem
711
        
712
        scene = self.scene()
713
 
714
        for item in scene.items():
715
            if type(item) is QEngineeringTextItem:
716
                if self.isOverlapItemAndPoint(item, point):
717
                    return (True, item)
718

    
719
        return (False,)
720

    
721
    '''
722
        @brief      Check Overlap
723
        @author     kyouho
724
        @date       18.07.17
725
    '''
726
    def isOverlapItemAndPoint(self, item, point):
727
        x = point.x()
728
        y = point.y()
729
        loc = item.loc
730
        size = item.size
731

    
732
        if loc[0] <= x and loc[0] + size[0] >= x and loc[1] <= y and loc[1] + size[1] >= y:
733
            return True
734
        else:
735
            return False
736

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

    
771
        try:
772
            if clear:
773
                self.clear_attr_and_assoc_item()
774

    
775
            configs = AppDocData.instance().getConfigs('Range', 'Detection Ratio')
776
            ratio = float(configs[0].value) if 1 == len(configs) else 1.5
777
            
778
            dist = max(self.sceneBoundingRect().height(), self.sceneBoundingRect().width()) * ratio
779
            center = self.sceneBoundingRect().center()
780

    
781
            minDist = None
782
            selected = None
783
            for attr in attributes:
784
                # size text and operation code text will find onwer themselves in findowner method
785
                if False:# type(attr) is QEngineeringSizeTextItem or type(attr) is QEngineeringValveOperCodeTextItem:
786
                    dx = attr.center().x() - center.x()
787
                    dy = attr.center().y() - center.y()
788
                    length = math.sqrt(dx*dx + dy*dy)
789
                    if (length < dist) and (minDist is None or length < minDist):
790
                        minDist = length
791
                        selected = attr
792
                elif type(attr) is QEngineeringInstrumentItem:
793
                    if not attr.is_connected:
794
                        dx = attr.center().x() - center.x()
795
                        dy = attr.center().y() - center.y()
796
                        if math.sqrt(dx*dx + dy*dy) < dist:
797
                            if self.add_assoc_item(attr):
798
                                attr.owner = self
799

    
800
            if selected is not None:
801
                if self.add_assoc_item(selected):
802
                    selected.owner = self
803

    
804
        except Exception as ex:
805
            from App import App 
806
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
807
            App.mainWnd().addMessage.emit(MessageType.Error, message)
808

    
809
    '''
810
        @brief      Double click event, Show rotate symbol dialog
811
        @author     euisung
812
        @date       2019.04.16
813
    '''
814
    def mouseDoubleClickEvent(self, event):
815
        from RotateSymbolDialog import QRotateSymbolDialog
816
        dialog = QRotateSymbolDialog(None, self.angle)
817
        (isAccept, angle) = dialog.showDialog()
818
  
819
        if isAccept:
820
            self.rotateSymbol(angle)
821

    
822
    '''
823
        @brief      get attribute
824
        @author     humkyung
825
        @date       2018.06.14
826
        @history    kyouho  2018.07.18  Add only attr QEngineeringTextItem
827
    '''
828
    def getAttributes(self):
829
        _attrs = {}
830
        try:
831
            from AppDocData import AppDocData
832
            from EngineeringAbstractItem import QEngineeringAbstractItem
833
            from EngineeringTextItem import QEngineeringTextItem
834
            from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
835

    
836
            """ get attributes of item from database """
837
            docData = AppDocData.instance()
838
            symbolAttrs = docData.getSymbolAttribute(self.type)
839

    
840
            _texts = self.texts()
841
            _symbols = self.symbols()
842
            for attr in symbolAttrs:
843
                matches = [_attr for _attr,_ in self.attrs.items() if _attr.UID == attr.UID]
844
                if matches:
845
                    attr.Freeze = matches[0].Freeze         ### update freeze value
846
                    attr.AssocItem = matches[0].AssocItem
847
                    _attrs[attr] = self.attrs[matches[0]]   ### copy attribute value
848
                else:
849
                    _attrs[attr] = ''
850
 
851
                if attr.Freeze: continue    ### do not evalulate value if attribute is frozen
852
                if attr.AttributeType == 'Size Text Item' or attr.AttributeType == 'Text Item' or attr.AttributeType == 'Valve Oper Code':
853
                    at = int(attr.AttrAt)
854
                    items = [text for text in _texts if QEngineeringAbstractItem.assoc_type(text) == attr.AttributeType]
855
                    if not attr.AssocItem and len(items) > at:
856
                        attr.AssocItem = items[at]
857
                        item = attr.AssocItem
858
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
859
                    elif attr.AssocItem:
860
                        item = attr.AssocItem
861
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
862
                    else:
863
                        _attrs[attr] = ''
864
                elif attr.AttributeType == 'Symbol Item':
865
                    at = int(attr.AttrAt)
866
                    if not attr.AssocItem and len(_symbols) > at:
867
                        attr.AssocItem = _symbols[at]
868
                        item = attr.AssocItem
869
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
870
                    elif attr.AssocItem:
871
                        item = attr.AssocItem
872
                        _attrs[attr] = eval(attr.Expression) if attr.Expression else ''
873
                    else:
874
                        _attrs[attr] = ''
875
            
876
            self.attrs = _attrs ### assign self.attrs
877
        except Exception as ex:
878
            from App import App 
879
            from AppDocData import MessageType
880

    
881
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
882
            App.mainWnd().addMessage.emit(MessageType.Error, message)
883
        
884
        return self.attrs
885

    
886
    '''
887
        @brief      generate xml code
888
        @author     humkyung
889
        @date       2018.04.23
890
        @history    humkyung 2018.04.25 add angle xml node
891
                    humkyung 2018.04.27 add originalpoint xml node
892
                    humkyung 2018.05.02 add attribute of symbol
893
                    Jeongwoo 2018.05.30 add attribute of symbol (parentSymbol, childSymbol, type, connectionPoint)
894
                    yecheol  2018.07.11 add attribute of symbol (hasInstrumentLabel)
895
                    humkyung 2018.07.20 write owner's uid to xml
896
                    humkyung 2018.07.23 write connected item's uid to xml
897
                    kyouho  2018.07.31 
898
                    humkyung 2018.09.06 write area to xml
899
    '''
900
    def toXml(self):
901
        import uuid
902
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
903
        from EngineeringAbstractItem import QEngineeringAbstractItem
904
        from EngineeringTextItem import QEngineeringTextItem
905
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
906
        from SymbolAttr import SymbolAttr
907

    
908
        try:
909
            node = Element('SYMBOL')
910
            uidNode = Element('UID')
911
            uidNode.text = str(self.uid)
912
            node.append(uidNode)
913

    
914
            dbUidNode = Element('DBUID')
915
            dbUidNode.text = str(self.dbUid)
916
            node.append(dbUidNode)
917

    
918
            nameNode = Element('NAME')
919
            nameNode.text = self.name
920
            node.append(nameNode)
921

    
922
            attributeValueNode = Element('ASSOCIATIONS')
923
            for assoc in self.associations():
924
                assoc_node = Element('ASSOCIATION')
925
                assoc_node.attrib['TYPE'] = QEngineeringAbstractItem.assoc_type(assoc)
926
                assoc_node.text = str(assoc.uid)
927
                attributeValueNode.append(assoc_node)
928
            node.append(attributeValueNode)
929

    
930
            typeNode = Element('TYPE')
931
            typeNode.text = self.type
932
            node.append(typeNode)
933

    
934
            # write owner's uid to xml
935
            ownerNode = Element('OWNER')
936
            if self.owner is not None:
937
                ownerNode.text = str(self.owner)
938
            else:
939
                ownerNode.text = 'None'
940
            node.append(ownerNode)
941
            # up to here
942

    
943
            originNode = Element('ORIGINALPOINT')
944
            originNode.text = '{},{}'.format(self.origin[0], self.origin[1])
945
            node.append(originNode)
946

    
947
            connectorsNode = Element('CONNECTORS')
948
            for connector in self.connectors:
949
                connectorsNode.append(connector.toXml())
950
            node.append(connectorsNode)
951

    
952
            connectionNode = Element('CONNECTIONPOINT')
953
            connection_point = []
954
            if self.connectors is not None:
955
                for connector in self.connectors:
956
                    connection_point.append(repr(connector))
957
            connectionNode.text = '/'.join(connection_point)
958
            node.append(connectionNode)
959

    
960
           #rect = self.sceneBoundingRect()
961
            locNode = Element('LOCATION')
962
            locNode.text = '{},{}'.format(self.loc[0], self.loc[1])
963
            node.append(locNode)
964

    
965
            sizeNode = Element('SIZE')
966
            #sizeNode.text = '{},{}'.format(rect.width(), rect.height())
967
            sizeNode.text = '{},{}'.format(self.size[0], self.size[1])
968
            node.append(sizeNode)
969

    
970
            angleNode = Element('ANGLE')
971
            angleNode.text = str(self.angle)
972
            node.append(angleNode)
973

    
974
            parentSymbolNode = Element('PARENT')
975
            parentSymbolNode.text = str(self.parentSymbol)
976
            node.append(parentSymbolNode)
977

    
978
            childSymbolNode = Element('CHILD')
979
            childSymbolNode.text = str(self.childSymbol)
980
            node.append(childSymbolNode)
981

    
982
            hasInstrumentLabelNode = Element('HASINSTRUMENTLABEL')
983
            hasInstrumentLabelNode.text = str(self.hasInstrumentLabel)
984
            node.append(hasInstrumentLabelNode)
985

    
986
            areaNode = Element('AREA')
987
            areaNode.text = self.area
988
            node.append(areaNode)
989

    
990
            flipNode = Element('FLIP')
991
            flipNode.text = str(self.flip)
992
            node.append(flipNode)
993
            
994
            properties_node = Element('PROPERTIES')
995
            for prop,value in self.properties.items():
996
                prop_node = prop.toXml()
997
                prop_node.text = str(value) if value else ''
998
                properties_node.append(prop_node)
999
            node.append(properties_node)
1000

    
1001
            attributesNode = Element('SYMBOLATTRIBUTES')
1002
            _attrs = self.getAttributes()
1003
            for attr in _attrs:
1004
                if type(attr) is SymbolAttr:
1005
                    _node = attr.toXml()
1006
                    if attr.AttributeType != 'Spec':
1007
                        _node.text = str(_attrs[attr])
1008
                    elif attr.AttributeType == 'Spec':
1009
                        _node.text = str(self.attrs[attr][0]) + ',' + str(self.attrs[attr][1])
1010
                    attributesNode.append(_node)
1011

    
1012
            node.append(attributesNode)
1013

    
1014
            currentPointModeIndexNode = Element('CURRENTPOINTMODEINDEX')
1015
            currentPointModeIndexNode.text = str(self.currentPointModeIndex)
1016
            node.append(currentPointModeIndexNode)
1017
        except Exception as ex:
1018
            from App import App 
1019
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1020
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1021

    
1022
            return None
1023

    
1024
        return node 
1025

    
1026
    '''
1027
        @brief      parse xml code
1028
        @author     humkyung
1029
        @date       2018.07.20
1030
        @history    humkyung 2018.07.20 parse uid from xml node
1031
                    humkyung 2018.07.23 parse connected item's uid from xml node
1032
                    kyouho  2018.07.31 
1033
                    humkyung 2018.09.06 assign area to item
1034
    '''
1035
    @staticmethod 
1036
    def fromXml(node):
1037
        import uuid
1038
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1039
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1040
        from SymbolAttr import SymbolAttr
1041
        item = None
1042

    
1043
        try:
1044
            uidNode = node.find('UID')
1045
            uid = uidNode.text if uidNode is not None else uuid.uuid4() # generate UUID
1046

    
1047
            pt = [float(x) for x in node.find('LOCATION').text.split(',')]
1048
            size = [float(x) for x in node.find('SIZE').text.split(',')]
1049

    
1050
            dbUidNode = node.find('DBUID')
1051
            dbUid = int(dbUidNode.text) if dbUidNode is not None else None
1052
            name = node.find('NAME').text if dbUidNode is None else AppDocData.instance().getSymbolByQuery('UID', dbUid).sName
1053

    
1054
            angle = float(node.find('ANGLE').text)
1055
            _type = node.find('TYPE').text
1056
            origin = [float(x) for x in node.find('ORIGINALPOINT').text.split(',')]
1057
            connPts = []
1058
            if node.find('CONNECTIONPOINT').text is not None:
1059
                for conn_pt in node.find('CONNECTIONPOINT').text.split('/'):
1060
                    tokens = conn_pt.split(',')
1061
                    connPts.append(('AUTO', float(tokens[0]), float(tokens[1]), '0') if len(tokens) == 2 else \
1062
                                   (tokens[0], float(tokens[1]), float(tokens[2]), '0') if len(tokens) == 3 else \
1063
                                   (tokens[0], float(tokens[1]), float(tokens[2]), tokens[3]))
1064
            baseSymbol = node.find('PARENT').text
1065
            childSymbolNode = node.find('CHILD')
1066
            childSymbol = ''
1067
            if childSymbolNode is not None:
1068
                childSymbol = childSymbolNode.text
1069
            
1070
            ownerNode = node.find('OWNER')
1071
            owner = ownerNode.text if ownerNode is not None and ownerNode.text != 'None' else None
1072

    
1073
            hasInstrumentLabelNode = node.find('HASINSTRUMENTLABEL')
1074
            hasInstrumentLabel = hasInstrumentLabelNode.text if hasInstrumentLabelNode is not None else 'False'
1075

    
1076
            flipLabelNode = node.find('FLIP')
1077
            flipLabel = int(flipLabelNode.text) if flipLabelNode is not None else 0
1078

    
1079
            appDocData = AppDocData.instance()
1080
            project = appDocData.getCurrentProject()
1081
            svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
1082
            if os.path.isfile(svgFilePath):
1083
                item = SymbolSvgItem.createItem(_type, svgFilePath, uid, owner=owner, flip=flipLabel)
1084
                item.setVisible(False)
1085
                item.buildItem(name, _type, angle, pt, size, origin, connPts, baseSymbol, childSymbol, hasInstrumentLabel, dbUid=dbUid)
1086

    
1087
                for prop_node in node.iter('PROPERTY'):
1088
                    matches = [prop for prop in item._properties.keys() if prop.Attribute == prop_node.attrib['Attribute']]
1089
                    if matches:
1090
                        #matches[0].parse_xml(prop_node)
1091
                        item._properties[matches[0]] = matches[0].parse_value(prop_node.text) if prop_node.text else ''
1092

    
1093
                ## assign area
1094
                areaNode = node.find('AREA')
1095
                if areaNode is None:
1096
                    for area in appDocData.getAreaList():
1097
                        if area.contains(pt):
1098
                            item.area = area.name
1099
                            break
1100
                else:
1101
                    item.area = areaNode.text
1102
                ## up to here
1103
                
1104
                connectors = node.find('CONNECTORS')
1105
                if connectors is not None:
1106
                    iterIndex = 0
1107
                    for connector in connectors.iter('CONNECTOR'):
1108
                        item.connectors[iterIndex].parse_xml(connector)
1109
                        iterIndex += 1
1110
                
1111
                # get associations 
1112
                attributeValue = node.find('ASSOCIATIONS')
1113
                if attributeValue is not None:
1114
                    for assoc in attributeValue.iter('ASSOCIATION'):
1115
                        _type = assoc.attrib['TYPE']
1116
                        if not _type in item._associations:
1117
                            item._associations[_type] = []
1118
                        item._associations[_type].append(uuid.UUID(assoc.text))
1119
                # up to here
1120

    
1121
                attributes = node.find('SYMBOLATTRIBUTES')
1122
                if attributes is not None:
1123
                    for attr in attributes.iter('ATTRIBUTE'):
1124
                        _attr = SymbolAttr.fromXml(attr)
1125
                        if type(item) is not QEngineeringSpecBreakItem and type(item) is not QEngineeringEndBreakItem:
1126
                            item.attrs[_attr] = attr.text
1127
                        else:
1128
                            if _attr.AttributeType == 'Spec':
1129
                                item.attrs[_attr] = [attr.text.split(',')[0], attr.text.split(',')[1]]
1130
                            elif _attr.Attribute == 'UpStream':
1131
                                for initKey in item.attrs.keys():
1132
                                    if initKey.Attribute == 'UpStream':
1133
                                        item.attrs[initKey] = attr.text
1134
                            elif _attr.Attribute == 'DownStream':
1135
                                for initKey in item.attrs.keys():
1136
                                    if initKey.Attribute == 'DownStream':
1137
                                        item.attrs[initKey] = attr.text
1138
                            
1139
                currentPointModeIndex = node.find('CURRENTPOINTMODEINDEX')
1140
                if currentPointModeIndex is not None:
1141
                    item.currentPointModeIndex = int(currentPointModeIndex.text)
1142

    
1143
                if type(item) is QEngineeringEndBreakItem:
1144
                    item.freeze_item.update_freeze(item.prop('Freeze'))
1145
                #if type(item) is QEngineeringSpecBreakItem:
1146
                #    item.connectors[0].setPos((item.connectors[0].connectPoint[0], item.connectors[0].connectPoint[1]))
1147
        except Exception as ex:
1148
            from App import App 
1149
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1150
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1151

    
1152
        return item
1153

    
1154
    '''
1155
        @brief      create item corresponding to given type
1156
        @author     humkyung
1157
        @date       2018.05.02
1158
        @history    2018.05.08  Jeongwoo    Change type name (Piping OPC''S → Piping OPC's)
1159
                    humkyung 2018.05.10 change symbol's color to blue
1160
                    humkyung 2018.07.19 create nozzle instance if type is 'Nozzles'
1161
    '''
1162
    @staticmethod
1163
    def createItem(type, path, uid=None, owner=None, flip=0):
1164
        from QEngineeringOPCItem import QEngineeringOPCItem
1165
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1166
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
1167
        from EngineeringNozzleItem import QEngineeringNozzleItem
1168
        from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1169
        from EngineeringReducerItem import QEngineeringReducerItem
1170
        from EngineeringErrorItem import QEngineeringErrorItem
1171
        from EngineeringEndBreakItem import QEngineeringEndBreakItem
1172
        from AppDocData import AppDocData
1173
        import uuid
1174

    
1175
        docData = AppDocData.instance()
1176

    
1177
        item = None
1178
        cateogry = docData.getSymbolCategoryByType(type)
1179
        if type == "Piping OPC's":
1180
            item = QEngineeringOPCItem(path, uid, flip=flip)
1181
        elif cateogry == 'Equipment':
1182
            item = QEngineeringEquipmentItem(path, uid, flip=flip)
1183
        elif cateogry == 'Instrumentation':
1184
            item = QEngineeringInstrumentItem(path, uid, flip=flip)
1185
        #elif type == 'Nozzles':
1186
        #    item = QEngineeringNozzleItem(path, uid, flip=flip)
1187
        elif type == 'Segment Breaks':
1188
            item = QEngineeringSpecBreakItem(path, uid, flip=flip)
1189
        elif type == 'Reducers':
1190
            item = QEngineeringReducerItem(path, uid, flip=flip)
1191
        elif type == 'Error':
1192
            item = QEngineeringErrorItem(path, uid, flip=flip)
1193
        elif type == 'End Break':
1194
            item = QEngineeringEndBreakItem(path, uid, flip=flip)
1195
        else:
1196
            item = SymbolSvgItem(path, uid, flip=flip)
1197

    
1198
        if owner is not None:
1199
            item.owner = uuid.UUID(owner, version=4)
1200

    
1201
        return item
1202

    
1203
    '''
1204
        @brief      change svg's color
1205
        @author     humkyung
1206
        @date       2018.05.10
1207
        @history    2018.05.11  Jeongwoo    Override QEngineeringAbstractItem's
1208
                    humkyung 2018.05.13 update after change color
1209
    '''
1210
    def setColor(self, color):
1211
        self.changeAttributes('fill', color)
1212
        self.changeAttributes('stroke', color)
1213
        self.renderer().load(self._document.toByteArray())
1214
        self.update()
1215

    
1216
    def getColor(self):
1217
        """ return hover color if mouse is over otherwise reutrn owner's color if owner exist else this color """
1218
        return SymbolSvgItem.HOVER_COLOR if self.hover else (self.owner._color if self.owner and hasattr(self.owner, '_color') else self._color)
1219

    
1220
    '''
1221
        @brief  get attributes from svg file
1222
        @author humkyung
1223
        @date   2019.03.08
1224
    '''
1225
    def get_attribute(self, attName):
1226
        root = self._document.documentElement()
1227
        node = root.firstChild()
1228
        while not node.isNull():
1229
            if node.isElement():
1230
                element = node.toElement()
1231
                if element.hasAttribute(attName):
1232
                    return element.attribute(attName)
1233

    
1234
                if element.hasChildNodes():
1235
                    att_val = self.recursive_get_attribute(element.firstChild(), attName)
1236
                    if att_val is not None: return att_val
1237

    
1238
            node = node.nextSibling()
1239

    
1240
        return None
1241

    
1242
    '''
1243
        @brief  get recursively attribute
1244
        @author humkyung
1245
        @date   2019.03.08
1246
    '''
1247
    def recursive_get_attribute(self, node, attName):
1248
        while not node.isNull():
1249
            if node.isElement():
1250
                element = node.toElement()
1251
                if element.hasAttribute(attName):
1252
                    return element.attribute(attName)
1253

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

    
1260
        return None
1261

    
1262
    '''
1263
        @brief  change attributes
1264
        @author humkyung
1265
        @date   2018.05.10
1266
    '''
1267
    def changeAttributes(self, attName, attValue):
1268
        root = self._document.documentElement()
1269
        node = root.firstChild()
1270
        while not node.isNull():
1271
            if node.isElement():
1272
                element = node.toElement()
1273
                if element.hasAttribute(attName):
1274
                    element.setAttribute(attName, attValue)
1275

    
1276
                if element.hasChildNodes():
1277
                    recursiveChangeAttributes(element.firstChild(), attName, attValue)
1278

    
1279
            node = node.nextSibling()
1280

    
1281
    '''
1282
        @brief  change attribute
1283
        @author humkyung
1284
        @date   2018.05.10
1285
    '''
1286
    def recursiveChangeAttributes(self, node, attName, attValue):
1287
        while not node.isNull():
1288
            if node.isElement():
1289
                element = node.toElement()
1290
                if element.hasAttribute(attName):
1291
                    element.setAttribute(attName, attValue)
1292

    
1293
                if node.hasChildNodes():
1294
                    recursiveChangeAttributes(node.firstChild(), attName, attValue)
1295
            
1296
            node = node.nextSibling()
1297

    
1298
    '''
1299
        @brief  draw rect when item is selected
1300
        @author humkyung
1301
        @date   2018.07.07
1302
    '''
1303
    def drawFocusRect(self, painter):
1304
        self.focuspen = QPen(Qt.DotLine)
1305
        self.focuspen.setColor(Qt.black)
1306
        self.focuspen.setWidthF(1.5)
1307
        hilightColor = QColor(255, 0, 0, 127)
1308
        painter.setBrush(QBrush(hilightColor))
1309
        painter.setPen(self.focuspen)
1310
        painter.drawRect(self.boundingRect())
1311

    
1312
    '''
1313
        @brief  override paint(draw connection points)
1314
        @author humkyung
1315
        @date   2018.04.21
1316
    '''
1317
    def paint(self, painter, options=None, widget=None):
1318
        from EngineeringAbstractItem import QEngineeringAbstractItem
1319
        from EngineeringTextItem import QEngineeringTextItem
1320

    
1321
        self.setColor(self.getColor())
1322

    
1323
        painter.setClipRect(options.exposedRect)
1324
        QGraphicsSvgItem.paint(self, painter, options, widget)
1325
        for attr in self.attrs:
1326
            if issubclass(type(attr), QEngineeringTextItem):
1327
                color = QEngineeringAbstractItem.HOVER_COLOR if self.hover else (self._owner._color if self._owner else QEngineeringAbstractItem.DEFAULT_COLOR)
1328
                attr.setColor(color)
1329
            elif issubclass(type(attr), SymbolSvgItem):
1330
                attr.setColor(self.getColor())
1331
                attr.update()
1332

    
1333
        if self.isSelected():
1334
            self.drawFocusRect(painter)
1335

    
1336
    '''
1337
        @brief      Add Svg Item into ImageViewer's Scene
1338
        @author     Jeongwoo
1339
        @date       2018.05.03
1340
        @history    add connectors which's parent is symbol
1341
                    kyouho  2018.07.30  remove connectors logic
1342
    '''
1343
    def addSvgItemToScene(self, scene):
1344
        transform = QTransform()
1345
        #print(self.symbolOrigin)
1346

    
1347
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
1348
        transform.rotateRadians(-self.angle)
1349
        currentPoint = self.getCurrentPoint()
1350
        transform.translate(-currentPoint[0], -currentPoint[1])
1351

    
1352
        if self.flip is 1:
1353
            transform.scale(-1.0, 1.0)
1354
            transform.translate(-self.size[0], 0)
1355
            #allowed_error = 0.01
1356
            #if abs(self.angle - 0) <= allowed_error or abs(self.angle - 3.14) <= allowed_error:
1357
            #    transform.translate(-self.size[0], 0)
1358
            #elif abs(self.angle - 1.57) <= allowed_error:
1359
            #    transform.translate(0, 0)
1360
            #else:
1361
            #    transform.translate(0, 0)
1362

    
1363
        self.setTransform(transform)
1364
        scene.addItem(self)
1365

    
1366
    '''
1367
        @brief      
1368
        @author     humkyung
1369
        @date       2018.07.27
1370
    '''
1371
    def onConnectorPosChaned(self, connector):
1372
        pass
1373

    
1374
    '''
1375
        @brief      set connector
1376
        @author     kyouho
1377
        @date       2018.07.26
1378
    '''
1379
    def setConnector(self, index):
1380
        connector = QEngineeringConnectorItem(parent=self, index=index)
1381
        connector.setParentItem(self)
1382
        self.connectors.append(connector)
1383

    
1384
    '''
1385
    '''
1386
    def refreshConnector(self):
1387
        for connector in self.connectors:
1388
            connector.buildItem()
1389

    
1390
    '''
1391
        @brief      Delete svg item
1392
        @author     Jeongwoo
1393
        @date       2018.05.25
1394
    '''
1395
    def deleteSvgItemFromScene(self):
1396
        for connector in self.connectors:
1397
            if connector.connectedItem is not None:
1398
                try:
1399
                    connector.connectedItem.removeSymbol(self)
1400
                except Exception as ex:
1401
                    from App import App
1402
                    from AppDocData import MessageType
1403

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

    
1408
        self.transfer.onRemoved.emit(self)
1409
        
1410
    '''
1411
        @brief      Return real item position
1412
        @author     Jeongwoo
1413
        @date       2018.05.25
1414
    '''
1415
    def boundingRectOnScene(self):
1416
        rect = self.boundingRect()
1417
        rect.moveTo(self.loc[0], self.loc[1])
1418
        return rect
1419

    
1420
    def flipSymbol(self):
1421
        '''
1422
            @brief  remove item when user press delete key
1423
            @author humkyung
1424
            @date   2018.04.23
1425
        '''
1426
        if self.flip is 0:
1427
            self.flip = 1
1428
        else:
1429
            self.flip = 0
1430

    
1431
        currentPoint = self.getCurrentPoint()
1432
        self.reSettingSymbol(currentPoint, self.angle)
1433

    
1434
    '''
1435
        @brief      rotate Symbol
1436
        @author     kyouho
1437
        @date       2018.07.24
1438
    '''
1439
    def rotateSymbol(self, angle=None):
1440
        if angle is None:
1441
            #degree 0
1442
            if 0 == self.angle:
1443
                self.angle = 1.57
1444
            #degree 90
1445
            elif (1.57 == self.angle):
1446
                self.angle = 3.14
1447
            #degree 180
1448
            elif 3.14 == self.angle:
1449
                self.angle = 4.71
1450
            #degree 270
1451
            elif 4.71 == self.angle :
1452
                self.angle = 0
1453
            else:
1454
                self.angle = 0
1455
                
1456
            self.size[0], self.size[1] = self.size[1], self.size[0]
1457
        else:
1458
            self.angle = angle
1459
        
1460
        #scene = self.scene()
1461
        #self.scene().removeItem(self)
1462
        #self.addSvgItemToScene(scene)
1463
        currentPoint = self.getCurrentPoint()
1464
        self.reSettingSymbol(currentPoint, self.angle)
1465

    
1466
    '''
1467
        @brief      resetting rotate Symbol
1468
        @author     kyouho
1469
        @date       2018.07.24
1470
    '''
1471
    def reSettingSymbol(self, standardPoint, angle):
1472
        transform = QTransform()
1473
        
1474
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
1475
        transform.rotateRadians(-angle)
1476
        transform.translate(-standardPoint[0], -standardPoint[1])
1477

    
1478
        if self.flip is 1:
1479
            transform.scale(-1.0, 1.0)
1480
            transform.translate(-self.size[0], 0)
1481

    
1482
        self.setTransform(transform)
1483

    
1484
        from EngineeringLineItem import QEngineeringLineItem
1485
        for connector in self.connectors:
1486
            if connector.connectedItem is not None and type(connector.connectedItem) == QEngineeringLineItem:
1487
                line = connector.connectedItem
1488
                line.reDrawLine(self, connector.center())
1489

    
1490
        for conn in self.connectors:
1491
            conn.sceneConnectPoint = (conn.sceneBoundingRect().center().x(), conn.sceneBoundingRect().center().y())
1492

    
1493
    '''
1494
        @brief      change Conn point 
1495
        @author     kyouho
1496
        @date       2018.07.25
1497
    '''
1498
    def changeConnPoint(self):
1499
        if len(self.connectors) == 2:
1500

    
1501
            conn1Item = self.connectors[0].connectedItem
1502
            conn2Item = self.connectors[1].connectedItem
1503
            self.connectors[0].connectedItem = conn2Item
1504
            self.connectors[1].connectedItem = conn1Item
1505

    
1506
            currentPoint = self.getCurrentPoint()
1507
            self.reSettingSymbol(currentPoint, self.angle)
1508

    
1509
    '''
1510
        @brief      change standard point
1511
        @author     kyouho
1512
        @date       2018.07.24
1513
    '''
1514
    def changeStandardPoint(self):
1515
        connPtsCount = len(self.connectors)
1516
        
1517
        if self.currentPointModeIndex < connPtsCount:
1518
            self.currentPointModeIndex += 1
1519
        else:
1520
            self.currentPointModeIndex = 0
1521

    
1522
        currentPoint = self.getCurrentPoint()
1523
        self.reSettingSymbol(currentPoint, self.angle)
1524
    
1525
    '''
1526
        @brief      get standard point
1527
        @author     kyouho
1528
        @date       2018.07.25
1529
    '''
1530
    def getCurrentPoint(self):
1531
        #from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1532

    
1533
        pointList = []
1534
        pointList.append(self.symbolOrigin)
1535
        for connector in self.connectors:
1536
            pointList.append(connector.connectPoint)
1537

    
1538
        #if type(self) is QEngineeringSpecBreakItem:
1539
        #    self.currentPointModeIndex = 1
1540

    
1541
        return pointList[self.currentPointModeIndex]
1542

    
1543
    '''
1544
        @brief      심볼 회전 시 서로 다른 기준점으로 회전하기 때문에 기준점을 이후 개발한 SymbolSvgItem 기준의 회전좌표로 이동하기 위해서 만듬 (loc 기준으로 회전해야함)
1545
        @author     kyouho
1546
        @date       18.08.06
1547
    '''
1548
    def reCalculationRotatedItem(self):
1549

    
1550
        transform = QTransform()
1551
        transform.translate(self.loc[0] + self.symbolOrigin[0], self.loc[1] + self.symbolOrigin[1])
1552
        transform.rotateRadians(-self.angle)
1553
        currentPoint = self.getCurrentPoint()
1554
        transform.translate(-currentPoint[0], -currentPoint[1])
1555
        # 시작점을 구하기 위해서
1556
        goPoint = transform.map(QPoint(self.symbolOrigin[0], self.symbolOrigin[1]))
1557
        
1558
        self.loc = [self.loc[0] + self.origin[0] - goPoint.x(), self.loc[1] + self.origin[1] - goPoint.y()]
1559

    
1560

    
1561
def recursiveChangeAttributes(node, attName, attValue):
1562
    while not node.isNull():
1563
        if node.isElement():
1564
            element = node.toElement()
1565
            if element.hasAttribute(attName):
1566
                element.setAttribute(attName, attValue)
1567

    
1568
            if node.hasChildNodes():
1569
                recursiveChangeAttributes(node.firstChild(), attName, attValue)
1570
        
1571
        node = node.nextSibling()
1572

    
1573
'''
1574
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
1575
    @author     Jeongwoo
1576
    @date       2018.06.18
1577
'''
1578
class Transfer(QObject):
1579
    onRemoved = pyqtSignal(QGraphicsItem)
1580

    
1581
    def __init__(self, parent = None):
1582
        QObject.__init__(self, parent)
1583

    
1584

    
1585
if __name__ == '__main__':
1586
    f = QFile('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/svg/ANGLE VALVE.svg')
1587
    f.open(QIODevice.ReadOnly)
1588
    array = f.readAll()
1589
    document = QDomDocument()
1590
    document.setContent(array)
1591

    
1592
    root = document.documentElement()
1593
    node = root.firstChild()
1594
    while not node.isNull():
1595
        if node.isElement():
1596
            element = node.toElement()
1597
            if element.hasAttribute('fill'):
1598
                element.setAttribute('fill', '#FFFFF')
1599

    
1600
            if element.hasChildNodes():
1601
                recursiveChangeAttributes(element.firstChild(), 'fill', '#FFFFF')
1602

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