프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / Shapes / EngineeringTextItem.py @ dcccf4dd

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

1
# coding: utf-8
2
import os.path
3
import copy
4
import sys
5
try:
6
    from PyQt5.QtCore import Qt, QPointF, QRectF, pyqtSignal, QObject, QT_VERSION_STR, QRect
7
    from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QBrush, QPen, QTransform, QFont, QColor, QFontMetricsF
8
    from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QGraphicsItem, QAbstractGraphicsShapeItem, QGraphicsTextItem
9
except ImportError:
10
    try:
11
        from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QRect, QObject, QT_VERSION_STR
12
        from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QFont, QColor, QFontMetricsF
13
    except ImportError:
14
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
15

    
16
from EngineeringPolylineItem import QEngineeringPolylineItem
17
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
18
from AppDocData import *
19
from EngineeringAbstractItem import QEngineeringAbstractItem
20
from TextInfo import TextInfo
21

    
22
class QEngineeringTextItem(QGraphicsTextItem, QEngineeringAbstractItem):
23
    HIGHLIGHT = '#BC4438'
24
    ZVALUE = 100
25

    
26
    '''
27
        @history    2018.05.17  Jeongwoo    Add self._owner variable
28
    '''
29
    def __init__(self, uid=None, parent=None):
30
        import uuid
31

    
32
        QGraphicsTextItem.__init__(self, parent)
33
        QEngineeringAbstractItem.__init__(self)
34

    
35
        self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid, version=4)
36
        self.type = 'TEXT'
37
        self.loc = None
38
        self.size = None
39
        self.angle = 0  # angle in radian
40
        self.conns = []
41
        self._owner = None
42
        self.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable)
43
        self.setAcceptHoverEvents(True)
44
        self.setAcceptTouchEvents(True)
45

    
46
        self.setColor(self._color)
47
        self._savedColor = None        
48

    
49
        self.delimiter = '"'
50
        self.lineNoDelimiter = '!-!'
51

    
52
        self.attribute = ''
53

    
54
        self.transfer = Transfer()
55
        self.setZValue(QEngineeringTextItem.ZVALUE)
56

    
57
    def __str__(self):
58
        """ return string represent uuid """
59
        return str(self.uid)
60

    
61
    '''
62
        @brief      Get owner
63
        @author     Jeongwoo
64
        @date       2018.05.17
65
    '''
66
    @property
67
    def owner(self):
68
        import uuid
69

    
70
        # find owner with uid
71
        if self._owner is not None and type(self._owner) is uuid.UUID:
72
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._owner)]
73
            if matches: self._owner = matches[0]
74
            return matches[0] if matches else None
75
        # up to here
76

    
77
        if type(self._owner) is not uuid.UUID and type(self._owner) is not str:
78
            return self._owner
79
        else:
80
            self._owner = None
81
            return None
82

    
83
    '''
84
        @brief      Set owner
85
        @author     Jeongwoo
86
        @date       2018.05.17
87
        @history    2018.05.17  Jeongwoo    Add Calling setColor if self._owner is None or not
88
    '''
89
    @owner.setter
90
    def owner(self, value):
91
        self._owner = value
92

    
93
        if self._owner is None:
94
            self._color = self.DEFAULT_COLOR
95
        self.setColor(self._color)
96

    
97
    '''
98
        @brief  return text string
99
        @author humkyung
100
        @date   2018.04.16
101
    '''
102
    def text(self):
103
        return self.toPlainText()
104
    
105
    '''
106
        @brief  return center position of text
107
        @author humkyung
108
        @date   2018.04.16
109
    '''
110
    def center(self):
111
        return self.sceneBoundingRect().center()
112

    
113
    def boundingRect(self):
114
        if self.angle == 1.57 or self.angle == 4.71:
115
            return QRectF(0, 0, self.size[1], self.size[0])
116
        else:
117
            return QRectF(0, 0, self.size[0], self.size[1])
118

    
119
    '''
120
        @brief      hover event
121
        @authro     humkyung
122
        @date       
123
    '''
124
    def hoverEnterEvent(self, event):
125
        self.highlight(True)
126

    
127
    def hoverLeaveEvent(self, event):
128
        self.highlight(False)
129

    
130
    '''
131
        @brief      set highlight
132
        @author     kyouho
133
        @date       2018.08.27
134
    '''
135
    def highlight(self, flag):
136
        self.hover = flag
137
        if flag:
138
            if self._savedColor is None:
139
                self._savedColor = self.getColor()
140
            self.setColor(QEngineeringTextItem.HIGHLIGHT)
141
        elif hasattr(self, '_savedColor'):
