如何通过拖动角来调整多边形的大小?

Posted

技术标签:

【中文标题】如何通过拖动角来调整多边形的大小?【英文标题】:How to resize polygon by dragging its corners? 【发布时间】:2021-11-24 00:59:43 【问题描述】:

我在 QGraphicsScene 上单击鼠标时保存点,将其附加到列表中,并通过循环遍历列表来创建 QGraphicsPolygonItem。

我使用自定义的 QGraphicsScene,只要有鼠标按下事件,它就会发出位置。代码如下。 (感谢SignalHelper class)

class SignalHelper(QObject):
    messageSignal = QtCore.Signal(object)

class Scene(QGraphicsScene):
    def __init__(self, parent=None):
        super(Scene, self).__init__(parent)
        self.helper = SignalHelper()

    def mousePressEvent(self, event):
        self.helper.messageSignal.emit(event.scenePos())

这是我的主窗口的代码。

class main_window(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(100, 100, 500, 500)
        self.pen = QtGui.QPen(QtGui.QColor(0,0,0))                     
        self.pen.setWidth(5)                                             

        self.view = QGraphicsView(self)
        self.scene = Scene()
        self.btn_record_points = QPushButton("Record")
        self.btn_finished = QPushButton("Finished")
        
        self.view.setSceneRect(0, 0, 500,500)
        self.view.setScene(self.scene)

        vbox = QVBoxLayout(self)
        vbox.addWidget(self.view)
        vbox.addWidget(self.btn_record_points)
        vbox.addWidget(self.btn_finished)

        self.setLayout(vbox)

        self.point_list = []
        self.record_points = False

        #Signals
        self.btn_record_points.clicked.connect(self.enable_record_points)
        self.btn_finished.clicked.connect(self.create_polygon)
        self.scene.helper.messageSignal.connect(self.draw_points)
        
    def create_polygon(self):
        # Remove ellipses
        drawn_points = self.scene.items()
        for i in drawn_points:
            self.scene.removeItem(i)

        polygon = QGraphicsPolygonItem(QtGui.QPolygonF(self.point_list))
        polygon.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.scene.addItem(polygon)
        self.record_points = False
        self.point_list.clear()

    QtCore.Slot(QtCore.QPointF)
    def draw_points(self, point):
        if self.record_points == True:
            self.point_list.append(point)
            self.scene.addEllipse(point.toTuple()[0], point.toTuple()[1], 1, 1) 

    QtCore.Slot(bool)
    def enable_record_points(self):
        self.record_points = True

这是一个随机多边形的示例:

使用注意事项:

    按“录制”按钮 在场景中点击多次 按“完成”按钮

既然创建了多边形,有没有办法通过拖动它的角来调整多边形的大小?

【问题讨论】:

【参考方案1】:

通过“拖动其角调整多边形的大小”我想象你想移动多边形的点。

所以这是一种方法。

因此,首先使您的多边形可选择,一旦选择了多边形,就遍历多边形的点并在每个点处绘制一个椭圆。存储椭圆、polygon_point_index 映射。

现在通过遍历mapping_list检查选择了哪个椭圆,然后使用这个椭圆,polygon_index映射来更新多边形的具体点。

这是一个例子(你可以改进):

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore


class Scene(QtWidgets.QGraphicsScene):

    def __init__(self, *args, **kwargs):
        super(Scene, self).__init__(*args, **kwargs)
        self.record_points = True
        self.selected = None  # the selected polygon
        self.points_lst = []  # points that are stored when recording
        self.corner_points = []  # This contains corner point and control point mapping
        self.selected_corner = None
        self.poly_points = [] # points that are stored when resizing (You could instead reuse points_lst)

    def record(self):
        self.record_points = True

    def removeControlPoints(self):
        """ removes the control points (i,e the ellipse)"""
        for ellipse, _ in self.corner_points:
            self.removeItem(ellipse)

        self.corner_points = []

    def mousePressEvent(self, event):
        super(Scene, self).mousePressEvent(event)

        if self.record_points:
            self.points_lst.append(event.scenePos())
            return

        for point in self.corner_points:
            if point[0].contains(event.scenePos()):
                self.selected_corner = point
                return

        if self.selectedItems():

            self.removeControlPoints()

            self.selected = self.selectedItems()[0]
            self.poly_points = [self.selected.mapToScene(x) for x in self.selected.polygon()]

            for index, point in enumerate(self.poly_points):
                x, y = point.x(), point.y()
                ellipse = self.addEllipse(QtCore.QRectF(x - 5, y - 5, 10, 10), brush=QtGui.QBrush(QtGui.QColor("red")))
                ellipse.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable)

                self.corner_points.append((ellipse, index))

        else:
            self.selected = None
            self.removeControlPoints()
            self.corner_points = []
            self.poly_points = []
            self.selected_corner = None

    def mouseMoveEvent(self, event) -> None:
        super(Scene, self).mouseMoveEvent(event)

        if self.selected_corner:
            self.poly_points[self.selected_corner[1]] = QtCore.QPointF(event.scenePos())
            self.selected.setPolygon(QtGui.QPolygonF(self.poly_points))

    def mouseReleaseEvent(self, event) -> None:
        super(Scene, self).mouseReleaseEvent(event)
        self.selected_corner = None

    def addPoints(self):  # adds the polygon to the scene
        self.record_points = False
        polygon = self.addPolygon(QtGui.QPolygonF(self.points_lst))
        polygon.setFlags(QtWidgets.QGraphicsItem.ItemIsSelectable)
        self.points_lst = []


class MainWindow(QtWidgets.QWidget):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setLayout(QtWidgets.QVBoxLayout())

        view = QtWidgets.QGraphicsView()
        scene = Scene()
        view.setScene(scene)

        record_btn = QtWidgets.QPushButton(text="Record", clicked=scene.record)
        finish_btn = QtWidgets.QPushButton(text="Finish", clicked=scene.addPoints)

        self.layout().addWidget(view)
        self.layout().addWidget(record_btn)
        self.layout().addWidget(finish_btn)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    win = MainWindow()
    win.show()

    sys.exit(app.exec_())

输出:

【讨论】:

以上是关于如何通过拖动角来调整多边形的大小?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过拖动视图边缘/角来实现缩放,如应用官方照片应用裁剪控制?

如何使所有绘制的(矩形、圆形、线条、多边形)可拖动?纯JS

通过在Android中拖动矩形的角来更改矩形的大小?

在 react-native 中拖动形状并调整其大小

如何拖动多边形?

调整在Python中使用Numpy和OpenCV绘制的多边形的大小