Не понимаю, как реализовать отмену действий Undo с помощью паттерна "Команда" (в графическом редакторе)
Не понимаю, как реализовать отмену действий(Undo) с помощью паттерна "Команда" (в графическом редакторе). Прописал несколько классов, необходимых для реализации отмены действия, но пока не совсем разобрался, что в них должно быть.
Данная программа рисует только прямоугольники, также присутствует кнопка отмены(пока без логики):
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import copy
#класс прямоугольник
class Rectangle:
def __init__(self, frame):
self.frame = frame
def draw(self, painter):
painter.rect(self.frame.x1, self.frame.y1, self.frame.x2, self.frame.y2)
class Frame:
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
# класс для отрисовки
class Painter:
def __init__(self):
super().__init__()
self.pen_color = QtGui.QColor("green")
self.pen_width = 1
self.brush_color = QtGui.QColor("green")
def set_port(self, painter):
self.painter = painter
def rect(self, x1, y1, x2, y2):
xmax = max(x1, x2)
xmin = min(x1, x2)
ymax = max(y1, y2)
ymin = min(y1, y2)
w = xmax - xmin
h = ymax - ymin
pen = QPen(self.pen_color, self.pen_width)
self.painter.addRect(xmin, ymin, w, h, pen, self.brush_color)
class GraphicsScene(QGraphicsScene):
clicked = pyqtSignal(QPointF)
released = pyqtSignal(QPointF)
moved = pyqtSignal(QPointF)
def mousePressEvent(self, event):
sp = event.scenePos()
self.clicked.emit(sp)
super().mousePressEvent(event)
def mouseReleaseEvent(self, event):
sp = event.scenePos()
self.released.emit(sp)
super().mouseReleaseEvent(event)
class Editor(QMainWindow):
def __init__(self):
super().__init__()
self.resize(520, 550)
self.grview = QGraphicsView()
self.grview.scale(1, -1)
self.scene = GraphicsScene()
self.setMouseTracking(True)
self.scene.setSceneRect(-250, -250, 500, 500)
self.grview.setScene(self.scene)
self.toolbar()
self.command_history = CommandHistory()
self.pen = QPen(Qt.red)
self.brush = QBrush(Qt.green)
self.scene.clicked.connect(self.point1)
self.scene.released.connect(self.point2)
self.scene.moved.connect(self.point2)
self.undo_action.triggered.connect(self.undo)
self.clear_action.triggered.connect(self.clear)
self.mass_rect = []
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
layout = QVBoxLayout(self.centralWidget)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.grview)
self.show()
def toolbar(self):
self.figure_toolbar = QToolBar("figure")
self.figure_toolbar.setIconSize(QSize(25, 25))
self.basicToolBar = self.addToolBar(self.figure_toolbar)
# кнопка undo
self.undo_action = QAction(QIcon("images/undo.ico"), 'undo', self)
self.undo_action.setStatusTip("undo")
self.figure_toolbar.addAction(self.undo_action)
# кнопка очистки
self.clear_action = QAction(QIcon("images/clear.png"), 'claer', self)
self.claer_action.setStatusTip("clear")
self.figure_toolbar.addAction(self.clear_action)
def point1(self, p):
self.x1 = p.x()
self.y1 = p.y()
def point2(self, p):
self.x2 = p.x()
self.y2 = p.y()
self.create_rect(self.x1, self.y1, self.x2, self.y2)
def create_rect(self, x1, y1, x2, y2):
#отрисовка фигур
painter = Painter()
painter.set_port(self.scene)
frame = Frame(x1, y1, x2, y2)
rect = Rectangle(frame)
self.mass_rect.append(rect)
for object in self.mass_rect:
object.draw(painter)
def undo(self):
command = self.command_history.pop()
def clear(self):
self.scene.clear()
# реализация шаблона команда
class Command:
def __init__(self, editor):
self.editor = copy.copy(editor)
def undo(self):
pass
class UndoCommand(Command):
def __init__(self, editor):
super().__init__(editor)
def execute(self):
pass
class CommandHistory:
def __init__(self):
self.history = []
def push(self, command):
self.history.append(command)
def pop(self):
return self.history.pop(-1)
def main():
app = QtWidgets.QApplication(sys.argv)
g = Editor()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Ответы (1 шт):
Автор решения: S. Nick
→ Ссылка
я не знаю правильно ли вас понял, но попробуйте так:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import copy
class Rectangle:
""" класс прямоугольник """
def __init__(self, frame):
self.frame = frame
def draw(self, painter):
painter.rect(self.frame.x1, self.frame.y1, self.frame.x2, self.frame.y2)
class Frame:
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
class Painter:
""" класс для отрисовки """
def __init__(self):
super().__init__()
self.pen_color = QtGui.QColor("green")
self.pen_width = 1
self.brush_color = QtGui.QColor("green")
def set_port(self, painter):
self.painter = painter
def rect(self, x1, y1, x2, y2):
xmax = max(x1, x2)
xmin = min(x1, x2)
ymax = max(y1, y2)
ymin = min(y1, y2)
w = xmax - xmin
h = ymax - ymin
pen = QPen(self.pen_color, self.pen_width)
self.painter.addRect(xmin, ymin, w, h, pen, self.brush_color)
class Command:
""" реализация шаблона команда """
def __init__(self, editor):
self.editor = copy.copy(editor)
def undo(self):
pass
class UndoCommand(Command):
def __init__(self, editor):
super().__init__(editor)
def execute(self):
pass
class CommandHistory:
def __init__(self, mass_rect): # + , mass_rect
self.history = mass_rect # - [], + mass_rect
def push(self, command):
self.history.append(command)
def pop(self):
# return self.history.pop(-1) # -
if self.history: # +
self.history.pop(-1)
return self.history
else: # +++
self.history = []
return self.history
class GraphicsScene(QGraphicsScene):
clicked = pyqtSignal(QPointF)
released = pyqtSignal(QPointF)
moved = pyqtSignal(QPointF)
def mousePressEvent(self, event):
sp = event.scenePos()
self.clicked.emit(sp)
super().mousePressEvent(event)
def mouseReleaseEvent(self, event):
sp = event.scenePos()
self.released.emit(sp)
super().mouseReleaseEvent(event)
class Editor(QMainWindow):
def __init__(self):
super().__init__()
self.resize(520, 550)
self.grview = QGraphicsView()
self.grview.scale(1, -1)
self.scene = GraphicsScene()
self.setMouseTracking(True)
self.scene.setSceneRect(-250, -250, 500, 500)
self.grview.setScene(self.scene)
self.toolbar()
# self.command_history = CommandHistory() # перенес см. ниже
self.pen = QPen(Qt.red)
self.brush = QBrush(Qt.green)
self.scene.clicked.connect(self.point1)
self.scene.released.connect(self.point2)
#? self.scene.moved.connect(self.point2)
self.undo_action.triggered.connect(self.undo)
self.mass_rect = []
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
layout = QVBoxLayout(self.centralWidget)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.grview)
self.show()
self.command_history = CommandHistory(self.mass_rect) # + self.mass_rect
def toolbar(self):
self.figure_toolbar = QToolBar("figure")
self.figure_toolbar.setIconSize(QSize(25, 25))
self.basicToolBar = self.addToolBar(self.figure_toolbar)
# кнопка line
self.undo_action = QAction(QIcon("images/undo.png"), 'undo', self) # undo.ico установите свое
self.undo_action.setStatusTip("undo")
self.figure_toolbar.addAction(self.undo_action)
def point1(self, p):
self.x1 = p.x()
self.y1 = p.y()
def point2(self, p):
self.x2 = p.x()
self.y2 = p.y()
self.create_rect(self.x1, self.y1, self.x2, self.y2)
def create_rect(self, x1, y1, x2, y2):
#отрисовка фигур
self.painter = Painter()
self.painter.set_port(self.scene)
frame = Frame(x1, y1, x2, y2)
rect = Rectangle(frame)
self.mass_rect.append(rect)
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self._draw(self.mass_rect, self.painter)
def _draw(self, mass_rect, painter):
if mass_rect:
for object in mass_rect:
object.draw(painter)
def undo(self):
command = self.command_history.pop()
self.mass_rect = command
self.scene.clear()
if self.mass_rect:
self._draw(self.mass_rect, self.painter)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def main():
app = QtWidgets.QApplication(sys.argv)
g = Editor()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

