프로젝트

일반

사용자정보

통계
| 개정판:

hytos / HYTOS / HYTOS / Shapes / EngineeringStreamlineItem.py @ bbb7f7da

이력 | 보기 | 이력해설 | 다운로드 (18.3 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
                connected_item_uid = connected[index]
89
                if connected_item_uid:
90
                    connector.connect(connected_item_uid)
91
                    target = QEngineeringConnectorItem.find_connector(connected_item_uid)
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
        from AppDocData import AppDocData
378
        if not self.isSelected(): return
379

    
380
        if event.key() == Qt.Key_Delete:            
381
            self.deleteStreamlineItemFromScene()
382

    
383
            appDocData = AppDocData.instance()
384
            activeDrawing = appDocData.activeDrawing
385
            if activeDrawing:
386
                activeDrawing.hmbTable.deleteByUID(self.uid)
387

    
388
            App.mainWnd().load_HMB()
389
        elif event.key() == Qt.Key_QuoteLeft:
390
            self.mouseDoubleClickEvent(event)    
391
           
392
    def deleteStreamlineItemFromScene(self):
393
        """ remove self from scene """
394
        try:
395
            for conn in self.connectors:
396
                if conn.connectedItem is not None:
397
                    conn.connectedItem.connect(None)
398
        except Exception as ex:
399
            from App import App
400
            from AppDocData import MessageType
401

    
402
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
403
            App.mainWnd().addMessage.emit(MessageType.Error, message)
404

    
405
        self.transfer.onRemoved.emit(self)
406

    
407

    
408

    
409
    def toSql(self):
410
        """ convert valve data to sql query """
411
        import uuid
412
        from AppDocData import AppDocData
413

    
414
        res = []
415
        appDocData = AppDocData.instance()
416

    
417
        symbolInfo = appDocData.getSymbolByQuery('name', 'Stream Line')
418
        dbUid = symbolInfo.uid
419
        uid = self.uid
420

    
421
        cols = ['UID', 'Drawings_UID', 'Symbols_UID']
422
        values = ['?','?','?']
423
        param = [str(uid), str(appDocData.activeDrawing.UID), str(dbUid)]
424
        sql = 'insert or replace into Components({}) values({})'.format(','.join(cols), ','.join(values))
425
        res.append((sql, tuple(param)))
426

    
427
        # save vertices to database
428
        index = 1
429
        for vertex in self._vertices:                                    
430
            if index == 1 and self.connectors[0].connectedItem is not None:
431
                cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y', 'ConnectedItem_UID']
432
                values = ['?', '?', '?', '?', '?', '?']
433
                param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1], str(self.connectors[0].connectedItem.uid)]
434
            elif index == 4 and self.connectors[1].connectedItem is not None:
435
                cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y', 'ConnectedItem_UID']
436
                values = ['?', '?', '?', '?', '?', '?']
437
                param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1], str(self.connectors[1].connectedItem.uid)]
438
            else:
439
                cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y']
440
                values = ['?', '?', '?', '?', '?']
441
                param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1]]
442
        
443
            sql = 'insert or replace into Points({}) values({})'.format(','.join(cols), ','.join(values))
444

    
445
            res.append((sql, tuple(param)))
446
            index += 1
447
        # up to here
448

    
449

    
450

    
451
        return res
452

    
453
    @staticmethod 
454
    def fromDatabase(componentInfos):
455
        from EngineeringConnectorItem import QEngineeringConnectorItem
456

    
457
        item = None
458
        try:
459
            uid = componentInfos[0][0]              #uid@Components
460

    
461
            item = QEngineeringStreamlineItem(uid)
462

    
463
            pointsUids = []
464
            for componentInfo in componentInfos:     
465
                pointsUid = componentInfo[11]           # uid@Points           
466
                x = componentInfo[13]                   # X@Points
467
                y = componentInfo[14]                   # Y@Points
468

    
469
                pointsUids.append(pointsUid)
470
                item._vertices.append((x, y))
471

    
472
            connectorItems = [componentInfos[0][15], componentInfos[-1][15]]
473

    
474
            item.setVisible(False)
475
            item.__buildItem()
476
            item.build_connectors(connectorItems, pointsUids)
477
  
478
            item.update()
479

    
480
        except Exception as ex:
481
            from App import App 
482
            from AppDocData import MessageType
483

    
484
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
485
            App.mainWnd().addMessage.emit(MessageType.Error, message)
486

    
487
        return item
488

    
489

    
490
'''
491
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
492
    @author     Jeongwoo
493
    @date       2018.06.18
494
'''
495
class Transfer(QObject):
496
    onRemoved = pyqtSignal(QGraphicsItem)
497

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