프로젝트

일반

사용자정보

통계
| 개정판:

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)
클립보드 이미지 추가 (최대 크기: 500 MB)