프로젝트

일반

사용자정보

통계
| 개정판:

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

이력 | 보기 | 이력해설 | 다운로드 (19.5 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

    
22
class QEngineeringStreamlineItem(QGraphicsPathItem, QEngineeringAbstractItem):
23
    """ This is EngineeringStreamlineItem Class """
24

    
25
    ARROW_SIZE = 10
26
    ZVALUE = 100
27
    onRemoved = pyqtSignal(QGraphicsItem)
28

    
29
    '''
30
        @history    2018.05.11  Jeongwoo    Declare variable self.pen
31
                    2018.05.15  Jeongwoo    Change method to call parent's __init__
32
                    2018.05.25  Jeongwoo    Change self.pen's default color (red → blue)
33
    '''
34

    
35
    def __init__(self, uid=None, parent=None):
36
        import uuid
37
        from EngineeringStreamNoTextItem import QEngineeringStreamNoTextItem
38

    
39
        try:
40
            QGraphicsPathItem.__init__(self, parent)
41
            QEngineeringAbstractItem.__init__(self)
42
            self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
43
            self.setAcceptHoverEvents(True)
44
            self.setAcceptTouchEvents(True)
45

    
46
            self.uid = uuid.uuid4() if uid is None else uid
47
            self.parent = parent
48
            self._vertices = []
49
            self.isCreated = False
50
            self._pt = None
51
            self.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # set default pen
52
            
53
            self._stream_no = 0
54
            self._stream_no_text = None
55

    
56
            self.transfer = Transfer()
57
            self.setZValue(QEngineeringStreamlineItem.ZVALUE)
58
        except Exception as ex:
59
            from App import App
60
            from AppDocData import MessageType
61
            
62
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
63
                                                           sys.exc_info()[-1].tb_lineno)
64
            App.mainWnd().addMessage.emit(MessageType.Error, message)
65

    
66
    def __repr__(self):
67
        """ return string represents stream line item """
68
        return 'Line_{}'.format(self._stream_no)
69

    
70
    @property
71
    def stream_no(self):
72
        """ return stream no """
73
        return self._stream_no
74

    
75
    @stream_no.setter
76
    def stream_no(self, value):
77
        """ set stream no with given value """
78
        from EngineeringStreamNoTextItem import QEngineeringStreamNoTextItem
79

    
80
        self._stream_no = value
81
        self._stream_no_text = QEngineeringStreamNoTextItem('<{}>'.format(self._stream_no), self)
82
        self.build_path()
83
        
84
    @property
85
    def data(self):
86
        """ return hmb data"""
87
        from AppDocData import AppDocData
88

    
89
        app_doc_data = AppDocData.instance()
90
        return app_doc_data.activeDrawing.hmbTable.get_hmb_data(self.uid)
91

    
92
    @property
93
    def density(self):
94
        """ return density """
95
        from AppDocData import AppDocData
96

    
97
        res = 0
98
        app_doc_data = AppDocData.instance()
99
        hmb_data = app_doc_data.activeDrawing.hmbTable.get_hmb_data(self.uid)
100
        if hmb_data: res = hmb_data.density
101

    
102
        return res
103

    
104
    @property
105
    def press_drop(self):
106
        """ return press drop """
107
        from AppDocData import AppDocData
108

    
109
        app_doc_data = AppDocData.instance()
110
        hmb_data = app_doc_data.activeDrawing.hmbTable.get_hmb_data(self.uid)
111

    
112
        return hmb_data.pressure_drop if hmb_data else 0
113

    
114
    def build_connectors(self, connected, pointsUids=None):
115
        """ build connectors for stream line
116
            connected is target connector
117
        """
118

    
119
        from SymbolSvgItem import SymbolSvgItem
120
        from EngineeringConnectorItem import QEngineeringConnectorItem 
121

    
122
        targets = []
123
        index = 0
124
        for vertex in [self._vertices[0],self._vertices[-1]]:          
125
            if pointsUids:
126
                connector = QEngineeringConnectorItem(pointsUids[index], parent=self, index=index+1)
