프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / Shapes / EngineeringLineItem.py @ 1e4a0ab0

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

1
# coding: utf-8
2
""" This is engineering line item module """
3
import sys
4
import cv2
5
import os
6

    
7
try:
8
    from PyQt5.QtCore import *
9
    from PyQt5.QtGui import *
10
    from PyQt5.QtWidgets import *
11
except ImportError:
12
    try:
13
        from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR, QRect
14
        from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QGraphicsLineItem
15
    except ImportError:
16
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
17

    
18
from EngineeringAbstractItem import QEngineeringAbstractItem
19
import shapely
20
from LineTypeConditions import LineTypeConditions
21

    
22
class QEngineeringLineItem(QGraphicsLineItem, QEngineeringAbstractItem):
23
    """ This is engineering line item """
24

    
25
    ARROW_SIZE = 30
26
    ZVALUE = 100
27
    HIGHLIGHT = '#BC4438'
28
    LINE_TYPE_COLORS = {}
29

    
30
    '''
31
        @history    2018.05.11  Jeongwoo    Make Comments self.setPen()
32
                    2018.05.15  Jeongwoo    Change method to call parent's __init__
33
                    humkyung 2018.06.21 add vertices to parameter
34
    '''
35
    def __init__(self, vertices=[], thickness=None, parent=None, uid=None):
36
        import uuid
37
        from EngineeringConnectorItem import QEngineeringConnectorItem
38
        from SymbolAttr import SymbolProp
39
        from AppDocData import AppDocData
40

    
41
        try:
42
            QGraphicsLineItem.__init__(self, parent)
43
            QEngineeringAbstractItem.__init__(self)
44

    
45
            self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid, version=4)
46
            self.thickness = thickness
47

    
48
            self.setPen(QPen(Qt.blue, 4, Qt.SolidLine)) # set default pen
49
            self.isCreated = True 
50

    
51
            self._owner = None
52
            self._flowMark = None
53
            configs = AppDocData.instance().getConfigs('Line', 'Default Type')
54
            self._lineType = None
55
            self.lineType = configs[0].value if 1 == len(configs) else 'Secondary' # defulat line type is 'Secondary'
56

    
57
            self._properties = \
58
                {\
59
                    SymbolProp(None, 'Size', 'Size Text Item', Expression='self.EvaluatedSize'):None,\
60
                    SymbolProp(None, 'Flow Mark', 'Size Text Item', Expression='self.EvaluatedSize'):None
61
                }
62

    
63
            self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
64

    
65
            self.setAcceptHoverEvents(True)
66
            self.setAcceptTouchEvents(True)
67

    
68
            self.transfer = Transfer()
69
            self.setZValue(QEngineeringLineItem.ZVALUE)
70

    
71
            if vertices:
72
                self.setLine(vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1])
73
                
74
                index = 0
75
                for vertex in vertices:
76
                    connector = QEngineeringConnectorItem(parent=self, index=index+1)
77
                    connector.setPos(vertex)
78
                    connector.setParentItem(self)
79
                    # connector의 connectPoint, sceneConnectPoint를 vertex로 함 추후 좀 알아봐서 수정 필요
80
                    connector.connectPoint = vertex
81
                    connector.sceneConnectPoint = vertex
82

    
83
                    # add connector move able
84
                    connector.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
85
                    connector.setAcceptTouchEvents(True)
86
                    connector.transfer.onPosChanged.connect(self.onConnectorPosChaned)
87

    
88
                    connector.setZValue(self.zValue() + 1)
89
                    self.connectors.append(connector)
90
                    index = index + 1
91

    
92
                self.update_arrow()
93
                
94
                tooltip = '<b>{}</b><br>({},{})-({},{})'.format(str(self.uid), vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1])
95
                self.setToolTip(tooltip)
96
        except Exception as ex:
97
            from App import App
98
            from AppDocData import MessageType
99

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

    
103
    def __str__(self):
104
        """ return string represent uuid """
105
        return str(self.uid)
106

    
107
    @property
108
    def properties(self):
109
        """ getter of flow mark """
110
        import uuid
111

    
112
        for prop,value in self._properties.items():
113
            if prop.is_selectable and type(value) is uuid.UUID and self.scene():
114
                matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(value)]
115
                if matches: self._properties[prop] = matches[0]
116
            
117
            if prop.Expression: self._properties[prop] = eval(prop.Expression)
118
            
119
        return self._properties
120

    
121
    def set_property(self, property, value):
122
        """ set property with given value """
123
        if issubclass(type(value), QEngineeringAbstractItem): self.add_assoc_item(value, 0)
124
        matches = [prop for prop,_ in self._properties.items() if prop.Attribute == property]
125
        if matches: self._properties[matches[0]] = value
126

    
127
    def prop(self, name):
128
        """ return the value of given property with name """
129
        matches = [(prop,value) for prop,value in self.properties.items() if prop.Attribute == name]
130
        if matches: return matches[0][1]
131

    
132
        return None
133

    
134
    @property
135
    def Size(self):
136
        """ always return None """
137
        return None
138
    
139
    @property
140
    def EvaluatedSize(self):
141
        from EngineeringReducerItem import QEngineeringReducerItem
142

    
143
        if self.Size: return self.Size
144
        if self.owner:
145
            matches = [run for run in self.owner.runs if self in run.items]
146
            if matches:
147
                at = matches[0].items.index(self)
148
                upstream = matches[0].items[:at]
149
                upstream.reverse()
150
                prev = self 
151
                for item in upstream:
152
                    if type(item) is QEngineeringReducerItem:
153
                        if item.connectors[0].connectedItem is prev:    ### Main Size
154
                            if item.MainSize: return item.MainSize
155
                        elif item.connectors[1].connectedItem is prev:  ### Sub Size
156
                            if item.SubSize: return item.SubSize
157
                    else:
158
                        if item.Size: return item.Size
159
                    prev = item
160

    
161
                downstream = matches[0].items[at:]
162
                prev = self
163
                for item in downstream:
164
                    if type(item) is QEngineeringReducerItem:
165
                        if item.connectors[0].connectedItem is prev:    ### Main Size
166
                            if item.MainSize: return item.MainSize
167
                        elif item.connectors[1].connectedItem is prev:  ### Sub Size
168
                            if item.SubSize: return item.SubSize
169
                    else:
170
                        if item.Size: return item.Size
171
                    prev = item
172

    
173
                if 'Drain' == matches[0].Type:
174
                    from AppDocData import AppDocData
175
                    return AppDocData.instance().drain_size
176
                    
177
            return self.owner.Size
178

    
179
        return None
180

    
181
    '''
182
        @breif  getter owner
183
        @author humkyung
184
        @date   2018.05.10
185
    '''
186
    @property
187
    def owner(self):
188
        import uuid
189

    
190
        if self._owner and type(self._owner) is uuid.UUID:
191
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
192
            if matches: self._owner = matches[0]
193
        
194
        if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
195
            return self._owner
196
        else:
197
            self._owner = None
198
            return None
199

    
200
    '''
201
        @brief  setter owner
202
        @author humkyung
203
        @date   2018.05.10
204
        @history    2018.05.17  Jeongwoo    Add Calling setColor
205
    '''
206
    @owner.setter
207
    def owner(self, value):
208
        self._owner = value
209

    
210
        if self._owner is None:
211
            self._color = self.DEFAULT_COLOR
212
        self.setColor(self._color)
213

    
214
    '''
215
        @brief  getter flow mark
216
        @author humkyung
217
        @date   2018.06.21
218
    '''
219
    @property
220
    def flowMark(self):
221
        return self._flowMark
222

    
223
    '''
224
        @brief  setter flow mark
225
        @author humkyung
226
        @date   2018.06.21
227
    '''
228
    @flowMark.setter
229
    def flowMark(self, value):
230
        self._flowMark = value
231

    
232
    '''
233
        @brief  getter of lineType
234
        @author humkyung
235
        @date   2018.06.27
236
    '''
237
    @property
238
    def lineType(self):
239
        return self._lineType
240
    
241
    '''
242
        @brief  setter of lineType
243
        @author humkyung
244
        @date   2018.06.27
245
    '''
246
    @lineType.setter
247
    def lineType(self, value):
248
        from AppDocData import AppDocData
249

    
250
        self._lineType = value
251

    
252
        docData = AppDocData.instance()
253
        configs = docData.getLineTypeConfig(self._lineType)
254
        if configs:
255
            _pen = self.pen() 
256
            _pen.setWidth(configs[2])
257
            _pen.setStyle(configs[3])
258
            self.setPen(_pen)
259
            self.setOpacity(float(configs[4]) / 100)
260
            self.update()
261

    
262
        if self.scene():
263
            self.update_arrow()
264

    
265
    '''
266
        @brief  clone an object
267
    '''
268
    def clone(self):
269
        clone = QEngineeringLineItem()
270
        #clone._vertices = copy.deepcopy(self._vertices)
271
        #for vertex in clone._vertices:
272
        #    clone._pol.append(QPointF(vertex[0], vertex[1]))
273
        clone.buildItem()
274
        clone.isCreated = self.isCreated
275

    
276
        return clone
277

    
278
    def set_line(self, line):
279
        """ set line """
280
        self.setLine(line[0][0], line[0][1], line[1][0], line[1][1])
281
        self.connectors[0].setPos(line[0])
282
        self.connectors[1].setPos(line[1])
283

    
284
    '''
285
        @brief  return start point
286
        @author humkyung
287
        @date   2018.04.16
288
    '''
289
    def startPoint(self):
290
        at = self.line().p1()
291
        return (at.x(), at.y())
292

    
293
    '''
294
        @brief  return last point
295
        @author humkyung
296
        @date   2018.04.16
297
    '''
298
    def endPoint(self):
299
        at = self.line().p2()
300
        return (at.x(), at.y())
301

    
302
    '''
303
        @brief  dot product of given two vectors
304
        @author humkyung
305
        @date   2018.04.14
306
    '''
307
    def dotProduct(self, lhs, rhs):
308
        return sum([lhs[i]*rhs[i] for i in range(len(lhs))])
309

    
310
    '''
311
        @brief  distance between line and point
312
        @author humkyung
313
        @date   2018.04.16
314
    '''
315
    def distanceTo(self, pt):
316
        from shapely.geometry import Point, LineString
317

    
318
        startPt = self.startPoint()
319
        endPt = self.endPoint()
320
        line = LineString([(startPt[0], startPt[1]), (endPt[0], endPt[1])])
321
        dist = line.distance(Point(pt[0], pt[1]))
322

    
323
        return dist
324

    
325
    '''
326
        @brief  return perpendicular vector
327
        @author humkyung
328
        @date   2018.04.21
329
    '''
330
    def perpendicular(self):
331
        import math
332

    
333
        dx = self.endPoint()[0] - self.startPoint()[0]
334
        dy = self.endPoint()[1] - self.startPoint()[1]
335
        dx,dy = -dy,dx
336
        length = math.sqrt(dx*dx + dy*dy)
337
        dx /= length
338
        dy /= length
339

    
340
        return (dx,dy)
341

    
342
    '''
343
        @brief  return angle of line in radian
344
        @author humkyung
345
        @date   2018.04.22
346
    '''
347
    def angle(self):
348
        import math
349

    
350
        startPt = self.startPoint()
351
        endPt = self.endPoint()
352
        dx = endPt[0] - startPt[0]
353
        dy = endPt[1] - startPt[1]
354
        dot = self.dotProduct((1,0), (dx, dy))
355
        length = math.sqrt(dx*dx + dy*dy)
356
        return math.acos(dot/length)
357

    
358
    '''
359
        @brief  return length of line
360
        @author humkyung
361
        @date   2018.05.08
362
    '''
363
    def length(self):
364
        import math
365

    
366
        startPt = self.startPoint()
367
        endPt = self.endPoint()
368
        dx = endPt[0] - startPt[0]
369
        dy = endPt[1] - startPt[1]
370
        return math.sqrt(dx*dx + dy*dy)
371

    
372
    '''
373
        @brief  check if line is horizontal
374
        @author humkyung
375
        @date   2018.04.27
376
    '''
377
    def isHorizontal(self):
378
        import math
379

    
380
        startPt = self.startPoint()
381
        endPt = self.endPoint()
382
        dx = endPt[0] - startPt[0]
383
        dy = endPt[1] - startPt[1]
384

    
385
        return math.fabs(dx) > math.fabs(dy)
386

    
387
    '''
388
        @brief  check if line is vertical 
389
        @author humkyung
390
        @date   2018.04.27
391
    '''
392
    def isVertical(self):
393
        import math
394

    
395
        startPt = self.startPoint()
396
        endPt = self.endPoint()
397
        dx = endPt[0] - startPt[0]
398
        dy = endPt[1] - startPt[1]
399

    
400
        return math.fabs(dy) > math.fabs(dx)
401

    
402
    '''
403
        @brief  get intersection point between this and given line
404
        @author humkyung
405
        @date   2018.04.21
406
        @history    Jeongwoo 2018.05.15 Add normalize
407
                    Jeongwoo 2018.05.16 Add length == 0 check
408
    '''
409
    def intersection(self, line):
410
        import math
411
        from shapely.geometry import Point, LineString
412

    
413
        startPt = self.startPoint()
414
        endPt = self.endPoint()
415
        dx = endPt[0] - startPt[0]
416
        dy = endPt[1] - startPt[1]
417
        length = math.sqrt(dx*dx + dy*dy)
418
        if length == 0:
419
            return None
420
        dx /= length
421
        dy /= length
422
        lhs = LineString([(startPt[0] - dx*20, startPt[1] - dy*20), (endPt[0] + dx*20, endPt[1] + dy*20)])
423
        rhs = LineString(line)
424
        return lhs.intersection(rhs)
425

    
426
    def getAngle(self, rhs):
427
        """ get angle between self and given line """
428
        import math
429

    
430
        try:
431
            return math.acos(self.dotProduct(self, rhs) / (self.length() * rhs.length()))
432
        except Exception as ex:
433
            return sys.float_info.max
434

    
435
    def isParallel(self, rhs):
436
        """ check if two lines are parallel """
437
        import math
438

    
439
        try:
440
            vectors = [(self.endPoint()[0]-self.startPoint()[0], self.endPoint()[1]-self.startPoint()[1]), (rhs.endPoint()[0]-rhs.startPoint()[0], rhs.endPoint()[1]-rhs.startPoint()[1])]
441
            angle = self.getAngle(rhs)
442
            if (angle == 0) or (angle == math.pi): return True
443
        except ZeroDivisionError:
444
            return True
445

    
446
        return False
