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)