127
            else: 
128
                connector = QEngineeringConnectorItem(uid=None, parent=self, index=index+1)
129

    
130
            connector.setPos(vertex)
131
            connector.setParentItem(self)
132
            connector.connectPoint = vertex
133
            connector.sceneConnectPoint = vertex
134

    
135
            # add connector move ables
136
            connector.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
137
            connector.setAcceptTouchEvents(True)
138
            connector.transfer.onPosChanged.connect(self.on_connector_pos_changed)
139

    
140
            connector.setZValue(self.zValue() + 1)
141
            self.connectors.append(connector)
142
            if len(connected) > index:
143
                connected_item_uid = connected[index]
144
                if connected_item_uid:
145
                    connector.connect(connected_item_uid)
146
                    target = QEngineeringConnectorItem.find_connector(connected_item_uid)
147
                    if target:
148
                        target.connect(connector)
149
                        targets.append(target)
150

    
151
            index = index + 1
152

    
153
        """ connect symbol's on_pos_changed signal """
154
        for symbol in [conn.parentItem() for conn in targets if type(conn.parentItem()) is SymbolSvgItem]:
155
            symbol.transfer.on_pos_changed.connect(self.on_symbol_pos_changed)
156
            symbol.transfer.on_size_changed.connect(self.on_symbol_pos_changed)
157

    
158
        self.update_arrow()
159

    
160
    def update_arrow(self):
161
        """ update flow arrow """
162
        import math
163
        from EngineeringArrowItem import QEngineeringArrowItem
164
        if len(self._vertices) < 2: return
165

    
166
        start = self._vertices[-2]
167
        end = self._vertices[-1]
168

    
169
        dx = end[0] - start[0]
170
        dy = end[1] - start[1]
171
        length = math.sqrt(dx*dx + dy*dy)
172
        if length == 0: return
173

    
174
        _dir = [(end[0] - start[0])/length, (end[1] - start[1])/length]
175
        perpendicular = (-_dir[1], _dir[0])
176
        polygon = QPolygonF()
177
        polygon.append(QPointF(end[0] - _dir[0] * QEngineeringStreamlineItem.ARROW_SIZE + perpendicular[
178
            0] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25,
179
                               end[1] - _dir[1] * QEngineeringStreamlineItem.ARROW_SIZE + perpendicular[
180
                                   1] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25))
181
        polygon.append(QPointF(end[0] - _dir[0] * QEngineeringStreamlineItem.ARROW_SIZE - perpendicular[
182
            0] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25,
183
                               end[1] - _dir[1] * QEngineeringStreamlineItem.ARROW_SIZE - perpendicular[
184
                                   1] * QEngineeringStreamlineItem.ARROW_SIZE * 0.25))
185
        polygon.append(QPointF(end[0], end[1]))
186
        polygon.append(polygon[0])  # close polygon
187

    
188
        if not hasattr(self, '_arrow'):
189
            self._arrow = QEngineeringArrowItem(polygon, self)
190
        else:
191
            self._arrow.setPolygon(polygon)
192

    
193
        self._arrow.setBrush(Qt.blue)
194
        self._arrow.update()
195

    
196
    '''
197
        @brief  construct a polyline
198
    '''
199

    
200
    def process(self, param):
201
        if ('mousePressEvent' == param[0]) and (param[1].button() == Qt.LeftButton):
202
            self._vertices.append(param[2])
203
        elif ('mouseMoveEvent' == param[0]):
204
            self._pt = param[2]
205
        elif ('mouseReleaseEvent' == param[0]) and (param[1].button() == Qt.RightButton):
206
            self.isCreated = True
207

    
208
    '''
209
        @brief  clone an object
210
    '''
211

    
212
    def clone(self):
213
        clone = QEngineeringStreamlineItem()
214
        clone._vertices = copy.deepcopy(self._vertices)
215
        clone.isCreated = self.isCreated
216

    
217
        return clone
218
        
219
    def init(self):
220
        self._vertices = []
