프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

hytos / HYTOS / HYTOS / Shapes / EngineeringConnectorItem.py @ 759bdc98

이력 | 보기 | 이력해설 | 다운로드 (23 KB)

1
# coding: utf-8
2
""" This is engineering connector item module """
3

    
4
import os.path
5
import copy, sys
6

    
7
try:
8
    from PyQt5.QtCore import *
9
    from PyQt5.QtGui import *
10
    from PyQt5.QtWidgets import *
11
except ImportError:
12
    try:
13
        from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR
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 AppDocData import *
19
from EngineeringAbstractItem import QEngineeringAbstractItem
20

    
21

    
22
class NozzleData:
23
    """ this is nozzle data class """
24

    
25
    def __init__(self):
26
        """ constructor """
27
        self.pressure = None
28
        self.pressure_drop = None
29
        self.elevation = None
30
        self.over_design_cv = None
31
        self.cv_type = None
32
        self.desc = None
33
        self.pos = None
34

    
35
    def parse(self, row):
36
        """ parse given row """
37

    
38
        try:
39
            if not row['Pressure'] is None:
40
                self.pressure = float(row['Pressure'])
41
            if not row['Pressure_Drop'] is None:
42
                self.pressure_drop = float(row['Pressure_Drop'])
43
            if not row['Elevation'] is None:
44
                self.elevation = float(row['Elevation'])
45
            if not row['Over_Design_CV'] is None:
46
                self.over_design_cv = float(row['Over_Design_CV'])
47
            if not row['CV_Type'] is None:
48
                self.cv_type = row['CV_Type']
49
            if not row['Name'] is None:
50
                self.desc = row['Name']
51
            if not row['X'] is None and not row['Y'] is None:
52
                self.pos = (float(row['X']), float(row['Y']))
53

    
54
        except Exception as ex:
55
            from App import App
56
            from AppDocData import MessageType
57

    
58
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
59
                                                           sys.exc_info()[-1].tb_lineno)
60
            App.mainWnd().addMessage.emit(MessageType.Error, message)
61

    
62

    
63
"""
64
    A {ConnectorItem} is the graphical representation of a {Symbol.Connectors}.
65
"""
66

    
67

    
68
class QEngineeringConnectorItem(QGraphicsEllipseItem):
69
    """ This is engineering connector item class """
70
    SMALL_SIZE = 10
71
    BIG_SIZE = 16
72
    HIGHLIGHT = '#BC4438'
73

    
74
    AXIS_MODE = 0
75
    FREE_MODE = 1
76

    
77
    def __init__(self, uid=None, parent=None, index=-1):
78
        """constructor of connector"""
79
        import uuid
80
        from SymbolSvgItem import SymbolSvgItem
81
        from EngineeringEqpDescTextItem import QEngineeringEqpDescTextItem
82

    
83
        QGraphicsEllipseItem.__init__(self, parent)
84

    
85
        self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid, version=4)
86
        self.parent = parent
87
        self.buildItem()
88
        self._direction = 'AUTO'
89
        self._symbol_idx = '0'
90
        self._connectedItem = None
91
        self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT  # default value is connected at pt
92
        self.connectPoint = None
93
        self._hoverItem = None
94
        self._data = None
95

    
96
        self.setAcceptHoverEvents(True)
97
        self.transfer = Transfer()
98

    
99
        self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
100

    
101
        if type(parent) is SymbolSvgItem:
102
            """ setup label for connector index """
103
            self._font = QFont('Arial', QEngineeringConnectorItem.SMALL_SIZE)
104
            self._font.setPointSizeF(5)
105

    
106
        self._conn_index = index
107

    
108
    def __repr__(self):
109
        """ return string represent parent item and connector index """
110
        return '{}_{}_N{}'.format(self.parentItem().name, self.parentItem().index, self._conn_index)
111

    
112
    @staticmethod
113
    def find_connector(uid):
114
        """ find a connector which has given uid """
115
        from App import App
116
        import uuid
117

    
118
        res = None
119
        if type(uid) is uuid.UUID or type(uid) is str:
120
            scene = App.mainWnd().graphicsView.scene
121
            matches = [x for x in scene.items() if hasattr(x, 'uid') and str(x.uid) == str(uid)]
122
            res = matches[0] if matches else None
123

    
124
        return res
125

    
126
    @property
127
    def data(self):
128
        """ getter of nozzle data """
129
        return self._data
130

    
131
    @data.setter
132
    def data(self, value):
133
        """ setter of nozzle data """
134
        self._data = value
135

    
136
    @property
137
    def elevation(self):
138
        """ return elevation """
139
        return self.data.elevation
