I want to show an image in Qt and paint some points and lines over it with QPainter. When the user scrolls or drags I would like only the background image to be zoomed or moved while the painted layer does not change. I am currently using QGraphicsView similarly to the answer here https://stackoverflow.com/a/35514531/9152951. I found out that I can draw my points in the drawForeground() function but then my paintings will be scaled and moved with the image. I tried to paint the points in the paintEvent() function:
class ImageViewer(QtWidgets.QGraphicsView):
image_clicked = QtCore.Signal(QtCore.QPoint)
def __init__(self, parent, corners: List[Point]):
super(ImageViewer, self).__init__(parent)
self.corners = corners
self._zoom = 0
self._empty = True
self._scene = QtWidgets.QGraphicsScene(self)
self._img = QtWidgets.QGraphicsPixmapItem()
self._scene.addItem(self._img)
self.setScene(self._scene)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtWidgets.QFrame.NoFrame)
def hasPhoto(self):
return not self._empty
def fitInView(self, scale=True):
rect = QtCore.QRectF(self._img.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def set_image(self, img=None):
self._zoom = 0
if img is not None:
if type(img) == np.ndarray:
img = np_img_to_q_img(img)
self._empty = False
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
self._img.setPixmap(img)
else:
self._empty = True
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
self._img.setPixmap(QtGui.QPixmap())
self.fitInView()
def wheelEvent(self, event):
if self.hasPhoto():
if event.angleDelta().y() > 0:
factor = 1.05
self._zoom += 1
else:
factor = 1 / 1.05
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def toggleDragMode(self):
if self.dragMode() == QtWidgets.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
elif not self._img.pixmap().isNull():
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
def mousePressEvent(self, event):
if self._img.isUnderMouse():
self.image_clicked.emit(self.mapToScene(event.pos()).toPoint())
super(ImageViewer, self).mousePressEvent(event)
def paintEvent(self, event: PySide2.QtGui.QPaintEvent) -> None:
qp = QPainter(self)
qp.begin(self)
q_color = QColor(10, 205, 255)
qp.setBrush(q_color)
for point in self.corners:
qp.drawEllipse(QPoint(point.x, point.y), 5, 5)
qp.end()
but it does not seem to be allowed as I get:
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setPen: Painter not active
QPainter::setBrush: Painter not active
QPainter::end: Painter not active, aborted
How can I solve this?