Search code examples
opencvpyqt5python-3.7qgraphicsviewqgraphicsscene

how to get only image pixel from QGraphicsScene


here is my simple code to display the image in QGraphicsView in pyqt python 3.7. I want an image pixel when the mouse is pressed on a scene or window of QGraphicsView or QGraphicsScene.

Mouse Press Function

Mouse Press Event Handler

def mousePressEvent(self):
    p = QtGui.QCursor.pos()
    print("pressed here: ", p)

Mouse Press Event caller

self.scene1.mousePressEvent = mousePressEvent

Main Code

 import cv2
 from PyQt5 import QtCore, QtGui, QtWidgets
 from PyQt5.QtGui import *
 from PyQt5.QtWidgets import QGraphicsScene, QAction


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
        self.graphicsView.setGeometry(QtCore.QRect(20, 10, 761, 561))
        self.graphicsView.setObjectName("graphicsView")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        #  ---- Mouse Press Event Handler ---- #
        def mousePressEvent(self):
            p = QtGui.QCursor.pos()  # Here I want image pixel coordinate (x,y) how we can..?
            print("pressed here: ", p)

        #  ---- Mouse Press Event caller ---- #
        self.scene1.mousePressEvent = mousePressEvent

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

        # -------------------------------------------------
        image = cv2.imread('lena.jpg')  # Read image
        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        height, width = image.shape  # read image size
        self.image_disp = QImage(image.data, width, height, QImage.Format_Grayscale8)
        # -------------------------------------------------
        self.scene1 = QGraphicsScene()
        pixMap = QPixmap.fromImage(self.image_disp)
        self.scene1.addPixmap(pixMap)
        self.graphicsView.setScene(self.scene1)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Solution

  • You should not modify the class generated by Qt Designer(1), instead create another class that inherits from a widget and use the initial class as an interface.

    Do not override the mousePressEvent method using self.scene1.mousePressEvent = mousePressEvent because you are deleting the default implementation, instead you can create a class that inherits from QGraphicsScene or use an event filter, in this case I will use the second method.

    To obtain the position of the mouse with respect to the image (QGraphicsPixmapItem), you must use the transformations between the different elements of the Qt Graphics Framework.

    import os
    
    import cv2
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(800, 600)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
            self.graphicsView.setGeometry(QtCore.QRect(20, 10, 761, 561))
            self.graphicsView.setObjectName("graphicsView")
            MainWindow.setCentralWidget(self.centralwidget)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
    
    
    class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setupUi(self)
    
            self.scene = QtWidgets.QGraphicsScene(self)
            self.graphicsView.setScene(self.scene)
            self.scene.installEventFilter(self)
    
            current_dir = os.path.dirname(os.path.realpath(__file__))
            filename = os.path.join(current_dir, "lena.jpg")
    
            image = cv2.imread(filename)
            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            height, width = image.shape
            image_disp = QtGui.QImage(
                image.data, width, height, QtGui.QImage.Format_Grayscale8
            )
            pixMap = QtGui.QPixmap.fromImage(image_disp)
            self.pixmap_item = self.scene.addPixmap(pixMap)
    
        def eventFilter(self, obj, event):
            if obj is self.scene and event.type() == QtCore.QEvent.GraphicsSceneMousePress:
                spf = event.scenePos()
                lpf = self.pixmap_item.mapFromScene(spf)
                brf = self.pixmap_item.boundingRect()
                if brf.contains(lpf):
                    lp = lpf.toPoint()
                    print(lp)
            return super().eventFilter(obj, event)
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())
    

    (1) Using the Generated Code