프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / Shapes / EngineeringLineItem.py @ 9168f672

이력 | 보기 | 이력해설 | 다운로드 (79.4 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, \
15
            QGraphicsLineItem
16
    except ImportError:
17
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
18

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

    
23

    
24
class QEngineeringLineItem(QGraphicsLineItem, QEngineeringAbstractItem):
25
    """ This is engineering line item """
26

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

    
32
    '''
33
        @history    2018.05.11  Jeongwoo    Make Comments self.setPen()
34
                    2018.05.15  Jeongwoo    Change method to call parent's __init__
35
                    humkyung 2018.06.21 add vertices to parameter
36
    '''
37

    
38
    def __init__(self, vertices=[], thickness=None, parent=None, uid=None):
39
        import uuid
40
        from EngineeringConnectorItem import QEngineeringConnectorItem
41
        from SymbolAttr import SymbolProp
42
        from AppDocData import AppDocData
43

    
44
        try:
45
            QGraphicsLineItem.__init__(self, parent)
46
            QEngineeringAbstractItem.__init__(self)
47

    
48
            self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid)
49
            self.thickness = thickness
50

    
51
            self.setPen(QPen(Qt.blue, 4, Qt.SolidLine))  # set default pen
52
            self.isCreated = True
53

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

    
60
            self._properties = {SymbolProp(None, 'Size', 'Size Text Item', Expression='self.EvaluatedSize'): None}
61

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

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

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

    
70
            if vertices:
71
                self.setLine(vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1])
72

    
73
                index = 0
74
                for vertex in vertices:
75
                    connector = QEngineeringConnectorItem(parent=self, index=index + 1)
76
                    connector.setPos(vertex)
77
                    connector.setParentItem(self)
78
                    connector.connectPoint = vertex
79
                    connector.recognized_pt = vertex    # 좌표 위치 저장
80

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

    
86
                    connector.setZValue(self.zValue() + 1)
87
                    self.connectors.append(connector)
88
                    index = index + 1
89

    
90
                self.update_arrow()
91
                self.setToolTip(self.tooltip)
92
        except Exception as ex:
93
            from App import App
94
            from AppDocData import MessageType
95

    
96
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
97
                                                           sys.exc_info()[-1].tb_lineno)
98
            App.mainWnd().addMessage.emit(MessageType.Error, message)
99

    
100
    def __str__(self):
101
        """ return string represent uuid """
102
        return str(self.uid)
103

    
104
    @property
105
    def tooltip(self):
106
        """return tooltip string"""
107

    
108
        start = self.connectors[0].center()
109
        end = self.connectors[1].center()
110
        tooltip = f"<b>TYPE=LINE</b><br>UID={str(self.uid)}<br>POS=({start[0]},{start[1]})-({end[0]},{end[1]})"
111

    
112
        return tooltip
113

    
114
    @property
115
    def Size(self):
116
        """ always return None """
117
        return None
118

    
119
    @property
120
    def EvaluatedSize(self):
121
        from EngineeringReducerItem import QEngineeringReducerItem
122
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
123

    
124
        if self.Size: return self.Size
125
        if self.owner and issubclass(type(self.owner), QEngineeringLineNoTextItem):
126
            matches = [run for run in self.owner.runs if self in run.items]
127
            if matches:
128
                at = matches[0].items.index(self)
129
                upstream = matches[0].items[:at]
130
                upstream.reverse()
131
                prev = self
132
                for item in upstream:
133
                    if type(item) is QEngineeringReducerItem:
134
                        if item.connectors[0].connectedItem is prev:  ### Main Size
135
                            if item.MainSubSize: return item.MainSubSize[0]
136
                        elif item.connectors[1].connectedItem is prev:  ### Sub Size
137
                            if item.MainSubSize: return item.MainSubSize[1]
138
                    else:
139
                        if item.Size: return item.Size
140
                    prev = item
141

    
142
                downstream = matches[0].items[at:]
143
                prev = self
144
                for item in downstream:
145
                    if type(item) is QEngineeringReducerItem:
146
                        if item.connectors[0].connectedItem is prev:  ### Main Size
147
                            if item.MainSubSize: return item.MainSubSize[0]
148
                        elif item.connectors[1].connectedItem is prev:  ### Sub Size
149
                            if item.MainSubSize: return item.MainSubSize[1]
150
                    else:
151
                        if item.Size: return item.Size
152
                    prev = item
153

    
154
                if 'Drain' == matches[0].Type:
155
                    from AppDocData import AppDocData
156
                    return AppDocData.instance().drain_size
157

    
158
            return self.owner.Size
159

    
160
        return None
161

    
162
    '''
163
        @breif  getter owner
164
        @author humkyung
165
        @date   2018.05.10
166
    '''
167

    
168
    @property
169
    def owner(self):
170
        import uuid
171

    
172
        if self._owner and type(self._owner) is uuid.UUID:
173
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
174
            if matches: self._owner = matches[0]
175

    
176
        if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
177
            return self._owner
178
        else:
179
            self._owner = None
180
            return None
181

    
182
    @property
183
    def stream_no(self):
184
        matches = [attr for attr in self.attrs if attr.Attribute.upper() == 'STREAM NO']
185
        if matches:
186
            return self.attrs[matches[0]]
187

    
188
        return None
189

    
190
    @stream_no.setter
191
    def stream_no(self, value):
192
        from AppDocData import AppDocData
193

    
194
        matches = [attr for attr in self.attrs if attr.Attribute.upper() == 'STREAM NO']
195
        if matches:
196
            self.attrs[matches[0]] = value
197
            """reset attributes for hmb with given stream no"""
198

    
199
            app_doc_data = AppDocData.instance()
200
            stream_data = app_doc_data.hmbTable.dataOfStreamNo(value)
201
            for attr in self.attrs:
202
                if attr.AttributeType == 'HMB':
203
                    matches = [data for data in stream_data if data.name == attr.Attribute]
204
                    if matches:
205
                        self.attrs[attr] = matches[0].value
206

    
207
    '''
208
        @brief  setter owner
209
        @author humkyung
210
        @date   2018.05.10
211
        @history    2018.05.17  Jeongwoo    Add Calling setColor
212
    '''
213

    
214
    @owner.setter
215
    def owner(self, value):
216
        self._owner = value
217

    
218
        if self._owner is None:
219
            self._color = self.DEFAULT_COLOR
220
        self.setColor(self._color)
221

    
222
    '''
223
        @brief  getter flow mark
224
        @author humkyung
225
        @date   2018.06.21
226
    '''
227

    
228
    @property
229
    def flowMark(self):
230
        return self._flowMark
231

    
232
    '''
233
        @brief  setter flow mark
234
        @author humkyung
235
        @date   2018.06.21
236
    '''
237

    
238
    @flowMark.setter
239
    def flowMark(self, value):
240
        self._flowMark = value
241

    
242
    '''
243
        @brief  getter of lineType
244
        @author humkyung
245
        @date   2018.06.27
246
    '''
247

    
248
    @property
249
    def lineType(self):
250
        return self._lineType
251

    
252
    '''
253
        @brief  setter of lineType
254
        @author humkyung
255
        @date   2018.06.27
256
    '''
257

    
258
    @lineType.setter
259
    def lineType(self, value):
260
        from AppDocData import AppDocData
261

    
262
        self._lineType = value
263

    
264
        line_type_style = self.line_type_style
265
        if line_type_style:
266
            _pen = self.pen()
267
            c = QColor()
268
            c.setNamedColor(line_type_style[1])
269
            _pen.setColor(c)
270
            _pen.setWidth(line_type_style[2])
271
            _pen.setStyle(line_type_style[3])
272
            self.setPen(_pen)
273
            self.setOpacity(float(line_type_style[4]) / 100)
274
            self.update()
275

    
276
        if self.scene():
277
            self.update_arrow()
278

    
279
    @property
280
    def line_type_style(self):
281
        """return line type style"""
282
        from AppDocData import AppDocData
283

    
284
        app_doc_data = AppDocData.instance()
285
        configs = app_doc_data.getLineTypeConfig(self._lineType)
286
        return configs
287

    
288
    '''
289
        @brief  clone an object
290
    '''
291

    
292
    def clone(self):
293
        clone = QEngineeringLineItem()
294
        # clone._vertices = copy.deepcopy(self._vertices)
295
        # for vertex in clone._vertices:
296
        #    clone._pol.append(QPointF(vertex[0], vertex[1]))
297
        clone.buildItem()
298
        clone.isCreated = self.isCreated
299

    
300
        return clone
301

    
302
    def set_line(self, line):
303
        """ set line """
304
        self.setLine(line[0][0], line[0][1], line[1][0], line[1][1])
305
        self.connectors[0].setPos(line[0])
306
        self.connectors[1].setPos(line[1])
307
        self.update_arrow()
308

    
309
    '''
310
        @brief  return start point
311
        @author humkyung
312
        @date   2018.04.16
313
    '''
314

    
315
    def start_point(self):
316
        at = self.line().p1()
317
        return (at.x(), at.y())
318

    
319
    '''
320
        @brief  return last point
321
        @author humkyung
322
        @date   2018.04.16
323
    '''
324

    
325
    def end_point(self):
326
        at = self.line().p2()
327
        return (at.x(), at.y())
328

    
329
    '''
330
        @brief  dot product of given two vectors
331
        @author humkyung
332
        @date   2018.04.14
333
    '''
334
    def dotProduct(self, lhs, rhs):
335
        return sum([lhs[i] * rhs[i] for i in range(len(lhs))])
336

    
337
    '''
338
        @brief  distance between line and point
339
        @author humkyung
340
        @date   2018.04.16
341
    '''
342

    
343
    def distanceTo(self, pt):
344
        from shapely.geometry import Point, LineString
345

    
346
        startPt = self.start_point()
347
        endPt = self.end_point()
348
        line = LineString([(startPt[0], startPt[1]), (endPt[0], endPt[1])])
349
        dist = line.distance(Point(pt[0], pt[1]))
