You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()也无法解决问题。


问题分析与解决方案

核心问题

代码存在两个关键错误:

  1. 坐标体系混淆:event.pos()返回的是相对于image_label的局部坐标,却错误使用了image_label在父控件中的全局坐标做判断,局部坐标原点是label左上角,无需额外加全局偏移。
  2. 忽略图像缩放与对齐:窗口调整时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:05:06