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