350

    
351
        return dist
352

    
353
    '''
354
        @brief  return perpendicular vector
355
        @author humkyung
356
        @date   2018.04.21
357
    '''
358

    
359
    def perpendicular(self):
360
        import math
361

    
362
        dx = self.end_point()[0] - self.start_point()[0]
363
        dy = self.end_point()[1] - self.start_point()[1]
364
        dx, dy = -dy, dx
365
        length = math.sqrt(dx * dx + dy * dy)
366
        dx /= length
367
        dy /= length
368

    
369
        return (dx, dy)
370

    
371
    '''
372
        @brief  return angle of line in radian
373
        @author humkyung
374
        @date   2018.04.22
375
    '''
376

    
377
    def angle(self):
378
        import math
379

    
380
        startPt = self.start_point()
381
        endPt = self.end_point()
382
        dx = endPt[0] - startPt[0]
383
        dy = endPt[1] - startPt[1]
384
        dot = self.dotProduct((1, 0), (dx, dy))
385
        length = math.sqrt(dx * dx + dy * dy)
386
        return math.acos(dot / length)
387

    
388
    '''
389
        @brief  return length of line
390
        @author humkyung
391
        @date   2018.05.08
392
    '''
393

    
394
    def length(self):
395
        import math
396

    
397
        startPt = self.start_point()
398
        endPt = self.end_point()
399
        dx = endPt[0] - startPt[0]
400
        dy = endPt[1] - startPt[1]
401
        return math.sqrt(dx * dx + dy * dy)
402

    
403
    '''
404
        @brief  check if line is horizontal
405
        @author humkyung
406
        @date   2018.04.27
407
    '''
408

    
409
    def isHorizontal(self):
410
        import math
411

    
412
        startPt = self.start_point()
413
        endPt = self.end_point()
414
        dx = endPt[0] - startPt[0]
415
        dy = endPt[1] - startPt[1]
416

    
417
        return math.fabs(dx) > math.fabs(dy)
418

    
419
    '''
420
        @brief  check if line is vertical 
421
        @author humkyung
422
        @date   2018.04.27
423
    '''
424

    
425
    def isVertical(self):
426
        import math
427

    
428
        startPt = self.start_point()
429
        endPt = self.end_point()
430
        dx = endPt[0] - startPt[0]
431
        dy = endPt[1] - startPt[1]
432

    
433
        return math.fabs(dy) > math.fabs(dx)
434

    
435
    '''
436
        @brief  get intersection point between this and given line
437
        @author humkyung
438
        @date   2018.04.21
439
        @history    Jeongwoo 2018.05.15 Add normalize
440
                    Jeongwoo 2018.05.16 Add length == 0 check
441
    '''
442

    
443
    def intersection(self, line):
444
        import math
445
        from shapely.geometry import Point, LineString
446

    
447
        startPt = self.start_point()
448
        endPt = self.end_point()
449
        dx = endPt[0] - startPt[0]
450
        dy = endPt[1] - startPt[1]
451
        length = math.sqrt(dx * dx + dy * dy)
452
        if length == 0:
453
            return None
454
        dx /= length
455
        dy /= length
456
        lhs = LineString([(startPt[0] - dx * 20, startPt[1] - dy * 20), (endPt[0] + dx * 20, endPt[1] + dy * 20)])
457
        rhs = LineString(line)
458
        return lhs.intersection(rhs)
459

    
460
    def getAngle(self, rhs):
461
        """ get angle between self and given line """
462
        import math
463

    
464
        try:
465
            return math.acos(self.dotProduct(self, rhs) / (self.length() * rhs.length()))
466
        except Exception as ex:
467
            return sys.float_info.max
468

    
469
    def isParallel(self, rhs):
470
        """ check if two lines are parallel """
471
        import math
472

    
473
        try:
474
            vectors = [(self.end_point()[0] - self.start_point()[0], self.end_point()[1] - self.start_point()[1]),
475
                       (rhs.end_point()[0] - rhs.start_point()[0], rhs.end_point()[1] - rhs.start_point()[1])]
476
            angle = self.getAngle(rhs)
477
            if (angle == 0) or (angle == math.pi): return True
478
        except ZeroDivisionError:
479
            return True
480

    
481
        return False
482

    
483
    '''
484
        @brief      check if two lines are connectable
485
        @author     humkyung
486
        @date       2018.05.12
487
        @history    Jeongwoo 18.05.15 Add check pt's type
488
                    Jeongwoo 18.05.16 Add length == 0 check
489
    '''
490

    
491
    def is_connectable(self, item, toler=20):
492
        import math
493
        from EngineeringConnectorItem import QEngineeringConnectorItem
494

    
495
        if type(item) is QEngineeringLineItem:
496
            startPt = item.start_point()
497
            endPt = item.end_point()
498

    
499
            dx = endPt[0] - startPt[0]
500
            dy = endPt[1] - startPt[1]
501
            length = math.sqrt(dx * dx + dy * dy)
502
            if length == 0:
503
                return False
504
            dx /= length
505
            dy /= length
506
            extendedLine = [(startPt[0] - dx * toler, startPt[1] - dy * toler),
507
                            (endPt[0] + dx * toler, endPt[1] + dy * toler)]
508
            pt = self.intersection(extendedLine)
509

    
510
            return (pt is not None) and (type(pt) == shapely.geometry.point.Point)
511
        elif type(item) is QEngineeringConnectorItem:
512
            start_pt = self.start_point()
513
            end_pt = self.end_point()
514

    
515
            lhs = [end_pt[0] - start_pt[0], end_pt[1] - start_pt[1]]
516
            length = math.sqrt(lhs[0] * lhs[0] + lhs[1] * lhs[1])
517
            if length == 0:
518
                return False
519

    
520
            rhs = [item.dir().x(), item.dir().y()]
521
            dot = sum([lhs[i] * rhs[i] for i in range(len(lhs))])
522
            angle = math.degrees(math.acos(dot / length))
523
            if (abs(angle) < 5) or (abs(angle - 180) < 5):
524
                _center = item.center()
525
                dx = [start_pt[0] - _center[0], end_pt[0] - _center[0]]
526
                dy = [start_pt[1] - _center[1], end_pt[1] - _center[1]]
527
                length = [math.sqrt(dx[0] * dx[0] + dy[0] * dy[0]), math.sqrt(dx[1] * dx[1] + dy[1] * dy[1])]
528
                return length[0] < toler or length[1] < toler
529

    
530
            return False
531

    
532
        return False
533

    
534
    '''
535
        @author     humkyung
536
        @date       2018.06.26
537
        @history    humkyung 2018.07.03 allow item to be line or symbol
538
    '''
539

    
540
    def is_connected(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
541
        """ check if given item is connected to self """
542

    
543
        _connectors = [connector for connector in self.connectors if
544
                       (connector.connectedItem == item and connector._connected_at == at)]
545
        return len(_connectors) > 0
546

    
547
    '''
548
        @brief      join line to symbol
549
        @author     kyouho
550
        @date       2018.07.25
551
    '''
552

    
553
    def joinTo(self, item=None):
554
        import math
555
        from SymbolSvgItem import SymbolSvgItem
556

    
557
        # line의 Point 정의
558
        startPoint = self.start_point()
559
        endPoint = self.end_point()
560

    
561
        if item is not None and type(item) is QEngineeringLineItem:
562
            pts = [item.start_point(), item.end_point()]
563
            selected = startPoint if self._selectedIndex == 0 else endPoint if self._selectedIndex else []
564

    
565
            if selected:
566
                for i in range(len(pts)):
567
                    dx = pts[i][0] - selected[0]
568
                    dy = pts[i][1] - selected[1]
569
                    if math.sqrt(dx * dx + dy * dy) < 10:
570
                        line = QLineF(QPointF(pts[i][0], pts[i][1]),
571
                                      QPointF(endPoint[0], endPoint[1])) if self._selectedIndex == 0 else QLineF(
572
                            QPointF(startPoint[0], startPoint[1]), QPointF(pts[i][0], pts[i][1]))
573
                        self.setLine(line)
574
                        self.update()
575
                        break
576
        else:
577
            if len(item.connectors) == 2:
578
                connector1Point = item.connectors[0].center()
579
                connector2Point = item.connectors[1].center()
580

    
581
                # startPoint와 같은 connPts 찾음
582
                if startPoint[0] == connector1Point[0] and startPoint[1] == connector1Point[1]:
583
                    self.connectors[0].connectedItem = item
584
                elif startPoint[0] == connector2Point[0] and startPoint[1] == connector2Point[1]:
585
                    self.connectors[0].connectedItem = item
586

    
587
                # endPoint와 같은 connPts 찾음
588
                if endPoint[0] == connector1Point[0] and endPoint[1] == connector1Point[1]:
589
                    self.connectors[1].connectedItem = item
590
                elif endPoint[0] == connector2Point[0] and endPoint[1] == connector2Point[1]:
591
                    self.connectors[1].connectedItem = item
592

    
593
    '''
594
        @brief      arrange vertex order
595
        @author     humkyung
596
        @date       2018.07.04
597
    '''
598

    
599
    def arrangeVertexOrder(self, arranged):
600
        import math
601

    
602
        lhs = [arranged.start_point(), arranged.end_point()]
603
        rhs = [self.start_point(), self.end_point()]
604

    
605
        index = 0
606
        indexed = 0
607
        minDist = None
608
        for pt in lhs:
609
            for _pt in rhs:
610
                index += 1
611
                dx = _pt[0] - pt[0]
612
                dy = _pt[1] - pt[1]
613
                dist = math.sqrt(dx * dx + dy * dy)
614
                if minDist is None or dist < minDist:
615
                    minDist = dist
616
                    indexed = index
617

    
618
        if indexed == 1 or indexed == 4:
619
            self.reverse()
620
    
621
    def is_piping(self, strong=False):
622
        """ return true if piping line """
623
        if strong:
624
            return self._lineType == 'Primary' or self._lineType == 'Secondary'
625
        else:
626
            return self._lineType == 'Primary' or self._lineType == 'Secondary' or \
627
                   self._lineType == 'Connect To Process'
628

    
629
    '''
630
        @brief      check if two lines are extendable
631
        @author     humkyung
632
        @date       2018.06.25
633
        @history    humkyung 2018.06.27 check line type
634
    '''
635
    def isExtendable(self, line, toler=5):
636
        import math
637
        from SymbolSvgItem import SymbolSvgItem
638

    
639
        if self.lineType == line.lineType:
640
            if self.isHorizontal() and line.isHorizontal():
641
                flag = (line.connectors[0].connectedItem is not None and issubclass(
642
                    type(line.connectors[0].connectedItem), SymbolSvgItem)) or (
643
                                   line.connectors[1].connectedItem is not None and issubclass(
644
                               type(line.connectors[1].connectedItem), SymbolSvgItem))
645
                return flag and (math.fabs(self.start_point()[1] - line.start_point()[1]) < toler)
646
            elif self.isVertical() and line.isVertical():
647
                flag = (line.connectors[0].connectedItem is not None and issubclass(
648
                    type(line.connectors[0].connectedItem), SymbolSvgItem)) or (
649
                                   line.connectors[1].connectedItem is not None and issubclass(
650
                               type(line.connectors[1].connectedItem), SymbolSvgItem))
651
                return flag and (math.fabs(self.start_point()[0] - line.start_point()[0]) < toler)
652

    
653
        return False
654

    
655
    def is_external_point(self, pt):
656
        """ check given pt is located outside of line """
657
        import math
658

    
659
        try:
660
            dx = self.end_point()[0] - self.start_point()[0]
661
            dy = self.end_point()[1] - self.start_point()[1]
662
            lineLength = math.sqrt(dx * dx + dy * dy)
663

    
664
            dx = pt.x - self.start_point()[0]
665
            dy = pt.y - self.start_point()[1]
666
            length = math.sqrt(dx * dx + dy * dy)
667
            if length > lineLength:
668
                return True
669

    
670
            dx = pt.x - self.end_point()[0]
671
            dy = pt.y - self.end_point()[1]
672
            length = math.sqrt(dx * dx + dy * dy)
673
            if length > lineLength:
674
                return True
675

    
676
            return False
677
        except Exception as ex:
678
            from App import App
679
            from AppDocData import MessageType
680

    
681
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
682
                                                           sys.exc_info()[-1].tb_lineno)