140

    
141
    @property
142
    def press_drop(self):
143
        return None
144

    
145
    @property
146
    def connectedItem(self):
147
        """ getter of connectedItem """
148
        import uuid
149

    
150
        if self.scene() and (type(self._connectedItem) is uuid.UUID or type(self._connectedItem) is str):
151
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._connectedItem)]
152
            if matches:
153
                self._connectedItem = matches[0]
154
            else:
155
                self._connectedItem = None
156

    
157
        return self._connectedItem
158

    
159
    @connectedItem.setter
160
    def connectedItem(self, value):
161
        """ setter of connectedItem """
162
        self._connectedItem = value
163

    
164
    '''
165
        @brief      getter of direction
166
        @author     humkyung
167
        @date       2018.09.01
168
    '''
169

    
170
    @property
171
    def direction(self):
172
        return self._direction
173

    
174
    '''
175
        @brief      setter of direction
176
        @author     humkyung
177
        @date       2018.09.01
178
    '''
179

    
180
    @direction.setter
181
    def direction(self, value):
182
        self._direction = value
183

    
184
    @property
185
    def symbol_idx(self):
186
        """ """
187
        return self._symbol_idx
188

    
189
    @symbol_idx.setter
190
    def symbol_idx(self, value):
191
        """ setter of symbol_idx """
192
        self._symbol_idx = value
193

    
194
    def validate(self):
195
        """validate connector's data"""
196
        from SymbolSvgItem import SymbolSvgItem
197
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
198

    
199
        res = []
200

    
201
        try:
202
            parent = self.parentItem()
203
            if type(parent) is SymbolSvgItem:
204
                if parent.category == 'Equipment - [ Pressurized ]':
205
                    if self.connectedItem and \
206
                            (self.data is None or (self.data.pressure is None or self.data.elevation is None)):
207
                        res.extend([self, 'pressure or elevation is invalid'])
208
                elif parent.category == 'Equipment - [ Pressure Drop ]' or parent.type == 'Flowmeter':
209
                    if self.connectedItem and \
210
                            (self.data is None or (self.data.pressure_drop is None or self.data.elevation is None)):
211
                        res.extend([self, 'pressure drop or elevation is invalid'])
212
                elif parent.category == 'Equipment - [ Rotating ]':
213
                    if self.connectedItem and parent.name.upper() in ['L_KOMP', 'R_KOMP', 'R_PUMP', 'L_PUMP', 'V_PUMP']:
214
                        if self.data is None or self.data.elevation is None:
215
                            res.extend([self, 'need to check elevation'])
216
                    elif self.connectedItem and parent.name.upper() in ['L_COMP', 'R_COMP']:
217
                        if self.data is None or self.data.pressure is None:
218
                            res.extend([self, 'need to check pressure'])
219
                        elif self.data is None or self.data.elevation is None:
220
                            res.extend([self, 'need to check elevation'])
221
                    elif self.connectedItem and (self.data.pressure_drop is None or self.data.elevation is None):
222
                        res.extend([self, 'need to check pressure drop and elevation'])
223
                elif self.connectedItem and parent.name in ['CV_H', 'CV_V']:
224
                    if self.data is None or self.data.elevation is None:
225
                        res.extend([self, 'need to check elevation'])
226
                elif parent.name == 'Line Splitter':  # set default value for line splitter
227
                    if self.connectedItem:
228
                        if self.data.pressure_drop is None:
229
                            self.data.pressure_drop = 0
230
                        if self.data.elevation is None:
231
                            self.data.elevation = 0
232
            elif type(parent) is QEngineeringStreamlineItem:
233
                pass
234

    
235
            # update connector's color
236
            if self.connectedItem and type(self.parentItem()) is SymbolSvgItem:
237
                self.setBrush(Qt.red) if res else self.setBrush(Qt.yellow)
238
            else:
239
                self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue)
240
            # up to here
241
        except Exception as ex:
242
            from App import App
243
            from AppDocData import MessageType
244

    
245
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
246
                                                           sys.exc_info()[-1].tb_lineno)
247
            App.mainWnd().addMessage.emit(MessageType.Error, message)
248

    
249
        return res
250

    
251
    def dir(self):
252
        """return direction vector"""
253
        if self._direction == 'LEFT':
254
            return -1, 0
255
        elif self._direction == 'RIGHT':
256
            return 1, 0
257
        elif self._direction == 'UP':
258
            return 0, -1
259
        elif self._direction == 'DOWN':
260
            return 0, 1
261

    
262
        return None
263

    
264
    def buildItem(self):
265
        """build connector item"""
266
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
267

    
268
        self.setBrush(Qt.blue)
