프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / QtImageViewer.py @ 25879e36

이력 | 보기 | 이력해설 | 다운로드 (32.5 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:
419
                if self.command is not None:
420
                    self.command.execute(['keyPressEvent', event, []])
421
                    if self.command.isTreated: return
422
            else:
423
                if self.command is not None:
424
                    self.command.execute(['keyPressEvent', event, []])
425
                    if self.command.isTreated: return
426
            if type(self.mainWindow) is QTrainingEditorDialog or type(self.mainWindow) is QTrainingSymbolEditorDialog:
427
                self.mainWindow.keyPressEvent(event)
428

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

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

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

    
444
        QGraphicsView.keyReleaseEvent(self, event)
445

    
446
    '''
447
        @brief      mouse wheel event
448
        @autor      humkyung
449
        @date       
450
    '''
451

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

    
464
    '''
465
        @brief      draw background
466
        @author     humkyung
467
        @date       2018.07.23
468
    '''
469

    
470
    """
471
    def drawBackground(self, painter, rect):
472
        QGraphicsView.drawBackground(self, painter, rect)
473
    """
474

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

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

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

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

    
512
    '''
513
        @brief  drag enter event
514
        @author humkyung
515
        @date   2018.04.17
516
    '''
517

    
518
    def dragEnterEvent(self, event):
519
        event.acceptProposedAction()
520

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

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

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

    
547
        event.acceptProposedAction()
548

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

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

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

    
571
        event.acceptProposedAction()
572

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

    
583
        app_doc_data = AppDocData.instance()
584

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

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

    
601
            return svg
602
        else:
603
            return None
604

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

    
619
        try:
620
            svg.transfer.onRemoved.connect(App.mainWnd().itemRemoved)
621

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

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

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

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

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

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

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

    
709

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

    
715
                svg.addSvgItemToScene(scene, True if not auto else False)
716

    
717
            # svg.reSettingConnetors()
718

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

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

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

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

    
744
    def findItemByUid(self, uid):
745
        from EngineeringConnectorItem import QEngineeringConnectorItem
746

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

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

    
754
        try:
755
            incomingImage = incomingImage.convertToFormat(QImage.Format_RGBA8888)
756

    
757
            width = incomingImage.width()
758
            height = incomingImage.height()
759

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

    
768

    
769
if __name__ == '__main__':
770
    import sys
771

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

    
781

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

    
787

    
788
    # Create the application.
789
    app = QApplication(sys.argv)
790

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

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

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