683
            App.mainWnd().addMessage.emit(MessageType.Error, message)
684

    
685
    '''
686
        @author     humkyung
687
        @date       2018.04.16
688
        @history    humkyung 2018.05.08 check if line is possible to be connected
689
                    Jeongwoo 2018.05.15 Split if-statement and Connect each symbol and line
690
    '''
691

    
692
    def connect_if_possible(self, obj, toler=20):
693
        """connect line or symbol is able to be connected and return symbol or line connected to connectors
694
        this method not update item's position' \
695
        """
696

    
697
        from shapely.geometry import Point
698
        from SymbolSvgItem import SymbolSvgItem
699
        from EngineeringConnectorItem import QEngineeringConnectorItem
700

    
701
        res = []
702

    
703
        start_pt = self.start_point()
704
        end_pt = self.end_point()
705

    
706
        try:
707
            if issubclass(type(obj), SymbolSvgItem):
708
                for i in range(len(obj.connectors)):
709
                    if not self.is_connectable(obj.connectors[i]):
710
                        continue
711

    
712
                    pt = obj.connectors[i].center()
713
                    """
714
                    dist = [(self.connectors[0], Point(start_pt[0], start_pt[1]).distance(Point(pt[0], pt[1])), start_pt),
715
                            (self.connectors[1], Point(end_pt[0], end_pt[1]).distance(Point(pt[0], pt[1])), end_pt)]
716

717
                    dist.sort(key=lambda x: x[1])
718

719
                    if dist[0][0].connectedItem is None and obj.connectors[i].connectedItem is None:
720
                        dist[0][0].connect(obj)
721
                        obj.connectors[i].connect(self)
722
                        # line, start, end
723
                        res.append(obj)
724
                        res.append(obj.connectors[i].center())
725
                        res.append(dist[0][2])
726
                    """
727
                    if Point(start_pt[0], start_pt[1]).distance(Point(pt[0], pt[1])) < toler:
728
                        if self.connectors[0].connectedItem is None and obj.connectors[i].connectedItem is None:
729
                            self.connectors[0].connect(obj)
730
                            obj.connectors[i].connect(self)
731
                            # line, start, end
732
                            res.append(obj)
733
                            res.append(obj.connectors[i].center())
734
                            res.append(end_pt)
735
                    elif Point(end_pt[0], end_pt[1]).distance(Point(pt[0], pt[1])) < toler:
736
                        if self.connectors[1].connectedItem is None and obj.connectors[i].connectedItem is None:
737
                            self.connectors[1].connect(obj)
738
                            obj.connectors[i].connect(self)
739
                            # line, start, end
740
                            res.append(obj)
741
                            res.append(start_pt)
742
                            res.append(obj.connectors[i].center())
743
            elif type(obj) is QEngineeringLineItem:
744
                _startPt = obj.start_point()
745
                _endPt = obj.end_point()
746
                if obj.connectors[0].connectedItem is None and self.distanceTo(_startPt) < toler:
747
                    if self.connectors[0].connectedItem is None and \
748
                            (Point(start_pt[0], start_pt[1]).distance(Point(_startPt[0], _startPt[1])) < toler):
749
                        self.connectors[0].connect(obj)
750
                        obj.connectors[0].connect(self)
751
                        res.append(obj)
752
                    elif self.connectors[1].connectedItem is None and \
753
                            (Point(end_pt[0], end_pt[1]).distance(Point(_startPt[0], _startPt[1])) < toler):
754
                        self.connectors[1].connect(obj)
755
                        obj.connectors[0].connect(self)
756
                        res.append(obj)
757
                    elif obj.connectors[0].connectedItem is None:
758
                        obj.connectors[0].connect(self, at=QEngineeringAbstractItem.CONNECTED_AT_BODY)
759
                        res.append(obj)
760

    
761
                elif obj.connectors[1].connectedItem is None and self.distanceTo(_endPt) < toler:
762
                    if self.connectors[0].connectedItem is None and \
763
                            (Point(start_pt[0], start_pt[1]).distance(Point(_endPt[0], _endPt[1])) < toler):
764
                        self.connectors[0].connect(obj)
765
                        obj.connectors[1].connect(self)
766
                        res.append(obj)
767
                    elif self.connectors[1].connectedItem is None and \
768
                            (Point(end_pt[0], end_pt[1]).distance(Point(_endPt[0], _endPt[1])) < toler):
769
                        self.connectors[1].connect(obj)
770
                        obj.connectors[1].connect(self)
771
                        res.append(obj)
772
                    elif obj.connectors[1].connectedItem is None:
773
                        obj.connectors[1].connect(self, at=QEngineeringAbstractItem.CONNECTED_AT_BODY)
774
                        res.append(obj)
775
        except Exception as ex:
776
            from App import App
777
            from AppDocData import MessageType
778

    
779
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
780
                                                           sys.exc_info()[-1].tb_lineno)
781
            App.mainWnd().addMessage.emit(MessageType.Error, message)
782

    
783
        return res
784

    
785
    '''
786
        @brief      disconnect connector item
787
        @author     kyouho
788
        @date       2018.08.30
789
    '''
790

    
791
    def disconnectedItemAtConnector(self, connector):
792
        for conn in self.connectors:
793
            if conn.isOverlapConnector(connector):
794
                conn.connectedItem = None
795

    
796
    def arrange_flow_direction(self, _from, reverse=False):
797
        """ reverse if from is connected to second connector """
798
        if not _from:
799
            raise ValueError
800

    
801
        if not reverse and self.connectors[0].connectedItem != _from:
802
            self.reverse()
803
        elif reverse and self.connectors[1].connectedItem != _from:
804
            self.reverse()
805

    
806
    def find_connected_objects(self):
807
        """find all connected items except equipment and instrument"""
808
        from EngineeringLineItem import QEngineeringLineItem
809
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
810
        from SymbolSvgItem import SymbolSvgItem
811

    
812
        left_visited, right_visited = [self], [self]
813

    
814
        try:
815
            left_pool, right_pool = [self.connectors[0].connectedItem] if self.connectors[0].connectedItem else [], \
816
                                    [self.connectors[1].connectedItem] if self.connectors[1].connectedItem else []
817

    
818
            while left_pool:
819
                obj = left_pool.pop()
820

    
821
                if issubclass(type(obj), QEngineeringEquipmentItem) or (len(obj.connectors) > 2) or \
822
                        not obj.is_connected(left_visited[-1]):
823
                    continue
824

    
825
                left_visited.append(obj)
826

    
827
                if (type(obj) is QEngineeringLineItem and self.is_piping(True)) or issubclass(type(obj), SymbolSvgItem):
828
                    founds = [conn.connectedItem for conn in obj.connectors if
829
                              conn.connectedItem and conn.connectedItem not in left_visited and
830
                              2 == len(conn.connectedItem.connectors)]
831
                    for found in founds:
832
                        matches = [conn.connectedItem for conn in found.connectors if conn.connectedItem is obj]
833
                        if matches:
834
                            left_pool.append(found)
835

    
836
            while right_pool:
837
                obj = right_pool.pop()
838

    
839
                if issubclass(type(obj), QEngineeringEquipmentItem) or (len(obj.connectors) > 2) or \
840
                        not obj.is_connected(right_visited[-1]):
841
                    continue
842

    
843
                right_visited.append(obj)
844

    
845
                if (type(obj) is QEngineeringLineItem and self.is_piping(True)) or issubclass(type(obj), SymbolSvgItem):
846
                    founds = [conn.connectedItem for conn in obj.connectors if
847
                              conn.connectedItem and conn.connectedItem not in right_visited and
848
                              2 == len(conn.connectedItem.connectors)]