269
        if self.parentItem() is not None:
270
            self.setZValue(self.parentItem().zValue() + 1)
271

    
272
    '''
273
        @brief      return center of connector
274
        @author     humkyung
275
        @date       2018.07.23
276
    '''
277

    
278
    def center(self):
279
        pt = self.sceneBoundingRect().center()
280
        return (pt.x(), pt.y())
281

    
282
    '''
283
        @brief  set position of connector
284
        @author humkyung
285
        @date   2018.05.02
286
    '''
287

    
288
    def setPos(self, pos):
289
        self._loc = [pos[0], pos[1]]
290
        self.setRect(self._loc[0] - round(self.SMALL_SIZE * 0.5), self._loc[1] - round(self.SMALL_SIZE * 0.5),
291
                     self.SMALL_SIZE, self.SMALL_SIZE)
292
        self.update()
293

    
294
        if hasattr(self, '_label'):
295
            """ set label positon at center of connector """
296
            self._label.setPos(QPointF(self._loc[0] - self._label.boundingRect().width() * 0.5,
297
                                       self._loc[1] - self._label.boundingRect().height() * 0.5))
298

    
299
    def connect(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
300
        """connect to given item where given position"""
301
        from SymbolSvgItem import SymbolSvgItem
302

    
303
        if item:
304
            self.connectedItem = item
305
            self._connected_at = at
306
        else:
307
            self.connectedItem = None
308

    
309
        self.validate()
310
        self.update()
311

    
312
    def hoverEnterEvent(self, event):
313
        """highlight connector"""
314
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
315

    
316
        try:
317
            if type(self.parentItem()) is not QEngineeringStreamlineItem:
318
                items = self.scene().items(event.scenePos())
319
                matched = [item for item in items if type(item.parentItem()) is QEngineeringStreamlineItem]
320
                if matched:
321
                    matched[0].hoverEnterEvent(event)
322
                else:
323
                    self.highlight(True)
324
            else:
325
                self.highlight(True)
326
        finally:
327
            QApplication.setOverrideCursor(Qt.CrossCursor)
328

    
329
    def hoverLeaveEvent(self, event):
330
        """unhighlight connector"""
331
        try:
332
            self.highlight(False)
333
        finally:
334
            QApplication.restoreOverrideCursor()
335

    
336
    def highlight(self, flag):
337
        """highlight or unhighlight"""
338

    
339
        if flag:
340
            self.setRect(self._loc[0] - round(self.BIG_SIZE * 0.5), self._loc[1] - round(self.BIG_SIZE * 0.5),
341
                         self.BIG_SIZE, self.BIG_SIZE)
342
            c = QColor()
343
            c.setNamedColor(QEngineeringConnectorItem.HIGHLIGHT)
344
            self.setPen(Qt.red)
345
            # self.setBrush(c)
346
            if hasattr(self, '_label'):
347
                font = self._label.font()
348
                font.setBold(True)
349
                self._label.setFont(font)
350
            self.parentItem().highlight(flag)
351
        else:
352
            self.setRect(self._loc[0] - round(self.SMALL_SIZE * 0.5), self._loc[1] - round(self.SMALL_SIZE * 0.5),
353
                         self.SMALL_SIZE, self.SMALL_SIZE)
354
            self.setPen(Qt.black)
355
            # self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue)
356
            if hasattr(self, '_label'):
357
                font = self._label.font()
358
                font.setBold(False)
359
                self._label.setFont(font)
360

    
361
            self.parentItem().highlight(flag)
362

    
363
        if self.scene() and self.scene().sceneRect().contains(self.sceneBoundingRect()):
364
            self.update()
365
            if self.scene(): self.scene().update()
366

    
367
    '''
368
        @brief      
369
        @author     humkyung
370
        @date       2018.07.27
371
    '''
372

    
373
    def mousePressEvent(self, event):
374
        if self.parentItem():
375
            if event.buttons() == Qt.LeftButton:
376
                self._savedPos = self._loc
377
                self._savedConnectedItem = self.connectedItem
378

    
379
        QGraphicsEllipseItem.mousePressEvent(self, event)
380

    
381
    '''
382
        @brief      
383
        @author     humkyung
384
        @date       2018.07.27
385
        @history    humkyung 2018.08.23 unhighlight item if underitem exists
386
    '''
387

    
388
    def mouseReleaseEvent(self, event):
389
        import shapely
390
        from SymbolSvgItem import SymbolSvgItem
391
        from EngineeringArrowItem import QEngineeringArrowItem
392
        from EngineeringTextItem import QEngineeringTextItem
393

    
394
        try:
395
            if self.parentItem() is not None and self._savedPos is not None:
396
                items = [item for item in self.scene().items(event.scenePos()) if
397
                         item is not self and item is not self.parentItem() and \
398
                         (issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or type(
399
                             item) is QEngineeringConnectorItem)]
400

    
401
                if items and type(items[0]) is QEngineeringConnectorItem:
402
                    self.setPos(items[0].center())
403

    
404
                    """ disconnect already connected item """
405
                    if self.connectedItem is not None:
406
                        self.connectedItem.connect(None)
407

    
408
                    """ connect each other """
409
                    self.connect(items[0])
410
                    items[0].connect(self)
411
                else:
412
                    pt = [event.scenePos().x(), event.scenePos().y()]
413

    
414
                    self.setPos(pt)
415
                    if self.connectedItem is not None:
416
                        self.connectedItem.connect(None)
417

    
418
                    self.connect(None)
419

    
420
                # unhighlight underitem
421
                if hasattr(self, '_underItem') and self._underItem is not None:
422
                    self._underItem.highlight(False)
423
                # up to here
424

    
425
                for symbol in [self.parentItem() for self in items if type(self.parentItem()) is SymbolSvgItem]:
426
                    symbol.transfer.on_pos_changed.connect(self.parent.on_symbol_pos_changed)
427

    
428
            if self.parentItem() is not None:
429
                self._savedPos = None
430
                self._savedConnectedItem = None
431
                self.transfer.onPosChanged.emit(self)
432
        finally:
433
            pass
434

    
435
        QGraphicsEllipseItem.mouseReleaseEvent(self, event)
436

    
437
    '''
438
        @brief      move connector's position while mouse drag
439
        @author     humkyung
440
        @date       2018.07.27
441
        @history    humkyung 2018.08.23 hightlight underitem
442
    '''
443

    
444
    def mouseMoveEvent(self, event):
445
        import shapely
446
        from SymbolSvgItem import SymbolSvgItem
447
        from EngineeringDimensionItem import QEngineeringDimensionItem
448
        from EngineeringTextItem import QEngineeringTextItem
449

    
450
        if self.parentItem() and self._savedPos is not None:
451
            if event.buttons() == Qt.LeftButton:
452
                items = [item for item in self.scene().items(event.scenePos()) if
453
                         item is not self and item is not self.parentItem() and \
454
                         (issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or
455
                          type(item) is QEngineeringConnectorItem)]
456
                # highlight underitem
457
                if len(items) > 0:
458
                    if not hasattr(self, '_underItem') or self._underItem is not items[0]:
459
                        if hasattr(self, '_underItem') and self._underItem is not None:
460
                            self._underItem.highlight(False)
461

    
462
                        self._underItem = items[0]
463
                        self._underItem.highlight(True)
464
                elif hasattr(self, '_underItem') and self._underItem is not None:
465
                    self._underItem.hoverLeaveEvent(event)
466
                    self._underItem = None
467
                # up to here
468

    
469
                if items and type(items[0]) is QEngineeringConnectorItem:
470
                    self.setPos(items[0].center())
471
                else:
472
                    self.setPos([event.scenePos().x(), event.scenePos().y()])
473

    
474
                self.update()
475

    
476
        QGraphicsEllipseItem.mouseMoveEvent(self, event)
477

    
478
    def hasConnectedItem(self):
479
        if self.connectedItem is not None:
480
            return True
481

    
482
        return False
483

    
484
    '''
485
        @brief      reject if user press Escape key
486
        @author     humkyung
487
        @date       2018.07.27
488
    '''
489

    
490
    def keyPressEvent(self, event):
491
        if event.key() == Qt.Key_Escape:
492
            if hasattr(self, '_savedPos') and self._savedPos is not None:
493
                self.setPos(self._savedPos)
494
                self.connectedItem = self._savedConnectedItem
495
                self._savedPos = None
496
                self.update()
497
        elif event.key() == Qt.Key_A:
498
            self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
499
        elif event.key() == Qt.Key_F:
500
            self._drawing_mode = QEngineeringConnectorItem.FREE_MODE
501

    
502
        QGraphicsEllipseItem.keyPressEvent(self, event)
503

    
504
    def paint(self, painter, options=None, widget=None):
505
        """override paint"""
506
        from SymbolSvgItem import SymbolSvgItem
507

    
508
        QGraphicsEllipseItem.paint(self, painter, options, widget)
509
        if type(self.parentItem()) is SymbolSvgItem:
510
            painter.setFont(self._font)
511
            painter.drawText(self.boundingRect(), str(self._conn_index), QTextOption(Qt.AlignCenter))
512

    
513
    '''
514
        @brief      check Overlap
515
        @author     kyouho
516
        @date       2018.08.30
517
    '''
518

    
519
    def isOverlapConnector(self, conn, toler=10):
520
        from shapely.geometry import Point
521

    
522
        x1 = self.center()[0]
523
        y1 = self.center()[1]
524
        x2 = conn.center()[0]
525
        y2 = conn.center()[1]
526

    
527
        if Point(x1, y1).distance(Point(x2, y2)) < toler:
528
            return True
529
        else:
530
            return False
531

    
532
    def parse_xml(self, node):
533
        """ parse given node """
534
        import uuid
535

    
536
        try:
537
            if 'UID' in node.attrib: self.uid = uuid.UUID(node.attrib['UID'], version=4)
538

    
539
            if 'CONNECTED_AT' in node.attrib:
540
                self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT if node.attrib[
541
                                                                                     'CONNECTED_AT'] == '0' else QEngineeringAbstractItem.CONNECTED_AT_BODY
542
            else:
543
                self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT
544

    
545
            connectedItemStr = node.find('CONNECTEDITEM').text
546
            connectPointStr = node.find('CONNECTPOINT').text.split(',')
547
            sceneConnectPointStr = node.find('SCENECONNECTPOINT').text.split(',')
548

    
549
            self._connectedItem = uuid.UUID(connectedItemStr, version=4) if connectedItemStr != 'None' else None
550
            self.connectPoint = (float(connectPointStr[0]), float(connectPointStr[1]))
551
            self.sceneConnectPoint = (float(sceneConnectPointStr[0]), float(sceneConnectPointStr[1]))
552

    
553
            self.setBrush(Qt.yellow) if self._connectedItem else self.setBrush(Qt.blue)
554
        except Exception as ex:
555
            from App import App
556
            from AppDocData import MessageType
557

    
558
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
559
                                                           sys.exc_info()[-1].tb_lineno)