142
            self.setColor(self._savedColor)
143

    
144
        self.update()
145

    
146
    def hoverMoveEvent(self, event):
147
        pass
148

    
149
    '''
150
        @brief      remove item when user press delete key
151
        @author     humkyung
152
        @date       2018.04.23
153
        @history    2018.05.25  Jeongwoo    Seperate delete item method
154
                    humkyung 2018.08.18 rotate text when user press 'R'
155
    '''
156
    def keyPressEvent(self, event): 
157
        if self.isSelected() and event.key() == Qt.Key_Delete:
158
            self.deleteTextItemFromScene()
159
        elif event.key() == Qt.Key_R:
160
            #degree 0
161
            if 0 == self.angle:
162
                self.angle = 1.57
163
            #degree 90
164
            elif (1.57 == self.angle):
165
                self.angle = 3.14
166
            #degree 180
167
            elif 3.14 == self.angle:
168
                self.angle = 4.71
169
            #degree 270
170
            elif 4.71 == self.angle :
171
                self.angle = 0
172

    
173
            width = self.size[0]
174
            height = self.size[1]
175
            self.size = (height, width)
176

    
177
            self.rotate()
178

    
179
        QGraphicsTextItem.keyPressEvent(self, event)
180

    
181
    '''
182
        @brief  draw rect when item is selected
183
        @author humkyung
184
        @date   2018.07.08
185
    '''
186
    def drawFocusRect(self, painter):
187
        self.focuspen = QPen(Qt.DotLine)
188
        self.focuspen.setColor(Qt.black)
189
        self.focuspen.setWidthF(1.5)
190
        hilightColor = QColor(255, 0, 0, 127)
191
        painter.setBrush(QBrush(hilightColor))
192
        painter.setPen(self.focuspen)
193
        painter.drawRect(self.boundingRect())
194

    
195
    '''
196
        @brief  override paint(draw connection points)
197
        @author humkyung
198
        @date   2018.07.08
199
    '''
200
    def paint(self, painter, options=None, widget=None):
201
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
202
        self.setColor(self.getColor())
203

    
204
        if self.angle == 1.57 or self.angle == 4.71:
205
            rect = QRectF(0, 0, self.size[1], self.size[0])
206
        else:
207
            rect = QRectF(0, 0, self.size[0], self.size[1])
208

    
209
        painter.setFont(self.font())
210
        color = self.defaultTextColor()
211
        #color = QColor('#151485')
212
        painter.setPen(QPen(color))
213
        painter.drawText(rect, Qt.AlignCenter, self.text())
214

    
215
        if self.isSelected():
216
            self.drawFocusRect(painter)
217

    
218
        if type(self) is QEngineeringLineNoTextItem and self.isSelected():
219
            for run in self.runs:
220
                for item in run.items:
221
                    item.setSelected(True)
222
        #elif type(self) is QEngineeringLineNoTextItem:
223
        #    for run in self.runs:
224
        #        for item in run.items:
225
        #            item.setSelected(False)
226

    
227
    '''
228
        @brief      Delete text item
229
        @author     Jeongwoo
230
        @date       2018.05.25
231
    '''
232
    def deleteTextItemFromScene(self):
233
        #self.transfer.onRemoved.emit(self)
234
        self.scene().removeItem(self)
235

    
236
    '''
237
        @brief      Return real item position
238
        @author     Jeongwoo
239
        @date       2018.05.25
240
    '''
241
    def boundingRectOnScene(self):
242
        rect = self.boundingRect()
243
        rect.moveTo(self.loc[0], self.loc[1])
244
        return rect
245

    
246
    '''
247
        @brief      Double click event, Show QOcrResultDialog
248
        @author     Jeongwoo
249
        @date       18.04.23
250
        @history    18.06.20    Jeongwoo    Resize QRect added 1
251
    '''
252
    def mouseDoubleClickEvent(self, event, isDataList=False):
253
        from TextItemFactory import TextItemFactory
254
        if event.buttons() == Qt.LeftButton:
255
            from OcrResultDialog import QOcrResultDialog
256
            dialog = QOcrResultDialog(None, AppDocData.instance().getCurrentPidSource().getQImageOnRect(
257
                QRect(self.loc[0] - 3, self.loc[1] - 3, self.size[0] + 6, self.size[1] + 6)), 
258
                QRect(self.loc[0], self.loc[1], self.size[0], self.size[1]), True, self.text())
259
            (isAccept, textInfoList) = dialog.showDialog()
260
  
261
            if isAccept:
262
                scene = self.scene()
263

    
264
                textInfo = textInfoList[0]
