프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / QtImageViewer.py @ c1d7640c

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

1
# coding: utf-8
2
import sys
3
import os.path
4

    
5
try:
6
    from PyQt5.QtCore import *
7
    from PyQt5.QtGui import *
8
    from PyQt5.QtWidgets import *
9
except ImportError:
10
    try:
11
        from PyQt4.QtCore import *
12
        from PyQt4.QtGui import *
13
    except ImportError:
14
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
15

    
16
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Commands')
17
import DefaultCommand
18

    
19
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes')
20
from EngineeringLineItem import QEngineeringLineItem
21
from EngineeringTextItem import QEngineeringTextItem
22
from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
23
from SymbolSvgItem import SymbolSvgItem
24

    
25
__author__ = "Marcel Goldschen-Ohm <marcel.goldschen@gmail.com>"
26
__version__ = '0.9.0'
27

    
28

    
29
class QtImageViewer(QGraphicsView):
30
    """ PyQt image viewer widget for a QPixmap in a QGraphicsView scene with mouse zooming and panning.
31
    Displays a QImage or QPixmap (QImage is internally converted to a QPixmap).
32
    To display any other image format, you must first convert it to a QImage or QPixmap.
33
    Some useful image format conversion utilities:
34
        qimage2ndarray: NumPy ndarray <==> QImage    (https://github.com/hmeine/qimage2ndarray)
35
        ImageQt: PIL Image <==> QImage  (https://github.com/python-pillow/Pillow/blob/master/PIL/ImageQt.py)
36
    Mouse interaction:
37
        Left mouse button drag: Pan image.
38
        Right mouse button drag: Zoom box.
39
        Right mouse button doubleclick: Zoom to show entire image.
40
    """
41

    
42
    # Mouse button signals emit image scene (x, y) coordinates.
43
    # !!! For image (row, column) matrix indexing, row = y and column = x.
44
    leftMouseButtonPressed = pyqtSignal(float, float)
45
    rightMouseButtonPressed = pyqtSignal(float, float)
46
    leftMouseButtonMoved = pyqtSignal(float, float)
47
    rightMouseButtonMoved = pyqtSignal(float, float)
48
    leftMouseButtonReleased = pyqtSignal(float, float)
49
    rightMouseButtonReleased = pyqtSignal(float, float)
50
    leftMouseButtonDoubleClicked = pyqtSignal(float, float)
51
    rightMouseButtonDoubleClicked = pyqtSignal(float, float)
52
    # itemRemoved = pyqtSignal(QGraphicsItem)
53
    startPointChanged = pyqtSignal(float, float)
54

    
55
    '''
56
        @history    2018.06.27  Jeongwoo    Change zoom rule (Qt.KeepAspectRatioByExpanding → Qt.KeepAspectRatio)
57
    '''
58

    
59
    def __init__(self, mainWindow=None):
60
        QGraphicsView.__init__(self)
61

    
62
        self.mainWindow = mainWindow
63
        # Image is displayed as a QPixmap in a QGraphicsScene attached to this QGraphicsView.
64
        self.command = None
65

    
66
        self.scaleFactor = 1.0
67
        self.numScheduledScalings = 0
68
        self.isOriginalPointSelected = False
69

    
70
        # Store a local handle to the scene's current image pixmap.
71
        self._pixmapHandle = None
72

    
73
        # Image aspect ratio mode.
74
        # !!! ONLY applies to full image. Aspect ratio is always ignored when zooming.
75
        #   Qt.IgnoreAspectRatio: Scale image to fit viewport.
76
        #   Qt.KeepAspectRatio: Scale image to fit inside viewport, preserving aspect ratio.
77
        #   Qt.KeepAspectRatioByExpanding: Scale image to fill the viewport, preserving aspect ratio.
78
        self.aspectRatioMode = Qt.KeepAspectRatio
79

    
80
        # Scroll bar behaviour.
81
        #   Qt.ScrollBarAlwaysOff: Never shows a scroll bar.
82
        #   Qt.ScrollBarAlwaysOn: Always shows a scroll bar.
83
        #   Qt.ScrollBarAsNeeded: Shows a scroll bar only when zoomed.
84
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
85
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
86
        # self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
87
        # self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
88

    
89
        # Stack of QRectF zoom boxes in scene coordinates.
90
        self.zoomStack = []
91

    
92
        self.setRenderHint(QPainter.Antialiasing)
93

    
94
        self.setAcceptDrops(True)  # enable drop
95

    
96
        # Flags for enabling/disabling mouse interaction.
97
        self.canZoom = True
98
        self.canPan = True
99
        self.setMouseTracking(True)
100
        self.command = None
101

    
102
        # set currentAttribute
103
        self.currentAttribute = ''
104

    
105
    '''
106
        @brief      Return Pixmap Handler
107
        @author     Jeongwoo
108
        @date       2018.06.11
109
    '''
110

    
111
    def getPixmapHandle(self):
112
        return self._pixmapHandle
113

    
114
    '''
115
        @brief      Use Default ImageViewer Command
116
        @author     Jeongwoo
117
        @date       18.04.10
118
        @history    .
119
    '''
120

    
121
    def useDefaultCommand(self):
122
        """ Use Default Command
123
        """
124
        self.command = DefaultCommand.DefaultCommand(self)
125

    
126
    def hasImage(self):
127
        """ Returns whether or not the scene contains an image pixmap.
128
        """
129
        return self._pixmapHandle is not None
130

    
131
    def clearImage(self):
132
        """ Removes the current image pixmap from the scene if it exists.
133
        """
134
        if self.hasImage():
135
            self.scene().removeItem(self._pixmapHandle)
136
            self._pixmapHandle = None
137

    
138
    def pixmap(self):
139
        """ Returns the scene's current image pixmap as a QPixmap, or else None if no image exists.
140
        :rtype: QPixmap | None
141
        """
142
        if self.hasImage():
143
            return self._pixmapHandle.pixmap()
144
        return None
145

    
146
    def image(self):
147
        """ Returns the scene's current image pixmap as a QImage, or else None if no image exists.
148
        :rtype: QImage | None
149
        """
150
        if self.hasImage():
151
            return self._pixmapHandle.pixmap().toImage()
152
        return None
153

    
154
    def setImage(self, image):
155
        """ Set the scene's current image pixmap to the input QImage or QPixmap.
156
        Raises a RuntimeError if the input image has type other than QImage or QPixmap.
157
        :type image: QImage | QPixmap
158
        """
159
        try:
160
            if type(image) is QPixmap:
161
                pixmap = image
162
            elif type(image) is QImage:
163
                pixmap = QPixmap.fromImage(image)
164
            else:
165
                raise RuntimeError("ImageViewer.setImage: Argument must be a QImage or QPixmap.")
166

    
167
            self.clearImage()
168
            self.scene().clear()
169

    
170
            if self.hasImage():
171
                self._pixmapHandle.setPixmap(pixmap)
172
            else:
173
                self._pixmapHandle = self.scene().addPixmap(pixmap)
174
                self._pixmapHandle.setFlags(QGraphicsItem.ItemClipsChildrenToShape)
175

    
176
            self.setSceneRect(QRectF(pixmap.rect()))  # Set scene size to image size.
177
            self.updateViewer()
178
        except Exception as ex:
179
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
180
                                                       sys.exc_info()[-1].tb_lineno))
181

    
182
    '''
183
        @brief  open a image file selected by user
184
        @author 
185
        @date
186
    '''
187

    
188
    def loadImageFromFile(self, drawing):
189
        import cv2
190
        import numpy as np
191
        from AppDocData import AppDocData
192
        """ Load an image from file.
193
        Without any arguments, loadImageFromFile() will popup a file dialog to choose the image file.
194
        With a fileName argument, loadImageFromFile(fileName) will attempt to load the specified image file directly.
195
        """
196

    
197
        file_path = None
198
        try:
199
            app_doc_data = AppDocData.instance()
200

    
201
            cvImg = None
202
            if drawing:
203
                file_path = drawing.file_path
204
                cvImg = drawing.image
205
            else:
206
                options = QFileDialog.Options()
207
                options |= QFileDialog.DontUseNativeDialog
208
                if QT_VERSION_STR[0] == '4':
209
                    file_path = QFileDialog.getOpenFileName(self, "Open image file",
210
                                                            app_doc_data.project.getDrawingFilePath(),
211
                                                            "Image files(*.png *.jpg)", options=options)
212
                elif QT_VERSION_STR[0] == '5':
213
                    file_path, _ = QFileDialog.getOpenFileName(self, "Open image file",
214
                                                               app_doc_data.project.getDrawingFilePath(),
215
                                                               "Image files(*.png *.jpg)", options=options)
216

    
217
                _bytes = None
218
                with open(file_path.encode('utf-8'), 'rb') as stream:
219
                    _bytes = stream.read()
220

    
221
                numpyArray = np.asarray(bytearray(_bytes), dtype=np.uint8)
222
                image = cv2.imdecode(numpyArray, cv2.IMREAD_UNCHANGED)
223
                cvImg = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
224
                cvImg = cv2.threshold(cvImg, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
225

    
226
                configs = app_doc_data.getConfigs('Filter', 'DilateSize')
227
                if 1 == len(configs) and int(configs[0].value) is not 0:
228
                    size = int(configs[0].value)
229
                    kernel = np.ones((size, size), np.uint8)
230
                    cvImg = cv2.erode(cvImg, kernel, iterations=1)
231

    
232
                configs = app_doc_data.getConfigs('Filter', 'FlatSize')
233
                if 1 == len(configs) and int(configs[0].value) is not 0:
234
                    size = int(configs[0].value)
235
                    kernel = np.ones((size, size), np.uint8)
236
                    cvImg = cv2.morphologyEx(cvImg, cv2.MORPH_CLOSE, kernel)
237
                    cvImg = cv2.morphologyEx(cvImg, cv2.MORPH_OPEN, kernel)
238

    
239
            bytesPerLine = cvImg.shape[1]
240
            image = QImage(cvImg.data, cvImg.shape[1], cvImg.shape[0], bytesPerLine, QImage.Format_Indexed8)
241
            self.setImage(image)
242
        except Exception as ex:
243
            from App import App
244
            from AppDocData import MessageType
245

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

    
250
        return file_path
251

    
252
    '''
253
        @history    2018.06.27  Jeongwoo    Change zoom rule (Qt.KeepAspectRatioByExpanding → Qt.KeepAspectRatio)
254
    '''
255

    
256
    def updateViewer(self, zoomNewRect=None):
257
        """Show current zoom (if showing entire image, apply current aspect ratio mode)."""
258
        if not self.hasImage():
259
            return
260

    
261
        if zoomNewRect is not None:
262
            self.fitInView(zoomNewRect, Qt.KeepAspectRatio)
263
        else:
264
            if self.zoomStack:
265
                if zoomNewRect is None:
266
                    self.fitInView(self.zoomStack[-1], Qt.KeepAspectRatio)  # Show zoomed rect (ignore aspect ratio).
267
            else:
268
                self.zoomStack = []  # Clear the zoom stack (in case we got here because of an invalid zoom).
269
                self.fitInView(self.sceneRect(),
270
                               self.aspectRatioMode)  # Show entire image (use current aspect ratio mode).
271

    
272
    def zoomImageInit(self):
273
        #from EngineeringConnectorItem import QEngineeringConnectorItem
274

    
275
        if self.hasImage():
276
            self.zoomStack = []
277
            self.updateViewer()
278
            self.setCursor(QCursor(Qt.ArrowCursor))
279

    
280
            #for conn in [item for item in self.scene().items() if type(item) is QEngineeringConnectorItem]:
281
            #    conn.setVisible(False)
282

    
283
    '''
284
        @brief      Zoom in & out image
285
        @author     Jeongwoo
286
        @date       -
287
        @history    18.04.11    Jeongwoo    add parameter 'adjust' (@ref ResultTreeWidget.itemClickEvent(self, item, columnNo))
288
    '''
289
    def zoomImage(self, isZoomIn, event, adjust=1):
290
        """Zoom in & out """
291

    
292
        HALF_SIZE = 300
293
        clickPos = event.pos()
294
        if isZoomIn:
295
            left_top = self.mapToScene(clickPos.x() - HALF_SIZE // adjust, clickPos.y() - HALF_SIZE // adjust)
296
            right_bottom = self.mapToScene(clickPos.x() + HALF_SIZE // adjust, clickPos.y() + HALF_SIZE // adjust)
297

    
298
            zoomArea = QRectF(left_top, right_bottom)
299
            self.scene().setSelectionArea(QPainterPath())  # Clear current selection area.
300
            if zoomArea.isValid():
301
                self.zoomStack.append(zoomArea)
302
                self.updateViewer(zoomArea)
303
        else:
304
            zoomNewRect = None
305
            clickPos = self.mapToScene(clickPos.x() // adjust, clickPos.y() // adjust)
306
            self.scene().clearSelection()
307
            if self.zoomStack:
308
                zoomNewRect = self.zoomStack.pop()
309
                zoomNewRect = QRectF(clickPos.x() - zoomNewRect.width() / 2, clickPos.y() - zoomNewRect.height() / 2, zoomNewRect.width(), zoomNewRect.height())
310
            self.updateViewer(zoomNewRect)
311

    
312
    '''
313
        @brief  mouse move event
314
    '''
315
    def mouseMoveEvent(self, event):
316
        try:
317
            scenePos = self.mapToScene(event.pos())
318
            if self.command is not None:
319
                self.command.execute(['mouseMoveEvent', event, scenePos])
320
                if self.command.name == "SelectAttribute":
321
                    QGraphicsView.mouseMoveEvent(self, event)
322
                if self.command.isTreated:
323
                    event.accept()
324
                    return
325
        except Exception as ex:
326
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
327
                                                       sys.exc_info()[-1].tb_lineno))
328

    
329
        if self.scene().guidesEnabled:
330
            self.scene().coords = self.mapToScene(event.pos())
331
            self.scene().invalidate()
332

    
333
        QGraphicsView.mouseMoveEvent(self, event)
334

    
335
    '''
336
        @brief      
337
        @author     
338
        @date       
339
        @history    block clear selection when right mouse button is clicked
340
    '''
341

    
342
    def mousePressEvent(self, event):
343
        try:
344
            if self.command is not None:
345
                scenePos = self.mapToScene(event.pos())
346
                self.command.execute(['mousePressEvent', event, scenePos])
347
                if self.command.isTreated:
348
                    event.accept()
349
                    return
350
        except Exception as ex:
351
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
352
                                                       sys.exc_info()[-1].tb_lineno))
353

    
354
        if event.button() != Qt.RightButton:
355
            QGraphicsView.mousePressEvent(self, event)
356

    
357
    '''
358
        @brief      
359
        @author     
360
        @date       
361
    '''
362

    
363
    def mouseReleaseEvent(self, event):
364
        try:
365
            if self.command is not None:
366
                scenePos = self.mapToScene(event.pos())
367
                instance = self.command.execute(['mouseReleaseEvent', event, scenePos])
368
                if instance is not None:
369
                    self.scene().addItem(instance)
370

    
371
                if self.command is not None and self.command.isTreated == True:
372
                    if self.command.name == 'Default' and self.command.isCopy:
373
                        return
374
                    self.command = DefaultCommand.DefaultCommand(self)
375
                    cursor = QCursor(Qt.ArrowCursor)
376
                    QApplication.instance().setOverrideCursor(cursor)
377
                    return
378
        except Exception as ex:
379
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
380
                                                       sys.exc_info()[-1].tb_lineno))
381

    
382
        QGraphicsView.mouseReleaseEvent(self, event)
383

    
384
    """
385
        @brief      Show entire image.
386
    """
387

    
388
    def mouseDoubleClickEvent(self, event):
389
        scenePos = self.mapToScene(event.pos())
390
        if self.command is not None:
391
            instance = self.command.execute(['mouseDoubleClickEvent', event, scenePos])
392
            if self.command.isTreated == True: return
393

    
394
        if event.button() == Qt.LeftButton:
395
            self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
396
        """
397
        elif event.button() == Qt.RightButton:
398
            if self.canZoom:
399
                self.zoomStack = []  # Clear zoom stack.
400
                self.updateViewer()
401
            self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
402
        """
403

    
404
        QGraphicsView.mouseDoubleClickEvent(self, event)
405

    
406
    '''
407
        @brief      key press event
408
        @author     Jeongwoo
409
        @date       2018.??.??
410
        @history    send escape key event to command
411
    '''
412

    
413
    def keyPressEvent(self, event):
414
        from TrainingEditorDialog import QTrainingEditorDialog
415
        from TrainingSymbolEditorDialog import QTrainingSymbolEditorDialog
416

    
417
        try:
418
            if event.key() == Qt.Key_Escape or event.key() == Qt.Key_Return:
419
                if self.command is not None:
420
                    self.command.execute(['keyPressEvent', event, []])
421
                    if self.command.isTreated:
422
                        self.command = DefaultCommand.DefaultCommand(self)
423
                        return
424
            else:
425
                if self.command is not None:
426
                    self.command.execute(['keyPressEvent', event, []])
427
                    if self.command.isTreated: return
428
            if type(self.mainWindow) is QTrainingEditorDialog or type(self.mainWindow) is QTrainingSymbolEditorDialog:
429
                self.mainWindow.keyPressEvent(event)
430

    
431
            QGraphicsView.keyPressEvent(self, event)
432
        except Exception as ex:
433
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
434
                                                       sys.exc_info()[-1].tb_lineno))
435

    
436
    '''
437
        @brief  key release event
438
        @author Jeongwoo
439
        @date   2018.??.??
440
    '''
441

    
442
    def keyReleaseEvent(self, event):
443
        if event.key() == Qt.Key_Delete:
444
            pass
445

    
446
        QGraphicsView.keyReleaseEvent(self, event)
447

    
448
    '''
449
        @brief      mouse wheel event
450
        @autor      humkyung
451
        @date       
452
    '''
453

    
454
    def wheelEvent(self, event):
455
        if event.modifiers() == Qt.ControlModifier:
456
            if self.canZoom and self.hasImage():
457
                numDegrees = event.angleDelta() / 8
458
                if numDegrees is not None:
459
                    if numDegrees.y() > 0:
460
                        self.zoomImage(True, event)
461
                    elif numDegrees.y() < 0:
462
                        self.zoomImage(False, event)
463
        else:
464
            super().wheelEvent(event)
465

    
466
    '''
467
        @brief      draw background
468
        @author     humkyung
469
        @date       2018.07.23
470
    '''
471

    
472
    """
473
    def drawBackground(self, painter, rect):
474
        QGraphicsView.drawBackground(self, painter, rect)
475
    """
476

    
477
    '''
478
        @history    2018.06.11  Jeongwoo    Change method to manage guideline items
479
                    humkyung 2018.08.28 remove guide lines before drawing
480
    '''
481
    GUIDELINE_ITEMS = []
482

    
483
    def showGuideline(self, pos, isShow):
484
        image = self.image()
485
        width = image.width()
486
        height = image.height()
487
        pen = QPen()
488
        pen.setColor(QColor(180, 180, 180))
489
        pen.setStyle(Qt.DashLine)
490
        pen.setWidthF(0.5)
491
        if isShow:
492
            items = self.scene().items()
493
            for item in self.GUIDELINE_ITEMS:
494
                if item in items:
495
                    self.scene().removeItem(item)
496
            self.GUIDELINE_ITEMS.clear()
497

    
498
            if pos is not None:
499
                verticalLine = self.scene().addLine(pos.x(), 0, pos.x(), height, pen)
500
                horizontalLine = self.scene().addLine(0, pos.y(), width, pos.y(), pen)
501
            else:
502
                verticalLine = self.scene().addLine(round(width * 0.5), 0, round(width * 0.5), height, pen)
503
                horizontalLine = self.scene().addLine(0, round(height * 0.5), width, round(height * 0.5), pen)
504

    
505
            self.GUIDELINE_ITEMS.append(verticalLine)
506
            self.GUIDELINE_ITEMS.append(horizontalLine)
507
        else:
508
            items = self.scene().items()
509
            for item in self.GUIDELINE_ITEMS:
510
                if item in items:
511
                    self.scene().removeItem(item)
512
            self.GUIDELINE_ITEMS.clear()
513

    
514
    '''
515
        @brief  drag enter event
516
        @author humkyung
517
        @date   2018.04.17
518
    '''
519

    
520
    def dragEnterEvent(self, event):
521
        event.acceptProposedAction()
522

    
523
    '''
524
        @brief      drag move event
525
        @author     humkyung
526
        @date       2018.04.17
527
        @history    humkyung 2018.08.21 highlight item under mouse
528
    '''
529

    
530
    def dragMoveEvent(self, event):
531
        scenePos = self.mapToScene(event.pos())
532
        items = [item for item in self.scene().items(scenePos) if
533
                 type(item) is not QGraphicsPixmapItem and type(item) is not QGraphicsTextItem]
