QGraphicsView additem incorrect size in rezised view
-
Hi all,
in my app i'm using a QGraphicsView to display a Microscope image and i want to set a new region for a higher magnification scan. Thus, my program displays an image, enables zoom by mousewheel and the cursor is shaped to a square that shows the user what area he will select. By clicking, i want to set the cursor square to its current position. I created a minimal example of this app with the help of ChatGPT thats shown below.Currently, the square that is drawn as item and depicted after clicking only coincides for one zooming level to the size of the cursor. Does anyone has a solution how to handle this coordinate mapping correctly?
import sys from PySide6.QtWidgets import QGraphicsPixmapItem, QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsRectItem from PySide6.QtCore import Qt, QRectF from PySide6.QtGui import QPixmap, QPainter, QPen, QColor from PySide6 import QtCore from PySide6 import QtGui from PIL import Image, ImageQt import numpy as np class GraphicsView(QGraphicsView): def __init__(self, scene): super().__init__(scene) self.setRenderHint(QPainter.Antialiasing) self.setMouseTracking(True) self.zoom_factor = 1.1 self.base_cursor_size = 10 self.rect_item = None self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) def wheelEvent(self, event): zoom_in_factor = self.zoom_factor zoom_out_factor = 1 / self.zoom_factor old_pos = self.mapToScene(event.position().toPoint()) if event.angleDelta().y() > 0: zoom_factor = zoom_in_factor else: zoom_factor = zoom_out_factor self.scale(zoom_factor, zoom_factor) new_pos = self.mapToScene(event.position().toPoint()) delta = new_pos - old_pos self.translate(delta.x(), delta.y()) self.set_square_cursor() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: if self.rect_item: self.scene().removeItem(self.rect_item) scene_pos = self.mapToScene(event.position().toPoint()) rect_size = 10 / self.transform().m11() # Adjust rectangle size according to zoom level self.rect_item = QGraphicsRectItem(scene_pos.x()-rect_size/2, scene_pos.y()-rect_size/2, rect_size, rect_size) self.rect_item.setPen(QPen(QColor(0, 127, 255), 1 / self.transform().m11())) self.scene().addItem(self.rect_item) super().mousePressEvent(event) def enterEvent(self, event: QtCore.QEvent) -> None: self.set_square_cursor() super().enterEvent(event) def leaveEvent(self, event: QtCore.QEvent) -> None: self.setCursor(QtCore.Qt.CursorShape.ArrowCursor) super().leaveEvent(event) def set_square_cursor(self): # Calculate the cursor size based on the current zoom level transform = self.transform() scale_factor = transform.m11() cursor_size = int(self.base_cursor_size * scale_factor) cursor_arr = np.zeros((cursor_size, cursor_size), dtype=np.uint8) padded_cursor_arr = np.pad(cursor_arr, 1, constant_values=1) rgba_cursor = np.stack([padded_cursor_arr] * 4, axis=-1) * [[[255, 127, 0, 255]]] rgba_cursor = rgba_cursor.astype(np.uint8) qim = QtGui.QImage(rgba_cursor, rgba_cursor.shape[1], rgba_cursor.shape[0], QtGui.QImage.Format.Format_ARGB32) pix = QtGui.QPixmap.fromImage(qim) cursor = QtGui.QCursor(pix, -cursor_size // 2, -cursor_size // 2) self.setCursor(cursor) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Zoomable QGraphicsView with Fixed Rectangle Cursor") self.setGeometry(100, 100, 800, 600) self.scene = QGraphicsScene() self.view = GraphicsView(self.scene) self.setCentralWidget(self.view) # add example image self.add_example_image() def add_example_image(self): rect = QRectF(0, 0, 800, 600) self.scene.setSceneRect(rect) # create example image pixmap = QPixmap.fromImage(ImageQt.ImageQt(Image.fromarray(np.random.randint(0, 20, (300, 400, 3), dtype=np.uint8)))) pixmap_item = QGraphicsPixmapItem(pixmap) scale = 500 / pixmap_item.pixmap().width() pixmap_item.setScale(scale) pixmap_item.setPos(0, 0) self.scene.addItem(pixmap_item) def main(): app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()Best and thanks in advance,
RDDev -
Hi and welcome to devnet,
Shouldn't:
rect_size = 10 / self.transform().m11() # Adjust rectangle size according to zoom level
rather be:
rect_size = 10 * self.transform().m11() # Adjust rectangle size according to zoom level?