265
                x = textInfo.getX()
266
                y = textInfo.getY()
267
                angle = textInfo.getAngle()
268
                text = textInfo.getText()
269
                width = textInfo.getW()
270
                height = textInfo.getH()
271
                item = TextItemFactory.instance().createTextItem(textInfo)
272
                if item is not None:
273
                    item.loc = (x, y)
274
                    item.size = (width, height)
275
                    item.angle = angle
276
                    item.area = self.area
277
                    item.addTextItemToScene(scene)
278
                    item.transfer.onRemoved.connect(scene.parent().parent().parent().itemRemoved)
279

    
280
                    self.transfer.onRemoved.emit(self)
281
                    if isDataList:
282
                        return item
283
                else:
284
                    message = 'error occured({}) in {}:{}'.format('텍스트 생성에 실패했습니다.', sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
285
                    self.addMessage.emit(MessageType.Normal, message)
286

    
287
    '''
288
        @brief      rotate text
289
        @author     humkyung
290
        @date       2018.08.18
291
    '''
292
    def rotate(self):
293
        sx = 1
294
        sy = 1
295
        width = self.size[0]
296
        height = self.size[1]
297
        x = self.loc[0]
298
        y = self.loc[1]
299

    
300
        transform = QTransform()
301
        if (1.57 == self.angle) or (4.71 == self.angle):
302
            rect = self.boundingRect()
303
            sx = width/rect.height()
304
            sy = height/rect.width()
305
                                        
306
            transform.translate(x, y)
307
            transform.translate(width*0.5, height*0.5)
308
            transform.scale(1, sy)
309
            transform.rotateRadians(-self.angle)
310
            transform.translate(-rect.width()*0.5, -rect.height()*0.5)
311
        elif 3.14 == self.angle:
312
            rect = self.boundingRect()
313
            sx = width/rect.width()
314
            sy = height/rect.height()
315

    
316
            transform.translate(x, y - round((rect.height()-height)*0.5))
317
            transform.scale(sx, 1)
318
            transform.rotateRadians(-self.angle)
319
            transform.translate(-width*0.5, -height*0.5)
320
        else:
321
            rect = self.boundingRect()
322
            sx = width/rect.width()
323
            sy = height/rect.height()
324

    
325
            #if '\n' not in text:
326
            transform.translate(x, y - round((rect.height()-height)*0.5))
327
            transform.scale(sx, 1)
328

    
329
        self.setTransform(transform)
330
        self.update()
331

    
332
    '''
333
        @brief      Put text on scene
334
        @author     Jeongwoo
335
        @date       18.04.23
336
        @history    humkyung 2018.06.30 apply font configuration
337
    '''
338
    def addTextItemToScene(self, scene):
339
        try:
340
            docData = AppDocData.instance()
341
            configs = docData.getConfigs('Text Style', 'Font Name')
342
            fontName = configs[0].value if configs else 'Arial'
343
            configs = docData.getConfigs('Text Style', 'Font Size')
344
            fontSize = int(configs[0].value) if configs else -1
345

    
346
            sx = 1
347
            sy = 1
348
            width = self.size[0]
349
            height = self.size[1]
350
            x = self.loc[0]
351
            y = self.loc[1]
352
            rect = None
353
            transform = QTransform()
354
            if (1.57 == self.angle) or (4.71 == self.angle):
355
                font = QFont(fontName, width if fontSize == -1 else fontSize)
356
                
357
                x_factor = width / QFontMetricsF(font).height()
358
                y_factor = height / QFontMetricsF(font).width(self.text())
359
                factor = min(x_factor, y_factor)
360
                font.setPointSizeF(font.pointSizeF()*factor)
361

    
362
                self.setFont(font)
363
                rect = self.boundingRect()
364
                sx = width/rect.height()
365
                sy = height/rect.width()
366
                                            
367
                transform.translate(x, y)
368
                transform.translate(width*0.5, height*0.5)
369
                transform.scale(1, sy)
370
                transform.rotateRadians(-self.angle)
371
                transform.translate(-rect.width()*0.5, -rect.height()*0.5)
372
            elif 3.14 == self.angle:
373
                font = QFont(fontName, height if fontSize == -1 else fontSize)
374

    
375
                x_factor = width / QFontMetricsF(font).width(self.text())
376
                y_factor = height / QFontMetricsF(font).height()
377
                factor = min(x_factor, y_factor)
378
                font.setPointSizeF(font.pointSizeF()*factor)
379

    
380
                self.setFont(font)
381
                rect = self.boundingRect()
382
                sx = width/rect.width()
383
                sy = height/rect.height()
384

    
385
                transform.translate(x, y - round((rect.height()-height)*0.5))
386
                transform.scale(sx, 1)
387
                transform.rotateRadians(-self.angle)
388
                transform.translate(-width*0.5, -height*0.5)
389
            else:
390
                font = QFont(fontName, height if fontSize == -1 else fontSize)
391

    
392
                x_factor = width / QFontMetricsF(font).width(self.text())
393
                y_factor = height / QFontMetricsF(font).height()
394
                factor = min(x_factor, y_factor)
395
                font.setPointSizeF(font.pointSizeF()*factor)
396

    
397
                self.setFont(font)
398
                rect = self.boundingRect()
399
                sx = width/rect.width()
400
                sy = height/rect.height()
401

    
402
                #if '\n' not in text:
403
                transform.translate(x, y)
404
                transform.scale(sx, 1)
405

    
406
            self.setTransform(transform)
407
                    
408
            scene.addItem(self)
409
        except Exception as ex:
410
            from App import App
411
            from AppDocData import MessageType
412

    
413
            message = 'error occured({}-{}) in {}:{}'.format(ex, self.text(), sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
414
            App.mainWnd().addMessage.emit(MessageType.Error, message)
415

    
416
    '''
417
        @brief  get connected items
418
        @author humkyung
419
        @date   2018.04.23
420
        @history    2018.11.22      euisung     fix note road
421
    '''
422
    def getConnectedItems(self):
423
        visited = []
424

    
425
        try:
426
            if 1 == len(self.conns):
427
                # iterate connected items
428
                pool = []
429
                visited = []
430
                pool.append(self.conns[0])
431
                while len(pool) > 0:
432
                    it = pool.pop()
433
                    visited.append(it)
434
                    for conn in it.conns:
435
                        if (conn is not None) and (conn not in visited): pool.append(conn)
436
                # up to here
437
        except Exception as ex:
438
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
439

    
440
        return visited
441
    
442
    '''
443
        @brief      parse xml code
444
        @author     humkyung
445
        @date       2018.09.15
446
    '''
447
    @staticmethod 
448
    def fromXml(node):
449
        import uuid
450
        from TextItemFactory import TextItemFactory
451
        from AppDocData import AppDocData
452
        from EngineeringNoteItem import QEngineeringNoteItem
453
        from QEngineeringSizeTextItem import QEngineeringSizeTextItem
454
        from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
455

    
456
        item = None
457

    
458
        try:
459
            location = node.find('LOCATION').text if node.find('LOCATION') is not None else '0,0'
460
            x = float(location.split(',')[0])
461
            y = float(location.split(',')[1])
462
            width = float(node.find('WIDTH').text) if node.find('WIDTH') is not None else 0
463
            height = float(node.find('HEIGHT').text) if node.find('HEIGHT') is not None else 0
464
            angle = float(node.find('ANGLE').text) if node.find('ANGLE') is not None else 0
465
            value = node.find('VALUE').text
466
            #attributeValue = node.find('ATTRIBUTEVALUE')
467
            name = node.find('NAME').text
468
            textInfo = TextInfo(value, x, y, width, height, angle)
469
            
470
            item = TextItemFactory.instance().createTextItem(textInfo)
471
            if item is not None:
472
                    item.loc = (x, y)
473
                    item.size = (width, height)
474
                    item.angle = angle
475
            '''
476
            if name == 'NOTE':
477
                item = TextItemFactory.instance().createTextItem(textInfo)
478
                if item is not None:
479
                    item.loc = (x, y)
480
                    item.size = (width, height)
481
                    item.angle = angle
482
                    item.setPlainText(value)
483
                    item.setToolTip('{} = {}'.format(item.type, value))
484
            elif name == 'SIZE':
485
                item = QEngineeringSizeTextItem()
486
                if item is not None:
487
                    item.loc = (x, y)
488
                    item.size = (width, height)
489
                    item.angle = angle
490
                    item.setPlainText(value)
491
                    item.setToolTip('{} = {}'.format(item.type, value))
492
            elif name == 'VALVE OPER CODE':
493
                item = QEngineeringValveOperCodeTextItem()
494
                if item is not None:
495
                    item.loc = (x, y)
496
                    item.size = (width, height)
497
                    item.angle = angle
498
                    item.setPlainText(value)
499
                    item.setToolTip('{} = {}'.format(item.type, value))
500
            else:
501
                item = TextItemFactory.instance().createTextItem(textInfo)
502
                if item is not None:
503
                    item.loc = (x, y)
504
                    item.size = (width, height)
505
                    item.angle = angle
506
            '''
507

    
508
            # set uid and owner of item
509
            if item is not None:
510
                item.uid = uuid.UUID(node.find('UID').text)
511
                item.setVisible(False)
512

    
513
                if node.find('OWNER') is not None and node.find('OWNER').text != 'None':
514
                    item._owner = uuid.UUID(node.find('OWNER').text, version=4)
515

    
516
            ## assign area
517
            if item is not None:
518
                if node.find('AREA') is None:
519
                    appDocData = AppDocData.instance()
520
                    for area in appDocData.getAreaList():
521
                        if area.contains([x, y]):
522
                            item.area = area.name
523
                            break
524
                else:
525
                    item.area = node.find('AREA').text  
526
            ## up to here
527
        except Exception as ex:
528
            from App import App
529
            from AppDocData import MessageType
530

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

    
534
        return item
535

    
536
    '''
537
        @brief      generate xml code
538
        @author     humkyung
539
        @date       2018.04.23
540
        @history    humkyung 2018.04.27 move to QEngineeringLineNoTextItem
541
                    humkyung 2018.05.02 add name as parameter
542
                    Jeongwoo 2018.05.30 Change variable [owner] is nullable and Add/Modify attributes
543
    '''
544
    def toXml(self, owner = None):
545
        from xml.etree.ElementTree import Element, SubElement, dump, ElementTree
546
        from EngineeringLineItem import QEngineeringLineItem
547
        from SymbolSvgItem import SymbolSvgItem
548

    
549
        try:
550
            node = Element('ATTRIBUTE')
551
            uidNode = Element('UID')
552
            uidNode.text = str(self.uid)
553
            node.append(uidNode)
554

    
555
            # write owner's uid to xml
556
            ownerNode = Element('OWNER')
557
            if self.owner is not None:
558
                ownerNode.text = str(self.owner)
559
            else:
560
                ownerNode.text = 'None'
561
            node.append(ownerNode)
562
            # up to here
563

    
564
            attributeValueNode = Element('ATTRIBUTEVALUE')
565
            attributeValueNode.text = self.attribute
566
            node.append(attributeValueNode)
567

    
568
            nameNode = Element('NAME')
569
            nameNode.text = self.type
570
            node.append(nameNode)
571
            
572
            locNode = Element('LOCATION')
573
            locNode.text = '{},{}'.format(self.loc[0], self.loc[1])
574
            node.append(locNode)
575

    
576
            valueNode = Element('VALUE')
577
            valueNode.text = self.text()
578
            node.append(valueNode)
579

    
580
            angleNode = Element('ANGLE')
581
            angleNode.text = str(self.angle)
582
            node.append(angleNode)
583

    
584
            widthNode = Element('WIDTH')
585
            widthNode.text = str(self.size[0])
586
            node.append(widthNode)
587

    
588
            heightNode = Element('HEIGHT')
589
            heightNode.text = str(self.size[1])
590
            node.append(heightNode)
591

    
592
            areaNode = Element('AREA')
593
            areaNode.text = self.area
594
            node.append(areaNode)
595

    
596
        except Exception as ex:
597
            from App import App
598
            from AppDocData import MessageType
599

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

    
603
            return None
604

    
605
        return node 
606
    
607
    '''
608
        @brief      Set Color. Override QEngineeringAbstractItem's
609
        @author     Jeongwoo
610
        @date       2018.05.11
611
        @history    humkyung 2018.05.13 update after change color
612
    '''
613
    def setColor(self, color):
614
        if QColor.isValidColor(color):
615
            c = QColor()
616
            c.setNamedColor(color)
617
            self.setDefaultTextColor(c)
618
            self.update()
619

    
620
    def toSql(self):
621
        """
622
        convert instrument data to sql query for title block now on
623
        """
624
        from AppDocData import AppDocData
625

    
626
        appDocData = AppDocData.instance()
627
        titleBlockProps = appDocData.getTitleBlockProperties()
628
        for titleBlockProp in titleBlockProps:
629
            if self.area == titleBlockProp[0]:
630
                cols = ['UID', 'Drawing_UID', 'TitleBlockProperties_UID', 'VALUE']
631
                values = ['?','?','?','?']
632
                param = [str(self.uid), appDocData.activeDrawing.name, self.area, self.text()]
633

    
634
                sql = 'insert or replace into TitleBlockValues values({})'.format(','.join(values))
635
                return (sql, tuple(param))
636

    
637
        return None
638

    
639

    
640
'''
641
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
642
    @author     Jeongwoo
643
    @date       2018.06.18
644
'''
645
class Transfer(QObject):
646
    onRemoved = pyqtSignal(QGraphicsItem)
647

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