534
        if len(items) > 0:
535
            if not hasattr(self, '_underItem') or self._underItem is not items[0]:
536
                if hasattr(self, '_underItem') and self._underItem is not None:
537
                    if hasattr(self._underItem, 'highlight') and self._underItem in self.scene().items():
538
                        self._underItem.highlight(False)
539
                    else:
540
                        self._underItem = None
541

    
542
                self._underItem = items[0]
543
                if hasattr(self._underItem, 'highlight'):
544
                    self._underItem.highlight(True)
545
        # elif hasattr(self, '_underItem') and self._underItem is not None:
546
        #    self._underItem.hoverLeaveEvent(event)
547
        #    self._underItem = None
548

    
549
        event.acceptProposedAction()
550

    
551
    def dropEvent(self, event):
552
        """drop a symbol"""
553
        from AppDocData import AppDocData
554
        import symbol
555

    
556
        if len(self.scene().items()) is 0:
557
            return
558
        if hasattr(self, '_underItem') and self._underItem is not None and self._underItem in self.scene().items():
559
            self._underItem.hoverLeaveEvent(None)
560
            self._underItem = None
561
        else:
562
            self._underItem = None
563

    
564
        scenePos = self.mapToScene(event.pos())
565
        name = event.mimeData().text()
566
        svg = QtImageViewer.createSymbolObject(name)
567
        QtImageViewer.matchSymbolToLine(self.scene(), svg, scenePos)
568
        if svg:
569
            svg.setSelected(True)
570
            svg.setFocus()
571
            self.setFocus()  # set focus to graphicview
572

    
573
        event.acceptProposedAction()
574

    
575
    '''
576
        @brief  drop create Symbol
577
        @author kyouho
578
        @date   2018.07.27
579
    '''
580
    @staticmethod
581
    def createSymbolObject(name):
582
        """create a symbol object has given uid"""
583
        from AppDocData import AppDocData
584

    
585
        app_doc_data = AppDocData.instance()
586

    
587
        symbol = app_doc_data.getSymbolByQuery('Name', name)
588
        if symbol:
589
            svg_file_name = symbol.sName
590
            svgFilePath = os.path.join(app_doc_data.getCurrentProject().getSvgFilePath(), symbol.getType(),
591
                                    svg_file_name + '.svg')
592
            svg = SymbolSvgItem.createItem(symbol.getType(), None, svgFilePath)
593
            connPts = None
594
            strConnPts = symbol.getConnectionPoint()
595
            if strConnPts is not None and strConnPts != '':
596
                connPts = [(float(x.split(',')[0]), float(x.split(',')[1])) if len(x.split(',')) == 2 else (
597
                    x.split(',')[0], float(x.split(',')[1]), float(x.split(',')[2])) \
598
                        for x in strConnPts.split('/')]
599

    
600
            svg.buildItem(svg_file_name, symbol.getType(), 0, None, None, None, connPts, symbol.getBaseSymbol(),
601
                        symbol.getAdditionalSymbol(), symbol.getHasInstrumentLabel())
602

    
603
            return svg
604
        else:
605
            return None
606

    
607
    '''
608
        @brief      match symbol to line
609
        @author     kyouho
610
        @date       2018.07.27
611
        @history    humkyung 2018.08.23 change scenePos to connector's center when symbol is placed on connector
612
    '''
613
    @staticmethod
614
    def matchSymbolToLine(scene, svg, scenePos, angle=None, flip=None, strict=False, auto=False):
615
        from EngineeringConnectorItem import QEngineeringConnectorItem
616
        from EngineeringLineItem import QEngineeringLineItem
617
        from SymbolSvgItem import SymbolSvgItem
618
        import math
619
        from App import App
620

    
621
        try:
622
            svg.transfer.onRemoved.connect(App.mainWnd().itemRemoved)
623

    
624
            items = [item for item in scene.items(scenePos) if
625
                    type(item) is not QGraphicsPixmapItem and type(item) is not QGraphicsTextItem]
626
            connectors = []
627
            if len(items) > 0 and type(items[0]) is QEngineeringConnectorItem:
628
                scenePos = QPointF(items[0].center()[0], items[0].center()[1])
