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.
