PySide6中如何在图像组件缩放后追踪鼠标位置并避免盲区?
PySide6图像组件点击位置判断问题
我正在开发PySide6应用,需求是仅在用户点击图像组件时打印鼠标位置,但遇到两个问题:
- 盲区问题:点击图像后存在部分区域无法正确追踪鼠标位置。
- 缩放后空白区域误判:调整窗口大小后,图像下方出现空白区域,点击该区域会被误判为点击了图像范围外。
以下是相关代码:
from PySide6.QtGui import QImage, QPixmap, Qt, QColor from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QSizePolicy import cv2 from utils.environment import Environment class LayoutMouseVisualisation(QWidget): def __init__(self, parent=None): super(LayoutMouseVisualisation, self).__init__(parent) self.setStyleSheet(""" QWidget { background-color: black; } """) self.controller = ControllerMouseVisualisation(self) self.layout = QVBoxLayout() self.setLayout(self.layout) self.image_label = QLabel() self.update_img(self.controller.get_teeth_image()) self.image_label.mousePressEvent = self.mousePressEvent self.layout.addWidget(self.image_label) def update_img(self, img): height, width, channel = img.shape bytes_per_line = 3 * width # noinspection all self.q_img = QImage(img.data, width, height, bytes_per_line, QImage.Format_RGB888) pixmap = QPixmap.fromImage(self.q_img) self.image_label.setPixmap(pixmap) def mousePressEvent(self, event): x = event.pos().x() y = event.pos().y() img_width, img_height, _ = self.controller.get_image_shape() label_rect = self.image_label.geometry() label_x = label_rect.x() label_y = label_rect.y() print(f"Label geometry: x={label_x}, y={label_y}, width={label_rect.width()}, height={label_rect.height()}") # Check if the click is inside the image area if label_x <= x <= img_width + label_x and label_y <= y <= img_height + label_y: print(f"Mouse clicked inside the image at x: {x}, y: {y}") else: print(f"Mouse clicked outside the image area at x: {x}, y: {y}") class ControllerMouseVisualisation: def __init__(self, view): self.view = view self.teeth_image = cv2.imread(Environment.resource_path('static/images/teeth.jpg')) self.colored_image = self.teeth_image.copy() def get_teeth_image(self): return self.colored_image def get_image_shape(self): return self.teeth_image.shape
最初我以为问题与label的size policies有关,但测试后发现并非如此,使用localPos()也无法解决问题。
问题分析与解决方案
核心问题
代码存在两个关键错误:
- 坐标体系混淆:
event.pos()返回的是相对于image_label的局部坐标,却错误使用了image_label在父控件中的全局坐标做判断,局部坐标原点是label左上角,无需额外加全局偏移。 - 忽略图像缩放与对齐:窗口调整时
QLabel尺寸可能大于图像实际尺寸,直接用原始图像宽高判断,未考虑图像在label中的缩放和显示区域。
修复后的代码
from PySide6.QtGui import QImage, QPixmap, Qt, QColor from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QSizePolicy import cv2 from utils.environment import Environment class LayoutMouseVisualisation(QWidget): def __init__(self, parent=None): super(LayoutMouseVisualisation, self).__init__(parent) self.setStyleSheet(""" QWidget { background-color: black; } """) self.controller = ControllerMouseVisualisation(self) self.layout = QVBoxLayout() self.setLayout(self.layout) self.image_label = QLabel() # 设置图像缩放模式,保持比例并居中显示 self.image_label.setScaledContents(False) self.image_label.setAlignment(Qt.AlignCenter) self.update_img(self.controller.get_teeth_image()) self.image_label.mousePressEvent = self.mousePressEvent self.layout.addWidget(self.image_label) def update_img(self, img): height, width, channel = img.shape bytes_per_line = 3 * width # noinspection all self.q_img = QImage(img.data, width, height, bytes_per_line, QImage.Format_RGB888) self.pixmap = QPixmap.fromImage(self.q_img) self.image_label.setPixmap(self.pixmap) def mousePressEvent(self, event): # 获取相对于label的局部坐标 pos = event.pos() # 获取图像在label中的实际显示区域 pixmap_rect = self.image_label.pixmap().rect() # 计算图像在label中的居中偏移 offset_x = (self.image_label.width() - pixmap_rect.width()) // 2 offset_y = (self.image_label.height() - pixmap_rect.height()) // 2 # 判断点击位置是否在图像显示区域内 if (offset_x <= pos.x() <= offset_x + pixmap_rect.width() and offset_y <= pos.y() <= offset_y + pixmap_rect.height()): # 转换为原始图像的坐标(按需使用) original_x = int((pos.x() - offset_x) * self.controller.get_image_shape()[1] / pixmap_rect.width()) original_y = int((pos.y() - offset_y) * self.controller.get_image_shape()[0] / pixmap_rect.height()) print(f"Mouse clicked inside image: label坐标({pos.x()}, {pos.y()}) | 原始图像坐标({original_x}, {original_y})") else: print(f"Mouse clicked outside image area at label坐标({pos.x()}, {pos.y()})") class ControllerMouseVisualisation: def __init__(self, view): self.view = view self.teeth_image = cv2.imread(Environment.resource_path('static/images/teeth.jpg')) self.colored_image = self.teeth_image.copy() def get_teeth_image(self): return self.colored_image def get_image_shape(self): return self.teeth_image.shape
关键修复点
- 坐标体系修正:直接使用
event.pos()的局部坐标,不再引入label的全局坐标偏移。 - 图像显示区域计算:通过
pixmap().rect()获取图像缩放后的尺寸,结合label尺寸计算图像实际位置偏移量,以此判断点击范围。 - 原始坐标转换:通过缩放比例将label坐标转换为原始图像坐标,保证窗口缩放后坐标准确。
- 设置对齐方式:显式设置
Qt.AlignCenter,确保图像在label中居中,偏移量计算更可靠。
内容的提问来源于stack exchange,提问作者Nazar Kuziv
相关产品推荐
相关产品推荐

