如何从 Qlabel 中的图像顶部的 MouseEvents 绘制多边形

How draw Polygon from MouseEvents on top of Image in Qlabel

使用 Python ,PYQT5 我想在 Qlabel 小部件中的图像顶部绘制多边形。我使用了一个简单的 Qmainwindow 和在 QT 设计器中生成的标签小部件(代码如下)。

我知道有几个关于在 Qmainwindow 中绘图的信息,如下所示:

如果您对此问题有解决方案,请告诉我。

import sys
from PyQt5.QtCore import Qt
from PyQt5 import QtWidgets, uic, QtCore, QtGui
from PyQt5.QtGui import QPixmap, QPainter, QPolygon, QPen, QBrush
from PyQt5.QtCore import QPoint

from polygon_ui import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, obj=None, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)

        pixmap = QPixmap("img.png")
        self.label.setPixmap(pixmap)

        self.label.mousePressEvent = self.getPixel
        self.pol = []

    def getPixel(self, event):
        x = event.pos().x()
        y = event.pos().y()

        self.pol.append(QPoint(int(x),int(y)))

        print(x,y, self.pol)

    def paintEvent(self, event):
        painter = QPainter(self)
        #painter.drawPixmap(self.rect(), self.image)

        painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
        painter.setBrush(QBrush(Qt.red, Qt.VerPattern))

        #points = QPolygon([ QPoint(10,10), QPoint(10,100),
        #     QPoint(100,10), QPoint(100,100)])

        points = QPolygon(self.pol)
        painter.drawPolygon(points)

    def mouseMoveEvent(self, event):
        pass

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

polygon_ui 的代码在这里 - 是由 QT-Designer 使用 Mainwindow + Qlabel 简单生成的:

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(621, 641)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(10, 10, 600, 600))
        self.label.setObjectName("label")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 621, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "TextLabel"))

如果你想在图像上添加多边形、线条、圆等元素,那么不要让 QLabel 复杂化,因为例如你当前的代码是在 window 中绘制的低于 QLabel,所以它不会被看到,与 QLabel 一起使用的可能解决方案是获取 QPixmap 并将其绘制在顶部。

更好的选择是使用 Qt 图形框架,其中图像设置为 QGraphicsPixmapItem,多边形作为 QGraphicsPixmapItem 的子项,如下所示:

from PyQt5 import QtCore, QtGui, QtWidgets


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        scene = QtWidgets.QGraphicsScene(self)
        self.setScene(scene)

        self._pixmap_item = QtWidgets.QGraphicsPixmapItem()
        scene.addItem(self.pixmap_item)

        self._polygon_item = QtWidgets.QGraphicsPolygonItem(self.pixmap_item)
        self.polygon_item.setPen(QtGui.QPen(QtCore.Qt.black, 5, QtCore.Qt.SolidLine))
        self.polygon_item.setBrush(QtGui.QBrush(QtCore.Qt.red, QtCore.Qt.VerPattern))

    @property
    def pixmap_item(self):
        return self._pixmap_item

    @property
    def polygon_item(self):
        return self._polygon_item

    def setPixmap(self, pixmap):
        self.pixmap_item.setPixmap(pixmap)

    def resizeEvent(self, event):
        self.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)
        super().resizeEvent(event)

    def mousePressEvent(self, event):
        sp = self.mapToScene(event.pos())
        lp = self.pixmap_item.mapFromScene(sp)

        poly = self.polygon_item.polygon()
        poly.append(lp)
        self.polygon_item.setPolygon(poly)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        view = GraphicsView()
        self.setCentralWidget(view)

        view.setPixmap(QtGui.QPixmap("img.png"))

        self.resize(640, 480)


if __name__ == "__main__":

    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

更新:

如果您想使用 OP 设计,实现起来很简单。

选项 1:

  1. 创建一个名为 graphicsview.py 的文件,其中实现了 GraphicsView 逻辑:

    graphicsview.py

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class GraphicsView(QtWidgets.QGraphicsView):
        def __init__(self, parent=None):
            super().__init__(parent)
            scene = QtWidgets.QGraphicsScene(self)
            self.setScene(scene)
    
            self._pixmap_item = QtWidgets.QGraphicsPixmapItem()
            scene.addItem(self.pixmap_item)
    
            self._polygon_item = QtWidgets.QGraphicsPolygonItem(self.pixmap_item)
            self.polygon_item.setPen(QtGui.QPen(QtCore.Qt.black, 5, QtCore.Qt.SolidLine))
            self.polygon_item.setBrush(QtGui.QBrush(QtCore.Qt.red, QtCore.Qt.VerPattern))
    
        @property
        def pixmap_item(self):
            return self._pixmap_item
    
        @property
        def polygon_item(self):
            return self._polygon_item
    
        def setPixmap(self, pixmap):
            self.pixmap_item.setPixmap(pixmap)
    
        def resizeEvent(self, event):
            self.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)
            super().resizeEvent(event)
    
        def mousePressEvent(self, event):
            sp = self.mapToScene(event.pos())
            lp = self.pixmap_item.mapFromScene(sp)
    
            poly = self.polygon_item.polygon()
            poly.append(lp)
            self.polygon_item.setPolygon(poly)
    
  2. 将 polygon_ui 中的 QLabel 替换为 QGraphicsView:

    polygon_ui.py

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    <b>from graphicsview import GraphicsView</b>
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(621, 641)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            <b>self.label = GraphicsView(self.centralwidget)</b>
            self.label.setGeometry(QtCore.QRect(10, 10, 600, 600))
            self.label.setObjectName("label")
            # ...
    
  3. 还原 main.py

    main.py

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    from polygon_ui import Ui_MainWindow
    
    
    class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
        def __init__(self, *args, obj=None, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)
            self.setupUi(self)
    
            pixmap = QtGui.QPixmap("img.png")
            self.label.setPixmap(pixmap)
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec_())
    

选项2:

另一种方法是推广小部件,在 SO 中有很多这种类型的示例,因此我将避免显示该过程:

选项 3:

另一种更简单的替代方法是使用 QLabel 作为容器并使用布局设置 GraphicsView:

import sys

from PyQt5 import QtCore, QtGui, QtWidgets

from polygon_ui import Ui_MainWindow


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        scene = QtWidgets.QGraphicsScene(self)
        self.setScene(scene)

        self._pixmap_item = QtWidgets.QGraphicsPixmapItem()
        scene.addItem(self.pixmap_item)

        self._polygon_item = QtWidgets.QGraphicsPolygonItem(self.pixmap_item)
        self.polygon_item.setPen(QtGui.QPen(QtCore.Qt.black, 5, QtCore.Qt.SolidLine))
        self.polygon_item.setBrush(QtGui.QBrush(QtCore.Qt.red, QtCore.Qt.VerPattern))

    @property
    def pixmap_item(self):
        return self._pixmap_item

    @property
    def polygon_item(self):
        return self._polygon_item

    def setPixmap(self, pixmap):
        self.pixmap_item.setPixmap(pixmap)

    def resizeEvent(self, event):
        self.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)
        super().resizeEvent(event)

    def mousePressEvent(self, event):
        sp = self.mapToScene(event.pos())
        lp = self.pixmap_item.mapFromScene(sp)

        poly = self.polygon_item.polygon()
        poly.append(lp)
        self.polygon_item.setPolygon(poly)


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, obj=None, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)

        self.graphicsview = GraphicsView()
        lay = QtWidgets.QVBoxLayout(self.label)
        lay.addWidget(self.graphicsview)

        pixmap = QtGui.QPixmap("img.png")
        self.graphicsview.setPixmap(pixmap)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())