629
                connectors = [connector for connector in items if issubclass(type(connector.parentItem()), SymbolSvgItem) \
630
                                and type(connector) is QEngineeringConnectorItem]
631
                if not connectors:
632
                    connectors = [connector for connector in items if type(connector.parentItem()) is QEngineeringLineItem \
633
                                        and type(connector) is QEngineeringConnectorItem]
634

    
635
            matches = [item for item in scene.items() if
636
                    (type(item) is QEngineeringLineItem) and (item.distanceTo((scenePos.x(), scenePos.y())) < 20)]
637
            allowed_error = 0.0001
638
            if False:  # len(matches) == 1:
639
                matches[0].insertSymbol(svg, scenePos)
640
            elif len(connectors) == 1 and len(svg.connectors) >= 2 and len(connectors[0].parentItem().connectors):
641
                # item assistant with line connection
642
                xl = connectors[0].parentItem().symbolOrigin[0] - connectors[0].connectPoint[0]
643
                yl = connectors[0].parentItem().symbolOrigin[1] - connectors[0].connectPoint[1]
644
                length = math.sqrt(xl * xl + yl * yl)
645
                ddx = (connectors[0].sceneBoundingRect().center().x() - connectors[0].parentItem().origin[0]) / length
646
                ddy = (connectors[0].sceneBoundingRect().center().y() - connectors[0].parentItem().origin[1]) / length
647
                dx, dy = abs(svg.connectors[1].connectPoint[0] - svg.symbolOrigin[0]), abs(
648
                    svg.connectors[1].connectPoint[1] - svg.symbolOrigin[1])
649
                length = math.sqrt(dx * dx + dy * dy)
650
                dx, dy = length * ddx, length * ddy
651

    
652
                # if abs(connectors[0].parentItem().angle - math.pi / 2) < allowed_error or abs(connectors[0].parentItem().angle - math.pi / 2 * 3) < allowed_error:
653
                #    dx, dy = ddx * dy, ddy * dx
654
                # else:
655
                #    dx, dy = ddx * dx, ddy * dy
656

    
657
                xxl = connectors[0].parentItem().origin[0] - connectors[0].center()[0]
658
                yyl = connectors[0].parentItem().origin[1] - connectors[0].center()[1]
659
                rAngle = -math.atan2(yyl, xxl)# if flip == 0 else math.atan2(yl, xl)
660
                rAngle = abs(rAngle) if rAngle < 0 + allowed_error else 2 * math.pi - rAngle
661
                svg.angle = rAngle
662

    
663
                x, y = connectors[0].sceneBoundingRect().center().x() + dx, \
664
                    connectors[0].sceneBoundingRect().center().y() + dy
665
                svg.loc = [x - svg.symbolOrigin[0], y - svg.symbolOrigin[1]]
666
                svg.origin = [x, y]
667
                svg.addSvgItemToScene(scene, True if not auto else False)
668

    
669
                items = [item for item in scene.items(scenePos) if
670
                        type(item) is not QGraphicsPixmapItem and type(item) is not QGraphicsTextItem]
671
                items = [item for item in items if item.parentItem() is svg and type(item) is QEngineeringConnectorItem]
672
                if items and connectors[0].connectedItem and type(connectors[0].connectedItem) is QEngineeringLineItem:
673
                    items[0].connect(connectors[0].parentItem())
674
                    anotherConns = [conn for conn in svg.connectors if conn is not items[0]]
675
                    anotherConns[0].connect(connectors[0].connectedItem)
676
                    for lineConn in connectors[0].connectedItem.connectors:
677
                        if lineConn.connectedItem is connectors[0].parentItem():
678
                            lineConn.connect(svg)
679
                            lineConn.setPos(anotherConns[0].center())
680
                            break
681
                    connectors[0].connect(svg)
682
                    lineConn.transfer.onPosChanged.emit(lineConn)
683
                elif items:
684
                    items[0].connect(connectors[0].parentItem())
685
                    #items[0].highlight(False)
686
                    if connectors[0].connectedItem:
687
                        for conn in connectors[0].connectedItem.connectors:
688
                            if conn.connectedItem is connectors[0].parentItem():
689
                                conn.connect(None)
690
                                #conn.highlight(False)
691
                                break
692
                    connectors[0].connect(svg)
693
                    #connectors[0].highlight(False)
694
            elif not strict:
695
                svg.angle = angle if angle else 0.0
696
                svg.flip = flip if flip else 0
697
                svg.loc = [round(scenePos.x() - svg.symbolOrigin[0], 1), round(scenePos.y() - svg.symbolOrigin[1], 1)]
698
                svg.origin = [round(scenePos.x(), 1), round(scenePos.y(), 1)]
699
                if len(svg.connectors) == 1:
700
                    # single connection item assistant
701
                    connectors = [connector for connector in connectors if connector.parentItem() is not svg and not connector.connectedItem]
702

    
703
                    if len(connectors) == 1:
704
                        xxl = connectors[0].parentItem().origin[0] - connectors[0].center()[0]
705
                        yyl = connectors[0].parentItem().origin[1] - connectors[0].center()[1]
706
                        rAngle = -math.atan2(yyl, xxl)
707
                        rAngle = abs(rAngle) if rAngle < 0 + allowed_error else 2 * math.pi - rAngle
708
                        rAngle = rAngle + math.pi if rAngle + math.pi < 2 * math.pi else rAngle - math.pi
709
                        svg.angle = rAngle
710

    
711

    
712
                        svg.connectors[0].connect(connectors[0].parentItem())
713
                        #svg.connectors[0].highlight(False)
714
                        connectors[0].connect(svg)
715
                        #items[0].highlight(False)
716

    
717
                svg.addSvgItemToScene(scene, True if not auto else False)
718

    
719
            # svg.reSettingConnetors()
720

    
721
            '''
722
            if not strict:
723
                # need fix
724
                App.mainWnd().symbolTreeWidget.clearFocus()
725
                scene.setFocus()
726
                #scene.clearFocus()
727
                for item in scene.selectedItems():
728
                    item.setSelected(False)
729

730
                svg.setSelected(True)
731
                scene.setFocusItem(svg)
732
            '''
733
        except Exception as ex:
734
            from App import App 
735
            from AppDocData import MessageType
736

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

    
740
    '''
741
        @brief  find item by uid (SymbolSvgItem 기반, QEngineeringConnectorItem 제외, QEngineeringLineItem 포함)
742
        @author kyouho
743
        @date   2018.07.31
744
    '''
745

    
746
    def findItemByUid(self, uid):
747
        from EngineeringConnectorItem import QEngineeringConnectorItem
748

    
749
        items = [item for item in self.scene().items() if hasattr(item, 'uid') and str(item.uid) == str(uid)]
750
        return items[0] if items else None
751

    
752
    def convertQImageToMat(self, incomingImage):
753
        '''Converts a QImage into an opencv MAT format'''
754
        import numpy as np
755

    
756
        try:
757
            incomingImage = incomingImage.convertToFormat(QImage.Format_RGBA8888)
758

    
759
            width = incomingImage.width()
760
            height = incomingImage.height()
761

    
762
            ptr = incomingImage.bits()
763
            ptr.setsize(incomingImage.byteCount())
764
            arr = np.array(ptr).reshape(height, width, 4)  # Copies the data
765
            return arr
766
        except Exception as ex:
767
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
768
                                                       sys.exc_info()[-1].tb_lineno))
769

    
770

    
771
if __name__ == '__main__':
772
    import sys
773

    
774
    try:
775
        from PyQt5.QtWidgets import QApplication
776
    except ImportError:
777
        try:
778
            from PyQt4.QtGui import QApplication
779
        except ImportError:
780
            raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
781
    print('Using Qt ' + QT_VERSION_STR)
782

    
783

    
784
    def handleLeftClick(x, y):
785
        row = int(y)
786
        column = int(x)
787
        print("Clicked on image pixel (row=" + str(row) + ", column=" + str(column) + ")")
788

    
789

    
790
    # Create the application.
791
    app = QApplication(sys.argv)
792

    
793
    # Create image viewer and load an image file to display.
794
    viewer = QtImageViewer(None)
795
    viewer.loadImageFromFile()  # Pops up file dialog.
796

    
797
    # Handle left mouse clicks with custom slot.
798
    viewer.leftMouseButtonPressed.connect(handleLeftClick)
799

    
800
    # Show viewer and run application.
801
    viewer.show()
802
    sys.exit(app.exec_())
클립보드 이미지 추가 (최대 크기: 500 MB)