簡體   English   中英

Pyqt5 圖像坐標

[英]Pyqt5 image coordinates

我用 Qlabel 顯示圖像。我需要圖像坐標/像素坐標,但是,我使用 mouseclickevent 它只顯示 Qlabel 坐標。

例如,我的圖像是 800*753,我的 Qlabel 幾何是 (701,451)。我在 (701,451) 中讀取坐標,但我需要 (800*753) 中的圖像坐標

def resimac(self):



    filename= QtWidgets.QFileDialog.getOpenFileName(None, 'Resim Yükle', '.', 'Image Files (*.png *.jpg *.jpeg *.bmp *.tif)')

    self.image=QtGui.QImage(filename[0])
    self.pixmap=QtGui.QPixmap.fromImage(self.image)
    self.resim1.setPixmap(self.pixmap)


    self.resim1.mousePressEvent=self.getPixel




def getPixel(self, event):


    x = event.pos().x()
    y = event.pos().y()


    print("X=",x," y= ",y)

由於您沒有提供最小的、可重現的示例,我將假設您可能正在設置scaledContents屬性,但這也可能不是真的(如果您為標簽設置了最大或固定大小)。

關於您的回答還有其他一些嚴重的問題,我將在本回答的末尾解決這些問題。

該點必須映射到像素圖坐標

將像素圖設置為 QLabel 時,Qt 會自動將標簽大小調整為其內容。
好吧,除非標簽有一些尺寸限制,否則它scaledContents :最大/固定尺寸小於像素圖,和/或 QLabel 將scaledContents屬性設置為 True 如上所述。 請注意,如果它的任何祖先有一些大小限制(例如,主窗口具有最大大小,或者它被最大化到小於窗口所需空間的屏幕),也會發生這種情況。
在任何一種情況下, mousePressEvent 顯然都會根據小部件而不是像素圖為您提供坐標。

首先,即使它看起來不那么重要,您也必須考慮到每個小部件都可以有一些內容邊距:小部件仍然會接收在這些邊距區域內發生的事件,即使它們是在其實際內容之外,因此您必須考慮該方面,並確保事件發生在小部件內容的真實幾何結構內(在本例中為像素圖)。 如果這是真的,您必須將事件位置轉換為該矩形以根據像素圖獲取其位置。

然后,如果scaledContents屬性為 true,則圖像將縮放到標簽的當前可用大小(這也意味着不會保持其縱橫比),因此您需要縮放位置。
這只是一個數學問題:計算圖像大小和(的內容)標簽之間的比例,然后使用該比例乘以該值。

    # click on the horizontal center of the widget
    mouseX = 100

    pixmapWidth = 400
    widgetWidth = 200
    xRatio = pixmapWidth / widgetWidth
    # xRatio = 2.0

    pixmapX = mouseX * xRatio
    # the resulting "x" is the horizontal center of the pixmap
    # pixmapX = 200

另一方面,如果內容未縮放,則必須考慮 QLabel alignment屬性; 它通常在左側對齊並垂直居中,但這取決於操作系統、當前使用的樣式和本地化(考慮從右到左的書寫語言)。 這意味着如果圖像小於可用尺寸,其邊距內將有一些空白空間,您必須注意這一點。

在下面的示例中,我試圖解決所有這些問題(老實說,我不是 100% 確定,因為由於各種原因,可能會有一些 1 像素的容差,大多數是關於整數-基於坐標和 DPI 意識)。 請注意,我沒有像您那樣覆蓋mousePressEvent ,而是使用了event filter ,之后我將解釋其原因。

from PyQt5 import QtCore, QtGui, QtWidgets