849
                    for found in founds:
850
                        matches = [conn.connectedItem for conn in found.connectors if conn.connectedItem is obj]
851
                        if matches:
852
                            right_pool.append(found)
853
        except Exception as ex:
854
            from App import App
855
            from AppDocData import MessageType
856

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

    
861
        return left_visited, right_visited
862

    
863
    def reverse(self, auto=False):
864
        """revere the line"""
865
        from EngineeringLineItem import QEngineeringLineItem
866
        from SymbolSvgItem import SymbolSvgItem
867

    
868
        line = self.line()
869
        self.setLine(QLineF(line.p2(), line.p1()))
870
        self.connectors[0], self.connectors[1] = self.connectors[1], self.connectors[0]
871
        self.update_arrow()
872
        self.update()
873

    
874
        if auto:
875
            left, right = self.find_connected_objects()
876

    
877
            if left:
878
                connected_item = self
879
                for obj in left[1:]:
880
                    if type(obj) is QEngineeringLineItem:
881
                        obj.arrange_flow_direction(connected_item, True)
882

    
883
                    connected_item = obj
884

    
885
            if right:
886
                connected_item = self
887
                for obj in right[1:]:
888
                    if type(obj) is QEngineeringLineItem:
889
                        obj.arrange_flow_direction(connected_item)
890

    
891
                    connected_item = obj
892

    
893
    '''
894
        @brief      add flow arrow
895
        @author     humkyung
896
        @date       2018.05.08
897
        @history    2018.05.24  Jeongwoo    Modifying Draw Flow Arrow
898
    '''
899

    
900
    def addFlowArrow(self):
901
        import numpy as np
902
        import cv2
903
        import math
904
        import sys
905
        global src
906
        from shapely.geometry import Point
907
        from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
908
        from AppDocData import AppDocData
909

    
910
        try:
911
            docData = AppDocData.instance()
912
            area = docData.getArea('Drawing')
913

    
914
            startPt = self.start_point()
915
            endPt = self.end_point()
916
            length = self.length()
917
            direction = [(endPt[0] - startPt[0]) / length, (endPt[1] - startPt[1]) / length]
918

    
919
            left = min(startPt[0], endPt[0])
920
            top = min(startPt[1], endPt[1])
921
            right = max(startPt[0], endPt[0])
922
            bottom = max(startPt[1], endPt[1])
923

    
924
            rect = None
925
            if self.isVertical():
926
                rect = QRectF(left - 10, top, (right - left) + 20, (bottom - top))
927
            else:
928
                rect = QRectF(left, top - 10, (right - left), (bottom - top) + 20)
929

    
930
            docData = AppDocData.instance()
931
            area = docData.getArea('Drawing')
932
            img = np.array(AppDocData.instance().getCurrentPidSource().getPyImageOnRect(rect))
933

    
934
            imgLine = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY)[1]
935
            # remove noise
936
            imgLine = cv2.bitwise_not(imgLine)
937
            imgLine = cv2.erode(imgLine, np.ones((10, 10), np.uint8))
938

    
939
            contours, hierarchy = cv2.findContours(imgLine, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
940
            if contours:
941
                contours = sorted(contours, key=cv2.contourArea, reverse=True)
942
                [x, y, w, h] = cv2.boundingRect(contours[0])
943
                if w > 10 and w < 100 and h > 10 and h < 100:  # check arrow mark size
944
                    imgArrowMark = imgLine[y:y + h, x:x + w]
945

    
946
                    # DEBUG - display flow arrow area
947
                    '''
948
                    item = QGraphicsBoundingBoxItem(rect.left() + x - 10, rect.top() + y - 10, w + 20, h + 20)
949
                    item.isSymbol = True
950
                    item.angle = 0
951
                    item.setPen(QPen(Qt.red, 1, Qt.SolidLine))
952
                    item.setBrush(QBrush(QColor(255,255,0,100)))
953
                    self.scene().addItem(item)
954
                    '''
955
                    # up to here
956

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

    
960
                    ####### HoughLinesP
961
                    if lines is not None:
962
                        maxLength = None
963
                        selected = None
964
                        for line in lines:
965
                            for x1, y1, x2, y2 in line:
966
                                dx = x2 - x1
967
                                dy = y2 - y1
968
                                selected = line
969
                                length = math.sqrt(dx * dx + dy * dy)
970
                                if maxLength is None or length > maxLength:
971
                                    maxLength = length
972
                                    selected = line
973

    
974
                        for x1, y1, x2, y2 in selected:
975
                            dx = math.fabs(x2 - x1)
976
                            dy = math.fabs(y2 - y1)
977
                            length = math.sqrt(dx * dx + dy * dy)
978
                            dx /= length
979
                            dy /= length
980
                            if (self.isVertical() and (dx < 0.001 or math.fabs(dx - 1) < 0.001)) or (
981
                                    self.isHorizontal() and (dx < 0.001 or math.fabs(dx - 1) < 0.001)): continue
982
                            dist1 = self.distanceTo((rect.left() + x + x1, rect.top() + y + y1))
983
                            dist2 = self.distanceTo((rect.left() + x + x2, rect.top() + y + y2))
984
                            if dist1 > dist2:  # point which's distance is longer would be start point
985
                                _start = (rect.left() + x + x1, rect.top() + y + y1)
986
                                _end = (rect.left() + x + x2, rect.top() + y + y2)
987
                            else:
988
                                _start = (rect.left() + x + x2, rect.top() + y + y2)
989
                                _end = (rect.left() + x + x1, rect.top() + y + y1)
990

    
991
                            # DEBUG display detected line
992
                            '''
993
                            poly = QEngineeringPolylineItem()
994
                            poly._pol.append(QPointF(_start[0], _start[1]))
995
                            poly._pol.append(QPointF(_end[0], _end[1]))
996
                            poly.setPen(QPen(Qt.red, 2, Qt.SolidLine))
997
                            poly.buildItem()
998
                            self.scene().addItem(poly)
999
                            '''
1000
                            # up to here
1001

    
1002
                            dist1 = Point(startPt[0], startPt[1]).distance(Point(_start[0], _start[1]))
1003
                            dist2 = Point(startPt[0], startPt[1]).distance(Point(_end[0], _end[1]))
1004
                            if dist1 > dist2:
1005
                                startPt, endPt = endPt, startPt
1006
                                direction[0], direction[1] = -direction[0], -direction[1]
1007
                                self.reverse()
1008

    
1009
                        '''
1010
                        center = [(startPt[0]+endPt[0])*0.5, (startPt[1]+endPt[1])*0.5]
1011
                        arrow = QEngineeringFlowArrowItem(center, direction)
1012
                        arrow.buildItem()
1013
                        self.scene().addItem(arrow)
1014
                        '''
1015

    
1016
                        x = round(rect.left() + x - 5)
1017
                        y = round(rect.top() + y - 5)
1018
                        self.flowMark = ([x, y, w + 10, h + 10], None)
1019
                else:
1020
                    pass
1021
        except Exception as ex:
1022
            from App import App
1023
            from AppDocData import MessageType
1024

    
1025
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1026
                                                           sys.exc_info()[-1].tb_lineno)
1027
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1028

    
1029
    '''
1030
        @breif  insert symbol
1031
        @author humkyung
1032
        @date   2018.04.22
1033
        @history    kyouho  2018.07.24  add symbol angle, transform rotateRadians(-angle -> angle)
1034
    '''
1035

    
1036
    def insertSymbol(self, symbol, pos):
1037
        import math
1038
        from shapely.geometry import Point
1039
        from shapely import affinity
1040

    
1041
        vec = self.perpendicular()
1042
        line = [(pos.x() - vec[0] * 20, pos.y() - vec[1] * 20), (pos.x() + vec[0] * 20, pos.y() + vec[1] * 20)]
1043
        origin = self.intersection(line)
1044
        transform = QTransform()
1045
        transform.translate(origin.x, origin.y)
1046
        angle = self.angle()
1047
        transform.rotateRadians(-angle)
1048
        transform.translate(-symbol.symbolOrigin[0], -symbol.symbolOrigin[1])
1049
        symbol.setTransform(transform)
1050
        # save angle
1051
        symbol.angle = round(angle, 2)
1052
        if 2 == len(symbol.connectors):  # 2 way component
1053
            for i in range(len(symbol.connectors)):
1054
                rotatedPt = affinity.rotate(Point(symbol.connectors[i].connectPoint[0] - symbol.symbolOrigin[0],
1055
                                                  symbol.connectors[i].connectPoint[1] - symbol.symbolOrigin[1]),
1056
                                            -angle, Point(0, 0), use_radians=True)
1057
                #symbol.connectors[i].sceneConnectPoint = (origin.x + rotatedPt.x, origin.y + rotatedPt.y)
1058

    
1059
            dx1 = symbol.connectors[0].center()[0] - self.start_point()[0]
1060
            dy1 = symbol.connectors[0].center()[1] - self.start_point()[1]
1061
            length1 = math.sqrt(dx1 * dx1 + dy1 * dy1)
1062
            dx2 = symbol.connectors[1].center()[0] - self.start_point()[0]
1063
            dy2 = symbol.connectors[1].center()[1] - self.start_point()[1]
1064
            length2 = math.sqrt(dx2 * dx2 + dy2 * dy2)
1065

    
1066
            if length1 < length2:
1067
                processLine = QEngineeringLineItem([symbol.connectors[1].center(), self.end_point()])
1068
                processLine.connectors[0].connectedItem = symbol
1069
                processLine.connectors[1].connectedItem = self.connectors[1].connectedItem
1070
                self.scene().addItem(processLine)
1071

    
1072
                line = QLineF(self.line().p1(), QPointF(symbol.connectors[0].center()[0],
1073
                                                        symbol.connectors[0].center()[1]))
1074
                self.setLine(line)
1075
                self.connectors[1].connectedItem = symbol
1076

    
1077
                symbol.connectors[0].connectedItem = self
1078
                symbol.connectors[1].connectedItem = processLine
1079
            else:
1080
                processLine = QEngineeringLineItem([symbol.connectors[0].center(), self.end_point()])
1081
                processLine.connectors[0].connectedItem = symbol
1082
                processLine.connectors[1].connectedItem = self.connectors[1].connectedItem
1083
                self.scene().addItem(processLine)
1084

    
1085
                line = QLineF(self.line().p1(), QPointF(symbol.connectors[1].center()[0],
1086
                                                        symbol.connectors[1].center()[1]))
1087
                self.setLine(line)
1088
                self.connectors[1].connectedItem = symbol
1089

    
1090
                symbol.connectors[0].connectedItem = processLine
1091
                symbol.connectors[1].connectedItem = self
1092

    
1093
            self.joinTo(symbol)
1094
            processLine.joinTo(symbol)
1095
            self.update()
1096

    
1097
        symbol.loc = [origin.x - symbol.symbolOrigin[0], origin.y - symbol.symbolOrigin[1]]
1098
        symbol.size = [symbol.boundingRect().width(), symbol.boundingRect().height()]
1099
        self.scene().addItem(symbol)
1100

    
1101
    def update_shape(self, symbol, point):
1102
        """update line shape"""
1103
        for index in range(len(self.connectors)):
1104
            if self.connectors[index].connectedItem == symbol:
1105
                # startPoint
1106
                if index == 0:
1107
                    line = QLineF(QPointF(point[0], point[1]), self.line().p2())
1108
                    self.setLine(line)
1109
                    self.connectors[0].setPos([point[0], point[1]])
1110
                # endpoint
1111
                else:
1112
                    line = QLineF(self.line().p1(), QPointF(point[0], point[1]))
1113
                    self.setLine(line)
1114
                    self.connectors[1].setPos([point[0], point[1]])
1115

    
1116
        ## startPoint에 symbol
1117
        # if self.connectors[0].connectedItem == symbol:
1118
        #    if self.startPoint()[0] == symbol.connectors[0].sceneConnectPoint[0] and self.startPoint()[1] == symbol.connectors[0].sceneConnectPoint[1]:
1119
        #        line = QLineF(QPointF(changedConnPoint1[0], changedConnPoint1[1]), self.line().p2())
1120
        #        self.setLine(line)
1121
        #    else:
1122
        #        line = QLineF(QPointF(changedConnPoint2[0], changedConnPoint2[1]), self.line().p2())
1123
        #        self.setLine(line)
1124
        ## endPoint에 symbol
1125
        # elif self.connectors[1].connectedItem == symbol:
1126
        #    if self.endPoint()[0] == symbol.connectors[0].sceneConnectPoint[0] and self.endPoint()[1] == symbol.connectors[0].sceneConnectPoint[1]:
1127
        #        line = QLineF(self.line().p1(), QPointF(changedConnPoint1[0], changedConnPoint1[1]))
1128
        #        self.setLine(line)
1129
        #    else:
1130
        #        line = QLineF(self.line().p1(), QPointF(changedConnPoint2[0], changedConnPoint2[1]))
1131
        #        self.setLine(line)
1132

    
1133
        self.update()
1134

    
1135
    '''
1136
        @brief  remove symbol
1137
        @author humkyung
1138
        @date   2018.04.23
1139
    '''
1140

    
1141
    def removeSymbol(self, symbol):
1142
        import math
1143

    
1144
        if 2 == len(symbol.connectors):  # 2-way component
1145
            connected = symbol.connectors[0].connectedItem if symbol.connectors[0].connectedItem is not self else \
1146
            symbol.connectors[1].connectedItem
1147

    
1148
            pts = []
1149
            pts.append(self.start_point())
1150
            pts.append(self.end_point())
1151
            pts.append(connected.start_point())
1152
            pts.append(connected.end_point())
1153

    
1154
            self.scene().removeItem(connected)
1155

    
1156
            start = None
1157
            end = None
1158
            maxDist = None
1159
            for i in range(len(pts)):
1160
                for j in range(i + 1, len(pts)):
1161
                    dx = pts[i][0] - pts[j][0]
1162
                    dy = pts[i][1] - pts[j][1]
1163
                    dist = math.sqrt(dx * dx + dy * dy)
1164
                    if maxDist is None:
1165
                        maxDist = dist
1166
                        start = pts[i]
1167
                        end = pts[j]
1168
                    elif dist > maxDist:
1169
                        maxDist = dist
1170
                        start = pts[i]
1171
                        end = pts[j]
1172

    
1173
            if (pts[0] == end) or (pts[1] == start): start, end = end, start
1174

    
1175
            line = QLineF(QPointF(start[0], start[1]), QPointF(end[0], end[1]))
1176
            self.setLine(line)
1177
            # self.buildItem()
1178
            self.update()
1179

    
1180
    def validate(self):
1181
        '''
1182
            @brief  validation check : connection
1183
            @author euisung
1184
            @date   2019.04.01
1185
        '''
1186
        from EngineeringAbstractItem import QEngineeringAbstractItem
1187
        from SymbolSvgItem import SymbolSvgItem
1188
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
1189
        from AppDocData import AppDocData
1190
        errors = []
1191

    
1192
        try:
1193
            _translate = QCoreApplication.translate
1194

    
1195
            docdata = AppDocData.instance()
1196
            dataPath = docdata.getErrorItemSvgPath()
1197

    
1198
            connectedUid = []
1199

    
1200
            for connector in self.connectors:
1201
                # for duplicattion check
1202
                if connector.connectedItem and issubclass(type(connector.connectedItem), QEngineeringAbstractItem):
1203
                    connectedUid.append(str(connector.connectedItem.uid))
1204

    
1205
                # check if there is not connected connector
1206
                if connector.connectedItem is None:
1207
                    error = SymbolSvgItem.createItem('Error', None, dataPath)
1208
                    error.setPosition(connector.center())
1209
                    error.parent = self
1210
                    error.msg = _translate('disconnected', 'disconnected')
1211
                    error.setToolTip(error.msg)
1212
                    error.area = 'Drawing'
1213
                    error.name = 'Error'
1214
                    errors.append(error)
1215

    
1216
                # check line to symbol
1217
                elif issubclass(type(connector.connectedItem), SymbolSvgItem) and type(
1218
                        connector.connectedItem) is not QEngineeringEquipmentItem:
1219
                    matches = [conn for conn in connector.connectedItem.connectors if conn.connectedItem is self]
1220
                    # check if two items are connected each other
1221
                    if not matches:
1222
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
1223
                        error.setPosition(connector.center())
1224
                        error.parent = self
1225
                        error.msg = _translate('disconnected from opposite side', 'disconnected from opposite side')
1226
                        error.setToolTip(error.msg)
1227
                        error.area = 'Drawing'
1228
                        error.name = 'Error'
1229
                        errors.append(error)
1230
                    # check connection position
1231
                    elif not self.isOverlap(connector.sceneBoundingRect(), matches[0].sceneBoundingRect()):
1232
                        error = SymbolSvgItem.createItem('Error', None, dataPath)
1233
                        error.setPosition(connector.center())
1234
                        error.parent = self
1235
                        error.msg = _translate('mismatched position', 'mismatched position')
1236
                        error.setToolTip(error.msg)
1237
                        error.area = 'Drawing'
1238
                        error.name = 'Error'
1239
                        errors.append(error)
1240

    
1241
                elif issubclass(type(connector.connectedItem), QEngineeringLineItem):
1242
                    # check if connected two lines has same direction
1243
                    if connector._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT:
1244
                        center = connector.center()
1245

    
1246
                        indices = [0, 0]
1247
                        indices[0] = 1 if QPointF(center[0], center[1]) == self.line().p1() else 2
1248
                        matches = [conn for conn in connector.connectedItem.connectors if
1249
                                   conn.connectedItem == self and conn._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT]
1250
                        if matches:
1251
                            indices[1] = 1 if QPointF(matches[0].center()[0], matches[0].center()[
1252
                                1]) == connector.connectedItem.line().p1() else 2
1253
                        else:
1254
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
1255
                            error.setPosition(connector.center())
1256
                            error.parent = self
1257
                            error.msg = _translate('disconnected from opposite side', 'disconnected from opposite side')
1258
                            error.setToolTip(error.msg)
1259
                            error.area = 'Drawing'
1260
                            error.name = 'Error'
1261
                            errors.append(error)
1262

    
1263
                        if indices[0] == indices[1]:
1264
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
1265
                            error.setPosition(connector.center())
1266
                            error.parent = self
1267
                            error.msg = _translate('flow direction error', 'flow direction error')
1268
                            error.setToolTip(error.msg)
1269
                            error.area = 'Drawing'
1270
                            error.name = 'Error'
1271
                            errors.append(error)
1272

    
1273
                        if self.lineType != connector.connectedItem.lineType:
1274
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
1275
                            error.setPosition(connector.center())
1276
                            error.parent = self
1277
                            error.msg = _translate('line type error', 'line type error')
1278
                            error.setToolTip(error.msg)
1279
                            error.area = 'Drawing'
1280
                            error.name = 'Error'
1281
                            errors.append(error)
1282
                    
1283
                    else:
1284
                        center = connector.center()
1285
                        line = connector.connectedItem
1286

    
1287
                        configs = docdata.getConfigs('Line Detector', 'Length to connect line')
1288
                        toler = int(configs[0].value) if configs else 20
1289

    
1290
                        if line.distanceTo(center) > toler * 2:
1291
                            error = SymbolSvgItem.createItem('Error', None, dataPath)
1292
                            error.setPosition(connector.center())
1293
                            error.parent = self
1294
                            error.msg = _translate('Line Position error', 'Line Position error')
1295
                            error.setToolTip(error.msg)
1296
                            error.area = 'Drawing'
1297
                            error.name = 'Error'
1298
                            errors.append(error)
1299

    
1300
                errors.extend(connector.validate())
1301

    
1302
            # check duplicated connection
1303
            if len(connectedUid) is not len(set(connectedUid)):
1304
                error = SymbolSvgItem.createItem('Error', None, dataPath)
