0

I am making an image acquisition program using a webcam in PyQt. Whenever I click on the image, which is inside a label widget, i need to put an additional fixed cursor at that position (to have a reference point for e.g). I created a cursor object, set a shape and position (obtained from the clicked position). yet I dont see an additional cursor being created at the clicked position i.e. Qpoint

below is the code snippet:

 def eventFilter(self, source, event):
     if  event.type()==QtCore.QEvent.MouseButtonPress:
         self.new_cursor=QtGui.QCursor()               # the additional cursori want to place
         self.new_cursor.setShape(self,Qt.PointingHandCursor) # setting shape

         self.cursor_clicked=event.pos()           # getting position from the click

         self.cursor_clicked=self.label.mapFromParent(event.pos()) #mapping to widget coords.

         self.cursor_x=self.cursor_clicked.x()
         self.cursor_y=self.cursor_clicked.y()
         self.new_cursor.setPos(self.cursor_x,self.cursor_y)
         self.setCursor(self.new_cursor)


     return QtWidgets.QWidget.eventFilter(self,source,event)   
0

1 Answer 1

1

QCursor is not a "static image", but is an "abstract" object related to the mouse cursor, so there's no use of it for your purpose.

What you're looking for is drawing on the existing image or the widget that shows it.
Since you probably want to leave the image unchanged, the second option is what you're looking for.

The idea is that you call the base class implementation of the paintEvent method and then draw over it.
Painting a crosshair by hand is not that hard using simple lines, but you'll need to draw an extra border around the cross using a different color to ensure its visibility even on lighter or darker backgrounds, which makes it unnecessary long; in this example I'm using the cursor image Qt uses in the CursorShape enum documentation, but you can use whatever image you want, as soon as its center is exactly at the center of it (hint: use a square image with odd sized width/height).

from PyQt5 import QtCore, QtGui, QtWidgets

class Window(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QGridLayout(self)

        self.label = QtWidgets.QLabel()
        layout.addWidget(self.label)
        self.label.setPixmap(QtGui.QPixmap('myimage.png'))
        self.label.installEventFilter(self)

        self.cursorPos = None
        # I'm using the crosshair cursor as shown at
        # https://doc.qt.io/qt-5/qt.html#CursorShape-enum
        self.cursorPixmap = QtGui.QPixmap('cursor-cross.png')

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            # set the current position and schedule a repaint of the label
            self.cursorPos = event.pos()
            self.label.update()
        elif event.type() == QtCore.QEvent.Paint:
            # intercept the paintEvent of the label and call the base
            # implementation to actually draw its contents
            self.label.paintEvent(event)
            if self.cursorPos is not None:
                # if the cursor position has been set, draw it
                qp = QtGui.QPainter(self.label)
                # translate the painter at the cursor position
                qp.translate(self.cursorPos)
                # paint the pixmap at an offset based on its center
                qp.drawPixmap(-self.cursorPixmap.rect().center(), self.cursorPixmap)
            return True
        return super().eventFilter(source, event)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

Besides that, another approach is to use the Graphics View Framework, add the pixmap to the scene and add/move another pixmap for the cursor when the user clicks on the image. Dealing with QGraphicsViews, QGraphicsScenes and their items is a bit more complex, but if you're going to need some more advanced level of interaction with an image, it usually is the better path to take.

from PyQt5 import QtCore, QtGui, QtWidgets

class Window(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QGridLayout(self)

        self.view = QtWidgets.QGraphicsView()
        layout.addWidget(self.view)
        # remove any border around the view
        self.view.setFrameShape(0)
        self.scene = QtWidgets.QGraphicsScene()
        self.view.setScene(self.scene)

        pixmap = QtGui.QPixmap('myimage.png')
        # adapt the view's size to that of the pixmap
        self.view.setFixedSize(pixmap.size())

        # add a pixmap to a scene, which returns a QGraphicsPixmapItem
        self.pixmapItem = self.scene.addPixmap(pixmap)

        self.crossHairItem = None

        self.view.installEventFilter(self)

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            if not self.crossHairItem:
                # as above, get a QGraphicsPixmapItem for the crosshair cursor
                pixmap = QtGui.QPixmap('cursor-cross.png')
                self.crossHairItem = self.scene.addPixmap(pixmap)
                # set an offset of the item, so that its position is always
                # based on the center of the pixmap
                self.crossHairItem.setOffset(-pixmap.rect().center())
            self.crossHairItem.setPos(self.view.mapToScene(event.pos()))
        return super().eventFilter(source, event)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

Both methods behave in the same way for the user, and as you can see they look exactly identical.

image comparison of the two methods

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.