hytos / DTI_PID / DTI_PID / Shapes / EngineeringLineItem.py @ d7080855
이력 | 보기 | 이력해설 | 다운로드 (28.9 KB)
1 |
# coding: utf-8
|
---|---|
2 |
import sys |
3 |
import os.path |
4 |
import copy |
5 |
import cv2 |
6 |
|
7 |
try:
|
8 |
from PyQt5.QtCore import Qt, QPointF, QRectF, pyqtSignal, QT_VERSION_STR, QRect |
9 |
from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QColor, QBrush, QPen, QTransform |
10 |
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QGraphicsItem, QAbstractGraphicsShapeItem, QGraphicsPathItem |
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 |
except ImportError: |
16 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
17 |
|
18 |
from GraphicsPolylineItem import QGraphicsPolylineItem |
19 |
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem |
20 |
import shapely |
21 |
|
22 |
class QEngineeringLineItem(QGraphicsPolylineItem): |
23 |
'''
|
24 |
@history 2018.05.11 Jeongwoo Make Comments self.setPen()
|
25 |
2018.05.15 Jeongwoo Change method to call parent's __init__
|
26 |
humkyung 2018.06.21 add vertices to parameter
|
27 |
'''
|
28 |
def __init__(self, vertices=[], parent=None): |
29 |
QGraphicsPolylineItem.__init__(self, parent)
|
30 |
self.isCreated = True |
31 |
|
32 |
self.conns = [None, None] |
33 |
self._owner = None |
34 |
self._flowMark = None |
35 |
self._lineType = 'Primary' |
36 |
|
37 |
# add vertex
|
38 |
for vertex in vertices: |
39 |
self._pol.append(QPointF(vertex[0], vertex[1])) |
40 |
# up to here
|
41 |
|
42 |
self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
|
43 |
|
44 |
self.setAcceptHoverEvents(True) |
45 |
self.setAcceptTouchEvents(True) |
46 |
|
47 |
ptStart = self.startPoint()
|
48 |
ptEnd = self.endPoint()
|
49 |
self.setToolTip('({},{})-({},{})'.format(ptStart[0], ptStart[1], ptEnd[0], ptEnd[1])) |
50 |
|
51 |
'''
|
52 |
@breif getter owner
|
53 |
@author humkyung
|
54 |
@date 2018.05.10
|
55 |
'''
|
56 |
@property
|
57 |
def owner(self): |
58 |
return self._owner |
59 |
|
60 |
'''
|
61 |
@brief setter owner
|
62 |
@author humkyung
|
63 |
@date 2018.05.10
|
64 |
@history 2018.05.17 Jeongwoo Add Calling setColor
|
65 |
'''
|
66 |
@owner.setter
|
67 |
def owner(self, value): |
68 |
self._owner = value
|
69 |
|
70 |
if self._owner is None: |
71 |
self._color = self.DEFAULT_COLOR |
72 |
self.setColor(self._color) |
73 |
|
74 |
'''
|
75 |
@brief getter flow mark
|
76 |
@author humkyung
|
77 |
@date 2018.06.21
|
78 |
'''
|
79 |
@property
|
80 |
def flowMark(self): |
81 |
return self._flowMark |
82 |
|
83 |
'''
|
84 |
@brief setter flow mark
|
85 |
@author humkyung
|
86 |
@date 2018.06.21
|
87 |
'''
|
88 |
@flowMark.setter
|
89 |
def flowMark(self, value): |
90 |
self._flowMark = value
|
91 |
|
92 |
'''
|
93 |
@brief getter of lineType
|
94 |
@author humkyung
|
95 |
@date 2018.06.27
|
96 |
'''
|
97 |
@property
|
98 |
def lineType(self): |
99 |
return self._lineType |
100 |
|
101 |
'''
|
102 |
@brief setter of lineType
|
103 |
@author humkyung
|
104 |
@date 2018.06.27
|
105 |
'''
|
106 |
@lineType.setter
|
107 |
def lineType(self, value): |
108 |
from AppDocData import AppDocData |
109 |
|
110 |
self._lineType = value
|
111 |
|
112 |
docData = AppDocData.instance() |
113 |
config = docData.getLineTypeConfig(self._lineType)
|
114 |
|
115 |
_pen = self.pen()
|
116 |
_pen.setWidth(config[1])
|
117 |
_pen.setStyle(config[2])
|
118 |
self.setPen(_pen)
|
119 |
self.update()
|
120 |
|
121 |
'''
|
122 |
@brief clone an object
|
123 |
'''
|
124 |
def clone(self): |
125 |
clone = QEngineeringLineItem() |
126 |
clone._vertices = copy.deepcopy(self._vertices)
|
127 |
for vertex in clone._vertices: |
128 |
clone._pol.append(QPointF(vertex[0], vertex[1])) |
129 |
clone.buildItem() |
130 |
clone.isCreated = self.isCreated
|
131 |
|
132 |
return clone
|
133 |
|
134 |
'''
|
135 |
@brief dot product of given two vectors
|
136 |
@author humkyung
|
137 |
@date 2018.04.14
|
138 |
'''
|
139 |
def dotProduct(self, lhs, rhs): |
140 |
return sum([lhs[i]*rhs[i] for i in range(len(lhs))]) |
141 |
|
142 |
'''
|
143 |
@brief distance between line and point
|
144 |
@author humkyung
|
145 |
@date 2018.04.16
|
146 |
'''
|
147 |
def distanceTo(self, pt): |
148 |
from shapely.geometry import Point, LineString |
149 |
|
150 |
startPt = self.startPoint()
|
151 |
endPt = self.endPoint()
|
152 |
line = LineString([(startPt[0], startPt[1]), (endPt[0], endPt[1])]) |
153 |
dist = line.distance(Point(pt[0], pt[1])) |
154 |
|
155 |
return dist
|
156 |
|
157 |
'''
|
158 |
@brief return perpendicular vector
|
159 |
@author humkyung
|
160 |
@date 2018.04.21
|
161 |
'''
|
162 |
def perpendicular(self): |
163 |
import math |
164 |
|
165 |
dx = self.endPoint()[0] - self.startPoint()[0] |
166 |
dy = self.endPoint()[1] - self.startPoint()[1] |
167 |
dx,dy = -dy,dx |
168 |
length = math.sqrt(dx*dx + dy*dy) |
169 |
dx /= length |
170 |
dy /= length |
171 |
|
172 |
return (dx,dy)
|
173 |
|
174 |
'''
|
175 |
@brief return angle of line in radian
|
176 |
@author humkyung
|
177 |
@date 2018.04.22
|
178 |
'''
|
179 |
def angle(self): |
180 |
import math |
181 |
|
182 |
startPt = self.startPoint()
|
183 |
endPt = self.endPoint()
|
184 |
dx = endPt[0] - startPt[0] |
185 |
dy = endPt[1] - startPt[1] |
186 |
dot = self.dotProduct((1,0), (dx, dy)) |
187 |
length = math.sqrt(dx*dx + dy*dy) |
188 |
return math.acos(dot/length)
|
189 |
|
190 |
'''
|
191 |
@brief return length of line
|
192 |
@author humkyung
|
193 |
@date 2018.05.08
|
194 |
'''
|
195 |
def length(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 |
return math.sqrt(dx*dx + dy*dy)
|
203 |
|
204 |
'''
|
205 |
@brief check if line is horizontal
|
206 |
@author humkyung
|
207 |
@date 2018.04.27
|
208 |
'''
|
209 |
def isHorizontal(self): |
210 |
import math |
211 |
|
212 |
startPt = self.startPoint()
|
213 |
endPt = self.endPoint()
|
214 |
dx = endPt[0] - startPt[0] |
215 |
dy = endPt[1] - startPt[1] |
216 |
|
217 |
return math.fabs(dx) > math.fabs(dy)
|
218 |
|
219 |
'''
|
220 |
@brief check if line is vertical
|
221 |
@author humkyung
|
222 |
@date 2018.04.27
|
223 |
'''
|
224 |
def isVertical(self): |
225 |
import math |
226 |
|
227 |
startPt = self.startPoint()
|
228 |
endPt = self.endPoint()
|
229 |
dx = endPt[0] - startPt[0] |
230 |
dy = endPt[1] - startPt[1] |
231 |
|
232 |
return math.fabs(dy) > math.fabs(dx)
|
233 |
|
234 |
'''
|
235 |
@brief get intersection point between this and given line
|
236 |
@author humkyung
|
237 |
@date 2018.04.21
|
238 |
@history Jeongwoo 2018.05.15 Add normalize
|
239 |
Jeongwoo 2018.05.16 Add length == 0 check
|
240 |
'''
|
241 |
def intersection(self, line): |
242 |
import math |
243 |
from shapely.geometry import Point, LineString |
244 |
|
245 |
startPt = self.startPoint()
|
246 |
endPt = self.endPoint()
|
247 |
dx = endPt[0] - startPt[0] |
248 |
dy = endPt[1] - startPt[1] |
249 |
length = math.sqrt(dx*dx + dy*dy) |
250 |
if length == 0: |
251 |
return None |
252 |
dx /= length |
253 |
dy /= length |
254 |
lhs = LineString([(startPt[0] - dx*20, startPt[1] - dy*20), (endPt[0] + dx*20, endPt[1] + dy*20)]) |
255 |
rhs = LineString(line) |
256 |
return lhs.intersection(rhs)
|
257 |
|
258 |
'''
|
259 |
@brief check if two lines are connectable
|
260 |
@author humkyung
|
261 |
@date 2018.05.12
|
262 |
@history Jeongwoo 18.05.15 Add check pt's type
|
263 |
Jeongwoo 18.05.16 Add length == 0 check
|
264 |
'''
|
265 |
def isConnectable(self, line, toler=20): |
266 |
import math |
267 |
|
268 |
startPt = line.startPoint() |
269 |
endPt = line.endPoint() |
270 |
|
271 |
dx = endPt[0] - startPt[0] |
272 |
dy = endPt[1] - startPt[1] |
273 |
length = math.sqrt(dx*dx + dy*dy) |
274 |
if length == 0: |
275 |
return False |
276 |
dx /= length |
277 |
dy /= length |
278 |
extendedLine = [(startPt[0] - dx*toler, startPt[1] - dy*toler), (endPt[0] + dx*toler, endPt[1] + dy*toler)] |
279 |
pt = self.intersection(extendedLine)
|
280 |
|
281 |
return (pt is not None) and (type(pt) == shapely.geometry.point.Point) |
282 |
|
283 |
'''
|
284 |
@brief check if line and given item is jointable
|
285 |
@author humkyung
|
286 |
@date 2018.06.26
|
287 |
@history humkyung 2018.07.03 allow item to be line or symbol
|
288 |
'''
|
289 |
def isJointed(self, item, toler=5): |
290 |
import math |
291 |
from SymbolSvgItem import SymbolSvgItem |
292 |
|
293 |
lhs = [self.startPoint(), self.endPoint()] |
294 |
if type(item) is QEngineeringLineItem: |
295 |
rhs = [item.startPoint(), item.endPoint()] |
296 |
elif issubclass(type(item), SymbolSvgItem): |
297 |
rhs = item.connPts |
298 |
else:
|
299 |
rhs = [] |
300 |
|
301 |
for pt in lhs: |
302 |
for _pt in rhs: |
303 |
dx = _pt[0] - pt[0] |
304 |
dy = _pt[1] - pt[1] |
305 |
if math.sqrt(dx*dx + dy*dy) < toler:
|
306 |
return True |
307 |
|
308 |
return False |
309 |
|
310 |
'''
|
311 |
@brief arrange vertex order
|
312 |
@author humkyung
|
313 |
@date 2018.07.04
|
314 |
'''
|
315 |
def arrangeVertexOrder(self, arranged): |
316 |
import math |
317 |
|
318 |
lhs = [arranged.startPoint(), arranged.endPoint()] |
319 |
rhs = [self.startPoint(), self.endPoint()] |
320 |
|
321 |
index = 0
|
322 |
indexed = 0
|
323 |
minDist = None
|
324 |
for pt in lhs: |
325 |
for _pt in rhs: |
326 |
index += 1
|
327 |
dx = _pt[0] - pt[0] |
328 |
dy = _pt[1] - pt[1] |
329 |
dist = math.sqrt(dx*dx + dy*dy) |
330 |
if minDist is None or dist < minDist: |
331 |
minDist = dist |
332 |
indexed = index |
333 |
|
334 |
if indexed == 1 or indexed == 4: |
335 |
self.reverse()
|
336 |
|
337 |
'''
|
338 |
@brief check if two lines are extendable
|
339 |
@author humkyung
|
340 |
@date 2018.06.25
|
341 |
@history humkyung 2018.06.27 check line type
|
342 |
'''
|
343 |
def isExtendable(self, line, toler=5): |
344 |
import math |
345 |
from SymbolSvgItem import SymbolSvgItem |
346 |
|
347 |
if self.lineType == line.lineType: |
348 |
if self.isHorizontal() and line.isHorizontal(): |
349 |
flag = (line.conns[0] is not None and issubclass(type(line.conns[0]), SymbolSvgItem)) or (line.conns[1] is not None and issubclass(type(line.conns[1]), SymbolSvgItem)) |
350 |
return (flag and (math.fabs(self.startPoint()[1] - line.startPoint()[1]) < toler)) |
351 |
elif self.isVertical() and line.isVertical(): |
352 |
flag = (line.conns[0] is not None and issubclass(type(line.conns[0]), SymbolSvgItem)) or (line.conns[1] is not None and issubclass(type(line.conns[1]), SymbolSvgItem)) |
353 |
return (flag and (math.fabs(self.startPoint()[0] - line.startPoint()[0]) < toler)) |
354 |
|
355 |
return False |
356 |
|
357 |
'''
|
358 |
@brief connect line and symbol is able to be connected and return symbol
|
359 |
@author humkyung
|
360 |
@date 2018.04.16
|
361 |
@history humkyung 2018.05.08 check if line is possible to be connected
|
362 |
Jeongwoo 2018.05.15 Split if-statement and Connect each symbol and line
|
363 |
'''
|
364 |
def connectIfPossible(self, obj, toler): |
365 |
from shapely.geometry import Point |
366 |
from SymbolSvgItem import SymbolSvgItem |
367 |
res = [] |
368 |
|
369 |
startPt = self.startPoint()
|
370 |
endPt = self.endPoint()
|
371 |
|
372 |
if issubclass(type(obj), SymbolSvgItem): |
373 |
for i in range(len(obj.connPts)): |
374 |
pt = obj.connPts[i] |
375 |
if len(obj.conns) <= i: obj.conns.append(None) |
376 |
|
377 |
if (self.conns[0] is None) and (Point(startPt[0], startPt[1]).distance(Point(pt[0], pt[1])) < toler): |
378 |
self.conns[0] = obj |
379 |
obj.conns[i] = self
|
380 |
res.append(obj) |
381 |
if (self.conns[1] is None) and (Point(endPt[0], endPt[1]).distance(Point(pt[0], pt[1])) < toler): |
382 |
self.conns[1] = obj |
383 |
obj.conns[i] = self
|
384 |
res.append(obj) |
385 |
elif type(obj) is QEngineeringLineItem: |
386 |
_startPt = obj.startPoint() |
387 |
_endPt = obj.endPoint() |
388 |
if((Point(startPt[0], startPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)): |
389 |
self.conns[0] = obj |
390 |
obj.conns[0] = self |
391 |
res.append(obj) |
392 |
if ((Point(startPt[0], startPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)): |
393 |
self.conns[0] = obj |
394 |
obj.conns[1] = self |
395 |
res.append(obj) |
396 |
|
397 |
if((Point(endPt[0], endPt[1]).distance(Point(_startPt[0], _startPt[1])) < toler)): |
398 |
self.conns[1] = obj |
399 |
obj.conns[0] = self |
400 |
res.append(obj) |
401 |
|
402 |
if ((Point(endPt[0], endPt[1]).distance(Point(_endPt[0], _endPt[1])) < toler)): |
403 |
self.conns[1] = obj |
404 |
obj.conns[1] = self |
405 |
res.append(obj) |
406 |
|
407 |
return res
|
408 |
|
409 |
'''
|
410 |
@brief reverse line
|
411 |
@author humkyung
|
412 |
@date 2018.07.03
|
413 |
'''
|
414 |
def reverse(self): |
415 |
clone = self._pol.toPolygon()
|
416 |
self._pol.clear()
|
417 |
for idx in reversed(range(clone.count())): |
418 |
self._pol.append(clone.at(idx))
|
419 |
|
420 |
'''
|
421 |
@brief add flow arrow
|
422 |
@author humkyung
|
423 |
@date 2018.05.08
|
424 |
@history 2018.05.24 Jeongwoo Modifying Draw Flow Arrow
|
425 |
'''
|
426 |
def addFlowArrow(self): |
427 |
import numpy as np |
428 |
import cv2 |
429 |
import math |
430 |
import sys |
431 |
global src
|
432 |
from shapely.geometry import Point |
433 |
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem |
434 |
from AppDocData import AppDocData |
435 |
|
436 |
try:
|
437 |
docData = AppDocData.instance() |
438 |
area = docData.getArea('Drawing')
|
439 |
|
440 |
startPt = self.startPoint()
|
441 |
endPt = self.endPoint()
|
442 |
length = self.length()
|
443 |
direction = [(endPt[0] - startPt[0]) / length, (endPt[1] - startPt[1]) / length] |
444 |
|
445 |
left = min(startPt[0], endPt[0]) |
446 |
top = min(startPt[1], endPt[1]) |
447 |
right = max(startPt[0], endPt[0]) |
448 |
bottom = max(startPt[1], endPt[1]) |
449 |
|
450 |
rect = None
|
451 |
if self.isVertical(): |
452 |
rect = QRectF(left - 10, top, (right - left) + 20, (bottom - top)) |
453 |
else:
|
454 |
rect = QRectF(left, top - 10, (right - left), (bottom - top) + 20) |
455 |
|
456 |
docData = AppDocData.instance() |
457 |
area = docData.getArea('Drawing')
|
458 |
img = np.array(AppDocData.instance().getCurrentPidSource().getPyImageOnRect(rect)) |
459 |
|
460 |
imgLine = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY)[1] |
461 |
# remove noise
|
462 |
imgLine = cv2.bitwise_not(imgLine) |
463 |
imgLine = cv2.erode(imgLine, np.ones((10, 10), np.uint8)) |
464 |
|
465 |
image, contours, hierarchy = cv2.findContours(imgLine, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) |
466 |
if contours:
|
467 |
contours = sorted(contours, key=cv2.contourArea, reverse=True) |
468 |
[x, y, w, h] = cv2.boundingRect(contours[0])
|
469 |
if w > 10 and w < 100 and h > 10 and h < 100: # check arrow mark size |
470 |
imgArrowMark = imgLine[y:y+h, x:x+w] |
471 |
|
472 |
# DEBUG - display flow arrow area
|
473 |
'''
|
474 |
item = QGraphicsBoundingBoxItem(rect.left() + x - 10, rect.top() + y - 10, w + 20, h + 20)
|
475 |
item.isSymbol = True
|
476 |
item.angle = 0
|
477 |
item.setPen(QPen(Qt.red, 1, Qt.SolidLine))
|
478 |
item.setBrush(QBrush(QColor(255,255,0,100)))
|
479 |
self.scene().addItem(item)
|
480 |
'''
|
481 |
# up to here
|
482 |
|
483 |
edges = cv2.Canny(imgArrowMark, 50, 150, apertureSize=3) |
484 |
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 10, minLineLength=10, maxLineGap=5) |
485 |
|
486 |
####### HoughLinesP
|
487 |
if lines is not None: |
488 |
maxLength = None
|
489 |
selected = None
|
490 |
for line in lines: |
491 |
for x1, y1, x2, y2 in line: |
492 |
dx = x2 - x1 |
493 |
dy = y2 - y1 |
494 |
selected = line |
495 |
length = math.sqrt(dx*dx + dy*dy) |
496 |
if maxLength is None or length > maxLength: |
497 |
maxLength = length |
498 |
selected = line |
499 |
|
500 |
for x1, y1, x2, y2 in selected: |
501 |
dx = math.fabs(x2 - x1) |
502 |
dy = math.fabs(y2 - y1) |
503 |
length = math.sqrt(dx*dx + dy*dy) |
504 |
dx /= length |
505 |
dy /= length |
506 |
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 |
507 |
dist1 = self.distanceTo((rect.left() + x + x1, rect.top() + y + y1))
|
508 |
dist2 = self.distanceTo((rect.left() + x + x2, rect.top() + y + y2))
|
509 |
if dist1 > dist2: # point which's distance is longer would be start point |
510 |
_start = (rect.left() + x + x1, rect.top() + y + y1) |
511 |
_end = (rect.left() + x + x2, rect.top() + y + y2) |
512 |
else:
|
513 |
_start = (rect.left() + x + x2, rect.top() + y + y2) |
514 |
_end = (rect.left() + x + x1, rect.top() + y + y1) |
515 |
|
516 |
# DEBUG display detected line
|
517 |
'''
|
518 |
poly = QGraphicsPolylineItem()
|
519 |
poly._pol.append(QPointF(_start[0], _start[1]))
|
520 |
poly._pol.append(QPointF(_end[0], _end[1]))
|
521 |
poly.setPen(QPen(Qt.red, 2, Qt.SolidLine))
|
522 |
poly.buildItem()
|
523 |
self.scene().addItem(poly)
|
524 |
'''
|
525 |
# up to here
|
526 |
|
527 |
dist1 = Point(startPt[0], startPt[1]).distance(Point(_start[0], _start[1])) |
528 |
dist2 = Point(startPt[0], startPt[1]).distance(Point(_end[0], _end[1])) |
529 |
if dist1 > dist2:
|
530 |
startPt,endPt = endPt,startPt |
531 |
direction[0],direction[1] = -direction[0],-direction[1] |
532 |
self.reverse()
|
533 |
|
534 |
center = [(startPt[0]+endPt[0])*0.5, (startPt[1]+endPt[1])*0.5] |
535 |
arrow = QEngineeringFlowArrowItem(center, direction) |
536 |
arrow.buildItem() |
537 |
self.scene().addItem(arrow)
|
538 |
|
539 |
x = round(rect.left() + x - 5) |
540 |
y = round(rect.top() + y - 5) |
541 |
self.flowMark = ([x, y, w + 10, h + 10], None) |
542 |
else:
|
543 |
pass
|
544 |
except Exception as ex: |
545 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
546 |
|
547 |
'''
|
548 |
@breif insert symbol
|
549 |
@author humkyung
|
550 |
@date 2018.04.22
|
551 |
'''
|
552 |
def insertSymbol(self, symbol, pos): |
553 |
import math |
554 |
from shapely.geometry import Point |
555 |
from shapely import affinity |
556 |
|
557 |
vec = self.perpendicular()
|
558 |
line = [(pos.x() - vec[0]*20, pos.y() - vec[1]*20),(pos.x() + vec[0]*20, pos.y() + vec[1]*20)] |
559 |
origin = self.intersection(line)
|
560 |
transform = QTransform() |
561 |
transform.translate(origin.x, origin.y) |
562 |
angle = self.angle()
|
563 |
transform.rotateRadians(-angle) |
564 |
transform.translate(-symbol.origin[0], -symbol.origin[1]) |
565 |
symbol.setTransform(transform) |
566 |
if 2 == len(symbol.connPts): # 2 way component |
567 |
for i in range(len(symbol.connPts)): |
568 |
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) |
569 |
symbol.connPts[i] = (origin.x+rotatedPt.x, origin.y+rotatedPt.y) |
570 |
|
571 |
dx1 = symbol.connPts[0][0] - self.startPoint()[0] |
572 |
dy1 = symbol.connPts[0][1] - self.startPoint()[1] |
573 |
length1 = math.sqrt(dx1*dx1 + dy1*dy1) |
574 |
dx2 = symbol.connPts[1][0] - self.startPoint()[0] |
575 |
dy2 = symbol.connPts[1][1] - self.startPoint()[1] |
576 |
length2 = math.sqrt(dx2*dx2 + dy2*dy2) |
577 |
|
578 |
if length1 < length2:
|
579 |
processLine = QEngineeringLineItem() |
580 |
processLine._pol.append(QPointF(symbol.connPts[1][0], symbol.connPts[1][1])) |
581 |
processLine._pol.append(QPointF(self.endPoint()[0], self.endPoint()[1])) |
582 |
processLine.buildItem() |
583 |
processLine.conns.append(symbol) |
584 |
processLine.conns.append(self.conns[1]) |
585 |
self.scene().addItem(processLine)
|
586 |
|
587 |
self._pol.replace(self._pol.count() - 1, QPointF(symbol.connPts[0][0], symbol.connPts[0][1])) |
588 |
self.conns[1] = symbol |
589 |
|
590 |
symbol.conns = [] |
591 |
symbol.conns.append(self)
|
592 |
symbol.conns.append(processLine) |
593 |
else:
|
594 |
processLine = QEngineeringLineItem() |
595 |
processLine._pol.append(QPointF(symbol.connPts[0][0], symbol.connPts[0][1])) |
596 |
processLine._pol.append(QPointF(self.endPoint()[0], self.endPoint()[1])) |
597 |
processLine.buildItem() |
598 |
processLine.conns.append(symbol) |
599 |
processLine.conns.append(self.conns[1]) |
600 |
self.scene().addItem(processLine)
|
601 |
|
602 |
self._pol.replace(self._pol.count() - 1, QPointF(symbol.connPts[1][0], symbol.connPts[1][1])) |
603 |
self.conns[1] = symbol |
604 |
|
605 |
symbol.conns = [] |
606 |
symbol.conns.append(processLine) |
607 |
symbol.conns.append(self)
|
608 |
|
609 |
self.buildItem()
|
610 |
self.update()
|
611 |
|
612 |
symbol.loc = [origin.x - symbol.origin[0], origin.y - symbol.origin[1]] |
613 |
symbol.size = [symbol.boundingRect().width(), symbol.boundingRect().height()] |
614 |
self.scene().addItem(symbol)
|
615 |
for connector in symbol.connectors: |
616 |
self.scene().addItem(connector)
|
617 |
|
618 |
'''
|
619 |
@brief remove symbol
|
620 |
@author humkyung
|
621 |
@date 2018.04.23
|
622 |
'''
|
623 |
def removeSymbol(self, symbol): |
624 |
import math |
625 |
|
626 |
if 2 == len(symbol.conns): # 2-way component |
627 |
connected = symbol.conns[0] if symbol.conns[0] is not self else symbol.conns[1] |
628 |
|
629 |
pts = [] |
630 |
pts.append(self.startPoint())
|
631 |
pts.append(self.endPoint())
|
632 |
pts.append(connected.startPoint()) |
633 |
pts.append(connected.endPoint()) |
634 |
|
635 |
self.scene().removeItem(connected)
|
636 |
|
637 |
start = None
|
638 |
end = None
|
639 |
maxDist = None
|
640 |
for i in range(len(pts)): |
641 |
for j in range(i+1,len(pts)): |
642 |
dx = pts[i][0] - pts[j][0] |
643 |
dy = pts[i][1] - pts[j][1] |
644 |
dist = math.sqrt(dx*dx + dy*dy) |
645 |
if maxDist is None: |
646 |
maxDist = dist |
647 |
start = pts[i] |
648 |
end = pts[j] |
649 |
elif dist > maxDist:
|
650 |
maxDist = dist |
651 |
start = pts[i] |
652 |
end = pts[j] |
653 |
|
654 |
if (pts[0] == end) or (pts[1] == start): start,end = end,start |
655 |
|
656 |
self._pol.clear()
|
657 |
self._pol.append(QPointF(start[0], start[1])) |
658 |
self._pol.append(QPointF(end[0], end[1])) |
659 |
self.buildItem()
|
660 |
self.update()
|
661 |
|
662 |
'''
|
663 |
@brief update line type
|
664 |
@author humkyung
|
665 |
@date 2018.07.05
|
666 |
'''
|
667 |
def updateLineType(self): |
668 |
from QEngineeringInstrumentItem import QEngineeringInstrumentItem |
669 |
|
670 |
if len(self.conns) == 2: |
671 |
lines = [item for item in self.conns if item is not None and type(item) is QEngineeringLineItem] |
672 |
insts = [item for item in self.conns if item is not None and type(item) is QEngineeringInstrumentItem] |
673 |
|
674 |
matches = [inst for inst in insts if (inst.measuredVariableCode + inst.typeModifier) not in ['FT', 'PT', 'TT', 'TI', 'TG', 'PG']] |
675 |
if matches:
|
676 |
self.lineType = 'Electric' |
677 |
|
678 |
pool = [item for item in self.conns if item is not None and type(item) is QEngineeringLineItem] |
679 |
visited = [] |
680 |
visited.extend(pool) |
681 |
while len(pool): |
682 |
line = pool.pop() |
683 |
line.lineType = 'Electric'
|
684 |
|
685 |
matches = [item for item in line.conns if item is not None and item not in visited and type(item) is QEngineeringLineItem] |
686 |
pool.extend(matches) |
687 |
visited.extend(matches) |
688 |
else:
|
689 |
matches = [inst for inst in insts if (inst.measuredVariableCode + inst.typeModifier) in ['FT', 'PT', 'TT', 'TI', 'TG', 'PG']] |
690 |
if matches:
|
691 |
self.lineType = 'Connect To Process' |
692 |
|
693 |
def hoverEnterEvent(self, event): |
694 |
pass
|
695 |
|
696 |
def hoverLeaveEvent(self, event): |
697 |
pass
|
698 |
|
699 |
def hoverMoveEvent(self, event): |
700 |
pass
|
701 |
|
702 |
'''
|
703 |
@brief remove item when user press delete key
|
704 |
@author humkyung
|
705 |
@date 2018.04.23
|
706 |
'''
|
707 |
def keyPressEvent(self, event): |
708 |
if event.key() == Qt.Key_Delete:
|
709 |
#self.removed.emit((QGraphicsPathItem)(self))
|
710 |
self.scene().removeItem(self) |
711 |
|
712 |
'''
|
713 |
@brief draw rect when item is selected
|
714 |
@author humkyung
|
715 |
@date 2018.07.07
|
716 |
'''
|
717 |
def drawFocusRect(self, painter): |
718 |
self.focuspen = QPen(Qt.DotLine)
|
719 |
self.focuspen.setColor(Qt.black)
|
720 |
self.focuspen.setWidthF(1.5) |
721 |
hilightColor = QColor(255, 0, 0, 127) |
722 |
painter.setBrush(QBrush(hilightColor)) |
723 |
painter.setPen(self.focuspen)
|
724 |
painter.drawRect(self.boundingRect())
|
725 |
|
726 |
'''
|
727 |
@brief override paint method
|
728 |
'''
|
729 |
def paint(self, painter, option, widget): |
730 |
QGraphicsPolylineItem.paint(self, painter, option, widget)
|
731 |
if self.isSelected(): |
732 |
self.drawFocusRect(painter)
|
733 |
|
734 |
'''
|
735 |
@brief draw self to given image
|
736 |
@author humkyung
|
737 |
@date 2018.06.21
|
738 |
'''
|
739 |
def drawToImage(self, img, color, thickness): |
740 |
try:
|
741 |
# write recognized lines to image
|
742 |
ptStart = self.startPoint()
|
743 |
ptEnd = self.endPoint()
|
744 |
cv2.line(img, (round(ptStart[0]), round(ptStart[1])), (round(ptEnd[0]), round(ptEnd[1])), color, thickness) |
745 |
# up to here
|
746 |
|
747 |
if self.flowMark is not None: |
748 |
x, y, w, h = self.flowMark[0] |
749 |
img[y:(y+h), x:(x+w)] = color |
750 |
except Exception as ex: |
751 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
752 |
|
753 |
'''
|
754 |
@brief parse xml code
|
755 |
@author humkyung
|
756 |
@date 2018.06.27
|
757 |
'''
|
758 |
@staticmethod
|
759 |
def fromXml(node): |
760 |
item = None
|
761 |
try:
|
762 |
startPoint = [float(x) for x in node.find('STARTPOINT').text.split(',')] |
763 |
endPoint = [float(x) for x in node.find('ENDPOINT').text.split(',')] |
764 |
lineType = node.find('TYPE').text if node.find('TYPE') else 'Primary' |
765 |
item = QEngineeringLineItem(vertices=[startPoint, endPoint]) |
766 |
item.lineType = lineType |
767 |
item.buildItem() |
768 |
except Exception as ex: |
769 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
770 |
|
771 |
return item
|
772 |
|
773 |
'''
|
774 |
@brief generate xml code
|
775 |
@author humkyung
|
776 |
@date 2018.04.23
|
777 |
@history humkyung 2018.06.27 write line type to xml
|
778 |
'''
|
779 |
def toXml(self): |
780 |
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree |
781 |
|
782 |
try:
|
783 |
node = Element('LINE')
|
784 |
uidNode = Element('UID')
|
785 |
uidNode.text = str(self.uid) |
786 |
node.append(uidNode) |
787 |
|
788 |
startPt = self.startPoint()
|
789 |
endPt = self.endPoint()
|
790 |
|
791 |
startNode = Element('STARTPOINT')
|
792 |
startNode.text = '{},{}'.format(startPt[0], startPt[1]) |
793 |
node.append(startNode) |
794 |
|
795 |
endNode = Element('ENDPOINT')
|
796 |
endNode.text = '{},{}'.format(endPt[0], endPt[1]) |
797 |
node.append(endNode) |
798 |
|
799 |
typeNode = Element('TYPE')
|
800 |
typeNode.text = self.lineType
|
801 |
node.append(typeNode) |
802 |
except Exception as ex: |
803 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
804 |
|
805 |
return node
|
806 |
|
807 |
'''
|
808 |
@brief Delete Line Item from scene
|
809 |
@author Jeongwoo
|
810 |
@date 2018.05.29
|
811 |
@history 2018.05.29 Add parameter 'self' / Make comments emit()
|
812 |
'''
|
813 |
def deleteLineItemFromScene(self): |
814 |
#self.removed.emit(self)
|
815 |
self.scene().removeItem(self) |
816 |
|
817 |
'''
|
818 |
@brief Add Line Item
|
819 |
@author Jeongwoo
|
820 |
@date 2018.05.29
|
821 |
'''
|
822 |
def addLineItemToScene(self, scene): |
823 |
scene.addItem(self)
|