hytos / HYTOS / HYTOS / Shapes / EngineeringConnectorItem.py @ 181ce52d
이력 | 보기 | 이력해설 | 다운로드 (18.5 KB)
1 |
# coding: utf-8
|
---|---|
2 |
""" This is engineering connector item module """
|
3 |
|
4 |
import os.path |
5 |
import copy, sys |
6 |
try:
|
7 |
from PyQt5.QtCore import * |
8 |
from PyQt5.QtGui import * |
9 |
from PyQt5.QtWidgets import * |
10 |
except ImportError: |
11 |
try:
|
12 |
from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR |
13 |
from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog |
14 |
except ImportError: |
15 |
raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.") |
16 |
|
17 |
from EngineeringAbstractItem import QEngineeringAbstractItem |
18 |
|
19 |
class NozzleData: |
20 |
""" this is nozzle data class """
|
21 |
|
22 |
def __init__(self): |
23 |
""" constructor """
|
24 |
self.pressure = None |
25 |
self.pressure_drop = None |
26 |
self.elevation = None |
27 |
self.over_design_cv = None |
28 |
|
29 |
def parse(self, row): |
30 |
""" parse given row """
|
31 |
|
32 |
try:
|
33 |
if not row['Pressure'] is None: |
34 |
self.pressure = float(row['Pressure']) |
35 |
if not row['Pressure_Drop'] is None: |
36 |
self.pressure_drop = float(row['Pressure_Drop']) |
37 |
if not row['Elevation'] is None: |
38 |
self.elevation = float(row['Elevation']) |
39 |
if not row['Over_Design_CV'] is None: |
40 |
self.over_design_cv = float(row['Over_Design_CV']) |
41 |
except Exception as ex: |
42 |
from App import App |
43 |
from AppDocData import MessageType |
44 |
|
45 |
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno) |
46 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
47 |
|
48 |
"""
|
49 |
A {ConnectorItem} is the graphical representation of a {Symbol.Connectors}.
|
50 |
"""
|
51 |
class QEngineeringConnectorItem(QGraphicsEllipseItem): |
52 |
""" This is engineering connector item class """
|
53 |
SMALL_SIZE = 10
|
54 |
BIG_SIZE = 16
|
55 |
HIGHLIGHT = '#BC4438'
|
56 |
|
57 |
AXIS_MODE = 0
|
58 |
FREE_MODE = 1
|
59 |
|
60 |
'''
|
61 |
@brief
|
62 |
@author
|
63 |
@date
|
64 |
@history humkyung 2018.07.27 add member for connectedItem
|
65 |
humkyung 2018.09.03 remove code to add connector item
|
66 |
'''
|
67 |
def __init__(self, uid=None, parent=None, index=-1): |
68 |
import uuid |
69 |
from SymbolSvgItem import SymbolSvgItem |
70 |
|
71 |
QGraphicsEllipseItem.__init__(self, parent)
|
72 |
|
73 |
self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid, version=4) |
74 |
self.parent = parent
|
75 |
self.buildItem()
|
76 |
self._direction = 'AUTO' |
77 |
self._symbol_idx = '0' |
78 |
self._connectedItem = None |
79 |
self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT # default value is connected at pt |
80 |
self.connectPoint = None |
81 |
self._hoverItem = None |
82 |
self._data = None |
83 |
|
84 |
self.setAcceptHoverEvents(True) |
85 |
self.transfer = Transfer()
|
86 |
|
87 |
self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
|
88 |
|
89 |
if type(parent) is SymbolSvgItem: |
90 |
""" setup label for connector index """
|
91 |
self._font = QFont('Arial', QEngineeringConnectorItem.SMALL_SIZE) |
92 |
self._font.setPointSizeF(5) |
93 |
|
94 |
self._conn_index = index
|
95 |
|
96 |
def __repr__(self): |
97 |
""" return string represent parent item and connector index """
|
98 |
return '{}_{}_N{}'.format(self.parentItem().name, self.parentItem().index, self._conn_index) |
99 |
|
100 |
@staticmethod
|
101 |
def find_connector(uid): |
102 |
""" find a connector which has given uid """
|
103 |
from App import App |
104 |
import uuid |
105 |
|
106 |
res = None
|
107 |
if type(uid) is uuid.UUID or type(uid) is str: |
108 |
scene = App.mainWnd().graphicsView.scene |
109 |
matches = [x for x in scene.items() if hasattr(x, 'uid') and str(x.uid) == str(uid)] |
110 |
res = matches[0] if matches else None |
111 |
|
112 |
return res
|
113 |
|
114 |
@property
|
115 |
def data(self): |
116 |
""" getter of nozzle data """
|
117 |
return self._data |
118 |
|
119 |
@data.setter
|
120 |
def data(self, value): |
121 |
""" setter of nozzle data """
|
122 |
self._data = value
|
123 |
|
124 |
@property
|
125 |
def elevation(self): |
126 |
""" return elevation """
|
127 |
return self.data.elevation |
128 |
|
129 |
@property
|
130 |
def press_drop(self): |
131 |
return None |
132 |
|
133 |
@property
|
134 |
def connectedItem(self): |
135 |
""" getter of connectedItem """
|
136 |
import uuid |
137 |
|
138 |
if self.scene() and (type(self._connectedItem) is uuid.UUID or type(self._connectedItem) is str): |
139 |
matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._connectedItem)] |
140 |
if matches:
|
141 |
self._connectedItem = matches[0] |
142 |
else:
|
143 |
self._connectedItem = None |
144 |
|
145 |
return self._connectedItem |
146 |
|
147 |
@connectedItem.setter
|
148 |
def connectedItem(self, value): |
149 |
""" setter of connectedItem """
|
150 |
self._connectedItem = value
|
151 |
|
152 |
'''
|
153 |
@brief getter of direction
|
154 |
@author humkyung
|
155 |
@date 2018.09.01
|
156 |
'''
|
157 |
@property
|
158 |
def direction(self): |
159 |
return self._direction |
160 |
|
161 |
'''
|
162 |
@brief setter of direction
|
163 |
@author humkyung
|
164 |
@date 2018.09.01
|
165 |
'''
|
166 |
@direction.setter
|
167 |
def direction(self, value): |
168 |
self._direction = value
|
169 |
|
170 |
@property
|
171 |
def symbol_idx(self): |
172 |
""" """
|
173 |
return self._symbol_idx |
174 |
|
175 |
@symbol_idx.setter
|
176 |
def symbol_idx(self, value): |
177 |
""" setter of symbol_idx """
|
178 |
self._symbol_idx = value
|
179 |
|
180 |
@property
|
181 |
def spec_break(self): |
182 |
""" getter of spec break """
|
183 |
return self._spec_break |
184 |
|
185 |
'''
|
186 |
@brief return direction vector
|
187 |
@author humkyung
|
188 |
@date 2018.09.01
|
189 |
'''
|
190 |
def dir(self): |
191 |
if self._direction == 'LEFT': |
192 |
return (-1, 0) |
193 |
elif self._direction == 'RIGHT': |
194 |
return (1, 0) |
195 |
elif self._direction == 'UP': |
196 |
return (0, -1) |
197 |
elif self._direction == 'DOWN': |
198 |
return (0, 1) |
199 |
|
200 |
return None |
201 |
|
202 |
'''
|
203 |
@brief build connector item
|
204 |
@author humkyung
|
205 |
@date 2018.05.02
|
206 |
'''
|
207 |
def buildItem(self): |
208 |
color = Qt.blue |
209 |
self.setBrush(color)
|
210 |
if self.parent is not None: |
211 |
self.setZValue(self.parent.zValue() + 1) |
212 |
|
213 |
'''
|
214 |
@brief return center of connector
|
215 |
@author humkyung
|
216 |
@date 2018.07.23
|
217 |
'''
|
218 |
def center(self): |
219 |
pt = self.sceneBoundingRect().center()
|
220 |
return (pt.x(), pt.y())
|
221 |
|
222 |
'''
|
223 |
@brief set position of connector
|
224 |
@author humkyung
|
225 |
@date 2018.05.02
|
226 |
'''
|
227 |
def setPos(self, pos): |
228 |
self._loc = [pos[0], pos[1]] |
229 |
self.setRect(self._loc[0] - round(self.SMALL_SIZE*0.5), self._loc[1] - round(self.SMALL_SIZE*0.5), self.SMALL_SIZE, self.SMALL_SIZE) |
230 |
self.update()
|
231 |
|
232 |
if hasattr(self, '_label'): |
233 |
""" set label positon at center of connector """
|
234 |
self._label.setPos(QPointF(self._loc[0] - self._label.boundingRect().width()*0.5, self._loc[1] - self._label.boundingRect().height()*0.5)) |
235 |
|
236 |
def connect(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT): |
237 |
""" connect to given item where given position """
|
238 |
|
239 |
if item:
|
240 |
self.connectedItem = item
|
241 |
self._connected_at = at
|
242 |
else:
|
243 |
self.connectedItem = None |
244 |
|
245 |
self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue) |
246 |
|
247 |
'''
|
248 |
@brief highlight connector
|
249 |
@authro humkyung
|
250 |
@date 2018.05.09
|
251 |
'''
|
252 |
def hoverEnterEvent(self, event): |
253 |
try:
|
254 |
self.highlight(True) |
255 |
finally:
|
256 |
QApplication.setOverrideCursor(Qt.CrossCursor) |
257 |
|
258 |
'''
|
259 |
@brief unhighlight connector
|
260 |
@author humkyung
|
261 |
@date 2018.05.09
|
262 |
'''
|
263 |
def hoverLeaveEvent(self, event): |
264 |
try:
|
265 |
self.highlight(False) |
266 |
finally:
|
267 |
QApplication.restoreOverrideCursor() |
268 |
|
269 |
def highlight(self, flag): |
270 |
if flag:
|
271 |
self.setRect(self._loc[0] - round(self.BIG_SIZE*0.5), self._loc[1] - round(self.BIG_SIZE*0.5), self.BIG_SIZE, self.BIG_SIZE) |
272 |
c = QColor() |
273 |
c.setNamedColor(QEngineeringConnectorItem.HIGHLIGHT) |
274 |
self.setPen(Qt.red)
|
275 |
self.setBrush(c)
|
276 |
if hasattr(self, '_label'): |
277 |
font = self._label.font()
|
278 |
font.setBold(True)
|
279 |
self._label.setFont(font)
|
280 |
else:
|
281 |
self.setRect(self._loc[0] - round(self.SMALL_SIZE*0.5), self._loc[1] - round(self.SMALL_SIZE*0.5), self.SMALL_SIZE, self.SMALL_SIZE) |
282 |
self.setPen(Qt.black)
|
283 |
self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue) |
284 |
if hasattr(self, '_label'): |
285 |
font = self._label.font()
|
286 |
font.setBold(False)
|
287 |
self._label.setFont(font)
|
288 |
|
289 |
if self.parentItem(): self.setZValue(self.parentItem().zValue() + 1) |
290 |
if self.scene() and self.scene().sceneRect().contains(self.sceneBoundingRect()): |
291 |
self.update()
|
292 |
if self.scene(): self.scene().update() |
293 |
|
294 |
'''
|
295 |
@brief
|
296 |
@author humkyung
|
297 |
@date 2018.07.27
|
298 |
'''
|
299 |
def mousePressEvent(self, event): |
300 |
if self.parentItem() is not None: |
301 |
if event.buttons() == Qt.LeftButton:
|
302 |
self._savedPos = self._loc |
303 |
self._savedConnectedItem = self.connectedItem |
304 |
|
305 |
QGraphicsEllipseItem.mousePressEvent(self, event)
|
306 |
|
307 |
'''
|
308 |
@brief
|
309 |
@author humkyung
|
310 |
@date 2018.07.27
|
311 |
@history humkyung 2018.08.23 unhighlight item if underitem exists
|
312 |
'''
|
313 |
def mouseReleaseEvent(self, event): |
314 |
import shapely |
315 |
from SymbolSvgItem import SymbolSvgItem |
316 |
from EngineeringArrowItem import QEngineeringArrowItem |
317 |
from EngineeringTextItem import QEngineeringTextItem |
318 |
|
319 |
try:
|
320 |
if self.parentItem() is not None and self._savedPos is not None: |
321 |
items = [item for item in self.scene().items(event.scenePos()) if item is not self and item is not self.parentItem() and \ |
322 |
(issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or type(item) is QEngineeringConnectorItem)] |
323 |
|
324 |
if items and type(items[0]) is QEngineeringConnectorItem: |
325 |
self.setPos(items[0].center()) |
326 |
|
327 |
""" disconnect alreay connected item """
|
328 |
if self.connectedItem is not None: |
329 |
self.connectedItem.connect(None) |
330 |
|
331 |
""" connect each other """
|
332 |
self.connect(items[0]) |
333 |
items[0].connect(self) |
334 |
else:
|
335 |
pt = [event.scenePos().x(), event.scenePos().y()] |
336 |
|
337 |
self.setPos(pt)
|
338 |
if self.connectedItem is not None: |
339 |
self.connectedItem.connect(None) |
340 |
|
341 |
self.connect(None) |
342 |
|
343 |
## unhighlight underitem
|
344 |
if hasattr(self, '_underItem') and self._underItem is not None: |
345 |
self._underItem.highlight(False) |
346 |
## up to here
|
347 |
|
348 |
for symbol in [self.parentItem() for self in items if type(self.parentItem()) is SymbolSvgItem]: |
349 |
symbol.transfer.on_pos_changed.connect(self.parent.on_symbol_pos_changed)
|
350 |
|
351 |
if self.parentItem() is not None: |
352 |
self._savedPos = None |
353 |
self._savedConnectedItem = None |
354 |
self.transfer.onPosChanged.emit(self) |
355 |
finally:
|
356 |
#self.ungrabKeyboard()
|
357 |
pass
|
358 |
|
359 |
QGraphicsEllipseItem.mouseReleaseEvent(self, event)
|
360 |
|
361 |
'''
|
362 |
@brief move connector's position while mouse drag
|
363 |
@author humkyung
|
364 |
@date 2018.07.27
|
365 |
@history humkyung 2018.08.23 hightlight underitem
|
366 |
'''
|
367 |
def mouseMoveEvent(self, event): |
368 |
import shapely |
369 |
from SymbolSvgItem import SymbolSvgItem |
370 |
from EngineeringArrowItem import QEngineeringArrowItem |
371 |
from EngineeringTextItem import QEngineeringTextItem |
372 |
|
373 |
if self.parentItem() is not None and self._savedPos is not None: |
374 |
if event.buttons() == Qt.LeftButton:
|
375 |
items = [item for item in self.scene().items(event.scenePos()) if item is not self and item is not self.parent and \ |
376 |
(issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or type(item) is QEngineeringConnectorItem)] |
377 |
## highlight underitem
|
378 |
if len(items) > 0: |
379 |
if not hasattr(self, '_underItem') or self._underItem is not items[0]: |
380 |
if hasattr(self, '_underItem') and self._underItem is not None: |
381 |
self._underItem.highlight(False) |
382 |
|
383 |
self._underItem = items[0] |
384 |
self._underItem.highlight(True) |
385 |
elif hasattr(self, '_underItem') and self._underItem is not None: |
386 |
self._underItem.hoverLeaveEvent(event)
|
387 |
self._underItem = None |
388 |
## up to here
|
389 |
|
390 |
if items and type(items[0]) is QEngineeringConnectorItem: |
391 |
self.setPos(items[0].center()) |
392 |
else:
|
393 |
pt = [event.scenePos().x(), event.scenePos().y()] |
394 |
|
395 |
self.setPos(pt)
|
396 |
|
397 |
self.update()
|
398 |
|
399 |
QGraphicsEllipseItem.mouseMoveEvent(self, event)
|
400 |
|
401 |
|
402 |
def hasConnectedItem(self): |
403 |
if self.connectedItem is not None: |
404 |
return True |
405 |
|
406 |
return False |
407 |
'''
|
408 |
@brief reject if user press Escape key
|
409 |
@author humkyung
|
410 |
@date 2018.07.27
|
411 |
'''
|
412 |
def keyPressEvent(self, event): |
413 |
if event.key() == Qt.Key_Escape:
|
414 |
if hasattr(self, '_savedPos') and self._savedPos is not None: |
415 |
self.setPos(self._savedPos) |
416 |
self.connectedItem = self._savedConnectedItem |
417 |
self._savedPos = None |
418 |
self.update()
|
419 |
elif event.key() == Qt.Key_A:
|
420 |
self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
|
421 |
elif event.key() == Qt.Key_F:
|
422 |
self._drawing_mode = QEngineeringConnectorItem.FREE_MODE
|
423 |
|
424 |
QGraphicsEllipseItem.keyPressEvent(self, event)
|
425 |
|
426 |
'''
|
427 |
@brief override paint(draw connection points)
|
428 |
@author humkyung
|
429 |
@date 2018.04.21
|
430 |
'''
|
431 |
def paint(self, painter, options=None, widget=None): |
432 |
from SymbolSvgItem import SymbolSvgItem |
433 |
|
434 |
QGraphicsEllipseItem.paint(self, painter, options, widget)
|
435 |
if type(self.parentItem()) is SymbolSvgItem: |
436 |
painter.setFont(self._font)
|
437 |
painter.drawText(self.boundingRect(), str(self._conn_index), QTextOption(Qt.AlignCenter)) |
438 |
|
439 |
'''
|
440 |
@brief check Overlap
|
441 |
@author kyouho
|
442 |
@date 2018.08.30
|
443 |
'''
|
444 |
def isOverlapConnector(self, conn, toler = 10): |
445 |
from shapely.geometry import Point |
446 |
|
447 |
x1 = self.center()[0] |
448 |
y1 = self.center()[1] |
449 |
x2 = conn.center()[0]
|
450 |
y2 = conn.center()[1]
|
451 |
|
452 |
if Point(x1, y1).distance(Point(x2, y2)) < toler:
|
453 |
return True |
454 |
else:
|
455 |
return False |
456 |
|
457 |
def parse_xml(self, node): |
458 |
""" parse given node """
|
459 |
import uuid |
460 |
|
461 |
try:
|
462 |
if 'UID' in node.attrib: self.uid = uuid.UUID(node.attrib['UID'], version=4) |
463 |
|
464 |
if 'CONNECTED_AT' in node.attrib: |
465 |
self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT if node.attrib['CONNECTED_AT'] == '0' else QEngineeringAbstractItem.CONNECTED_AT_BODY |
466 |
else:
|
467 |
self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT
|
468 |
|
469 |
connectedItemStr = node.find('CONNECTEDITEM').text
|
470 |
connectPointStr = node.find('CONNECTPOINT').text.split(',') |
471 |
sceneConnectPointStr = node.find('SCENECONNECTPOINT').text.split(',') |
472 |
|
473 |
self._connectedItem = uuid.UUID(connectedItemStr, version=4) if connectedItemStr != 'None' else None |
474 |
self.connectPoint = (float(connectPointStr[0]), float(connectPointStr[1])) |
475 |
self.sceneConnectPoint = (float(sceneConnectPointStr[0]), float(sceneConnectPointStr[1])) |
476 |
|
477 |
self.setBrush(Qt.yellow) if self._connectedItem else self.setBrush(Qt.blue) |
478 |
except Exception as ex: |
479 |
from App import App |
480 |
from AppDocData import MessageType |
481 |
|
482 |
message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno) |
483 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
484 |
|
485 |
def toSql(self, index): |
486 |
""" generate sql string to save connectors to database """
|
487 |
res = [] |
488 |
|
489 |
cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y'] |
490 |
values = ['?', '?', '?', '?', '?'] |
491 |
|
492 |
param = [str(self.uid), str(self.parent.uid), index, self.center()[0], self.center()[1]] |
493 |
sql = 'insert or replace into Points({}) values({})'.format(','.join(cols), ','.join(values)) |
494 |
res.append((sql, tuple(param)))
|
495 |
|
496 |
cols = ['Points_UID', 'Pressure', 'Pressure_Drop', 'Elevation', 'Over_Design_CV'] |
497 |
values = ['?', '?', '?', '?', '?'] |
498 |
if self.data: |
499 |
|
500 |
pressure = self.data.pressure
|
501 |
if pressure is None: |
502 |
pressure = None
|
503 |
else:
|
504 |
pressure = float(pressure)
|
505 |
|
506 |
pressure_drop = self.data.pressure_drop
|
507 |
if pressure_drop is None: |
508 |
pressure_drop = None
|
509 |
else:
|
510 |
pressure_drop = float(pressure_drop)
|
511 |
|
512 |
elevation = self.data.elevation
|
513 |
if elevation is None: |
514 |
elevation = None
|
515 |
else:
|
516 |
elevation = float(elevation)
|
517 |
|
518 |
over_design_cv = self.data.over_design_cv
|
519 |
if over_design_cv is None: |
520 |
over_design_cv = None
|
521 |
else:
|
522 |
over_design_cv = float(over_design_cv)
|
523 |
|
524 |
|
525 |
param = [str(self.uid), pressure, pressure_drop, elevation, over_design_cv] |
526 |
sql = 'insert or replace into Nozzles({}) values({})'.format(','.join(cols), ','.join(values)) |
527 |
res.append((sql, tuple(param)))
|
528 |
|
529 |
return res
|
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 |
class Transfer(QObject): |
537 |
onPosChanged = pyqtSignal(QGraphicsItem) |
538 |
onRemoved = pyqtSignal(QGraphicsItem) |
539 |
|
540 |
def __init__(self, parent = None): |
541 |
QObject.__init__(self, parent)
|