447

    
448
    '''
449
        @brief      check if two lines are connectable
450
        @author     humkyung
451
        @date       2018.05.12
452
        @history    Jeongwoo 18.05.15 Add check pt's type
453
                    Jeongwoo 18.05.16 Add length == 0 check
454
    '''
455
    def is_connectable(self, line, toler=20):
456
        import math
457

    
458
        startPt = line.startPoint()
459
        endPt = line.endPoint()
460

    
461
        dx = endPt[0] - startPt[0]
462
        dy = endPt[1] - startPt[1]
463
        length = math.sqrt(dx*dx + dy*dy)
464
        if length == 0:
465
            return False
466
        dx /= length
467
        dy /= length
468
        extendedLine = [(startPt[0] - dx*toler, startPt[1] - dy*toler), (endPt[0] + dx*toler, endPt[1] + dy*toler)]
469
        pt = self.intersection(extendedLine)
470

    
471
        return (pt is not None) and (type(pt) == shapely.geometry.point.Point)
472

    
473
    '''
474
        @author     humkyung
475
        @date       2018.06.26
476
        @history    humkyung 2018.07.03 allow item to be line or symbol
477
    '''
478
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
479
        """ check if given item is connected to self """
480

    
481
        _connectors = [connector for connector in self.connectors if (connector.connectedItem == item and connector._connected_at == at)]
482
        return len(_connectors) > 0
483

    
484
    '''
485
        @brief      join line to symbol
486
        @author     kyouho
487
        @date       2018.07.25
488
    '''
489
    def joinTo(self, item = None):
490
        import math
491
        from SymbolSvgItem import SymbolSvgItem
492

    
493
        # line의 Point 정의
494
        startPoint = self.startPoint()
495
        endPoint = self.endPoint()
496

    
497
        if item is not None and type(item) is QEngineeringLineItem:
498
            pts = [item.startPoint(), item.endPoint()]
499
            selected = startPoint if self._selectedIndex == 0 else endPoint if self._selectedIndex else []
500

    
501
            if selected:
502
                for i in range(len(pts)):
503
                    dx = pts[i][0] - selected[0]
504
                    dy = pts[i][1] - selected[1]
505
                    if math.sqrt(dx*dx + dy*dy) < 10:
506
                        line = QLineF(QPointF(pts[i][0], pts[i][1]), QPointF(endPoint[0], endPoint[1])) if self._selectedIndex == 0 else QLineF(QPointF(startPoint[0], startPoint[1]), QPointF(pts[i][0], pts[i][1]))
507
                        self.setLine(line)
508
                        self.update()
509
                        break
510
        else:
511
            if len(item.connectors) == 2:
512
                connector1Point = item.connectors[0].sceneConnectPoint
513
                connector2Point = item.connectors[1].sceneConnectPoint
514

    
515
                # startPoint와 같은 connPts 찾음
516
                if startPoint[0] == connector1Point[0] and startPoint[1] == connector1Point[1]:
517
                    self.connectors[0].connectedItem = item
518
                elif startPoint[0] == connector2Point[0] and startPoint[1] == connector2Point[1]:
519
                    self.connectors[0].connectedItem = item
520

    
521
                # endPoint와 같은 connPts 찾음
522
                if endPoint[0] == connector1Point[0] and endPoint[1] == connector1Point[1]:
523
                    self.connectors[1].connectedItem = item
524
                elif endPoint[0] == connector2Point[0] and endPoint[1] == connector2Point[1]:
525
                    self.connectors[1].connectedItem = item
526

    
527

    
528
    '''
529
        @brief      arrange vertex order
530
        @author     humkyung
531
        @date       2018.07.04
532
    '''
533
    def arrangeVertexOrder(self, arranged):
534
        import math
535

    
536
        lhs = [arranged.startPoint(), arranged.endPoint()]
537
        rhs = [self.startPoint(), self.endPoint()]
538

    
539
        index = 0
540
        indexed = 0
541
        minDist = None
542
        for pt in lhs:
543
            for _pt in rhs:
544
                index += 1
545
                dx = _pt[0] - pt[0]
546
                dy = _pt[1] - pt[1]
547
                dist = math.sqrt(dx*dx + dy*dy)
548
                if minDist is None or dist < minDist:
549
                    minDist = dist
550
                    indexed = index
551
                    
552
        if indexed == 1 or indexed == 4:
553
            self.reverse()
554

    
555
    '''
556
        @brief      check if two lines are extendable
557
        @author     humkyung
558
        @date       2018.06.25
559
        @history    humkyung 2018.06.27 check line type
560
    '''
561
    def isExtendable(self, line, toler=5):
562
        import math
563
        from SymbolSvgItem import SymbolSvgItem
564

    
565
        if self.lineType == line.lineType:
566
            if self.isHorizontal() and line.isHorizontal():
567
                flag = (line.connectors[0].connectedItem is not None and issubclass(type(line.connectors[0].connectedItem), SymbolSvgItem)) or (line.connectors[1].connectedItem is not None and issubclass(type(line.connectors[1].connectedItem), SymbolSvgItem))
568
                return (flag and (math.fabs(self.startPoint()[1] - line.startPoint()[1]) < toler))
569
            elif self.isVertical() and line.isVertical():
570
                flag = (line.connectors[0].connectedItem is not None and issubclass(type(line.connectors[0].connectedItem), SymbolSvgItem)) or (line.connectors[1].connectedItem is not None and issubclass(type(line.connectors[1].connectedItem), SymbolSvgItem))
571
                return (flag and (math.fabs(self.startPoint()[0] - line.startPoint()[0]) < toler))
572
            
573
        return False
574
        
575
    def is_external_point(self, pt):
576
        """ check given pt is located outside of line """
577
        import math
578

    
579
        try:
580
            dx = self.endPoint()[0] - self.startPoint()[0]
581
            dy = self.endPoint()[1] - self.startPoint()[1]
582
            lineLength = math.sqrt(dx * dx + dy * dy)
583
            
584
            dx = pt.x - self.startPoint()[0]
585
            dy = pt.y - self.startPoint()[1]
586
            length = math.sqrt(dx * dx + dy * dy)
587
            if length > lineLength:
588
                return True
589

    
590
            dx = pt.x - self.endPoint()[0]
591
            dy = pt.y - self.endPoint()[1]
592
            length = math.sqrt(dx * dx + dy * dy)
593
            if length > lineLength:
594
                return True
595

    
596
            return False
597
        except Exception as ex:
598
            from App import App 
599
            from AppDocData import MessageType
600

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

    
604
    '''
605
        @author     humkyung
606
        @date       2018.04.16
607
        @history    humkyung 2018.05.08 check if line is possible to be connected
608
                    Jeongwoo 2018.05.15 Split if-statement and Connect each symbol and line
609
    '''
610
    def connect_if_possible(self, obj, toler):
611
        """ connect line or symbol is able to be connected and return symbol or line connected to connectors """
612

    
613
        from shapely.geometry import Point
614
        from SymbolSvgItem import SymbolSvgItem
615
        from EngineeringConnectorItem import QEngineeringConnectorItem
616

    
617
        res = []
618

    
619
        startPt = self.startPoint() 
620
        endPt = self.endPoint()
621

    
622
        try:
623
            if issubclass(type(obj), SymbolSvgItem):
624
                for i in range(len(obj.connectors)):
625
                    pt = obj.connectors[i].sceneConnectPoint
