프로젝트

일반

사용자정보

통계
| 개정판:

hytos / HYTOS / HYTOS / Shapes / EngineeringConnectorItem.py @ ca43fed5

이력 | 보기 | 이력해설 | 다운로드 (18 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.elevation = None
26

    
27
"""
28
    A {ConnectorItem} is the graphical representation of a {Symbol.Connectors}.
29
"""
30
class QEngineeringConnectorItem(QGraphicsEllipseItem):
31
    """ This is engineering connector item class """
32
    SMALL_SIZE = 10
33
    BIG_SIZE = 16
34
    HIGHLIGHT = '#BC4438'
35

    
36
    AXIS_MODE = 0
37
    FREE_MODE = 1
38

    
39
    '''
40
        @brief  
41
        @author 
42
        @date   
43
        @history        humkyung 2018.07.27 add member for connectedItem
44
                        humkyung 2018.09.03 remove code to add connector item
45
    '''
46
    def __init__(self, uid=None, parent=None, index=-1):
47
        import uuid
48
        from SymbolSvgItem import SymbolSvgItem
49

    
50
        QGraphicsEllipseItem.__init__(self, parent)
51

    
52
        self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid, version=4)
53
        self.parent = parent
54
        self.buildItem()
55
        self._direction = 'AUTO'
56
        self._symbol_idx = '0'
57
        self._spec_break = None
58
        self._connectedItem = None
59
        self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT      # default value is connected at pt
60
        self.sceneConnectPoint = None
61
        self.connectPoint = None
62
        self._hoverItem = None
63
        self._nozzle_data = None
64

    
65
        self.setAcceptHoverEvents(True)
66
        self.transfer = Transfer()
67

    
68
        self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
69

    
70
        if type(parent) is SymbolSvgItem:
71
            """ setup label for connector index """
72
            self._font = QFont('Arial', QEngineeringConnectorItem.SMALL_SIZE)
73
            self._font.setPointSizeF(5)
74
            self._conn_index = index
75

    
76
    @staticmethod
77
    def find_connector(uid):
78
        """ find a connector which has given uid """
79
        from App import App 
80
        import uuid
81

    
82
        res = None
83
        if type(uid) is uuid.UUID or type(uid) is str:
84
            scene = App.mainWnd().graphicsView.scene
85
            matches = [x for x in scene.items() if hasattr(x, 'uid') and str(x.uid) == str(uid)]
86
            res = matches[0] if matches else None
87

    
88
        return res
89

    
90
    @property
91
    def nozzle_data(self):
92
        """ getter of nozzle data """
93
        return self._nozzle_data
94

    
95
    @nozzle_data.setter
96
    def nozzle_data(self, value):
97
        """ setter of nozzle data """
98
        self._nozzle_data = value
99

    
100
    @property
101
    def connectedItem(self):
102
        """ getter of connectedItem """
103
        import uuid
104

    
105
        if self.scene() and (type(self._connectedItem) is uuid.UUID or type(self._connectedItem) is str):
106
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._connectedItem)]
107
            if matches:
108
                self._connectedItem = matches[0]
109
            else:
110
                self._connectedItem = None
111

    
112
        return self._connectedItem
113
        
114
    @connectedItem.setter
115
    def connectedItem(self, value):
116
        """ setter of connectedItem """
117
        self._connectedItem = value
118

    
119
    '''
120
        @brief      getter of direction
121
        @author     humkyung
122
        @date       2018.09.01
123
    '''
124
    @property
125
    def direction(self):
126
        return self._direction
127

    
128
    '''
129
        @brief      setter of direction
130
        @author     humkyung
131
        @date       2018.09.01
132
    '''
133
    @direction.setter
134
    def direction(self, value):
135
        self._direction = value
136
    
137
    @property
138
    def symbol_idx(self):
139
        """
140
        """
141
        return self._symbol_idx
142

    
143
    @symbol_idx.setter
144
    def symbol_idx(self, value):
145
        """
146
        setter of symbol_idx
147
        """
148
        self._symbol_idx = value
149

    
150
    @property
151
    def spec_break(self):
152
        """
153
        getter of spec break
154
        """
155
        return self._spec_break
156

    
157
    @spec_break.setter
158
    def spec_idx(self, value):
159
        """
160
        setter of spec_break
161
        """
162
        self._spec_break = value
163

    
164
    '''
165
        @brief      return direction vector
166
        @author     humkyung
167
        @date       2018.09.01
168
    '''
169
    def dir(self):
170
        if self._direction == 'LEFT':
171
            return (-1, 0)
172
        elif self._direction == 'RIGHT':
173
            return (1, 0)
174
        elif self._direction == 'UP':
175
            return (0, -1)
176
        elif self._direction == 'DOWN':
177
            return (0, 1)
178

    
179
        return None
180

    
181
    def __repr__(self):
182
        return '{},{},{},{}'.format(self._direction, self.sceneConnectPoint[0], self.sceneConnectPoint[1] , self._symbol_idx)
183

    
184
    '''
185
        @brief  build connector item
186
        @author humkyung
187
        @date   2018.05.02
188
    '''
189
    def buildItem(self):
190
        color = Qt.blue
191
        self.setBrush(color)
192
        if self.parent is not None:
193
            self.setZValue(self.parent.zValue() + 1)
194

    
195
    '''
196
        @brief      return center of connector
197
        @author     humkyung
198
        @date       2018.07.23
199
    '''
200
    def center(self):
201
        pt = self.sceneBoundingRect().center()
202
        return (pt.x(), pt.y())
203
        
204
    '''
205
        @brief  set position of connector
206
        @author humkyung
207
        @date   2018.05.02
208
    '''
209
    def setPos(self, pos):
210
        self._loc = [pos[0], pos[1]] 
211
        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)
212
        self.update()
213

    
214
        if hasattr(self, '_label'):
215
            """ set label positon at center of connector """
216
            self._label.setPos(QPointF(self._loc[0] - self._label.boundingRect().width()*0.5, self._loc[1] - self._label.boundingRect().height()*0.5))
217
    
218
    def connect(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
219
        """ connect to given item where given position """
220

    
221
        if item:
222
            self.connectedItem = item
223
            self._connected_at = at
224
        else:
225
            self.connectedItem = None
226

    
227
        self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue)
228

    
229
    '''
230
        @brief  highlight connector
231
        @authro humkyung
232
        @date   2018.05.09
233
    '''
234
    def hoverEnterEvent(self, event):
235
        try:
236
            self.highlight(True)
237
        finally:
238
            QApplication.setOverrideCursor(Qt.CrossCursor)
239

    
240
    '''
241
        @brief  unhighlight connector
242
        @author humkyung
243
        @date   2018.05.09
244
    '''
245
    def hoverLeaveEvent(self, event):
246
        try:
247
            self.highlight(False)
248
        finally:
249
            QApplication.restoreOverrideCursor()
250

    
251
    def highlight(self, flag):
252
        if flag:
253
            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)
254
            c = QColor()
255
            c.setNamedColor(QEngineeringConnectorItem.HIGHLIGHT)
256
            self.setPen(Qt.red)
257
            self.setBrush(c)
258
            if hasattr(self, '_label'):
259
                font = self._label.font()
260
                font.setBold(True)
261
                self._label.setFont(font)
262
        else:
263
            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)
264
            self.setPen(Qt.black)
265
            self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue)
266
            if hasattr(self, '_label'):
267
                font = self._label.font()
268
                font.setBold(False)
269
                self._label.setFont(font)
270

    
271
        if self.parentItem(): self.setZValue(self.parentItem().zValue() + 1)
272
        if self.scene() and self.scene().sceneRect().contains(self.sceneBoundingRect()):
273
            self.update()
274
            if self.scene(): self.scene().update()
275

    
276
    '''
277
        @brief      
278
        @author     humkyung
279
        @date       2018.07.27
280
    '''
281
    def mousePressEvent(self, event):
282
        if self.parentItem() is not None:
283
            if event.buttons() == Qt.LeftButton:
284
                self._savedPos = self._loc
285
                self._savedConnectedItem = self.connectedItem
286
                
287
        QGraphicsEllipseItem.mousePressEvent(self, event)
288

    
289
    '''
290
        @brief      
291
        @author     humkyung
292
        @date       2018.07.27
293
        @history    humkyung 2018.08.23 unhighlight item if underitem exists
294
    '''
295
    def mouseReleaseEvent(self, event):
296
        import shapely
297
        from SymbolSvgItem import SymbolSvgItem
298
        from EngineeringArrowItem import QEngineeringArrowItem
299
        from EngineeringTextItem import QEngineeringTextItem
300

    
301
        try:            