1305
                error.setPosition([self.sceneBoundingRect().center().x(), self.sceneBoundingRect().center().y()])
1306
                error.parent = self
1307
                error.msg = _translate('duplicated connection', 'duplicated connection')
1308
                error.setToolTip(error.msg)
1309
                error.area = 'Drawing'
1310
                error.name = 'Error'
1311
                errors.append(error)
1312

    
1313
        except Exception as ex:
1314
            from App import App
1315
            from AppDocData import MessageType
1316

    
1317
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1318
                                                           sys.exc_info()[-1].tb_lineno)
1319
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1320

    
1321
        return errors
1322

    
1323
    '''
1324
        @brief  update line type
1325
        @author humkyung
1326
        @date   2018.07.05
1327
    '''
1328

    
1329
    def update_line_type(self):
1330
        import uuid
1331
        from LineTypeConditions import LineTypeConditions
1332

    
1333
        try:
1334
            pool, visited, items = [self], [], []
1335
            while pool:
1336
                obj = pool.pop()
1337
                visited.append(obj)
1338

    
1339
                """ connected items """
1340
                connected = [connector.connectedItem for connector in obj.connectors if
1341
                             connector.connectedItem and not type(connector.connectedItem) is uuid.UUID]
1342
                """ connected lines at point """
1343
                lines = [connector.connectedItem for connector in obj.connectors if connector.connectedItem and type(
1344
                    connector.connectedItem) is QEngineeringLineItem and connector._connected_at == QEngineeringAbstractItem.CONNECTED_AT_PT]
1345
                """ add items not in 'connected' to items list """
1346
                items.extend([item for item in connected if item not in lines])
1347
                """ add items not visited to pool """
1348
                pool.extend([item for item in lines if item not in visited])
1349

    
1350
            for condition in LineTypeConditions.items():
1351
                if condition.eval(items):
1352
                    self.lineType = condition.name
1353
                    break
1354
                if condition.eval(items, reverse=True):
1355
                    self.lineType = condition.name
1356
                    break
1357
        except Exception as ex:
1358
            from App import App
1359
            from AppDocData import MessageType
1360

    
1361
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1362
                                                           sys.exc_info()[-1].tb_lineno)
1363
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1364

    
1365
    def hoverEnterEvent(self, event):
1366
        """ hilight item and it's children """
1367
        self.highlight(True)
1368

    
1369
    def hoverLeaveEvent(self, event):
1370
        """ restore original color """
1371
        self.highlight(False)
1372

    
1373
    def highlight(self, flag):
1374
        self.hover = flag
1375
        self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(QEngineeringLineItem.ZVALUE)
1376
        self.update()
1377

    
1378
        for connector in self.connectors:
1379
            connector.highlight(flag)
1380

    
1381
    def hoverMoveEvent(self, event):
1382
        pass
1383

    
1384
    '''
1385
        @brief      remove item when user press delete key
1386
        @author     humkyung
1387
        @date       2018.04.23
1388
        @history    swap start, end point when user press 'c' key
1389
    '''
1390

    
1391
    def keyPressEvent(self, event):
1392
        if self.isSelected() and event.key() == Qt.Key_Delete:
1393
            self.scene().removeItem(self)
1394
        elif event.key() == Qt.Key_C and self.is_piping(True):
1395
            self.reverse(True)
1396
        elif event.key() == Qt.Key_A:
1397
            self.toggleFlowMark()
1398
        elif event.key() == Qt.Key_Up:  # translate up/down/left/right symbol
1399
            modifiers = QApplication.keyboardModifiers()
1400
            delta = 10 if modifiers == Qt.ControlModifier else 1
1401

    
1402
            dx, dy = 0*delta, -1*delta
1403
            self.set_line([[self.start_point()[0] + dx, self.start_point()[1] + dy],
1404
                           [self.end_point()[0] + dx, self.end_point()[1] + dy]])
1405
        elif event.key() == Qt.Key_Down:
1406
            modifiers = QApplication.keyboardModifiers()
1407
            delta = 10 if modifiers == Qt.ControlModifier else 1
1408

    
1409
            dx, dy = 0*delta, 1*delta
1410
            self.set_line([[self.start_point()[0] + dx, self.start_point()[1] + dy],
1411
                           [self.end_point()[0] + dx, self.end_point()[1] + dy]])
1412
        elif event.key() == Qt.Key_Left:
1413
            modifiers = QApplication.keyboardModifiers()
1414
            delta = 10 if modifiers == Qt.ControlModifier else 1
1415

    
1416
            dx, dy = -1*delta, 0*delta
1417
            self.set_line([[self.start_point()[0] + dx, self.start_point()[1] + dy],
1418
                           [self.end_point()[0] + dx, self.end_point()[1] + dy]])
1419
        elif event.key() == Qt.Key_Right:
1420
            modifiers = QApplication.keyboardModifiers()
1421
            delta = 10 if modifiers == Qt.ControlModifier else 1
1422

    
1423
            dx, dy = 1*delta, 0*delta
1424
            self.set_line([[self.start_point()[0] + dx, self.start_point()[1] + dy],
1425
                           [self.end_point()[0] + dx, self.end_point()[1] + dy]])
1426

    
1427
    def toggleFlowMark(self):
1428
        from AppDocData import AppDocData
1429

    
1430
        if self.flowMark:
1431
            self.flowMark = None
1432
        else:
1433
            configs = AppDocData.instance().getConfigs('Flow Mark', 'Position')
1434
            self.flowMark = int(configs[0].value) if 1 == len(configs) else 100
1435
        self.update_arrow()
1436

    
1437
    '''
1438
        @brief  draw rect when item is selected
1439
        @author humkyung
1440
        @date   2018.07.07
1441
    '''
1442

    
1443
    def drawFocusRect(self, painter):
1444
        self.focuspen = QPen(Qt.DotLine)
1445
        self.focuspen.setColor(Qt.black)
1446
        self.focuspen.setWidthF(1.5)
1447
        hilightColor = QColor(255, 0, 0, 127)
1448
        painter.setBrush(QBrush(hilightColor))
1449
        painter.setPen(self.focuspen)
1450
        painter.drawRect(self.boundingRect())
1451

    
1452
    '''
1453
        @brief      override paint method
1454
        @history    humkyung 2018.08.30 draw flowmark only for Primary or Secondary
1455
    '''
1456

    
1457
    def paint(self, painter, option, widget):
1458
        color = self.getColor()
1459
        self.setColor(color)
1460

    
1461
        QGraphicsLineItem.paint(self, painter, option, widget)
1462

    
1463
        if self.isSelected():
1464
            self.drawFocusRect(painter)
1465

    
1466
    def drawToImage(self, img, color, thickness):
1467
        """write recognized lines to image"""
1468
        try:
1469
            ptStart = self.start_point()
1470
            ptEnd = self.end_point()
1471
            cv2.line(img, (round(ptStart[0]), round(ptStart[1])), (round(ptEnd[0]), round(ptEnd[1])), color, thickness)
1472
            # up to here
1473
        except Exception as ex:
1474
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1475
                                                       sys.exc_info()[-1].tb_lineno))
1476

    
1477
    @staticmethod
1478
    def from_database(component):
1479
        """ get line from database """
1480
        import uuid
1481
        from AppDocData import AppDocData
1482
        from SymbolAttr import SymbolAttr
1483

    
1484
        item = None
1485
        try:
1486
            uidNode = component['UID']
1487
            uid = uidNode if uidNode is not None else uuid.uuid4()  # generate UUID
1488
            owner = uuid.UUID(component['Owner']) if component['Owner'] and component['Owner'] != 'None' else None
1489

    
1490
            app_doc_data = AppDocData.instance()
1491
            connectors = app_doc_data.get_component_connectors(uid)
1492
            if 2 != len(connectors): return item
1493
            startPoint = [float(connectors[0]['X']), float(connectors[0]['Y'])]
1494
            endPoint = [float(connectors[1]['X']), float(connectors[1]['Y'])]
1495

    
1496
            item = QEngineeringLineItem(vertices=[startPoint, endPoint], uid=uid)
1497
            item.setVisible(False)
1498
            attrs = app_doc_data.get_component_attributes(uid)
1499
            matches = [attr for attr in attrs if attr['Attribute'] == 'LineType']
1500
            item.lineType = matches[0]['Value'] if matches else 'Secondary'
1501
            ## assign area
1502
            if component['Area']:
1503
                for area in app_doc_data.getAreaList():
1504
                    if area.contains(startPoint) and area.contains(endPoint):
1505
                        item.area = area.name
1506
                        break
1507
            else:
1508
                item.area = component['Area']
1509
            ## up to here
1510

    
1511
            matches = [attr for attr in attrs if attr['Attribute'] == 'Thickness']
1512
            item.thickness = int(matches[0]['Value']) if matches and matches[0]['Value'] != 'None' else None
1513

    
1514
            matches = [attr for attr in attrs if attr['Attribute'] == 'FlowMark']
1515
            item.flowMark = int(matches[0]['Value']) if matches and matches[0]['Value'] != 'None' else None
1516

    
1517
            if connectors:
1518
                iterIndex = 0
1519
                for connector in connectors:
1520
                    item.connectors[iterIndex].parse_record(connector)
1521
                    iterIndex += 1
1522

    
1523
            # get associations
1524
            associations = app_doc_data.get_component_associations(uid)
1525
            if associations:
1526
                for assoc in associations:
1527
                    _type = assoc['Type']
1528
                    if not _type in item._associations:
1529
                        item._associations[_type] = []
1530
                    item._associations[_type].append(uuid.UUID(assoc['Association']))
1531
            # up to here
1532
        except Exception as ex:
1533
            from App import App
1534
            from AppDocData import MessageType
1535

    
1536
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1537
                                                           sys.exc_info()[-1].tb_lineno)
1538
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1539

    
1540
        return item if item.length() > 1 else None
1541

    
1542
    '''
1543
        @brief      parse xml code
1544
        @author     humkyung
1545
        @date       2018.06.27
1546
    '''
1547

    
1548
    @staticmethod
1549
    def fromXml(node):
1550
        import uuid
1551
        from AppDocData import AppDocData
1552
        from SymbolAttr import SymbolAttr
1553

    
1554
        item = None
1555
        try:
1556
            uidNode = node.find('UID')
1557
            uid = uidNode.text if uidNode is not None else uuid.uuid4()  # generate UUID
1558
            owner = uuid.UUID(node.attrib['OWNER']) if 'OWNER' in node.attrib and node.attrib[
1559
                'OWNER'] != 'None' else None
1560

    
1561
            startPoint = [float(x) for x in node.find('STARTPOINT').text.split(',')]
1562
            endPoint = [float(x) for x in node.find('ENDPOINT').text.split(',')]
1563

    
1564
            item = QEngineeringLineItem(vertices=[startPoint, endPoint], uid=uid)
1565
            item.setVisible(False)
1566
            item.lineType = node.find('TYPE').text if node.find('TYPE') is not None else 'Secondary'
1567
            # assign area
1568
            if node.find('AREA') is None:
1569
                appDocData = AppDocData.instance()
1570
                for area in appDocData.getAreaList():
1571
                    if area.contains(startPoint) and area.contains(endPoint):
1572
                        item.area = area.name
1573
                        break
1574
            else:
1575
                item.area = node.find('AREA').text
1576
            # up to here
1577

    
1578
            thicknessNode = node.find('THICKNESS')
1579
            item.thickness = int(
1580
                thicknessNode.text) if thicknessNode is not None and thicknessNode.text != 'None' else None
1581

    
1582
            flowMarkNode = node.find('FLOWMARK')
1583
            item.flowMark = int(flowMarkNode.text) if flowMarkNode is not None and flowMarkNode.text != 'None' else None
1584

    
1585
            connectors = node.find('CONNECTORS')
1586
            if connectors is not None:
1587
                iterIndex = 0
1588
                for connector in connectors.iter('CONNECTOR'):
1589
                    item.connectors[iterIndex].parse_xml(connector)
1590
                    iterIndex += 1
1591

    
1592
            # get associations
1593
            attributeValue = node.find('ASSOCIATIONS')
1594
            if attributeValue is not None:
1595
                for assoc in attributeValue.iter('ASSOCIATION'):
1596
                    _type = assoc.attrib['TYPE']
1597
                    if not _type in item._associations:
1598
                        item._associations[_type] = []
1599
                    item._associations[_type].append(uuid.UUID(assoc.text))
1600
            # up to here
1601

    
1602
            attributes = node.find('SYMBOLATTRIBUTES')
1603
            if attributes is not None:
1604
                for attr in attributes.iter('ATTRIBUTE'):
1605
                    _attr = SymbolAttr.fromXml(attr)
1606
                    item.attrs[_attr] = attr.text
1607

    
1608
        except Exception as ex:
1609
            from App import App
1610
            from AppDocData import MessageType
1611

    
1612
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1613
                                                           sys.exc_info()[-1].tb_lineno)
1614
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1615

    
1616
        return item if item.length() > 1 else None
1617

    
1618
    '''
1619
        @brief      generate xml code
1620
        @author     humkyung
1621
        @date       2018.04.23
1622
        @history    humkyung 2018.06.27 write line type to xml
1623
                    humkyung 2018.07.23 write connected item's uid to xml
1624
    '''
1625

    
1626
    def toXml(self):
1627
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1628
        from LineTypeConditions import LineTypeConditions
1629
        from SymbolAttr import SymbolAttr
1630

    
1631
        try:
1632
            node = Element('LINE')
1633
            node.attrib['OWNER'] = str(self._owner) if self._owner else 'None'
1634
            uidNode = Element('UID')
1635
            uidNode.text = str(self.uid)
1636
            node.append(uidNode)
1637

    
1638
            startPt = self.start_point()
1639
            endPt = self.end_point()
1640

    
1641
            startNode = Element('STARTPOINT')
1642
            startNode.text = '{},{}'.format(startPt[0], startPt[1])
1643
            node.append(startNode)
1644

    
1645
            endNode = Element('ENDPOINT')
1646
            endNode.text = '{},{}'.format(endPt[0], endPt[1])
1647
            node.append(endNode)
1648

    
1649
            typeNode = Element('TYPE')
1650
            typeNode.text = self.lineType
1651
            for lineType in LineTypeConditions.items():
1652
                if self.lineType == lineType.name:
1653
                    typeNode.attrib['TYPEUID'] = str(lineType)
1654
                    break
1655
            node.append(typeNode)
1656

    
1657
            areaNode = Element('AREA')
1658
            areaNode.text = self.area
1659
            node.append(areaNode)
1660

    
1661
            thicknessNode = Element('THICKNESS')
1662
            thicknessNode.text = str(self.thickness)
1663
            node.append(thicknessNode)
1664

    
1665
            flowMarkNode = Element('FLOWMARK')
1666
            flowMarkNode.text = str(self.flowMark)
1667
            node.append(flowMarkNode)
1668

    
1669
            connectorsNode = Element('CONNECTORS')
1670
            for connector in self.connectors:
1671
                connectorsNode.append(connector.toXml())
1672
            node.append(connectorsNode)
1673

    
1674
            attributeValueNode = Element('ASSOCIATIONS')
1675
            for assoc in self.associations():
1676
                assoc_node = Element('ASSOCIATION')
1677
                assoc_node.attrib['TYPE'] = QEngineeringAbstractItem.assoc_type(assoc)
1678
                assoc_node.text = str(assoc.uid)
1679
                attributeValueNode.append(assoc_node)
1680
            node.append(attributeValueNode)
1681

    
1682
            attributesNode = Element('SYMBOLATTRIBUTES')
1683
            _attrs = self.getAttributes()
1684
            for attr in _attrs:
1685
                if type(attr) is SymbolAttr:
1686
                    _node = attr.toXml()
1687
                    _node.text = str(_attrs[attr])
1688
                    attributesNode.append(_node)
1689

    
1690
            node.append(attributesNode)
1691

    
1692
            # up to here
1693
        except Exception as ex:
1694
            from App import App
1695
            from AppDocData import MessageType
1696

    
1697
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1698
                                                           sys.exc_info()[-1].tb_lineno)
1699
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1700
            return None
1701

    
1702
        return node
1703

    
1704
    def to_svg(self, parent) -> list:
1705
        """convert line item to svg"""
1706
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
1707
        from App import App
1708
        from AppDocData import AppDocData
1709

    
1710
        res = []
1711
        try:
1712
            app_doc_data = AppDocData.instance()
1713
            prj = app_doc_data.getCurrentProject()
1714

    
1715
            node = Element('g')
1716
            node.attrib['id'] = str(self.uid)
1717
            node.attrib['stroke'] = self.pen().color().name()
1718
            node.attrib['stroke-width'] = str(int(self.pen().width()*0.5))
1719

    
1720
            line_style_map = {Qt.DotLine: '2', Qt.DashLine: '4 2',
1721
                              Qt.DashDotLine: '4 2 2', Qt.DashDotDotLine: '4 2 2 2 2 2'}
1722
            line_type_style = self.line_type_style
1723
            if line_type_style[3] in line_style_map:
1724
                node.attrib['stroke-dasharray'] = line_style_map[line_type_style[3]]
1725

    
1726
            if not parent:
1727
                trans = QTransform()
1728
            else:
1729
                trans, _ = parent.sceneTransform().inverted()
1730

    
1731
            node.attrib['transform'] = f"matrix(" \
1732
                                       f"{trans.m11()},{trans.m12()}," \
1733
                                       f"{trans.m21()},{trans.m22()}," \
1734
                                       f"{trans.m31()},{trans.m32()}" \
1735
                                       f")"
1736

    
1737
            polyline = Element('polyline')
1738
            polyline.attrib['fill'] = 'none'
1739
            start_pt = self.start_point()
1740
            end_pt = self.end_point()
1741
            polyline.attrib['points'] = f"{start_pt[0]},{start_pt[1]} " \
1742
                                        f"{end_pt[0]},{end_pt[1]}"
1743
            node.append(polyline)
1744

    
1745
            res.append(node)
1746
        except Exception as ex:
1747
            from App import App
1748
            from AppDocData import MessageType
1749
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1750
                                                          sys.exc_info()[-1].tb_lineno)
1751
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1752

    
1753
        return res
1754

    
1755
    def toSql_return_separately(self):
1756
        """ generate sql phrase to save line to database """
1757
        import uuid
1758
        from AppDocData import AppDocData
1759

    
1760
        res = []
1761
        resLater = []
1762

    
1763
        app_doc_data = AppDocData.instance()
1764
        cols = ['UID', 'Drawings_UID', 'Symbol_UID', 'X', 'Y', 'Width', 'Height', 'Rotation', 'Area', 'Owner',
1765
                'SpecialItemTypes_UID']
1766
        values = ['?', '?', "(select UID from Symbol where Name='Line' and SymbolType_UID=-1)", '?', '?', '?', '?', '?',
1767
                  '?', '?', '?']
1768

    
1769
        rect = self.sceneBoundingRect()
1770
        param = [
1771
            (str(self.uid), str(app_doc_data.activeDrawing.UID), rect.x(), rect.y(), rect.width(), rect.height(), 0,
1772
             self.area, str(self.owner) if self.owner else None,
1773
             str(self.special_item_type) if self.special_item_type else None)]
1774
        sql = 'insert into Components({}) values({})'.format(','.join(cols), ','.join(values))
1775
        res.append((sql, tuple(param)))
1776

    
1777
        # save connectors to database
1778
        cols = ['Components_UID', '[Index]', 'X', 'Y', 'Connected', 'Connected_At']
1779
        values = ['?', '?', '?', '?', '?', '?']
1780
        params = []
1781
        index = 1
1782
        for connector in self.connectors:
1783
            params.append((  # str(connector.uid),
1784
                str(self.uid), index, connector.center()[0], connector.center()[1], \
1785
                str(connector.connectedItem.uid) if connector.connectedItem else None, \
1786
                str(connector._connected_at)))
1787
            index += 1
1788
        sql = 'insert into Points({}) values({})'.format(','.join(cols), ','.join(values))
1789
        resLater.append((sql, tuple(params)))
1790
        # up to here
1791

    
1792
        # save attributes
1793
        cols = ['UID', 'Components_UID', 'SymbolAttribute_UID', 'Value']
1794
        values = ['?', '?', "(select UID from SymbolAttribute where Attribute='LineType' and SymbolType_UID=-1)", '?']
1795
        param = [(str(uuid.uuid4()), str(self.uid), self.lineType)]
1796
        sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
1797
        res.append((sql, tuple(param)))
1798
        values = ['?', '?', "(select UID from SymbolAttribute where Attribute='Thickness' and SymbolType_UID=-1)", '?']
1799
        param = [(str(uuid.uuid4()), str(self.uid), str(self.thickness))]
1800
        sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
1801
        res.append((sql, tuple(param)))
1802
        values = ['?', '?', "(select UID from SymbolAttribute where Attribute='FlowMark' and SymbolType_UID=-1)", '?']
1803
        param = [(str(uuid.uuid4()), str(self.uid), str(self.flowMark))]
1804
        sql = 'insert into Attributes({}) values({})'.format(','.join(cols), ','.join(values))
1805
        res.append((sql, tuple(param)))
1806
        # up to here
1807

    
1808
        return res, resLater
1809

    
1810
    '''
1811
        @brief      Delete Line Item from scene
1812
        @author     Jeongwoo
1813
        @date       2018.05.29
1814
        @history    2018.05.29  Add parameter 'self' / Make comments emit()
1815
    '''
1816

    
1817
    def deleteLineItemFromScene(self):
1818
        self.scene().removeItem(self)
1819

    
1820
    def getColor(self):
1821
        """ return line's color """
1822
        from AppDocData import AppDocData
1823
        from DisplayColors import DisplayColors
1824
        from DisplayColors import DisplayOptions
1825
        from EngineeringAbstractItem import QEngineeringAbstractItem
1826

    
1827
        if DisplayOptions.DisplayByLineType == DisplayColors.instance().option:
1828
            if self.hover:
1829
                return QEngineeringAbstractItem.HOVER_COLOR
1830
            else:
1831
                if self.lineType in QEngineeringLineItem.LINE_TYPE_COLORS:
1832
                    return QEngineeringLineItem.LINE_TYPE_COLORS[self.lineType]
1833
                else:
1834
                    app_doc_data = AppDocData.instance()
1835
                    configs = app_doc_data.getConfigs('LineTypes', self.lineType)
1836
                    if configs:
1837
                        tokens = configs[0].value.split(',')
1838
                        QEngineeringLineItem.LINE_TYPE_COLORS[self.lineType] = tokens[0] if len(tokens) == 4 else \
1839
                            QEngineeringAbstractItem.DEFAULT_COLOR
1840
                        return QEngineeringLineItem.LINE_TYPE_COLORS[self.lineType]
1841
                    else:
1842
                        return QEngineeringAbstractItem.DEFAULT_COLOR
1843
        else:
1844
            if self.owner is None:
1845
                return QEngineeringAbstractItem.getColor(self)
1846
            else:
1847
                return self.owner.getColor()
1848

    
1849
    '''
1850
        @brief      Set Color. Override QEngineeringAbstractItem's
1851
        @author     Jeongwoo
1852
        @date       2018.05.11
1853
        @history    2018.05.11  Jeongwoo    Add self.setPen() Method
1854
        @history    humkyung 2018.05.13 call setPen method to apply changed color
1855
    '''
1856
    def setColor(self, color):
1857
        c = QColor()
1858
        c.setNamedColor(color)
1859
        _pen = self.pen()
1860
        _pen.setColor(c)
1861
        self.setPen(_pen)
1862
        self.update()
1863

    
1864
    def clear_labels(self):
1865
        """ clear spec labels """
1866
        attrs = self.getAttributes()
1867
        index = 0
1868
        for key in attrs.keys():
1869
            if index >= 6:
1870
                break
1871
            if key.AssocItem and not key.Freeze:
1872
                key.AssocItem.owner = None
1873
                self.remove_assoc_item(key.AssocItem)
1874
                key.AssocItem = None
1875
            if not key.Freeze:
1876
                attrs[key] = ''
1877
            index += 1
1878
    
1879
    def update_flow_mark(self, position, length):
1880
        """ update flow mark for flow arrow """
1881
        import math
1882

    
1883
        to_item = self.connectors[1].connectedItem
1884
        if type(to_item) is QEngineeringLineItem and self.length() > length and not self.isParallel(to_item):
1885
            self.flowMark = position
1886

    
1887
    @staticmethod
1888
    def update_arrows(lines):
1889
        for line in lines:
1890
            line.update_arrow()
1891

    
1892
        return lines
1893

    
1894
    def update_arrow(self):
1895
        """ update flow arrow """
1896
        import math
1897
        from EngineeringArrowItem import QEngineeringArrowItem
1898

    
1899
        if self.length() < 0.01:
1900
            return
1901

    
1902
        start = self.line().p1()
1903
        end = self.line().p2()
1904

    
1905
        dx = end.x() - start.x()
1906
        dy = end.y() - start.y()
1907
        _dir = [dx / self.length(), dy / self.length()]
1908

    
1909
        arrow_size = QEngineeringLineItem.ARROW_SIZE * 0.25
1910

    
1911
        if not self.is_piping():
1912
            self.flowMark = None
1913

    
1914
        if self.flowMark:
1915
            arrow_size *= 2
1916
            end = QPointF(start.x() + dx * self.flowMark / 100, start.y() + dy * self.flowMark / 100)
1917

    
1918
        perpendicular = (-_dir[1], _dir[0])
1919
        polygon = QPolygonF()
1920
        polygon.append(QPointF(end.x() - _dir[0] * QEngineeringLineItem.ARROW_SIZE + perpendicular[0] * arrow_size,
1921
                               end.y() - _dir[1] * QEngineeringLineItem.ARROW_SIZE + perpendicular[1] * arrow_size))
1922
        polygon.append(QPointF(end.x() - _dir[0] * QEngineeringLineItem.ARROW_SIZE - perpendicular[0] * arrow_size,
1923
                               end.y() - _dir[1] * QEngineeringLineItem.ARROW_SIZE - perpendicular[1] * arrow_size))
1924
        polygon.append(end)
1925
        polygon.append(polygon[0])  # close polygon
1926

    
1927
        if not hasattr(self, '_arrow'):
1928
            self._arrow = QEngineeringArrowItem(polygon, self)
1929
        else:
1930
            self._arrow.setPolygon(polygon)
1931

    
1932
        if self.flowMark:
1933
            self._arrow.setBrush(Qt.red)
1934
        else:
1935
            self._arrow.setBrush(Qt.blue)
1936
        self._arrow.update()
1937

    
1938
    def onConnectorPosChaned(self, connector):
1939
        """update line shape when connector is moved"""
1940

    
1941
        start = self.connectors[0].center()
1942
        end = self.connectors[1].center()
1943
        self.setLine(start[0], start[1], end[0], end[1])
1944
        self.update()
1945

    
1946
        self.setToolTip(self.tooltip)
1947
        self.update_arrow()
1948

    
1949
        self.scene().contents_changed.emit()
1950

    
1951
        # register resize command
1952
        pt_start = self.connectors[0].pressed_position if self.connectors[0].pressed_position else None
1953
        pt_end = self.connectors[-1].pressed_position if self.connectors[-1].pressed_position else None
1954
        if pt_start or pt_end:
1955
            from ResizeCommand import ResizeCommand
1956
            cmd = ResizeCommand(self.scene(), self,
1957
                                [pt_start if pt_start else
1958
                                 QPointF(self.connectors[0].center()[0], self.connectors[0].center()[1]),
1959
                                 pt_end if pt_end else
1960
                                 QPointF(self.connectors[-1].center()[0], self.connectors[-1].center()[-1])])
1961
            self.scene().undo_stack.push(cmd)
1962

    
1963
    '''
1964
        @brief      
1965
        @author     humkyung
1966
        @date       2018.07.24
1967
    '''
1968

    
1969
    def mousePressEvent(self, event):
1970
        import math
1971

    
1972
        if event.buttons() == Qt.LeftButton:
1973
            pos = event.scenePos()
1974
            ptStart = self.start_point()
1975
            dx = ptStart[0] - pos.x()
1976
            dy = ptStart[1] - pos.y()
1977
            if math.sqrt(dx * dx + dy * dy) < 10:
1978
                self._selectedIndex = 0
1979
                return
1980

    
1981
            ptEnd = self.end_point()
1982
            dx = ptEnd[0] - pos.x()
1983
            dy = ptEnd[1] - pos.y()
1984
            if math.sqrt(dx * dx + dy * dy) < 10:
1985
                self._selectedIndex = 1
1986
                return
1987

    
1988
            self._selectedIndex = -1
1989

    
1990
        QGraphicsLineItem.mousePressEvent(self, event)
1991

    
1992
    def mouseReleaseEvent(self, event):
1993
        self._selectedIndex = -1
1994

    
1995
        QGraphicsLineItem.mouseReleaseEvent(self, event)
1996

    
1997
    def contextMenuEvent(self, event):
1998
        menu = QMenu()
1999
        testAction = QAction('Test', None)
2000
        testAction.triggered.connect(self.print_out)
2001
        menu.addAction(testAction)
2002
        menu.exec_(event.screenPos())
2003

    
2004
    def print_out(self):
2005
        print('Triggered')
2006

    
2007
'''
2008
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
2009
    @author     Jeongwoo
2010
    @date       2018.06.18
2011
'''
2012

    
2013

    
2014
class Transfer(QObject):
2015
    onRemoved = pyqtSignal(QGraphicsItem)
2016

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