560
            App.mainWnd().addMessage.emit(MessageType.Error, message)
561

    
562
    def toSql(self, index):
563
        """ generate sql string to save connectors to database """
564
        res = []
565

    
566
        try:
567
            cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y']
568
            values = ['?', '?', '?', '?', '?']
569

    
570
            param = [str(self.uid), str(self.parent.uid), index, self.center()[0], self.center()[1]]
571
            sql = 'insert or replace into Points({}) values({})'.format(','.join(cols), ','.join(values))
572
            res.append((sql, tuple(param)))
573

    
574
            cols = ['Points_UID', 'Pressure', 'Pressure_Drop', 'Elevation', 'Over_Design_CV', 'CV_Type']
575
            values = ['?', '?', '?', '?', '?', '?']
576
            if self.data:
577

    
578
                pressure = self.data.pressure
579
                if pressure is None:
580
                    pressure = None
581
                else:
582
                    pressure = float(pressure)
583

    
584
                pressure_drop = self.data.pressure_drop
585
                if pressure_drop is None:
586
                    pressure_drop = None
587
                else:
588
                    pressure_drop = float(pressure_drop)
589

    
590
                elevation = self.data.elevation
591
                if elevation is None:
592
                    elevation = None
593
                else:
594
                    elevation = float(elevation)
595

    
596
                over_design_cv = self.data.over_design_cv
597
                if over_design_cv is None:
598
                    over_design_cv = None
599
                else:
600
                    over_design_cv = float(over_design_cv)
601

    
602
                cv_type = self.data.cv_type
603
                if cv_type is None:
604
                    cv_type = None
605
                else:
606
                    cv_type = str(cv_type)
607

    
608
                param = [str(self.uid), pressure, pressure_drop, elevation, over_design_cv, cv_type]
609
                sql = 'insert or replace into Nozzles({}) values({})'.format(','.join(cols), ','.join(values))
610
                res.append((sql, tuple(param)))
611

    
612
        except Exception as ex:
613
            from App import App
614
            from AppDocData import MessageType
615

    
616
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
617
                                                           sys.exc_info()[-1].tb_lineno)
618
            App.mainWnd().addMessage.emit(MessageType.Error, message)
619

    
620
        return res
621

    
622

    
623
'''
624
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
625
    @author     Jeongwoo
626
    @date       2018.06.18
627
'''
628

    
629

    
630
class Transfer(QObject):
631
    onPosChanged = pyqtSignal(QGraphicsItem)
632
    onRemoved = pyqtSignal(QGraphicsItem)
633

    
634
    def __init__(self, parent=None):
635
        QObject.__init__(self, parent)
클립보드 이미지 추가 (최대 크기: 500 MB)