221
        self._pt = None
222
        self.isCreated = False
223

    
224
    '''
225
        @build  build path
226
        @author humkyung
227
        @date   2018.04.23
228
    '''
229

    
230
    def build_path(self):
231
        if not self._vertices or len(self._vertices) < 2:
232
            return
233

    
234
        try:
235
            path = QPainterPath()
236
            path.moveTo(self._vertices[0][0], self._vertices[0][1])
237

    
238
            max_length = 0
239
            max_length_line = [None, None]
240
            for i in range(1, len(self._vertices)):
241
                path.lineTo(self._vertices[i][0], self._vertices[i][1])
242
                dx = self._vertices[i][0] - self._vertices[i-1][0]
243
                dy = self._vertices[i][1] - self._vertices[i-1][1]
244
                if dx*dx + dy*dy > max_length:
245
                    max_length = dx*dx + dy*dy
246
                    max_length_line[0] = self._vertices[i-1]
247
                    max_length_line[1] = self._vertices[i]
248

    
249
            self.setPath(path)
250
            self.isCreated = True
251

    
252
            if self._stream_no_text:
253
                if max_length_line[0] is not None and max_length_line[1] is not None:
254
                    x = (max_length_line[0][0] + max_length_line[1][0] - self._stream_no_text.textWidth())*0.5
255
                    y = (max_length_line[0][1] + max_length_line[1][1])*0.5
256
                    self._stream_no_text.setPos(QPointF(x, y))
257
                    dx = max_length_line[0][0] - max_length_line[1][0]
258
                    dy = max_length_line[0][1] - max_length_line[1][1]
259
                    self._stream_no_text.setRotation(90) if abs(dy) > abs(dx) else self._stream_no_text.setRotation(0)
260
        except Exception as ex:
261
            from App import App
262
            from AppDocData import MessageType
263

    
264
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
265
                                                           sys.exc_info()[-1].tb_lineno)
266
            App.mainWnd().addMessage.emit(MessageType.Error, message)
267

    
268
    '''
269
        @brief      return bounding rectangle of polyline
270
        @author     humkyung
271
        @date       2018.07.23
272
    '''
273

    
274
    def boundingRect(self):
275
        rect = QRectF()
276
        
277
        if self.isCreated:
278
            rect = self.path().boundingRect()
279
            rect = QRectF(rect.left() - 5, rect.top() - 5, rect.width() + 10, rect.height() + 10)
280
        else:
281
            minX = None
282
            minY = None
283
            maxX = None
284
            maxY = None
285
            for pt in self._vertices:
286
                if minX is None or pt[0] < minX:
287
                    minX = pt[0]
288
                if minY is None or pt[1] < minY:
289
                    minY = pt[1]
290
                
291
                if maxX is None or pt[0] > maxX:
292
                    maxX = pt[0]
293
                if maxY is None or pt[1] > maxY:
294
                    maxY = pt[1]
295
            
296
            if self._pt is not None:
297
                if minX is None or self._pt[0] < minX:
298
                    minX = self._pt[0]
299
                if minY is None or self._pt[1] < minY:
300
                    minY = self._pt[1]
301
                
302
                if maxX is None or self._pt[0] > maxX:
303
                    maxX = self._pt[0]
304
                if maxY is None or self._pt[1] > maxY:
305
                    maxY = self._pt[1]
306

    
307
            if minX is not None and minY is not None and maxX is not None and maxY is not None:
308
                rect = QRectF(minX - 5, minY - 5, maxX - minX + 10, maxY - minY + 10)
309
        
310
        return rect
311

    
312
    '''
313
        @brief      
314
        @author     humkyung
315
        @date       2018.07.26
316
    '''
317

    
318
    def onMouseMoved(self, event, scenePos):
319
        from SymbolSvgItem import SymbolSvgItem
320
        from EngineeringConnectorItem import QEngineeringConnectorItem
321

    
322
        # get connection point near by mouse point
323
        pt = (scenePos.x(), scenePos.y())
324
        item = self.scene().itemAt(scenePos, QTransform())
325
        if (item is not None) and (type(item) is SymbolSvgItem):
326
            connPt = item.getConnectionPointCloseTo(pt, 5)
327
            if connPt is not None: pt = connPt
328
        elif (item is not None) and (type(item) is QEngineeringConnectorItem):
329
            pt = item.center()
330
        # up to here
331
            
332
        del self._vertices[1:]
333
        dx = pt[0] - self._vertices[0][0]
334
        dy = pt[1] - self._vertices[0][1]
335
        self._vertices.append((self._vertices[0][0] + dx*0.5, self._vertices[0][1]))
336
        self._vertices.append((self._vertices[-1][0], self._vertices[-1][1] + dy))
337
        self._vertices.append(pt)
338

    
339
        self.build_path()
340
        self.update()
341
        
342
    def hoverEnterEvent(self, event):
343
        """ hilight item and it's children """
344
        self.highlight(True)
345

    
346
    def hoverLeaveEvent(self, event):
347
        """ unhighlight item """
348
        self.highlight(False)
349

    
350
    def highlight(self, flag):
351
        self.hover = flag 
352
        self.setZValue(QEngineeringAbstractItem.HOVER_ZVALUE) if flag else self.setZValue(
353
            QEngineeringStreamlineItem.ZVALUE)
354
        self.update()
355

    
356
    def paint(self, painter, option, widget):
357
        """ override paint method """
358
        if self.isSelected():
359
            hilightColor = QColor(255, 0, 0, 127)
360
            focuspen = QPen(Qt.DotLine)
361
            focuspen.setColor(hilightColor)
362
            focuspen.setWidthF(3)
363
            painter.setPen(focuspen)
364
            painter.drawPath(self.path())
365
        else:
366
            color = self.getColor()
367
            self.setColor(color)
368
            QGraphicsPathItem.paint(self, painter, option, widget)
369
        
370
    '''
371
        @brief      Return real item position
372
        @authro     Jeongwoo
373
        @date       2018.05.29
374
    '''
375

    
376
    def boundingRectOnScene(self):
377
        rect = self.boundingRect()
378
        sp = self.startPoint()
379
        rect.moveTo(sp[0], sp[1])
380
        return rect
381

    
382
    '''
383
        @brief      Set Color. Override QEngineeringAbstractItem's
384
        @author     Jeongwoo
385
        @date       2018.05.11
386
        @history    2018.05.11  Jeongwoo    Add self.setPen() Method
387
        @history    humkyung 2018.05.13 call setPen method to apply changed color
388
    '''
389

    
390
    def setColor(self, color):
391
        c = QColor()
392
        c.setNamedColor(color)
393
        _pen = self.pen()
394
        _pen.setColor(c)
395
        self.setPen(_pen)
396
        self.update()
397

    
398
    def on_symbol_pos_changed(self, symbol):
399
        """ rebuild stream line because symbol position is changed """
400
        if self.connectors[0].connectedItem is not None: self.connectors[0].setPos(
401
            self.connectors[0].connectedItem.center())
402
        if self.connectors[-1].connectedItem is not None: self.connectors[-1].setPos(
403
            self.connectors[-1].connectedItem.center())
404
        self.on_connector_pos_changed(None)
405

    
406
    def on_connector_pos_changed(self, connector):
407
        """ rebuild stream line """
408

    
409
        self._vertices = []
410

    
411
        self._vertices.append(self.connectors[0].center())
412
        dx = self.connectors[-1].center()[0] - self._vertices[0][0]
413
        dy = self.connectors[-1].center()[1] - self._vertices[0][1]
414
        self._vertices.append((self._vertices[0][0] + dx*0.5, self._vertices[0][1]))
415
        self._vertices.append((self._vertices[-1][0], self._vertices[-1][1] + dy))
416
        self._vertices.append(self.connectors[-1].center())
417
        self.build_path()
418
        self.update_arrow()
419
        self.update()
420

    
421
    def mouseDoubleClickEvent(self, event):