302
            if self.parentItem() is not None and self._savedPos is not None:
303
                items = [item for item in self.scene().items(event.scenePos()) if item is not self and item is not self.parentItem() and \
304
                    (issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or type(item) is QEngineeringConnectorItem)]
305

    
306
                if items and type(items[0]) is QEngineeringConnectorItem:
307
                    self.setPos(items[0].center())
308
                    
309
                    """ disconnect alreay connected item """
310
                    if self.connectedItem is not None:
311
                        self.connectedItem.connect(None)
312

    
313
                    """ connect each other """
314
                    self.connect(items[0])
315
                    items[0].connect(self)
316
                else:
317
                    pt = [event.scenePos().x(), event.scenePos().y()]
318
                    
319
                    self.setPos(pt)
320
                    if self.connectedItem is not None:
321
                        self.connectedItem.connect(None)
322

    
323
                    self.connect(None)
324

    
325
                ## unhighlight underitem
326
                if hasattr(self, '_underItem') and self._underItem is not None:
327
                    self._underItem.highlight(False)
328
                ## up to here
329

    
330
                for symbol in [self.parentItem() for self in items if type(self.parentItem()) is SymbolSvgItem]:
331
                    symbol.transfer.on_pos_changed.connect(self.parent.on_symbol_pos_changed)
332

    
333
            if self.parentItem() is not None:
334
                self._savedPos = None 
335
                self._savedConnectedItem = None
336
                self.transfer.onPosChanged.emit(self)                
337
        finally:
338
            #self.ungrabKeyboard()
339
            pass
340

    
341
        QGraphicsEllipseItem.mouseReleaseEvent(self, event)
342

    
343
    '''
344
        @brief      move connector's position while mouse drag
345
        @author     humkyung
346
        @date       2018.07.27
347
        @history    humkyung 2018.08.23 hightlight underitem
348
    '''
349
    def mouseMoveEvent(self, event):
350
        import shapely
351
        from SymbolSvgItem import SymbolSvgItem
352
        from EngineeringArrowItem import QEngineeringArrowItem
353
        from EngineeringTextItem import QEngineeringTextItem
354

    
355
        if self.parentItem() is not None and self._savedPos is not None:
356
            if event.buttons() == Qt.LeftButton:
357
                items = [item for item in self.scene().items(event.scenePos()) if item is not self and item is not self.parent and \
358
                    (issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or type(item) is QEngineeringConnectorItem)]
359
                ## highlight underitem
360
                if len(items) > 0:
361
                    if not hasattr(self, '_underItem') or self._underItem is not items[0]:
362
                        if hasattr(self, '_underItem') and self._underItem is not None:
363
                            self._underItem.highlight(False)
364

    
365
                        self._underItem = items[0]
366
                        self._underItem.highlight(True)
367
                elif hasattr(self, '_underItem') and self._underItem is not None:
368
                    self._underItem.hoverLeaveEvent(event)
369
                    self._underItem = None
370
                ## up to here
371

    
372
                if items and type(items[0]) is QEngineeringConnectorItem:
373
                    self.setPos(items[0].center())
374
                else:
375
                    pt = [event.scenePos().x(), event.scenePos().y()]
376
                    
377
                    self.setPos(pt)
378

    
379
                self.update()
380

    
381
        QGraphicsEllipseItem.mouseMoveEvent(self, event)
382

    
383

    
384
    def hasConnectedItem(self):
385
        if self.connectedItem is not None:
386
            return True
387
        
388
        return False
389
    '''
390
        @brief      reject if user press Escape key
391
        @author     humkyung
392
        @date       2018.07.27
393
    '''
394
    def keyPressEvent(self, event):
395
        if event.key() == Qt.Key_Escape:
396
            if hasattr(self, '_savedPos') and self._savedPos is not None:
397
                self.setPos(self._savedPos)
398
                self.connectedItem = self._savedConnectedItem
399
                self._savedPos = None
400
                self.update()
401
        elif event.key() == Qt.Key_A:
402
            self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
403
        elif event.key() == Qt.Key_F:
404
            self._drawing_mode = QEngineeringConnectorItem.FREE_MODE
405

    
406
        QGraphicsEllipseItem.keyPressEvent(self, event)
407

    
408
    '''
409
        @brief  override paint(draw connection points)
410
        @author humkyung
411
        @date   2018.04.21
412
    '''
413
    def paint(self, painter, options=None, widget=None):
414
        from SymbolSvgItem import SymbolSvgItem
415

    
416
        QGraphicsEllipseItem.paint(self, painter, options, widget)
417
        if type(self.parentItem()) is SymbolSvgItem:
418
            painter.setFont(self._font)
419
            painter.drawText(self.boundingRect(), str(self._conn_index), QTextOption(Qt.AlignCenter))
420

    
421
    '''
422
        @brief      check Overlap
423
        @author     kyouho
424
        @date       2018.08.30
425
    '''
426
    def isOverlapConnector(self, conn, toler = 10):
427
        from shapely.geometry import Point
428

    
429
        x1 = self.center()[0]
430
        y1 = self.center()[1]
431
        x2 = conn.center()[0]
432
        y2 = conn.center()[1]
433

    
434
        if Point(x1, y1).distance(Point(x2, y2)) < toler:
435
            return True
436
        else:
437
            return False
438

    
439
    def parse_xml(self, node):
440
        """ parse given node """
441
        import uuid
442

    
443
        try:
444
            if 'UID' in node.attrib: self.uid = uuid.UUID(node.attrib['UID'], version=4)
445

    
446
            if 'CONNECTED_AT' in node.attrib:
447
                self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT if node.attrib['CONNECTED_AT'] == '0' else QEngineeringAbstractItem.CONNECTED_AT_BODY
448
            else:
449
                self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT
450

    
451
            connectedItemStr = node.find('CONNECTEDITEM').text
452
            connectPointStr = node.find('CONNECTPOINT').text.split(',')
453
            sceneConnectPointStr = node.find('SCENECONNECTPOINT').text.split(',')
454

    
455
            self._connectedItem = uuid.UUID(connectedItemStr, version=4) if connectedItemStr != 'None' else None
456
            self.connectPoint = (float(connectPointStr[0]), float(connectPointStr[1]))
457
            self.sceneConnectPoint = (float(sceneConnectPointStr[0]), float(sceneConnectPointStr[1]))
458

    
459
            self.setBrush(Qt.yellow) if self._connectedItem else self.setBrush(Qt.blue)
460
        except Exception as ex:
461
            from App import App 
462
            from AppDocData import MessageType
463

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

    
467
    @staticmethod
468
    def fromXml(node):
469
        """ generate connector instance from xml node """
470

    
471
        attr = QEngineeringConnectorItem()
472

    
473
        return attr
474

    
475
    def toXml(self):
476
        """ generate xml code for connector """
477
        import uuid
478
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
479

    
480
        try:
481
            node = Element('CONNECTOR')
482
            node.attrib['UID'] = str(self.uid)
483
            node.attrib['CONNECTED_AT'] = str(self._connected_at)
484
            connectedItemNode = Element('CONNECTEDITEM')
485
            connectedItemNode.text = str(self.connectedItem.uid) if self.connectedItem and not type(self.connectedItem) is uuid.UUID else 'None'
486
            connectPointNode = Element('CONNECTPOINT')
487
            connectPointNode.text = str(self.connectPoint[0]) + ',' + str(self.connectPoint[1])
488
            sceneConnectPointNode = Element('SCENECONNECTPOINT')
489
            sceneConnectPointNode.text = str(self.sceneConnectPoint[0]) + ',' + str(self.sceneConnectPoint[1])
490

    
491
            node.append(connectedItemNode)
492
            node.append(connectPointNode)
493
            node.append(sceneConnectPointNode)
494
        except Exception as ex:
495
            from App import App 
496
            from AppDocData import MessageType
497

    
498
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
499
            App.mainWnd().addMessage.emit(MessageType.Error, message)
500
            return None
501

    
502
        return node
503

    
504
    def toSql(self, index):
505
        """ generate sql string to save connectors to database """
506
        cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y']
507
        values = ['?', '?', '?', '?', '?']
508
       
509
        param = [str(self.uid), str(self.parent.uid), index, self.center()[0], self.center()[1]]        
510
        sql = 'insert or replace into Points({}) values({})'.format(','.join(cols), ','.join(values))
511

    
512
        return (sql, tuple(param))
513

    
514
'''
515
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
516
    @author     Jeongwoo
517
    @date       2018.06.18
518
'''
519
class Transfer(QObject):
520
    onPosChanged = pyqtSignal(QGraphicsItem)
521
    onRemoved = pyqtSignal(QGraphicsItem)
522

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