626

    
627
                    if (Point(startPt[0], startPt[1]).distance(Point(pt[0], pt[1])) < toler):
628
                        if self.connectors[0].connectedItem is None:
629
                            self.connectors[0].connect(obj) 
630
                        if obj.connectors[i].connectedItem is None:
631
                            obj.connectors[i].connect(self)
632

    
633
                        res.append(obj)
634
                    if (Point(endPt[0], endPt[1]).distance(Point(pt[0], pt[1])) < toler):
635
                        if self.connectors[1].connectedItem is None:
636
                            self.connectors[1].connect(obj)
637
                        if obj.connectors[i].connectedItem is None:
638
                            obj.connectors[i].connect(self)
639

    
640
                        res.append(obj)
641
            elif type(obj) is QEngineeringLineItem:
642
                _startPt = obj.startPoint()
643
                _endPt = obj.endPoint()
644
                if self.distanceTo(_startPt) < toler:
645
                    if((Point(startPt[0], startPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)):
646
                        self.connectors[0].connect(obj)
647
                        obj.connectors[0].connect(self)
648
                        res.append(obj)
649
                    elif((Point(endPt[0], endPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)):
650
                        self.connectors[1].connect(obj)
651
                        obj.connectors[0].connect(self)
652
                        res.append(obj)
653
                    else:
654
                        obj.connectors[0].connect(self, at=QEngineeringAbstractItem.CONNECTED_AT_BODY)
655

    
656
                if self.distanceTo(_endPt) < toler:
657
                    if ((Point(startPt[0], startPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)):
658
                        self.connectors[0].connect(obj)
659
                        obj.connectors[1].connect(self)
660
                        res.append(obj)
661
                    elif ((Point(endPt[0], endPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)):
662
                        self.connectors[1].connect(obj)
663
                        obj.connectors[1].connect(self)
664
                        res.append(obj)
665
                    else:
666
                        obj.connectors[1].connect(self, at=QEngineeringAbstractItem.CONNECTED_AT_BODY)
667
        except Exception as ex:
668
            from App import App 
669
            from AppDocData import MessageType
670

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

    
674
        return res
675

    
676
    '''
677
        @brief      disconnect connector item
678
        @author     kyouho
679
        @date       2018.08.30
680
    '''
681
    def disconnectedItemAtConnector(self, connector):
682
        for conn in self.connectors:
683
            if conn.isOverlapConnector(connector):
684
                conn.connectedItem = None
685

    
686
    def arrange_flow_direction(self, _from):
687
        """ reverse if from is connected to second connector """
688
        if not _from: raise ValueError
689

    
690
        if self.connectors[1].connectedItem == _from: self.reverse() 
691

    
692
    '''
693
        @brief      reverse line
694
        @author     humkyung
695
        @date       2018.07.03
696
    '''
697
    def reverse(self):
698
        line = self.line()
699
        self.setLine(QLineF(line.p2(), line.p1()))
700
        self.connectors[0], self.connectors[1] = self.connectors[1], self.connectors[0]
701
        self.update_arrow()
702
        self.update()
703

    
704
    '''
705
        @brief      add flow arrow
706
        @author     humkyung
707
        @date       2018.05.08
708
        @history    2018.05.24  Jeongwoo    Modifying Draw Flow Arrow
709
    '''
710
    def addFlowArrow(self):
711
        import numpy as np
712
        import cv2
713
        import math
714
        import sys
715
        global src
716
        from shapely.geometry import Point
717
        from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
718
        from AppDocData import AppDocData
719
        
720
        try:
721
            docData = AppDocData.instance()
722
            area = docData.getArea('Drawing')
723

    
724
            startPt = self.startPoint()
725
            endPt = self.endPoint()
726
            length = self.length()
727
            direction = [(endPt[0] - startPt[0]) / length, (endPt[1] - startPt[1]) / length]
728

    
729
            left = min(startPt[0], endPt[0])
730
            top = min(startPt[1], endPt[1])
731
            right = max(startPt[0], endPt[0])
732
            bottom = max(startPt[1], endPt[1])
733

    
734
            rect = None
735
            if self.isVertical():
736
                rect = QRectF(left - 10, top, (right - left) + 20, (bottom - top))
737
            else:
738
                rect = QRectF(left, top - 10, (right - left), (bottom - top) + 20)
739

    
740
            docData = AppDocData.instance()
741
            area = docData.getArea('Drawing')
742
            img = np.array(AppDocData.instance().getCurrentPidSource().getPyImageOnRect(rect))
743
            
744
            imgLine = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY)[1]
745
            # remove noise
746
            imgLine = cv2.bitwise_not(imgLine)
747
            imgLine = cv2.erode(imgLine, np.ones((10, 10), np.uint8))
748

    
749
            contours, hierarchy = cv2.findContours(imgLine, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
750
            if contours:
751
                contours = sorted(contours, key=cv2.contourArea, reverse=True)
752
                [x, y, w, h] = cv2.boundingRect(contours[0])
753
                if w > 10 and w < 100 and h > 10 and h < 100:   # check arrow mark size
754
                    imgArrowMark = imgLine[y:y+h, x:x+w]
755

    
756
                    # DEBUG - display flow arrow area
757
                    '''
758
                    item = QGraphicsBoundingBoxItem(rect.left() + x - 10, rect.top() + y - 10, w + 20, h + 20)
759
                    item.isSymbol = True
760
                    item.angle = 0
761
                    item.setPen(QPen(Qt.red, 1, Qt.SolidLine))
762
                    item.setBrush(QBrush(QColor(255,255,0,100)))
763
                    self.scene().addItem(item)
764
                    '''
765
                    # up to here
766

    
767
                    edges = cv2.Canny(imgArrowMark, 50, 150, apertureSize=3)
768
                    lines = cv2.HoughLinesP(edges, 1, np.pi/180, 10, minLineLength=10, maxLineGap=5)
769

    
770
                    ####### HoughLinesP
771
                    if lines is not None:
772
                        maxLength = None
773
                        selected = None
774
                        for line in lines:
775
                            for x1, y1, x2, y2 in line:
776
                                dx = x2 - x1
777
                                dy = y2 - y1
778
                                selected = line
779
                                length = math.sqrt(dx*dx + dy*dy)
780
                                if maxLength is None or length > maxLength:
781
                                    maxLength = length
782
                                    selected = line
783

    
784
                        for x1, y1, x2, y2 in selected:
785
                            dx = math.fabs(x2 - x1)
786
                            dy = math.fabs(y2 - y1)
787
                            length = math.sqrt(dx*dx + dy*dy)
788
                            dx /= length
789
                            dy /= length
790
                            if (self.isVertical() and (dx < 0.001 or math.fabs(dx - 1) < 0.001)) or (self.isHorizontal() and (dx < 0.001 or math.fabs(dx - 1) < 0.001)): continue 
791
                            dist1 = self.distanceTo((rect.left() + x + x1, rect.top() + y + y1))
792
                            dist2 = self.distanceTo((rect.left() + x + x2, rect.top() + y + y2))
793
                            if dist1 > dist2:   # point which's distance is longer would be start point
794
                                _start = (rect.left() + x + x1, rect.top() + y + y1)
795
                                _end = (rect.left() + x + x2, rect.top() + y + y2)
796
                            else:
797
                                _start = (rect.left() + x + x2, rect.top() + y + y2)
798
                                _end = (rect.left() + x + x1, rect.top() + y + y1)
799
                            
800
                            # DEBUG display detected line
801
                            '''
802
                            poly = QEngineeringPolylineItem()
803
                            poly._pol.append(QPointF(_start[0], _start[1]))
804
                            poly._pol.append(QPointF(_end[0], _end[1]))
805
                            poly.setPen(QPen(Qt.red, 2, Qt.SolidLine))
806
                            poly.buildItem()
807
                            self.scene().addItem(poly)
808
                            '''
809
                            # up to here
810

    
811
                            dist1 = Point(startPt[0], startPt[1]).distance(Point(_start[0], _start[1]))
812
                            dist2 = Point(startPt[0], startPt[1]).distance(Point(_end[0], _end[1]))
813
                            if dist1 > dist2:
814
                                startPt,endPt = endPt,startPt
815
                                direction[0],direction[1] = -direction[0],-direction[1]
816
                                self.reverse()
817
                                    
818
                        '''
819
                        center = [(startPt[0]+endPt[0])*0.5, (startPt[1]+endPt[1])*0.5]
820
                        arrow = QEngineeringFlowArrowItem(center, direction)
821
                        arrow.buildItem()
822
                        self.scene().addItem(arrow)
823
                        '''
824

    
825
                        x = round(rect.left() + x - 5)
826
                        y = round(rect.top() + y - 5)
827
                        self.flowMark = ([x, y, w + 10, h + 10], None)
828
                else:
829
                    pass
830
        except Exception as ex:
831
            from App import App 
832
            from AppDocData import MessageType
833

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

    
837
    '''
838
        @breif  insert symbol
839
        @author humkyung
840
        @date   2018.04.22
841
        @history    kyouho  2018.07.24  add symbol angle, transform rotateRadians(-angle -> angle)
842
    '''
843
    def insertSymbol(self, symbol, pos):
844
        import math
845
        from shapely.geometry import Point
846
        from shapely import affinity
847

    
848
        vec = self.perpendicular()
849
        line = [(pos.x() - vec[0]*20, pos.y() - vec[1]*20),(pos.x() + vec[0]*20, pos.y() + vec[1]*20)]
850
        origin = self.intersection(line)
851
        transform = QTransform()
852
        transform.translate(origin.x, origin.y)
853
        angle = self.angle()
854
        transform.rotateRadians(-angle)
855
        transform.translate(-symbol.symbolOrigin[0], -symbol.symbolOrigin[1])
856
        symbol.setTransform(transform)
857
        #save angle
858
        symbol.angle = round(angle, 2)
859
        if 2 == len(symbol.connectors):    # 2 way component
860
            for i in range(len(symbol.connectors)):
861
                rotatedPt = affinity.rotate(Point(symbol.connectors[i].connectPoint[0] - symbol.symbolOrigin[0], symbol.connectors[i].connectPoint[1] - symbol.symbolOrigin[1]), -angle, Point(0, 0), use_radians=True)
862
                symbol.connectors[i].sceneConnectPoint = (origin.x+rotatedPt.x, origin.y+rotatedPt.y)
863

    
864
            dx1 = symbol.connectors[0].sceneConnectPoint[0] - self.startPoint()[0]
865
            dy1 = symbol.connectors[0].sceneConnectPoint[1] - self.startPoint()[1]
866
            length1 = math.sqrt(dx1*dx1 + dy1*dy1)
867
            dx2 = symbol.connectors[1].sceneConnectPoint[0] - self.startPoint()[0]
868
            dy2 = symbol.connectors[1].sceneConnectPoint[1] - self.startPoint()[1]
869
            length2 = math.sqrt(dx2*dx2 + dy2*dy2)
870

    
871
            if length1 < length2:
872
                processLine = QEngineeringLineItem([symbol.connectors[1].sceneConnectPoint, self.endPoint()])
873
                processLine.connectors[0].connectedItem = symbol
874
                processLine.connectors[1].connectedItem = self.connectors[1].connectedItem
875
                self.scene().addItem(processLine)
876

    
877
                line = QLineF(self.line().p1(), QPointF(symbol.connectors[0].sceneConnectPoint[0], symbol.connectors[0].sceneConnectPoint[1]))
878
                self.setLine(line)
879
                self.connectors[1].connectedItem = symbol
880

    
881
                symbol.connectors[0].connectedItem = self
882
                symbol.connectors[1].connectedItem = processLine
883
            else:
884
                processLine = QEngineeringLineItem([symbol.connectors[0].sceneConnectPoint, self.endPoint()])
885
                processLine.connectors[0].connectedItem = symbol
886
                processLine.connectors[1].connectedItem = self.connectors[1].connectedItem
887
                self.scene().addItem(processLine)
888

    
889
                line = QLineF(self.line().p1(), QPointF(symbol.connectors[1].sceneConnectPoint[0], symbol.connectors[1].sceneConnectPoint[1]))
890
                self.setLine(line)
891
                self.connectors[1].connectedItem = symbol
892

    
893
                symbol.connectors[0].connectedItem = processLine
894
                symbol.connectors[1].connectedItem = self
895
                
896

    
897
            self.joinTo(symbol)
898
            processLine.joinTo(symbol)
899
            self.update()
900
            
901
        symbol.loc = [origin.x - symbol.symbolOrigin[0], origin.y - symbol.symbolOrigin[1]]
902
        symbol.size = [symbol.boundingRect().width(), symbol.boundingRect().height()]
903
        self.scene().addItem(symbol)
904
    
905
    '''
906
        @brief  redraw symbol
907
        @author kyouho
908
        @date   2018.07.25
909
    '''
910
    def reDrawLine(self, symbol, point):
911
        for index in range(len(self.connectors)):
912
            if self.connectors[index].connectedItem == symbol:
913
                #startPoint
914
                if index == 0:
915
                    line = QLineF(QPointF(point[0], point[1]), self.line().p2())
916
                    self.setLine(line)
917
                    self.connectors[0].setPos([point[0], point[1]])
918
                #endpoint
919
                else:
920
                    line = QLineF(self.line().p1(), QPointF(point[0], point[1]))
921
                    self.setLine(line)
922
                    self.connectors[1].setPos([point[0], point[1]])
923
        
924

    
925
        
926
        ## startPoint에 symbol
927
        #if self.connectors[0].connectedItem == symbol:
928
        #    if self.startPoint()[0] == symbol.connectors[0].sceneConnectPoint[0] and self.startPoint()[1] == symbol.connectors[0].sceneConnectPoint[1]:
929
        #        line = QLineF(QPointF(changedConnPoint1[0], changedConnPoint1[1]), self.line().p2())
930
        #        self.setLine(line)
931
        #    else:
932
        #        line = QLineF(QPointF(changedConnPoint2[0], changedConnPoint2[1]), self.line().p2())
933
        #        self.setLine(line)
934
        ## endPoint에 symbol
935
        #elif self.connectors[1].connectedItem == symbol:
936
        #    if self.endPoint()[0] == symbol.connectors[0].sceneConnectPoint[0] and self.endPoint()[1] == symbol.connectors[0].sceneConnectPoint[1]:
937
        #        line = QLineF(self.line().p1(), QPointF(changedConnPoint1[0], changedConnPoint1[1]))
938
        #        self.setLine(line)
939
        #    else:
940
        #        line = QLineF(self.line().p1(), QPointF(changedConnPoint2[0], changedConnPoint2[1]))
941
        #        self.setLine(line)
942

    
943
        self.update()
944

    
945
    '''
946
        @brief  remove symbol
947
        @author humkyung
948
        @date   2018.04.23
949
    '''
950
    def removeSymbol(self, symbol):
951
        import math
952

    
953
        if 2 == len(symbol.connectors):  # 2-way component
954
            connected = symbol.connectors[0].connectedItem if symbol.connectors[0].connectedItem is not self else symbol.connectors[1].connectedItem
955
            
956
            pts = []
957
            pts.append(self.startPoint())
958
            pts.append(self.endPoint())
959
            pts.append(connected.startPoint())
960
            pts.append(connected.endPoint())
961

    
962
            self.scene().removeItem(connected)
963

    
964
            start = None
965
            end = None
966
            maxDist = None
967
            for i in range(len(pts)):
968
                for j in range(i+1,len(pts)):
969
                    dx = pts[i][0] - pts[j][0]
970
                    dy = pts[i][1] - pts[j][1]
971
                    dist = math.sqrt(dx*dx + dy*dy)
972
                    if maxDist is None:
973
                        maxDist = dist
974
                        start = pts[i]
975
                        end = pts[j]
976
                    elif dist > maxDist:
977
                        maxDist = dist
978
                        start = pts[i]
979
                        end = pts[j]
980

    
981
            if (pts[0] == end) or (pts[1] == start): start,end = end,start
982

    
983
            line = QLineF(QPointF(start[0], start[1]), QPointF(end[0], end[1]))
984
            self.setLine(line)
985
            #self.buildItem()
986
            self.update()
987

    
988
    def validate(self):
989
        '''
990
            @brief  validation check : connection
991
            @author euisung
992
            @date   2019.04.01
993
        '''
994
        from SymbolSvgItem import SymbolSvgItem
995
        from AppDocData import AppDocData
996
        errors = []
997

    
998
        try:
999
            docdata = AppDocData.instance()
1000
            dataPath = docdata.getErrorItemSvgPath()
1001
            origin = [int(pt) for pt in docdata.getAppConfigs('app', 'error origin point')[0].value.split(',')]
1002
            needFlowCheck = True
1003
            for connector in self.connectors:
1004
                if connector.connectedItem is None:
1005
                    error = SymbolSvgItem.createItem('Error', dataPath)
1006
                    error.setPosition(connector.center(), origin)
1007
                    error.parent = self
1008
                    error.msg = 'disconnected'
1009
                    error.setToolTip(error.msg)
1010
                    error.area = 'Drawing'
1011
                    error.name = 'Error'
1012
                    errors.append(error)
1013
                    needFlowCheck = False
1014
            if needFlowCheck and type(self.connectors[0].connectedItem) is QEngineeringLineItem and type(self.connectors[1].connectedItem) is QEngineeringLineItem:
1015
                pass
1016

    
1017
        except Exception as ex:
1018
            from App import App 
1019
            from AppDocData import MessageType
1020

    
1021
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1022
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1023
        
1024
        return errors
1025

    
1026
    '''
1027
        @brief  update line type
1028
        @author humkyung
1029
        @date   2018.07.05
1030
    '''
1031
    def update_line_type(self):
1032
        import uuid
1033
        from LineTypeConditions import LineTypeConditions
1034

    
1035
        try:
1036
            pool,visited,items = [self],[],[]
1037
            while pool:
1038
                obj = pool.pop()
1039
                visited.append(obj)
1040

    
1041
                """ connected items """
1042
                connected = [connector.connectedItem for connector in obj.connectors if connector.connectedItem and not type(connector.connectedItem) is uuid.UUID]
1043
                """ connected lines at point """
1044
                lines = [connector.connectedItem for connector in obj.connectors if connector.connectedItem and type(connector.connectedItem) is QEngineeringLineItem and connector._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT]
1045
                """ add items not in 'connected' to items list """
1046
                items.extend([item for item in connected if item not in lines])
1047
                """ add items not visited to pool """
1048
                pool.extend([item for item in lines if item not in visited])
1049

    
1050
            for condition in LineTypeConditions.items():
1051
                if condition.eval(items):
1052
                    self.lineType = condition.name
1053
                    break
1054
        except Exception as ex:
1055
            from App import App 
1056
            from AppDocData import MessageType
1057

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

    
1061
    def hoverEnterEvent(self, event):
1062
        """ hilight item and it's children """
1063
        self.highlight(True)
1064

    
1065
    def hoverLeaveEvent(self, event):
1066
        """ restore original color """
1067
        self.highlight(False)
1068

    
1069
    def highlight(self, flag):
1070
        self.hover = flag 
1071
        self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(QEngineeringLineItem.ZVALUE)
1072
        self.update()
1073

    
1074
        for connector in self.connectors:
1075
            connector.highlight(flag)
1076

    
1077
    def hoverMoveEvent(self, event):
1078
        pass
1079
    
1080
    '''
1081
        @brief      remove item when user press delete key
1082
        @author     humkyung
1083
        @date       2018.04.23
1084
        @history    swap start, end point when user press 'c' key
1085
    '''
1086
    def keyPressEvent(self, event): 
1087
        if self.isSelected() and event.key() == Qt.Key_Delete:
1088
            self.scene().removeItem(self)
1089
        elif event.key() == Qt.Key_C:
1090
            self.reverse()
1091
    
1092
    '''
1093
        @brief  draw rect when item is selected
1094
        @author humkyung
1095
        @date   2018.07.07
1096
    '''
1097
    def drawFocusRect(self, painter):
1098
        self.focuspen = QPen(Qt.DotLine)
1099
        self.focuspen.setColor(Qt.black)
1100
        self.focuspen.setWidthF(1.5)
1101
        hilightColor = QColor(255, 0, 0, 127)
1102
        painter.setBrush(QBrush(hilightColor))
1103
        painter.setPen(self.focuspen)
1104
        painter.drawRect(self.boundingRect())
1105

    
1106
    '''
1107
        @brief      override paint method
1108
        @history    humkyung 2018.08.30 draw flowmark only for Primary or Secondary
1109
    '''
1110
    def paint(self, painter, option, widget):
1111
        color = self.getColor()
1112
        self.setColor(color)
1113

    
1114
        QGraphicsLineItem.paint(self, painter, option, widget)
1115

    
1116
        if self.isSelected():
1117
            self.drawFocusRect(painter)
1118

    
1119
    '''
1120
        @brief  draw self to given image
1121
        @author humkyung
1122
        @date   2018.06.21
1123
    '''
1124
    def drawToImage(self, img, color, thickness):
1125
        try:
1126
            # write recognized lines to image
1127
            ptStart = self.startPoint()
1128
            ptEnd = self.endPoint()
1129
            cv2.line(img, (round(ptStart[0]), round(ptStart[1])), (round(ptEnd[0]), round(ptEnd[1])), color, thickness)
1130
            # up to here
1131

    
1132
            #if self.flowMark is not None:
1133
            #    x, y, w, h = self.flowMark[0]
1134
            #    img[y:(y+h), x:(x+w)] = color
1135
        except Exception as ex:
1136
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
1137

    
1138
    '''
1139
        @brief      parse xml code
1140
        @author     humkyung
1141
        @date       2018.06.27
1142
    '''
1143
    @staticmethod 
1144
    def fromXml(node):
1145
        import uuid
1146
        from AppDocData import AppDocData
1147
        from SymbolAttr import SymbolAttr
1148
        
1149
        item = None
1150
        try:
1151
            uidNode = node.find('UID')
1152
            uid = uidNode.text if uidNode is not None else uuid.uuid4() # generate UUID
1153
            owner = uuid.UUID(node.attrib['OWNER'], version=4) if 'OWNER' in node.attrib and node.attrib['OWNER'] != 'None' else None
1154

    
1155
            startPoint = [float(x) for x in node.find('STARTPOINT').text.split(',')]
1156
            endPoint = [float(x) for x in node.find('ENDPOINT').text.split(',')]
1157

    
1158
            item = QEngineeringLineItem(vertices=[startPoint, endPoint], uid=uid)
1159
            item.setVisible(False)
1160
            item.lineType = node.find('TYPE').text if node.find('TYPE') is not None else 'Secondary'
1161
            ## assign area
1162
            if node.find('AREA') is None:
1163
                appDocData = AppDocData.instance()
1164
                for area in appDocData.getAreaList():
1165
                    if area.contains(startPoint) and area.contains(endPoint):
1166
                        item.area = area.name
1167
                        break
1168
            else:
1169
                item.area = node.find('AREA').text
1170
            ## up to here
1171

    
1172
            thicknessNode = node.find('THICKNESS')
1173
            item.thickness = int(thicknessNode.text) if thicknessNode is not None and thicknessNode.text != 'None' else None
1174

    
1175
            flowMarkNode = node.find('FLOWMARK')
1176
            item.flowMark = int(flowMarkNode.text) if flowMarkNode is not None and flowMarkNode.text != 'None' else None
1177

    
1178
            connectors = node.find('CONNECTORS')
1179
            if connectors is not None:
1180
                iterIndex = 0
1181
                for connector in connectors.iter('CONNECTOR'):
1182
                    item.connectors[iterIndex].parse_xml(connector)
1183
                    iterIndex += 1
1184

    
1185
            # get associations 
1186
            attributeValue = node.find('ASSOCIATIONS')
1187
            if attributeValue is not None:
1188
                for assoc in attributeValue.iter('ASSOCIATION'):
1189
                    _type = assoc.attrib['TYPE']
1190
                    if not _type in item._associations:
1191
                        item._associations[_type] = []
1192
                    item._associations[_type].append(uuid.UUID(assoc.text))
1193
            # up to here
1194

    
1195
            attributes = node.find('SYMBOLATTRIBUTES')
1196
            if attributes is not None:
1197
                for attr in attributes.iter('ATTRIBUTE'):
1198
                    _attr = SymbolAttr.fromXml(attr)
1199
                    item.attrs[_attr] = attr.text
1200
                    
1201
        except Exception as ex:
1202
            from App import App 
1203
            from AppDocData import MessageType
1204

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

    
1208
        return item if item.length() > 1 else None
1209

    
1210
    '''
1211
        @brief      generate xml code
1212
        @author     humkyung
1213
        @date       2018.04.23
1214
        @history    humkyung 2018.06.27 write line type to xml
1215
                    humkyung 2018.07.23 write connected item's uid to xml
1216
    '''
1217
    def toXml(self):
1218
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1219
        from LineTypeConditions import LineTypeConditions
1220
        from SymbolAttr import SymbolAttr
1221

    
1222
        try:
1223
            node = Element('LINE')
1224
            node.attrib['OWNER'] = str(self._owner) if self._owner else 'None'
1225
            uidNode = Element('UID')
1226
            uidNode.text = str(self.uid)
1227
            node.append(uidNode)
1228

    
1229
            startPt = self.startPoint()
1230
            endPt = self.endPoint()
1231

    
1232
            startNode = Element('STARTPOINT')
1233
            startNode.text = '{},{}'.format(startPt[0], startPt[1])
1234
            node.append(startNode)
1235

    
1236
            endNode = Element('ENDPOINT')
1237
            endNode.text = '{},{}'.format(endPt[0], endPt[1])
1238
            node.append(endNode)
1239

    
1240
            typeNode = Element('TYPE')
1241
            typeNode.text = self.lineType
1242
            for lineType in LineTypeConditions.items():
1243
                if self.lineType == lineType.name:
1244
                    typeNode.attrib['TYPEUID'] = str(lineType)
1245
                    break
1246
            node.append(typeNode)
1247

    
1248
            areaNode = Element('AREA')
1249
            areaNode.text = self.area
1250
            node.append(areaNode)
1251

    
1252
            thicknessNode = Element('THICKNESS')
1253
            thicknessNode.text = str(self.thickness)
1254
            node.append(thicknessNode)
1255

    
1256
            flowMarkNode = Element('FLOWMARK')
1257
            flowMarkNode.text = str(self.flowMark)
1258
            node.append(flowMarkNode)
1259

    
1260
            connectorsNode = Element('CONNECTORS')
1261
            for connector in self.connectors:
1262
                connectorsNode.append(connector.toXml())
1263
            node.append(connectorsNode)
1264

    
1265
            attributeValueNode = Element('ASSOCIATIONS')
1266
            for assoc in self.associations():
1267
                assoc_node = Element('ASSOCIATION')
1268
                assoc_node.attrib['TYPE'] = QEngineeringAbstractItem.assoc_type(assoc)
1269
                assoc_node.text = str(assoc.uid)
1270
                attributeValueNode.append(assoc_node)
1271
            node.append(attributeValueNode)
1272

    
1273
            attributesNode = Element('SYMBOLATTRIBUTES')
1274
            _attrs = self.getAttributes()
1275
            for attr in _attrs:
1276
                if type(attr) is SymbolAttr:
1277
                    _node = attr.toXml()
1278
                    _node.text = str(_attrs[attr])
1279
                    attributesNode.append(_node)
1280

    
1281
            node.append(attributesNode)
1282

    
1283
            # up to here
1284
        except Exception as ex:
1285
            from App import App 
1286
            from AppDocData import MessageType
1287

    
1288
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1289
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1290
            return None
1291

    
1292
        return node
1293

    
1294
    def toSql(self):
1295
        """ generate sql phrase to save line to database """
1296
        import uuid
1297
        from AppDocData import AppDocData
1298

    
1299
        res = []
1300

    
1301
        app_doc_data = AppDocData.instance()
1302
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner', 'SpecialItemTypes_UID']
1303
        values = ['?', '?', "(select UID from Symbol where Name='Line' and SymbolType_UID='-1')", '?', '?', '?', '?', '?', '?', '?', '?']
1304

    
1305
        rect = self.sceneBoundingRect()
1306
        param = [str(self.uid), str(app_doc_data.activeDrawing.UID), rect.x(), rect.y(), rect.width(), rect.height(), 0, 
1307
        self.area, str(self.owner) if self.owner else None, str(self.special_item_type) if self.special_item_type else None]
1308
        sql = 'insert or replace into Components({}) values({})'.format(','.join(cols), ','.join(values))
1309
        res.append((sql, tuple(param)))
1310

    
1311
        # save connectors to database
1312
        for connector in self.connectors:
1313
            res.append(connector.toSql())
1314
        # up to here
1315
        
1316
        return res
1317

    
1318
    '''
1319
        @brief      Delete Line Item from scene
1320
        @author     Jeongwoo
1321
        @date       2018.05.29
1322
        @history    2018.05.29  Add parameter 'self' / Make comments emit()
1323
    '''
1324
    def deleteLineItemFromScene(self):
1325
        self.scene().removeItem(self)
1326
        
1327
    def getColor(self):
1328
        """ return line's color """
1329
        from AppDocData import AppDocData
1330
        from DisplayColors import DisplayColors
1331
        from DisplayColors import DisplayOptions
1332
        from EngineeringAbstractItem import QEngineeringAbstractItem
1333

    
1334
        if DisplayOptions.DisplayByLineType == DisplayColors.instance().option:
1335
            if self.hover:
1336
                return QEngineeringAbstractItem.HOVER_COLOR
1337
            else:
1338
                if self.lineType in QEngineeringLineItem.LINE_TYPE_COLORS:
1339
                    return QEngineeringLineItem.LINE_TYPE_COLORS[self.lineType]
1340
                else:
1341
                    app_doc_data = AppDocData.instance()
1342
                    configs = app_doc_data.getConfigs('LineTypes', self.lineType)
1343
                    if configs:
1344
                        tokens = configs[0].value.split(',')
1345
                        QEngineeringLineItem.LINE_TYPE_COLORS[self.lineType] = tokens[0] if len(tokens) == 4 else QEngineeringAbstractItem.DEFAULT_COLOR
1346
                        return QEngineeringLineItem.LINE_TYPE_COLORS[self.lineType]
1347
                    else:
1348
                        return QEngineeringAbstractItem.DEFAULT_COLOR
1349
        else:
1350
            if self.owner is None:
1351
                return QEngineeringAbstractItem.getColor(self)
1352
            else:
1353
                return self.owner.getColor()
1354

    
1355

    
1356
    '''
1357
        @brief      Set Color. Override QEngineeringAbstractItem's
1358
        @author     Jeongwoo
1359
        @date       2018.05.11
1360
        @history    2018.05.11  Jeongwoo    Add self.setPen() Method
1361
        @history    humkyung 2018.05.13 call setPen method to apply changed color
1362
    '''
1363
    def setColor(self, color):
1364
        c = QColor()
1365
        c.setNamedColor(color)
1366
        _pen = self.pen()
1367
        _pen.setColor(c)
1368
        self.setPen(_pen)
1369
        self.update()
1370

    
1371
    def update_arrow(self):
1372
        """ update flow arrow """
1373
        import math
1374
        from EngineeringArrowItem import QEngineeringArrowItem
1375
        if self.length() < 0.01:
1376
            return
1377

    
1378
        start = self.line().p1()
1379
        end = self.line().p2()
1380

    
1381
        dx = end.x() - start.x()
1382
        dy = end.y() - start.y()
1383
        _dir = [dx/self.length(), dy/self.length()]
1384

    
1385
        arrow_size = QEngineeringLineItem.ARROW_SIZE * 0.25
1386
        
1387
        if not (self._lineType == 'Primary' or self._lineType == 'Secondary'):
1388
            self.flowMark = None
1389

    
1390
        if self.flowMark:
1391
            arrow_size *= 2
1392
            end = QPointF(start.x() + dx * self.flowMark / 100, start.y() + dy * self.flowMark / 100)
1393

    
1394
        perpendicular = (-_dir[1], _dir[0])
1395
        polygon = QPolygonF()
1396
        polygon.append(QPointF(end.x() - _dir[0]*QEngineeringLineItem.ARROW_SIZE + perpendicular[0]*arrow_size, 
1397
                            end.y() - _dir[1]*QEngineeringLineItem.ARROW_SIZE + perpendicular[1]*arrow_size))
1398
        polygon.append(QPointF(end.x() - _dir[0]*QEngineeringLineItem.ARROW_SIZE - perpendicular[0]*arrow_size, 
1399
                            end.y() - _dir[1]*QEngineeringLineItem.ARROW_SIZE - perpendicular[1]*arrow_size))
1400
        polygon.append(end)
1401
        polygon.append(polygon[0])  # close polygon
1402

    
1403
        if not hasattr(self, '_arrow'):
1404
            self._arrow = QEngineeringArrowItem(polygon, self)
1405
        else:
1406
            self._arrow.setPolygon(polygon)
1407

    
1408
        if self.flowMark:
1409
            self._arrow.setBrush(Qt.red)
1410
        else:
1411
            self._arrow.setBrush(Qt.blue)
1412
        self._arrow.update()
1413

    
1414
        '''
1415
        if self._flowMark:
1416
            flowMark = self._flowMark[0]
1417
            flowMark.angle = math.atan2(_dir[0], _dir[1]) - math.pi / 2
1418
            flowMark.loc = [self.connectors[1].center()[0] - flowMark.symbolOrigin[0] - _dir[0] * 20, self.connectors[1].center()[1] - flowMark.symbolOrigin[1] - _dir[1] * 20]
1419
            flowMark.origin = [self.connectors[1].center()[0] - _dir[0] * 20, self.connectors[1].center()[1] - _dir[1] * 20]
1420
            scene = flowMark.scene()
1421
            scene.removeItem(flowMark)
1422
            flowMark.addSvgItemToScene(scene)
1423
        '''
1424

    
1425
    '''
1426
        @brief      reshape line
1427
        @author     humkyung
1428
        @date       2018.07.27
1429
    '''
1430
    def onConnectorPosChaned(self, connector):
1431
        from EngineeringArrowItem import QEngineeringArrowItem
1432

    
1433
        start = self.connectors[0].center()
1434
        end = self.connectors[1].center()
1435
        self.setLine(start[0], start[1], end[0], end[1])
1436
        self.update()
1437

    
1438
        tooltip = '<b>{}</b><br>({},{})-({},{})'.format(str(self.uid), start[0], start[1], end[0], end[1])
1439
        self.setToolTip(tooltip)
1440

    
1441
        self.update_arrow()
1442

    
1443
    '''
1444
        @brief      
1445
        @author     humkyung
1446
        @date       2018.07.24
1447
    '''
1448
    def mousePressEvent(self, event):
1449
        import math
1450

    
1451
        if event.buttons() == Qt.LeftButton:
1452
            pos = event.scenePos()
1453
            ptStart = self.startPoint()
1454
            dx = ptStart[0] - pos.x()
1455
            dy = ptStart[1] - pos.y()
1456
            if math.sqrt(dx*dx + dy*dy) < 10:
1457
                self._selectedIndex = 0
1458
                return
1459

    
1460
            ptEnd = self.endPoint()
1461
            dx = ptEnd[0] - pos.x()
1462
            dy = ptEnd[1] - pos.y()
1463
            if math.sqrt(dx*dx + dy*dy) < 10:
1464
                self._selectedIndex = 1
1465
                return
1466

    
1467
            self._selectedIndex = -1
1468
            
1469
        QGraphicsLineItem.mousePressEvent(self, event)
1470

    
1471
    def mouseReleaseEvent(self, event):
1472
        self._selectedIndex = -1
1473

    
1474
        QGraphicsLineItem.mouseReleaseEvent(self, event)
1475

    
1476
'''
1477
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
1478
    @author     Jeongwoo
1479
    @date       2018.06.18
1480
'''
1481
class Transfer(QObject):
1482
    onRemoved = pyqtSignal(QGraphicsItem)
1483

    
1484
    def __init__(self, parent = None):
1485
        QObject.__init__(self, parent)
클립보드 이미지 추가 (최대 크기: 500 MB)