class Window(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        layout = QtWidgets.QGridLayout(self)

        self.getImageButton = QtWidgets.QPushButton('Select')
        layout.addWidget(self.getImageButton)
        self.getImageButton.clicked.connect(self.resimac)

        self.resim1 = QtWidgets.QLabel()
        layout.addWidget(self.resim1)
        self.resim1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
        # I'm assuming the following...
        self.resim1.setScaledContents(True)
        self.resim1.setFixedSize(701,451)

        # install an event filter to "capture" mouse events (amongst others)
        self.resim1.installEventFilter(self)

    def resimac(self):
        filename, filter = QtWidgets.QFileDialog.getOpenFileName(None, 'Resim Yükle', '.', 'Image Files (*.png *.jpg *.jpeg *.bmp *.tif)')
        if not filename:
            return
        self.resim1.setPixmap(QtGui.QPixmap(filename))

    def eventFilter(self, source, event):
        # if the source is our QLabel, it has a valid pixmap, and the event is
        # a left click, proceed in trying to get the event position
        if (source == self.resim1 and source.pixmap() and not source.pixmap().isNull() and 
            event.type() == QtCore.QEvent.MouseButtonPress and
            event.button() == QtCore.Qt.LeftButton):
                self.getClickedPosition(event.pos())
        return super().eventFilter(source, event)

    def getClickedPosition(self, pos):
        # consider the widget contents margins
        contentsRect = QtCore.QRectF(self.resim1.contentsRect())
        if pos not in contentsRect:
            # outside widget margins, ignore!
            return

        # adjust the position to the contents margins
        pos -= contentsRect.topLeft()

        pixmapRect = self.resim1.pixmap().rect()
        if self.resim1.hasScaledContents():
            x = pos.x() * pixmapRect.width() / contentsRect.width()
            y = pos.y() * pixmapRect.height() / contentsRect.height()
            pos = QtCore.QPoint(x, y)
        else:
            align = self.resim1.alignment()
            # for historical reasons, QRect (which is based on integer values),
            # returns right() as (left+width-1) and bottom as (top+height-1),
            # and so their opposite functions set/moveRight and set/moveBottom
            # take that into consideration; using a QRectF can prevent that; see:
            # https://doc.qt.io/qt-5/qrect.html#right
            # https://doc.qt.io/qt-5/qrect.html#bottom
            pixmapRect = QtCore.QRectF(pixmapRect)

            # the pixmap is not left aligned, align it correctly
            if align & QtCore.Qt.AlignRight:
                pixmapRect.moveRight(contentsRect.x() + contentsRect.width())
            elif align & QtCore.Qt.AlignHCenter:
                pixmapRect.moveLeft(contentsRect.center().x() - pixmapRect.width() / 2)
            # the pixmap is not top aligned (note that the default for QLabel is
            # Qt.AlignVCenter, the vertical center)
            if align & QtCore.Qt.AlignBottom:
                pixmapRect.moveBottom(contentsRect.y() + contentsRect.height())
            elif align & QtCore.Qt.AlignVCenter:
                pixmapRect.moveTop(contentsRect.center().y() - pixmapRect.height() / 2)

            if not pos in pixmapRect:
                # outside image margins, ignore!
                return
            # translate coordinates to the image position and convert it back to
            # a QPoint, which is integer based
            pos = (pos - pixmapRect.topLeft()).toPoint()

        print('X={}, Y={}'.format(pos.x(), pos.y()))


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Window()
    w.show()
    sys.exit(app.exec_())

現在。 幾個建議。

不要用 [other] 對象的實例屬性覆蓋現有的子對象方法

這不是一個好主意的原因有很多,在處理 Qt 時,其中最重要的是 Qt 對虛函數使用函數緩存 這意味着一旦一次調用虛擬函數,該函數將始終被調用。 雖然您的方法可以在簡單的情況下工作(特別是如果覆蓋發生在父級的__init__ ),但如果您不非常小心,它通常容易出現難以調試的意外行為。

這正是您的情況:我想resimac不會在父實例化時調用,直到發生其他事件(可能是單擊的按鈕)之后。 但是,如果用戶出於某種原因在加載新像素圖之前單擊標簽,則您應該被覆蓋的方法將永遠不會被調用:那時,您還沒有覆蓋它,因此用戶單擊標簽,Qt 調用QLabel 的基類mousePressEvent實現,然后從那時起將始終調用該方法,無論您是否嘗試覆蓋它。

要解決這個問題,您至少有 3 個選擇:

  1. 使用事件過濾器(如上例); 事件過濾器是“捕獲”小部件事件並允許您觀察(並與之交互)的東西; 您還可以決定是否將該事件傳播到小部件的父級(這主要是鍵/鼠標事件的情況:如果小部件對其中一個事件不“感興趣”,它會“告訴”其父級關心它); 這是最簡單的方法,但在復雜情況下可能難以實現和調試;
  2. 將小部件子類化並在代碼中手動將其添加到 GUI;
  3. 如果您使用 Qt 的設計器,則將其子類化並“提升”小部件;

您不需要為 QLabel 使用 QImage。

這不是一個問題,這只是一個建議:QPixmap 在使用路徑作為參數構造它時,已經在其 C++ 代碼中使用了(有點fromImage ,因此沒有必要。

始終,始終提供可用的、可重現的示例代碼。

看:

獲得“MRE”可能需要時間,甚至數小時,但這是值得的:總會有人可以回答您,但由於各種原因不想或無法深入研究您的代碼(主要是因為它是不完整、模糊、無法使用、缺乏上下文,甚至過於擴展)。 如果因任何原因,就只是一個用戶,你會失去你的機會來解決你的問題。 要有耐心,仔細准備你的問題,你可能會從中獲得大量的互動和有用的見解。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM