hytos / HYTOS / HYTOS / Shapes / EngineeringStreamlineItem.py @ c19a2508
이력 | 보기 | 이력해설 | 다운로드 (19.5 KB)
1 |
# coding: utf-8
|
---|---|
2 |
""" This is stream line item module """
|
3 |
|
4 |
import sys |
5 |
import os.path |
6 |
import copy |
7 |
import numpy as np |
8 |
|
9 |
try:
|
10 |
from PyQt5.QtCore import * |
11 |
from PyQt5.QtGui import * |
12 |
from PyQt5.QtWidgets import * |
13 |
except ImportError: |
14 |
try:
|
15 |
from PyQt4.QtCore import Qt, QRectF, QObject, pyqtSignal, QT_VERSION_STR |
16 |
from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QColor |
17 |
except ImportError: |
18 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
19 |
from EngineeringAbstractItem import QEngineeringAbstractItem |
20 |
|
21 |
|
22 |
class QEngineeringStreamlineItem(QGraphicsPathItem, QEngineeringAbstractItem): |
23 |
""" This is EngineeringStreamlineItem Class """
|
24 |
|
25 |
ARROW_SIZE = 10
|
26 |
ZVALUE = 100
|
27 |
onRemoved = pyqtSignal(QGraphicsItem) |
28 |
|
29 |
'''
|
30 |
@history 2018.05.11 Jeongwoo Declare variable self.pen
|
31 |
2018.05.15 Jeongwoo Change method to call parent's __init__
|
32 |
2018.05.25 Jeongwoo Change self.pen's default color (red → blue)
|
33 |
'''
|
34 |
|
35 |
def __init__(self, uid=None, parent=None): |
36 |
import uuid |
37 |
from EngineeringStreamNoTextItem import QEngineeringStreamNoTextItem |
38 |
|
39 |
try:
|
40 |
QGraphicsPathItem.__init__(self, parent)
|
41 |
QEngineeringAbstractItem.__init__(self)
|
42 |
self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
|
43 |
self.setAcceptHoverEvents(True) |
44 |
self.setAcceptTouchEvents(True) |
45 |
|
46 |
self.uid = uuid.uuid4() if uid is None else uid |
47 |
self.parent = parent
|
48 |
self._vertices = []
|
49 |
self.isCreated = False |
50 |
self._pt = None |
51 |
self.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # set default pen |
52 |
|
53 |
self._stream_no = 0 |
54 |
self._stream_no_text = None |
55 |
|
56 |
self.transfer = Transfer()
|
57 |
self.setZValue(QEngineeringStreamlineItem.ZVALUE)
|
58 |
except Exception as ex: |
59 |
from App import App |
60 |
from AppDocData import MessageType |
61 |
|
62 |
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
63 |
sys.exc_info()[-1].tb_lineno)
|
64 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
65 |
|
66 |
def __repr__(self): |
67 |
""" return string represents stream line item """
|
68 |
return 'Line_{}'.format(self._stream_no) |
69 |
|
70 |
@property
|
71 |
def stream_no(self): |
72 |
""" return stream no """
|
73 |
return self._stream_no |
74 |
|
75 |
@stream_no.setter
|
76 |
def stream_no(self, value): |
77 |
""" set stream no with given value """
|
78 |
from EngineeringStreamNoTextItem import QEngineeringStreamNoTextItem |
79 |
|
80 |
self._stream_no = value
|
81 |
self._stream_no_text = QEngineeringStreamNoTextItem('<{}>'.format(self._stream_no), self) |
82 |
self.build_path()
|
83 |
|
84 |
@property
|
85 |
def data(self): |
86 |
""" return hmb data"""
|
87 |
from AppDocData import AppDocData |
88 |
|
89 |
app_doc_data = AppDocData.instance() |
90 |
return app_doc_data.activeDrawing.hmbTable.get_hmb_data(self.uid) |
91 |
|
92 |
@property
|
93 |
def density(self): |
94 |
""" return density """
|
95 |
from AppDocData import AppDocData |
96 |
|
97 |
res = 0
|
98 |
app_doc_data = AppDocData.instance() |
99 |
hmb_data = app_doc_data.activeDrawing.hmbTable.get_hmb_data(self.uid)
|
100 |
if hmb_data: res = hmb_data.density
|
101 |
|
102 |
return res
|
103 |
|
104 |
@property
|
105 |
def press_drop(self): |
106 |
""" return press drop """
|
107 |
from AppDocData import AppDocData |
108 |
|
109 |
app_doc_data = AppDocData.instance() |
110 |
hmb_data = app_doc_data.activeDrawing.hmbTable.get_hmb_data(self.uid)
|
111 |
|
112 |
return hmb_data.pressure_drop if hmb_data else 0 |
113 |
|
114 |
def build_connectors(self, connected, pointsUids=None): |
115 |
""" build connectors for stream line
|
116 |
connected is target connector
|
117 |
"""
|
118 |
|
119 |
from SymbolSvgItem import SymbolSvgItem |
120 |
from EngineeringConnectorItem import QEngineeringConnectorItem |
121 |
|
122 |
targets = [] |
123 |
index = 0
|
124 |
for vertex in [self._vertices[0],self._vertices[-1]]: |
125 |
if pointsUids:
|
126 |
connector = QEngineeringConnectorItem(pointsUids[index], parent=self, index=index+1) |
127 |
else:
|
128 |
connector = QEngineeringConnectorItem(uid=None, parent=self, index=index+1) |
129 |
|
130 |
connector.setPos(vertex) |
131 |
connector.setParentItem(self)
|
132 |
connector.connectPoint = vertex |
133 |
connector.sceneConnectPoint = vertex |
134 |
|
135 |
# add connector move ables
|
136 |
connector.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable) |
137 |
connector.setAcceptTouchEvents(True)
|
138 |
connector.transfer.onPosChanged.connect(self.on_connector_pos_changed)
|
139 |
|
140 |
connector.setZValue(self.zValue() + 1) |
141 |
self.connectors.append(connector)
|
142 |
if len(connected) > index: |
143 |
connected_item_uid = connected[index] |
144 |
if connected_item_uid:
|
145 |
connector.connect(connected_item_uid) |
146 |
target = QEngineeringConnectorItem.find_connector(connected_item_uid) |
147 |
if target:
|
148 |
target.connect(connector) |
149 |
targets.append(target) |
150 |
|
151 |
index = index + 1
|
152 |
|
153 |
""" connect symbol's on_pos_changed signal """
|
154 |
for symbol in [conn.parentItem() for conn in targets if type(conn.parentItem()) is SymbolSvgItem]: |
155 |
symbol.transfer.on_pos_changed.connect(self.on_symbol_pos_changed)
|
156 |
symbol.transfer.on_size_changed.connect(self.on_symbol_pos_changed)
|
157 |
|
158 |
self.update_arrow()
|
159 |
|
160 |
def update_arrow(self): |
161 |
""" update flow arrow """
|
162 |
import math |
163 |
from EngineeringArrowItem import QEngineeringArrowItem |
164 |
if len(self._vertices) < 2: return |
165 |
|
166 |
start = self._vertices[-2] |
167 |
end = self._vertices[-1] |
168 |
|
169 |
dx = end[0] - start[0] |
170 |
dy = end[1] - start[1] |
171 |
length = math.sqrt(dx*dx + dy*dy) |
172 |
if length == 0: return |
173 |
|
174 |
_dir = [(end[0] - start[0])/length, (end[1] - start[1])/length] |
175 |
perpendicular = (-_dir[1], _dir[0]) |
176 |
polygon = QPolygonF() |
177 |
polygon.append(QPointF(end[0] - _dir[0] * QEngineeringStreamlineItem.ARROW_SIZE + perpendicular[ |
178 |
0] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25, |
179 |
end[1] - _dir[1] * QEngineeringStreamlineItem.ARROW_SIZE + perpendicular[ |
180 |
1] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25)) |
181 |
polygon.append(QPointF(end[0] - _dir[0] * QEngineeringStreamlineItem.ARROW_SIZE - perpendicular[ |
182 |
0] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25, |
183 |
end[1] - _dir[1] * QEngineeringStreamlineItem.ARROW_SIZE - perpendicular[ |
184 |
1] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25)) |
185 |
polygon.append(QPointF(end[0], end[1])) |
186 |
polygon.append(polygon[0]) # close polygon |
187 |
|
188 |
if not hasattr(self, '_arrow'): |
189 |
self._arrow = QEngineeringArrowItem(polygon, self) |
190 |
else:
|
191 |
self._arrow.setPolygon(polygon)
|
192 |
|
193 |
self._arrow.setBrush(Qt.blue)
|
194 |
self._arrow.update()
|
195 |
|
196 |
'''
|
197 |
@brief construct a polyline
|
198 |
'''
|
199 |
|
200 |
def process(self, param): |
201 |
if ('mousePressEvent' == param[0]) and (param[1].button() == Qt.LeftButton): |
202 |
self._vertices.append(param[2]) |
203 |
elif ('mouseMoveEvent' == param[0]): |
204 |
self._pt = param[2] |
205 |
elif ('mouseReleaseEvent' == param[0]) and (param[1].button() == Qt.RightButton): |
206 |
self.isCreated = True |
207 |
|
208 |
'''
|
209 |
@brief clone an object
|
210 |
'''
|
211 |
|
212 |
def clone(self): |
213 |
clone = QEngineeringStreamlineItem() |
214 |
clone._vertices = copy.deepcopy(self._vertices)
|
215 |
clone.isCreated = self.isCreated
|
216 |
|
217 |
return clone
|
218 |
|
219 |
def init(self): |
220 |
self._vertices = []
|
221 |
self._pt = None |
222 |
self.isCreated = False |
223 |
|
224 |
'''
|
225 |
@build build path
|
226 |
@author humkyung
|
227 |
@date 2018.04.23
|
228 |
'''
|
229 |
|
230 |
def build_path(self): |
231 |
if not self._vertices or len(self._vertices) < 2: |
232 |
return
|
233 |
|
234 |
try:
|
235 |
path = QPainterPath() |
236 |
path.moveTo(self._vertices[0][0], self._vertices[0][1]) |
237 |
|
238 |
max_length = 0
|
239 |
max_length_line = [None, None] |
240 |
for i in range(1, len(self._vertices)): |
241 |
path.lineTo(self._vertices[i][0], self._vertices[i][1]) |
242 |
dx = self._vertices[i][0] - self._vertices[i-1][0] |
243 |
dy = self._vertices[i][1] - self._vertices[i-1][1] |
244 |
if dx*dx + dy*dy > max_length:
|
245 |
max_length = dx*dx + dy*dy |
246 |
max_length_line[0] = self._vertices[i-1] |
247 |
max_length_line[1] = self._vertices[i] |
248 |
|
249 |
self.setPath(path)
|
250 |
self.isCreated = True |
251 |
|
252 |
if self._stream_no_text: |
253 |
if max_length_line[0] is not None and max_length_line[1] is not None: |
254 |
x = (max_length_line[0][0] + max_length_line[1][0] - self._stream_no_text.textWidth())*0.5 |
255 |
y = (max_length_line[0][1] + max_length_line[1][1])*0.5 |
256 |
self._stream_no_text.setPos(QPointF(x, y))
|
257 |
dx = max_length_line[0][0] - max_length_line[1][0] |
258 |
dy = max_length_line[0][1] - max_length_line[1][1] |
259 |
self._stream_no_text.setRotation(90) if abs(dy) > abs(dx) else self._stream_no_text.setRotation(0) |
260 |
except Exception as ex: |
261 |
from App import App |
262 |
from AppDocData import MessageType |
263 |
|
264 |
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
265 |
sys.exc_info()[-1].tb_lineno)
|
266 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
267 |
|
268 |
'''
|
269 |
@brief return bounding rectangle of polyline
|
270 |
@author humkyung
|
271 |
@date 2018.07.23
|
272 |
'''
|
273 |
|
274 |
def boundingRect(self): |
275 |
rect = QRectF() |
276 |
|
277 |
if self.isCreated: |
278 |
rect = self.path().boundingRect()
|
279 |
rect = QRectF(rect.left() - 5, rect.top() - 5, rect.width() + 10, rect.height() + 10) |
280 |
else:
|
281 |
minX = None
|
282 |
minY = None
|
283 |
maxX = None
|
284 |
maxY = None
|
285 |
for pt in self._vertices: |
286 |
if minX is None or pt[0] < minX: |
287 |
minX = pt[0]
|
288 |
if minY is None or pt[1] < minY: |
289 |
minY = pt[1]
|
290 |
|
291 |
if maxX is None or pt[0] > maxX: |
292 |
maxX = pt[0]
|
293 |
if maxY is None or pt[1] > maxY: |
294 |
maxY = pt[1]
|
295 |
|
296 |
if self._pt is not None: |
297 |
if minX is None or self._pt[0] < minX: |
298 |
minX = self._pt[0] |
299 |
if minY is None or self._pt[1] < minY: |
300 |
minY = self._pt[1] |
301 |
|
302 |
if maxX is None or self._pt[0] > maxX: |
303 |
maxX = self._pt[0] |
304 |
if maxY is None or self._pt[1] > maxY: |
305 |
maxY = self._pt[1] |
306 |
|
307 |
if minX is not None and minY is not None and maxX is not None and maxY is not None: |
308 |
rect = QRectF(minX - 5, minY - 5, maxX - minX + 10, maxY - minY + 10) |
309 |
|
310 |
return rect
|
311 |
|
312 |
'''
|
313 |
@brief
|
314 |
@author humkyung
|
315 |
@date 2018.07.26
|
316 |
'''
|
317 |
|
318 |
def onMouseMoved(self, event, scenePos): |
319 |
from SymbolSvgItem import SymbolSvgItem |
320 |
from EngineeringConnectorItem import QEngineeringConnectorItem |
321 |
|
322 |
# get connection point near by mouse point
|
323 |
pt = (scenePos.x(), scenePos.y()) |
324 |
item = self.scene().itemAt(scenePos, QTransform())
|
325 |
if (item is not None) and (type(item) is SymbolSvgItem): |
326 |
connPt = item.getConnectionPointCloseTo(pt, 5)
|
327 |
if connPt is not None: pt = connPt |
328 |
elif (item is not None) and (type(item) is QEngineeringConnectorItem): |
329 |
pt = item.center() |
330 |
# up to here
|
331 |
|
332 |
del self._vertices[1:] |
333 |
dx = pt[0] - self._vertices[0][0] |
334 |
dy = pt[1] - self._vertices[0][1] |
335 |
self._vertices.append((self._vertices[0][0] + dx*0.5, self._vertices[0][1])) |
336 |
self._vertices.append((self._vertices[-1][0], self._vertices[-1][1] + dy)) |
337 |
self._vertices.append(pt)
|
338 |
|
339 |
self.build_path()
|
340 |
self.update()
|
341 |
|
342 |
def hoverEnterEvent(self, event): |
343 |
""" hilight item and it's children """
|
344 |
self.highlight(True) |
345 |
|
346 |
def hoverLeaveEvent(self, event): |
347 |
""" unhighlight item """
|
348 |
self.highlight(False) |
349 |
|
350 |
def highlight(self, flag): |
351 |
self.hover = flag
|
352 |
self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue( |
353 |
QEngineeringStreamlineItem.ZVALUE) |
354 |
self.update()
|
355 |
|
356 |
def paint(self, painter, option, widget): |
357 |
""" override paint method """
|
358 |
if self.isSelected(): |
359 |
hilightColor = QColor(255, 0, 0, 127) |
360 |
focuspen = QPen(Qt.DotLine) |
361 |
focuspen.setColor(hilightColor) |
362 |
focuspen.setWidthF(3)
|
363 |
painter.setPen(focuspen) |
364 |
painter.drawPath(self.path())
|
365 |
else:
|
366 |
color = self.getColor()
|
367 |
self.setColor(color)
|
368 |
QGraphicsPathItem.paint(self, painter, option, widget)
|
369 |
|
370 |
'''
|
371 |
@brief Return real item position
|
372 |
@authro Jeongwoo
|
373 |
@date 2018.05.29
|
374 |
'''
|
375 |
|
376 |
def boundingRectOnScene(self): |
377 |
rect = self.boundingRect()
|
378 |
sp = self.startPoint()
|
379 |
rect.moveTo(sp[0], sp[1]) |
380 |
return rect
|
381 |
|
382 |
'''
|
383 |
@brief Set Color. Override QEngineeringAbstractItem's
|
384 |
@author Jeongwoo
|
385 |
@date 2018.05.11
|
386 |
@history 2018.05.11 Jeongwoo Add self.setPen() Method
|
387 |
@history humkyung 2018.05.13 call setPen method to apply changed color
|
388 |
'''
|
389 |
|
390 |
def setColor(self, color): |
391 |
c = QColor() |
392 |
c.setNamedColor(color) |
393 |
_pen = self.pen()
|
394 |
_pen.setColor(c) |
395 |
self.setPen(_pen)
|
396 |
self.update()
|
397 |
|
398 |
def on_symbol_pos_changed(self, symbol): |
399 |
""" rebuild stream line because symbol position is changed """
|
400 |
if self.connectors[0].connectedItem is not None: self.connectors[0].setPos( |
401 |
self.connectors[0].connectedItem.center()) |
402 |
if self.connectors[-1].connectedItem is not None: self.connectors[-1].setPos( |
403 |
self.connectors[-1].connectedItem.center()) |
404 |
self.on_connector_pos_changed(None) |
405 |
|
406 |
def on_connector_pos_changed(self, connector): |
407 |
""" rebuild stream line """
|
408 |
|
409 |
self._vertices = []
|
410 |
|
411 |
self._vertices.append(self.connectors[0].center()) |
412 |
dx = self.connectors[-1].center()[0] - self._vertices[0][0] |
413 |
dy = self.connectors[-1].center()[1] - self._vertices[0][1] |
414 |
self._vertices.append((self._vertices[0][0] + dx*0.5, self._vertices[0][1])) |
415 |
self._vertices.append((self._vertices[-1][0], self._vertices[-1][1] + dy)) |
416 |
self._vertices.append(self.connectors[-1].center()) |
417 |
self.build_path()
|
418 |
self.update_arrow()
|
419 |
self.update()
|
420 |
|
421 |
def mouseDoubleClickEvent(self, event): |
422 |
from StreamDataDialog import QStreamDataDialog |
423 |
|
424 |
dialog = QStreamDataDialog() |
425 |
res = dialog.showDialog(self)
|
426 |
if res == True: |
427 |
self.load_HMB()
|
428 |
|
429 |
def load_HMB(self): |
430 |
from App import App |
431 |
|
432 |
App.mainWnd().load_HMB() |
433 |
|
434 |
def keyPressEvent(self, event): |
435 |
from App import App |
436 |
from AppDocData import AppDocData |
437 |
if not self.isSelected(): return |
438 |
|
439 |
if event.key() == Qt.Key_Delete:
|
440 |
self.transfer.onRemoved.emit(self) |
441 |
elif event.key() == Qt.Key_QuoteLeft:
|
442 |
self.mouseDoubleClickEvent(event)
|
443 |
|
444 |
def toSql(self): |
445 |
""" convert valve data to sql query """
|
446 |
import uuid |
447 |
from AppDocData import AppDocData |
448 |
|
449 |
res = [] |
450 |
appDocData = AppDocData.instance() |
451 |
|
452 |
symbolInfo = appDocData.getSymbolByQuery('name', 'Stream Line') |
453 |
dbUid = symbolInfo.uid |
454 |
uid = self.uid
|
455 |
|
456 |
cols = ['UID', 'Symbols_UID', 'Name'] |
457 |
values = ['?','?', '?'] |
458 |
param = [str(uid), str(dbUid), str(self.stream_no)] |
459 |
sql = 'insert or replace into Components({}) values({})'.format(','.join(cols), ','.join(values)) |
460 |
res.append((sql, tuple(param)))
|
461 |
|
462 |
# save vertices to database
|
463 |
index = 1
|
464 |
for vertex in self._vertices: |
465 |
if index == 1 and self.connectors[0].connectedItem is not None: |
466 |
cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y', 'ConnectedItem_UID'] |
467 |
values = ['?', '?', '?', '?', '?', '?'] |
468 |
param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1], |
469 |
str(self.connectors[0].connectedItem.uid)] |
470 |
elif index == 4 and self.connectors[1].connectedItem is not None: |
471 |
cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y', 'ConnectedItem_UID'] |
472 |
values = ['?', '?', '?', '?', '?', '?'] |
473 |
param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1], |
474 |
str(self.connectors[1].connectedItem.uid)] |
475 |
else:
|
476 |
cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y'] |
477 |
values = ['?', '?', '?', '?', '?'] |
478 |
param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1]] |
479 |
|
480 |
sql = 'insert or replace into Points({}) values({})'.format(','.join(cols), ','.join(values)) |
481 |
|
482 |
res.append((sql, tuple(param)))
|
483 |
index += 1
|
484 |
# up to here
|
485 |
|
486 |
return res
|
487 |
|
488 |
@staticmethod
|
489 |
def fromDatabase(componentInfos): |
490 |
from EngineeringConnectorItem import QEngineeringConnectorItem |
491 |
from AppDocData import AppDocData |
492 |
|
493 |
item = None
|
494 |
try:
|
495 |
app_doc_data = AppDocData.instance() |
496 |
|
497 |
uid = componentInfos[0][0] #uid@Components |
498 |
|
499 |
item = QEngineeringStreamlineItem(uid) |
500 |
hmb_data = app_doc_data.activeDrawing.hmbTable.get_hmb_data(uid) |
501 |
item.stream_no = int(hmb_data.stream_no) if hmb_data else 1 # stream no |
502 |
|
503 |
pointsUids = [] |
504 |
for componentInfo in componentInfos: |
505 |
pointsUid = componentInfo[11] # uid@Points |
506 |
x = componentInfo[13] # X@Points |
507 |
y = componentInfo[14] # Y@Points |
508 |
|
509 |
pointsUids.append(pointsUid) |
510 |
item._vertices.append((x, y)) |
511 |
|
512 |
connectorItems = [componentInfos[0][15], componentInfos[-1][15]] |
513 |
|
514 |
item.setVisible(False)
|
515 |
item.build_path() |
516 |
item.build_connectors(connectorItems, pointsUids) |
517 |
|
518 |
item.update() |
519 |
|
520 |
except Exception as ex: |
521 |
from App import App |
522 |
from AppDocData import MessageType |
523 |
|
524 |
message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
525 |
sys.exc_info()[-1].tb_lineno)
|
526 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
527 |
|
528 |
return item
|
529 |
|
530 |
|
531 |
'''
|
532 |
@brief The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
|
533 |
@author Jeongwoo
|
534 |
@date 2018.06.18
|
535 |
'''
|
536 |
|
537 |
|
538 |
class Transfer(QObject): |
539 |
onRemoved = pyqtSignal(QGraphicsItem) |
540 |
|
541 |
def __init__(self, parent = None): |
542 |
QObject.__init__(self, parent)
|