from PySide6.QtGui import QUndoCommand from bedit_core.models import Component, ComponentID, Connection, ConnectionID, EquationImplementation, Graph, GraphImplementation, Interface class AddEmptyGraphComponent(QUndoCommand): def __init__(self, document: object, parent: Component) -> None: super().__init__("Add graph component") if not isinstance(parent.implementation, GraphImplementation): raise TypeError("parent component must have a graph implementation") self.document = document self.graph = parent.implementation.graph self.component_id = ComponentID() self.component = Component(name="New Graph Component", interface=Interface(), parameters={}, implementation=GraphImplementation(Graph())) def redo(self) -> None: self.graph.components[self.component_id] = self.component self.document.model_changed.emit(self.document.model) def undo(self) -> None: del self.graph.components[self.component_id] self.document.model_changed.emit(self.document.model) class AddEmptyEquationComponent(QUndoCommand): def __init__(self, document: object, parent: Component) -> None: super().__init__("Add equation component") if not isinstance(parent.implementation, GraphImplementation): raise TypeError("parent component must have a graph implementation") self.document = document self.graph = parent.implementation.graph self.component_id = ComponentID() self.component = Component(name="New Equation Component", interface=Interface(), parameters={}, implementation=EquationImplementation()) def redo(self) -> None: self.graph.components[self.component_id] = self.component self.document.model_changed.emit(self.document.model) def undo(self) -> None: del self.graph.components[self.component_id] self.document.model_changed.emit(self.document.model) class DeleteComponent(QUndoCommand): def __init__(self, document: object, component: Component) -> None: super().__init__("Delete component") self.document = document self.component = component self.components, self.component_id, self.parent_graph = self._find_component(document.model.root, component) self.component_index = list(self.components).index(self.component_id) port_ids = set(component.interface.ports) self.connections: list[tuple[int, ConnectionID, Connection]] = [] if self.parent_graph is not None: for index, (connection_id, connection) in enumerate(self.parent_graph.connections.items()): if connection.source in port_ids or connection.target in port_ids: self.connections.append((index, connection_id, connection)) self.icons = {} for component_id in self._component_ids(self.component_id, component): icon = document.stored_component_icon(component_id) if icon is not None: self.icons[component_id] = icon def redo(self) -> None: if self.parent_graph is not None: for _, connection_id, _ in self.connections: self.parent_graph.connections.pop(connection_id, None) self.components.pop(self.component_id, None) self.document.model_changed.emit(self.document.model) for component_id in self.icons: self.document._set_component_icon(component_id, None) def undo(self) -> None: self._restore_item(self.components, self.component_id, self.component, self.component_index) if self.parent_graph is not None: for index, connection_id, connection in self.connections: self._restore_item(self.parent_graph.connections, connection_id, connection, index) self.document.model_changed.emit(self.document.model) for component_id, icon in self.icons.items(): self.document._set_component_icon(component_id, icon) @classmethod def _find_component(cls, components: dict[ComponentID, Component], target: Component, parent_graph: Graph | None = None) -> tuple[dict[ComponentID, Component], ComponentID, Graph | None]: for component_id, component in components.items(): if component is target: return components, component_id, parent_graph if isinstance(component.implementation, GraphImplementation): try: return cls._find_component(component.implementation.graph.components, target, component.implementation.graph) except ValueError: pass raise ValueError("component is not part of this document") @classmethod def _component_ids(cls, component_id: ComponentID, component: Component) -> list[ComponentID]: component_ids = [component_id] if isinstance(component.implementation, GraphImplementation): for child_id, child in component.implementation.graph.components.items(): component_ids.extend(cls._component_ids(child_id, child)) return component_ids @staticmethod def _restore_item(items: dict, item_id: object, item: object, index: int) -> None: values = list(items.items()) values.insert(index, (item_id, item)) items.clear() items.update(values)