422
        from StreamDataDialog import QStreamDataDialog
423
        
424
        dialog = QStreamDataDialog()
425
        res = dialog.showDialog(self)
426
        if res == True:
427
            self.load_HMB()
428

    
429
    def load_HMB(self):        
430
        from App import App 
431

    
432
        App.mainWnd().load_HMB()
433
        
434
    def keyPressEvent(self, event):
435
        from App import App
436
        from AppDocData import AppDocData
437
        if not self.isSelected(): return
438

    
439
        if event.key() == Qt.Key_Delete: 
440
            self.transfer.onRemoved.emit(self)
441
        elif event.key() == Qt.Key_QuoteLeft:
442
            self.mouseDoubleClickEvent(event)    
443
           
444
    def toSql(self):
445
        """ convert valve data to sql query """
446
        import uuid
447
        from AppDocData import AppDocData
448

    
449
        res = []
450
        appDocData = AppDocData.instance()
451

    
452
        symbolInfo = appDocData.getSymbolByQuery('name', 'Stream Line')
453
        dbUid = symbolInfo.uid
454
        uid = self.uid
455

    
456
        cols = ['UID', 'Symbols_UID', 'Name']
457
        values = ['?','?', '?']
458
        param = [str(uid), str(dbUid), str(self.stream_no)]
459
        sql = 'insert or replace into Components({}) values({})'.format(','.join(cols), ','.join(values))
460
        res.append((sql, tuple(param)))
461

    
462
        # save vertices to database
463
        index = 1
464
        for vertex in self._vertices:                                    
465
            if index == 1 and self.connectors[0].connectedItem is not None:
466
                cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y', 'ConnectedItem_UID']
467
                values = ['?', '?', '?', '?', '?', '?']
468
                param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1],
469
                         str(self.connectors[0].connectedItem.uid)]
470
            elif index == 4 and self.connectors[1].connectedItem is not None:
471
                cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y', 'ConnectedItem_UID']
472
                values = ['?', '?', '?', '?', '?', '?']
473
                param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1],
474
                         str(self.connectors[1].connectedItem.uid)]
475
            else:
476
                cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y']
477
                values = ['?', '?', '?', '?', '?']
478
                param = [str(uuid.uuid4()), str(uid), index, vertex[0], vertex[1]]
479
        
480
            sql = 'insert or replace into Points({}) values({})'.format(','.join(cols), ','.join(values))
481

    
482
            res.append((sql, tuple(param)))
483
            index += 1
484
        # up to here
485

    
486
        return res
487

    
488
    @staticmethod 
489
    def fromDatabase(componentInfos):
490
        from EngineeringConnectorItem import QEngineeringConnectorItem
491
        from AppDocData import AppDocData
492

    
493
        item = None
494
        try:
495
            app_doc_data = AppDocData.instance()
496

    
497
            uid = componentInfos[0][0]              #uid@Components
498

    
499
            item = QEngineeringStreamlineItem(uid)
500
            hmb_data = app_doc_data.activeDrawing.hmbTable.get_hmb_data(uid)
501
            item.stream_no = int(hmb_data.stream_no) if hmb_data else 1 # stream no
502

    
503
            pointsUids = []
504
            for componentInfo in componentInfos:     
505
                pointsUid = componentInfo[11]           # uid@Points           
506
                x = componentInfo[13]                   # X@Points
507
                y = componentInfo[14]                   # Y@Points
508

    
509
                pointsUids.append(pointsUid)
510
                item._vertices.append((x, y))
511

    
512
            connectorItems = [componentInfos[0][15], componentInfos[-1][15]]
513

    
514
            item.setVisible(False)
515
            item.build_path()
516
            item.build_connectors(connectorItems, pointsUids)
517
  
518
            item.update()
519

    
520
        except Exception as ex:
521
            from App import App 
522
            from AppDocData import MessageType
523

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

    
528
        return item
529

    
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

    
537

    
538
class Transfer(QObject):
539
    onRemoved = pyqtSignal(QGraphicsItem)
540

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