简体   繁体   中英

Getting mouse position relative to image limits

I display an image within a custom QLabel and get clicks on this label. I'm interested in clicks within the image only, and in a position expressed by a number between 0 and 1, 0 being the leftmost or topmost pixel and 1 the rightmost or bottommost pixel, regardless of the image actual size.

I can't get the image rectangle to compute the position. When I call self.pixmap.rect() , width and height are the original image dimensions, not the dimensions of the image which was scaled to fit into the label.

What am I doing doing wrong?

from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QGridLayout
from PyQt5.QtGui import QPixmap

class Window(QWidget):
    def __init__(self):
        super().__init__()
        image_path = '092.jpg'
        image_area = Image_Area(QPixmap(image_path))
        image_area.left_click.connect(self.image_clicked)

        layout = QGridLayout()
        layout.addWidget(image_area, 0, 0)
        self.setLayout(layout)

    # Left click on image
    def image_clicked(self, x, y):
        print(f'{x:.3f},{y:.3f}')


class Image_Area(QLabel):

    left_click = pyqtSignal(float, float)

    def __init__(self, pixmap):
        super().__init__()
        self.pixmap = pixmap
        self.setPixmap(pixmap)
        self.setScaledContents(False)
        self.setMinimumSize(100, 100)

    def resizeEvent(self, e):
        if self.pixmap != None:
            width, height = int(self.width()), int(self.height())
            scaled_pixmap = self.pixmap.scaled(width, height, Qt.KeepAspectRatio)
            self.setPixmap(scaled_pixmap)

        return super().resizeEvent(e)

    def mousePressEvent(self, e):
        x, y, = e.x(), e.y()

        if self.pixmap:
            # Get pixmap rectangle
            r = self.pixmap.rect()
            x0, y0 = r.x(), r.y()
            x1, y1 = x0+r.width(), y0+r.height()

            # Check we clicked on the pixmap
            if x >= x0 and x < x1 and y >= y0 and y < y1:

                # emit position relative to pixmap bottom-left corner
                x_relative = (x - x0) / (x1 - x0)
                y_relative = (y - y0) / (y1 - y0)
                self.left_click.emit(x_relative, y_relative)

        super().mousePressEvent(e)


app = QApplication([])
window = Window()
window.show()
app.exec()

Update: After the answer was provided, I realized the computation of the click position relative to the image origin was wrong, here is an accurate version for the persons interested:

def mousePressEvent(self, e):
        # Mouse position is in label coordinates
        x, y, = e.x(), e.y()

        if self.pixmap():
            # pixmap is not a widget, we don't have its location,
            # so we assume the pixmap is centered in the label and
            # compute the location with respect to label
            label_size = self.size()
            pixmap_size = self.pixmap().size()
            width = pixmap_size.width()
            height = pixmap_size.height()

            x0 = int((label_size.width() - width) / 2)
            y0 = int((label_size.height() - height) / 2)

            # Check we clicked on the pixmap
            if (x >= x0 and x < (x0 + width) and
                y >= y0 and y < (y0 + height)):

                # emit position relative to pixmap top-left corner
                x_relative = (x - x0) / width
                y_relative = (y - y0) / height
                self.left_click.emit(x_relative, y_relative)

        super().mousePressEvent(e)

You have to use the QPixmap established in the QLabel that can be obtained through the pixmap() method. The problem is that you are obfuscating the access to that method since you have an attribute with a similar name. So the solution is to use pixmap() and rename the attribute pixmap .

class Image_Area(QLabel):

    left_click = pyqtSignal(float, float)

    def __init__(self, pixmap):
        super().__init__()
        self._pixmap = pixmap
        self.setPixmap(pixmap)
        self.setScaledContents(False)
        self.setMinimumSize(100, 100)

    def resizeEvent(self, e):
        if self._pixmap is not None:
            scaled_pixmap = self._pixmap.scaled(self.size(), Qt.KeepAspectRatio)
            self.setPixmap(scaled_pixmap)
        return super().resizeEvent(e)

    def mousePressEvent(self, e):
        x, y, = (
            e.x(),
            e.y(),
        )
        if self.pixmap:
            r = self.pixmap().rect()
            x0, y0 = r.x(), r.y()
            x1, y1 = x0 + r.width(), y0 + r.height()
            if x >= x0 and x < x1 and y >= y0 and y < y1:
                x_relative = (x - x0) / (x1 - x0)
                y_relative = (y - y0) / (y1 - y0)
                self.left_click.emit(x_relative, y_relative)
        super().mousePressEvent(e)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM