gr-mcp/grc/gui_qt/components/undoable_actions.py

236 lines
7.4 KiB
Python

from qtpy.QtWidgets import QUndoCommand
import logging
from copy import copy
from qtpy.QtCore import QPointF
from .canvas.flowgraph import FlowgraphScene
from .canvas.block import Block
from ...core.base import Element
log = logging.getLogger(f"grc.application.{__name__}")
# Movement, rotation, enable/disable/bypass, bus ports,
# change params, toggle type.
# Basically anything that's not cut/paste or new/delete
class ChangeStateAction(QUndoCommand):
def __init__(self, scene: FlowgraphScene):
QUndoCommand.__init__(self)
log.debug("init ChangeState")
self.old_states = []
self.old_params = []
self.new_states = []
self.new_params = []
self.scene = scene
self.g_blocks = scene.selected_blocks()
for g_block in self.g_blocks:
self.old_states.append(copy(g_block.core.states))
self.new_states.append(copy(g_block.core.states))
self.old_params.append(copy(g_block.core.params))
self.new_params.append(copy(g_block.core.params))
def redo(self):
for i in range(len(self.g_blocks)):
self.g_blocks[i].set_states(self.new_states[i])
self.g_blocks[i].core.params = (self.new_params[i])
self.scene.update()
def undo(self):
for i in range(len(self.g_blocks)):
self.g_blocks[i].set_states(self.old_states[i])
self.g_blocks[i].params = (self.old_params[i])
self.scene.update()
class RotateAction(QUndoCommand):
def __init__(self, scene: FlowgraphScene, delta_angle: int):
QUndoCommand.__init__(self)
log.debug("init RotateAction")
self.setText('Rotate')
self.g_blocks = scene.selected_blocks()
self.scene = scene
self.delta_angle = delta_angle
def redo(self):
self.scene.rotate_selected(self.delta_angle)
def undo(self):
self.scene.rotate_selected(-self.delta_angle)
class MoveAction(QUndoCommand):
def __init__(self, scene: FlowgraphScene, diff: QPointF):
QUndoCommand.__init__(self)
log.debug("init MoveAction")
self.setText('Move')
self.g_blocks = scene.selected_blocks()
self.scene = scene
self.x = diff.x()
self.y = diff.y()
for block in self.g_blocks:
block.core.states["coordinate"] = (block.x(), block.y())
self.first = True
# redo() is called when the MoveAction is first created.
# At this point, the item is already at the correct position.
# Therefore, do nothing.
def redo(self):
if self.first:
self.first = False
return
for g_block in self.g_blocks:
g_block.move(self.x, self.y)
self.scene.update()
def undo(self):
for g_block in self.g_blocks:
g_block.move(-self.x, -self.y)
self.scene.update()
class EnableAction(ChangeStateAction):
def __init__(self, scene: FlowgraphScene):
ChangeStateAction.__init__(self, scene)
log.debug("init EnableAction")
self.setText('Enable')
for i in range(len(self.g_blocks)):
self.new_states[i]['state'] = 'enabled'
class DisableAction(ChangeStateAction):
def __init__(self, scene: FlowgraphScene):
ChangeStateAction.__init__(self, scene)
log.debug("init DisableAction")
self.setText('Disable')
for i in range(len(self.g_blocks)):
self.new_states[i]['state'] = 'disabled'
class BypassAction(ChangeStateAction):
def __init__(self, scene: FlowgraphScene):
ChangeStateAction.__init__(self, scene)
log.debug("init BypassAction")
self.setText('Bypass')
for i in range(len(self.g_blocks)):
self.new_states[i]['state'] = 'bypassed'
# Change properties
# This can only be performed on one block at a time
class BlockPropsChangeAction(QUndoCommand):
def __init__(self, scene: FlowgraphScene, c_block: Block):
QUndoCommand.__init__(self)
log.debug("init BlockPropsChangeAction")
self.setText(f'{c_block.name} block: Change properties')
self.scene = scene
self.c_block = c_block
self.old_data = copy(c_block.old_data)
self.new_data = copy(c_block.export_data())
self.first = True
def redo(self):
if self.first:
self.first = False
return
try:
name = self.new_data['name']
except KeyError:
name = self.new_data['parameters']['id']
self.c_block.import_data(name, self.new_data['states'], self.new_data['parameters'])
self.c_block.rewrite()
self.c_block.validate()
self.c_block.gui.create_shapes_and_labels()
self.scene.update()
def undo(self):
try:
name = self.old_data['name']
except KeyError:
name = self.old_data['parameters']['id']
self.c_block.import_data(name, self.old_data['states'], self.old_data['parameters'])
self.c_block.rewrite()
self.c_block.validate()
self.c_block.gui.create_shapes_and_labels()
self.scene.update()
class BussifyAction(QUndoCommand):
def __init__(self, scene: FlowgraphScene, direction: str): # direction is either "sink" or "source"
QUndoCommand.__init__(self)
log.debug("init BussifyAction")
self.setText(f'Toggle bus {direction}')
self.scene = scene
self.direction = direction
self.g_blocks = scene.selected_blocks()
def bussify(self):
for g_block in self.g_blocks:
g_block.core.bussify(self.direction)
self.scene.update()
def redo(self):
self.bussify()
def undo(self):
self.bussify()
# Blocks and connections
class NewElementAction(QUndoCommand):
def __init__(self, scene: FlowgraphScene, element: Element):
QUndoCommand.__init__(self)
log.debug("init NewElementAction")
self.setText('New')
self.scene = scene
self.element = element
self.first = True
def redo(self):
if self.first:
self.first = False
return
if self.element.is_block:
self.scene.core.blocks.append(self.element)
elif self.element.is_connection:
self.scene.core.connections.add(self.element)
self.scene.addItem(self.element.gui)
self.scene.update()
def undo(self):
self.scene.remove_element(self.element.gui)
self.scene.update()
class DeleteElementAction(QUndoCommand):
def __init__(self, scene: FlowgraphScene):
QUndoCommand.__init__(self)
log.debug("init DeleteElementAction")
self.setText('Delete')
self.scene = scene
self.g_connections = scene.selected_connections()
self.g_blocks = scene.selected_blocks()
for block in self.g_blocks:
for conn in block.core.connections():
self.g_connections = self.g_connections + [conn.gui]
def redo(self):
for con in self.g_connections:
self.scene.remove_element(con)
for block in self.g_blocks:
self.scene.remove_element(block)
self.scene.update()
def undo(self):
for block in self.g_blocks:
self.scene.core.blocks.append(block.core)
self.scene.addItem(block)
for con in self.g_connections:
self.scene.core.connections.add(con.core)
self.scene.addItem(con)
self.scene.update()