Удаление нарисованных объектов PyQt

Код, который рисует примитивы на изображении.

Как теперь правильно написать удаление фигуры? Допустим, накидал прямоугольников и нужно удалить лишние. Мышкой выделяю нужный и удаляю. Как это правильно сделать?

import sys

from PyQt5 import QtCore, QtGui, QtWidgets  # PyQt5


class MyWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600, 400)
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()
        self.pix = QtGui.QPixmap(600, 400)
        self.pix.fill(QtCore.Qt.white)

def paintEvent(self, event):
    qp = QtGui.QPainter(self)
    qp.drawPixmap(0, 0, self.pix)
    br = QtGui.QBrush(QtGui.QColor("transparent"))
    qp.setBrush(br)
    if self.begin and self.end:
        rect = QtCore.QRect(self.begin, self.end)
        qp.drawRect(rect)

def mousePressEvent(self, event):
    if event.button() == QtCore.Qt.LeftButton:
        self.begin = event.pos()
        self.end = self.begin
        self.update()


def mouseMoveEvent(self, event):
    if event.buttons() == QtCore.Qt.LeftButton:
        self.end = event.pos()
        self.update()

def mouseReleaseEvent(self, event):
    if event.button() == QtCore.Qt.LeftButton:
        painter = QtGui.QPainter(self.pix)
        rect = QtCore.QRect(self.begin, self.end)
        painter.drawRect(rect)
        self.begin = self.end = QtCore.QPoint()
        self.update()


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

Ответы (1 шт):

Автор решения: S. Nick

Qt's Undo Framework - это реализация шаблона Command для реализации функций undo/redo в приложениях. Шаблон Command основан на идее, что все редактирование в приложении выполняется путем создания экземпляров командных объектов.
Командные объекты применяют изменения к документу и сохраняются в стеке команд. Более того, каждая команда знает, как отменить свои изменения, чтобы вернуть документ в его предыдущее состояние.
Пока приложение использует только объекты команд для изменения состояния документа, можно отменить последовательность команд, просматривая стек вниз и вызывая отмену для каждой команды по очереди.
Также возможно повторить последовательность команд, просматривая стек вверх и вызывая повтор для каждой команды.

Больше https://doc.qt.io/qt-5/qundocommand.html#details
и
https://doc.qt.io/qt-5/qundostack.html#details

import sys
from PyQt5.QtWidgets import QMessageBox, QMainWindow, QApplication, \
    QUndoCommand, QUndoStack
from PyQt5.QtGui import QPainter, QColor, QImage, QPen
from PyQt5.QtCore import Qt, QRectF, QRect


class UndoCommand(QUndoCommand):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.mPrevImage = parent.image.copy()
        self.mCurrImage = parent.image.copy()

    def undo(self):
        self.mCurrImage = self.parent.image.copy()
        self.parent.image = self.mPrevImage
        self.parent.update()

    def redo(self):
        self.parent.image = self.mCurrImage
        self.parent.update()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.mUndoStack = QUndoStack(self)
        self.mUndoStack.setUndoLimit(20)

        self.mUndoStack.canUndoChanged.connect(self.can_undo_changed)
        self.mUndoStack.canRedoChanged.connect(self.can_redo_changed)

        self.actionUndo = self.menuBar().addAction("Undo")
        self.actionUndo.triggered.connect(self.mUndoStack.undo)
        self.actionRedo = self.menuBar().addAction("Redo")
        self.actionRedo.triggered.connect(self.mUndoStack.redo)

        self.can_undo_changed(self.mUndoStack.canUndo())
        self.can_redo_changed(self.mUndoStack.canRedo())

        self.image = QImage(self.size(), QImage.Format_ARGB32)
        self.image.fill(Qt.transparent)

        self.start_pos = None
        self.end_pos = None
        self.is_pressed = False

    def can_undo_changed(self, enabled):
        self.actionUndo.setEnabled(enabled)

    def can_redo_changed(self, enabled):
        self.actionRedo.setEnabled(enabled)

    def make_undo_command(self):
        self.mUndoStack.push(UndoCommand(self))

    def draw(self, parent):
        painter = QPainter(parent)
        painter.setRenderHint(QPainter.HighQualityAntialiasing)
        painter.setPen(Qt.NoPen)
        painter.setBrush(QColor('#9900917C'))         
        rect = QRect(self.start_pos, self.end_pos)
        painter.drawEllipse(rect)
        
        painter.setPen(QPen(Qt.red, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        painter.setBrush(QColor('#44fb743e'))
        painter.drawRect(rect)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawImage(self.image.rect(), self.image)
        if self.is_pressed:
            self.draw(self)

    def mousePressEvent(self, event):
        self.is_pressed = True
        self.start_pos = event.pos()

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            self.end_pos = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        self.is_pressed = False
        self.end_pos = event.pos()

        self.make_undo_command()
        self.draw(self.image)
        self.update()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    w.resize(600, 400)
    w.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка