프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / Shapes / QEngineeringLineItem.py @ 47bd6b4e

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

1
# coding: utf-8
2
import os.path
3
import copy
4
try:
5
    from PyQt5.QtCore import Qt, QPointF, QRectF, pyqtSignal, QT_VERSION_STR, QRect
6
    from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QBrush, QPen, QTransform
7
    from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QGraphicsItem, QAbstractGraphicsShapeItem, QGraphicsPathItem
8
except ImportError:
9
    try:
10
        from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR, QRect
11
        from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog
12
    except ImportError:
13
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
14

    
15
from QGraphicsPolylineItem import QGraphicsPolylineItem
16
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
17
import shapely
18

    
19
class QEngineeringLineItem(QGraphicsPolylineItem):
20
    removed = pyqtSignal(QGraphicsPathItem)
21

    
22
    '''
23
        @history    2018.05.11  Jeongwoo    Make Comments self.setPen()
24
                    2018.05.15  Jeongwoo    Change method to call parent's __init__
25
    '''
26
    def __init__(self, parent=None):
27
        #super(QEngineeringLineItem, self).__init__()
28
        QGraphicsPolylineItem.__init__(self, parent)
29

    
30
        self.conns = [None, None]
31
        self._owner = None
32

    
33
        #self.setPen(QPen(Qt.blue, 5, Qt.SolidLine))
34
        self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
35

    
36
        self.setAcceptHoverEvents(True)
37
        self.setAcceptTouchEvents(True)
38

    
39
    '''
40
        @breif  getter owner
41
        @author humkyung
42
        @date   2018.05.10
43
    '''
44
    @property
45
    def owner(self):
46
        return self._owner
47

    
48
    '''
49
        @brief  setter owner
50
        @author humkyung
51
        @date   2018.05.10
52
        @history    2018.05.17  Jeongwoo    Add Calling setColor
53
    '''
54
    @owner.setter
55
    def owner(self, value):
56
        self._owner = value
57

    
58
        if self._owner is None:
59
            self._color = self.DEFAULT_COLOR
60
        self.setColor(self._color)
61

    
62
    '''
63
        @brief  construct a ProcessLineItem
64
        @author humkyung
65
    '''
66
    def process(self, param):
67
        from SymbolSvgItem import SymbolSvgItem
68

    
69
        if ('mousePressEvent' == param[0]) and (param[1].button() == Qt.LeftButton):
70
            self._vertices.append(self._pt)
71
        elif ('mouseMoveEvent' == param[0]):
72
            # get connection point near by mouse point
73
            pt = (param[2].x(), param[2].y())
74
            item = self.scene().itemAt(pt[0], pt[1], QTransform())
75
            if (item is not None) and (type(item) is SymbolSvgItem):
76
                connPt = item.getConnectionPointCloseTo(pt, 5)
77
                if connPt is not None: pt = connPt
78
            # up to here
79
                
80
            if len(self._vertices) > 0:
81
                dx = abs(self._vertices[-1][0] - pt[0])
82
                dy = abs(self._vertices[-1][1] - pt[1])
83
                if dx < dy:
84
                    self._pt = (self._vertices[-1][0], pt[1])
85
                else:
86
                    self._pt = (pt[0], self._vertices[-1][1])
87
            else:
88
                self._pt = pt
89
        elif ('mouseReleaseEvent' == param[0]) and (param[1].button() == Qt.RightButton):
90
            self.isCreated = True
91

    
92
    '''
93
        @brief  clone an object
94
    '''
95
    def clone(self):
96
        clone = QEngineeringLineItem()
97
        clone._vertices = copy.deepcopy(self._vertices)
98
        for vertex in clone._vertices:
99
            clone._pol.append(QPointF(vertex[0], vertex[1]))
100
        clone.buildItem()
101
        clone.isCreated = self.isCreated
102

    
103
        return clone
104

    
105
    '''
106
        @brief  dot product of given two vectors
107
        @author humkyung
108
        @date   2018.04.14
109
    '''
110
    def dotProduct(self, lhs, rhs):
111
        return sum([lhs[i]*rhs[i] for i in range(len(lhs))])
112

    
113
    '''
114
        @brief  distance between line and point
115
        @author humkyung
116
        @date   2018.04.16
117
    '''
118
    def distanceTo(self, pt):
119
        from shapely.geometry import Point, LineString
120

    
121
        startPt = self.startPoint()
122
        endPt = self.endPoint()
123
        line = LineString([(startPt[0], startPt[1]), (endPt[0], endPt[1])])
124
        dist = line.distance(Point(pt[0], pt[1]))
125

    
126
        return dist
127

    
128
    '''
129
        @brief  return perpendicular vector
130
        @author humkyung
131
        @date   2018.04.21
132
    '''
133
    def perpendicular(self):
134
        import math
135

    
136
        dx = self.endPoint()[0] - self.startPoint()[0]
137
        dy = self.endPoint()[1] - self.startPoint()[1]
138
        dx,dy = -dy,dx
139
        length = math.sqrt(dx*dx + dy*dy)
140
        dx /= length
141
        dy /= length
142

    
143
        return (dx,dy)
144

    
145
    '''
146
        @brief  return angle of line in radian
147
        @author humkyung
148
        @date   2018.04.22
149
    '''
150
    def angle(self):
151
        import math
152

    
153
        startPt = self.startPoint()
154
        endPt = self.endPoint()
155
        dx = endPt[0] - startPt[0]
156
        dy = endPt[1] - startPt[1]
157
        dot = self.dotProduct((1,0), (dx, dy))
158
        length = math.sqrt(dx*dx + dy*dy)
159
        return math.acos(dot/length)
160

    
161
    '''
162
        @brief  return length of line
163
        @author humkyung
164
        @date   2018.05.08
165
    '''
166
    def length(self):
167
        import math
168

    
169
        startPt = self.startPoint()
170
        endPt = self.endPoint()
171
        dx = endPt[0] - startPt[0]
172
        dy = endPt[1] - startPt[1]
173
        return math.sqrt(dx*dx + dy*dy)
174

    
175
    '''
176
        @brief  check if line is horizontal
177
        @author humkyung
178
        @date   2018.04.27
179
    '''
180
    def isHorizontal(self):
181
        import math
182

    
183
        startPt = self.startPoint()
184
        endPt = self.endPoint()
185
        dx = endPt[0] - startPt[0]
186
        dy = endPt[1] - startPt[1]
187

    
188
        return math.fabs(dx) > math.fabs(dy)
189

    
190
    '''
191
        @brief  check if line is vertical 
192
        @author humkyung
193
        @date   2018.04.27
194
    '''
195
    def isVertical(self):
196
        import math
197

    
198
        startPt = self.startPoint()
199
        endPt = self.endPoint()
200
        dx = endPt[0] - startPt[0]
201
        dy = endPt[1] - startPt[1]
202

    
203
        return math.fabs(dy) > math.fabs(dx)
204

    
205
    '''
206
        @brief  get intersection point between this and given line
207
        @author humkyung
208
        @date   2018.04.21
209
        @history    Jeongwoo 2018.05.15 Add normalize
210
                    Jeongwoo 2018.05.16 Add length == 0 check
211
    '''
212
    def intersection(self, line):
213
        import math
214
        from shapely.geometry import Point, LineString
215

    
216
        startPt = self.startPoint()
217
        endPt = self.endPoint()
218
        dx = endPt[0] - startPt[0]
219
        dy = endPt[1] - startPt[1]
220
        length = math.sqrt(dx*dx + dy*dy)
221
        if length == 0:
222
            return None
223
        dx /= length
224
        dy /= length
225
        lhs = LineString([(startPt[0] - dx*20, startPt[1] - dy*20), (endPt[0] + dx*20, endPt[1] + dy*20)])
226
        rhs = LineString(line)
227
        return lhs.intersection(rhs)
228

    
229
    '''
230
        @brief  check if two lines are connectable
231
        @author humkyung
232
        @date   2018.05.12
233
        @history    Jeongwoo 18.05.15 Add check pt's type
234
                    Jeongwoo 18.05.16 Add length == 0 check
235
    '''
236
    def isConnectable(self, line):
237
        import math
238

    
239
        startPt = line.startPoint()
240
        endPt = line.endPoint()
241

    
242
        dx = endPt[0] - startPt[0]
243
        dy = endPt[1] - startPt[1]
244
        length = math.sqrt(dx*dx + dy*dy)
245
        if length == 0:
246
            return False
247
        dx /= length
248
        dy /= length
249
        extendedLine = [(startPt[0] - dx*20, startPt[1] - dy*20), (endPt[0] + dx*20, endPt[1] + dy*20)]
250
        pt = self.intersection(extendedLine)
251

    
252
        return (pt is not None) and (type(pt) == shapely.geometry.point.Point)
253

    
254
    '''
255
        @brief      connect line and symbol is able to be connected and return symbol
256
        @author     humkyung
257
        @date       2018.04.16
258
        @history    humkyung 2018.05.08 check if line is possible to be connected
259
                    Jeongwoo 2018.05.15 Split if-statement and Connect each symbol and line
260
    '''
261
    def connectIfPossible(self, obj, toler):
262
        from shapely.geometry import Point
263
        from SymbolSvgItem import SymbolSvgItem
264
        res = []
265

    
266
        startPt = self.startPoint() 
267
        endPt = self.endPoint()
268

    
269
        if issubclass(type(obj), SymbolSvgItem):
270
            for i in range(len(obj.connPts)):
271
                pt = obj.connPts[i]
272
                if len(obj.conns) <= i: obj.conns.append(None)
273

    
274
                if (self.conns[0] is None) and (Point(startPt[0], startPt[1]).distance(Point(pt[0], pt[1])) < toler):
275
                    self.conns[0] = obj 
276
                    obj.conns[i] = self
277
                    res.append(obj)
278
                if (self.conns[1] is None) and (Point(endPt[0], endPt[1]).distance(Point(pt[0], pt[1])) < toler):
279
                    self.conns[1] = obj
280
                    obj.conns[i] = self
281
                    res.append(obj)
282
        elif type(obj) is QEngineeringLineItem:
283
            _startPt = obj.startPoint()
284
            _endPt = obj.endPoint()
285
            if((Point(startPt[0], startPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)):
286
                self.conns[0] = obj
287
                obj.conns[0] = self
288
                res.append(obj)
289
            if ((Point(startPt[0], startPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)):
290
                self.conns[0] = obj
291
                obj.conns[1] = self
292
                res.append(obj)
293
            
294
            if((Point(endPt[0], endPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)):
295
                self.conns[1] = obj
296
                obj.conns[0] = self
297
                res.append(obj)
298
            
299
            if ((Point(endPt[0], endPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)):
300
                self.conns[1] = obj
301
                obj.conns[1] = self
302
                res.append(obj)
303

    
304
        return res
305

    
306
    '''
307
        @brief  add flow arrow
308
        @author humkyung
309
        @date   2018.05.08
310
        @history    2018.05.24  Jeongwoo    Modifying Draw Flow Arrow
311
    '''
312
    def addFlowArrow(self):
313
        from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
314
        from AppDocData import AppDocData
315
        import numpy as np
316
        import cv2
317
        import math
318
        import sys
319
        
320
        try:
321
            rect = self.boundingRect()
322
            adjustRect = None
323
            adjustValue = 10
324
            if self.isVertical():
325
                adjustRect = rect.adjusted(-adjustValue, 0, adjustValue, 0)
326
            else:
327
                adjustRect = rect.adjusted(0, -adjustValue, 0, adjustValue)
328
            img = np.array(AppDocData.instance().getCurrentPidSource().getPyImageOnRect(adjustRect))
329
            imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
330

    
331
            edges = cv2.Canny(imgGray, 50, 150, apertureSize=3)
332
            lines = cv2.HoughLinesP(edges, 1, np.pi/180, 10, minLineLength=10, maxLineGap=5)
333

    
334
        #    ####### HoughLinesP
335
            if lines is not None:
336
                for line in lines:
337
                    for x1, y1, x2, y2 in line:
338
                        dx = math.fabs(x2 - x1)
339
                        dy = math.fabs(y2 - y1)
340
                        length = math.sqrt(dx*dx + dy*dy)
341
                        dx /= length
342
                        dy /= length
343
                        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 
344
                        cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)
345

    
346
            cv2.imshow('img', img)
347
            cv2.waitKey(0)
348
            cv2.destroyAllWindows()
349
        except Exception as ex:
350
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
351

    
352
        startPt = self.startPoint()
353
        endPt = self.endPoint()
354

    
355
        center = [(startPt[0]+endPt[0])*0.5, (startPt[1]+endPt[1])*0.5]
356
        length = self.length()
357
        dx = (endPt[0] - startPt[0]) / length
358
        dy = (endPt[1] - startPt[1]) / length
359

    
360
        arrow = QEngineeringFlowArrowItem(center, (dx,dy))
361
        arrow.buildItem()
362
        self.scene().addItem(arrow)
363

    
364
    '''
365
        @breif  insert symbol
366
        @author humkyung
367
        @date   2018.04.22
368
    '''
369
    def insertSymbol(self, symbol, pos):
370
        import math
371
        from shapely.geometry import Point
372
        from shapely import affinity
373

    
374
        vec = self.perpendicular()
375
        line = [(pos.x() - vec[0]*20, pos.y() - vec[1]*20),(pos.x() + vec[0]*20, pos.y() + vec[1]*20)]
376
        origin = self.intersection(line)
377
        transform = QTransform()
378
        transform.translate(origin.x, origin.y)
379
        angle = self.angle()
380
        transform.rotateRadians(-angle)
381
        transform.translate(-symbol.origin[0], -symbol.origin[1])
382
        symbol.setTransform(transform)
383
        if 2 == len(symbol.connPts):    # 2 way component
384
            for i in range(len(symbol.connPts)):
385
                rotatedPt = affinity.rotate(Point(symbol.connPts[i][0] - symbol.origin[0], symbol.connPts[i][1] - symbol.origin[1]), -angle, Point(0, 0), use_radians=True)
386
                symbol.connPts[i] = (origin.x+rotatedPt.x, origin.y+rotatedPt.y)
387

    
388
            dx1 = symbol.connPts[0][0] - self.startPoint()[0]
389
            dy1 = symbol.connPts[0][1] - self.startPoint()[1]
390
            length1 = math.sqrt(dx1*dx1 + dy1*dy1)
391
            dx2 = symbol.connPts[1][0] - self.startPoint()[0]
392
            dy2 = symbol.connPts[1][1] - self.startPoint()[1]
393
            length2 = math.sqrt(dx2*dx2 + dy2*dy2)
394

    
395
            if length1 < length2:
396
                processLine = QEngineeringLineItem()
397
                processLine._pol.append(QPointF(symbol.connPts[1][0], symbol.connPts[1][1]))
398
                processLine._pol.append(QPointF(self.endPoint()[0], self.endPoint()[1]))
399
                processLine.buildItem()
400
                processLine.setPen(QPen(Qt.blue, 5, Qt.SolidLine))
401
                processLine.conns.append(symbol)
402
                processLine.conns.append(self.conns[1])
403
                self.scene().addItem(processLine)
404

    
405
                self._pol.replace(self._pol.count() - 1, QPointF(symbol.connPts[0][0], symbol.connPts[0][1]))
406
                self.conns[1] = symbol
407

    
408
                symbol.conns = []
409
                symbol.conns.append(self)
410
                symbol.conns.append(processLine)
411
            else:
412
                processLine = QEngineeringLineItem()
413
                processLine._pol.append(QPointF(symbol.connPts[0][0], symbol.connPts[0][1]))
414
                processLine._pol.append(QPointF(self.endPoint()[0], self.endPoint()[1]))
415
                processLine.buildItem()
416
                processLine.setPen(QPen(Qt.blue, 5, Qt.SolidLine))
417
                processLine.conns.append(symbol)
418
                processLine.conns.append(self.conns[1])
419
                self.scene().addItem(processLine)
420

    
421
                self._pol.replace(self._pol.count() - 1, QPointF(symbol.connPts[1][0], symbol.connPts[1][1]))
422
                self.conns[1] = symbol
423

    
424
                symbol.conns = []
425
                symbol.conns.append(processLine)
426
                symbol.conns.append(self)
427

    
428
            self.buildItem()
429
            self.update()
430

    
431
        symbol.loc = [origin.x - symbol.origin[0], origin.y - symbol.origin[1]]
432
        symbol.size = [symbol.boundingRect().width(), symbol.boundingRect().height()]
433
        self.scene().addItem(symbol)
434
        for connector in symbol.connectors:
435
            self.scene().addItem(connector)
436
    
437
    '''
438
        @brief  remove symbol
439
        @author humkyung
440
        @date   2018.04.23
441
    '''
442
    def removeSymbol(self, symbol):
443
        import math
444

    
445
        if 2 == len(symbol.conns):  # 2-way component
446
            connected = symbol.conns[0] if symbol.conns[0] is not self else symbol.conns[1]
447
            
448
            pts = []
449
            pts.append(self.startPoint())
450
            pts.append(self.endPoint())
451
            pts.append(connected.startPoint())
452
            pts.append(connected.endPoint())
453

    
454
            self.scene().removeItem(connected)
455

    
456
            start = None
457
            end = None
458
            maxDist = None
459
            for i in range(len(pts)):
460
                for j in range(i+1,len(pts)):
461
                    dx = pts[i][0] - pts[j][0]
462
                    dy = pts[i][1] - pts[j][1]
463
                    dist = math.sqrt(dx*dx + dy*dy)
464
                    if maxDist is None:
465
                        maxDist = dist
466
                        start = pts[i]
467
                        end = pts[j]
468
                    elif dist > maxDist:
469
                        maxDist = dist
470
                        start = pts[i]
471
                        end = pts[j]
472

    
473
            if (pts[0] == end) or (pts[1] == start): start,end = end,start
474

    
475
            self._pol.clear()
476
            self._pol.append(QPointF(start[0], start[1]))
477
            self._pol.append(QPointF(end[0], end[1]))
478
            self.buildItem()
479
            self.update()
480

    
481
    def hoverEnterEvent(self, event):
482
        pass
483

    
484
    def hoverLeaveEvent(self, event):
485
        pass
486

    
487
    def hoverMoveEvent(self, event):
488
        pass
489
    
490
    '''
491
        @brief  remove item when user press delete key
492
        @author humkyung
493
        @date   2018.04.23
494
    '''
495
    def keyPressEvent(self, event): 
496
        if event.key() == Qt.Key_Delete:
497
            #self.removed.emit((QGraphicsPathItem)(self))
498
            self.scene().removeItem(self)
499

    
500
    '''
501
        @brief  generate xml code
502
        @author humkyung
503
        @date   2018.04.23
504
    '''
505
    def toXml(self):
506
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
507

    
508
        try:
509
            node = Element('LINE')
510
            uidNode = Element('UID')
511
            uidNode.text = str(self.uid)
512

    
513
            startPt = self.startPoint()
514
            endPt = self.endPoint()
515

    
516
            startNode = Element('STARTPOINT')
517
            startNode.text = '{},{}'.format(startPt[0], startPt[1])
518

    
519
            endNode = Element('ENDPOINT')
520
            endNode.text = '{},{}'.format(endPt[0], endPt[1])
521

    
522
            node.append(uidNode)
523
            node.append(startNode)
524
            node.append(endNode)
525
        except Exception as ex:
526
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
527

    
528
        return node
클립보드 이미지 추가 (최대 크기: 500 MB)