Saving sim results
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import json
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import Qt, Slot
|
||||
from PySide6.QtCore import QByteArray, QMimeData, Qt, Slot
|
||||
from PySide6.QtGui import QCloseEvent
|
||||
from PySide6.QtWidgets import (
|
||||
QButtonGroup,
|
||||
QApplication,
|
||||
QFileDialog,
|
||||
QMainWindow,
|
||||
QMenu,
|
||||
@@ -12,7 +14,7 @@ from PySide6.QtWidgets import (
|
||||
QTabWidget,
|
||||
)
|
||||
|
||||
from bedit.core.model import Component
|
||||
from bedit.core.model import Component, Connection
|
||||
from bedit.core.application_log import get_logger
|
||||
from bedit.core.serializer import DocumentSerializer
|
||||
from bedit.core.simulation import Simulation
|
||||
@@ -22,12 +24,15 @@ from bedit.gui.dialogs.graph_parameters import GraphParametersDialog
|
||||
from bedit.gui.dialogs.item_options import ItemOptionsDialog
|
||||
from bedit.gui.models.library_repository import LibraryRepository
|
||||
from bedit.gui.models.library_tree import (
|
||||
COMPONENT_MIME_TYPE,
|
||||
COMPONENT_ROLE,
|
||||
COMPONENT_ID_ROLE,
|
||||
COMPONENT_INSTANCE_ROLE,
|
||||
ITEM_KIND_ROLE,
|
||||
DocumentTreeModel,
|
||||
LibraryTreeModel,
|
||||
)
|
||||
from bedit.gui.graphics.workspace import SELECTION_MIME_TYPE
|
||||
from bedit.gui.dialogs.settings import SettingsDialog
|
||||
from bedit.gui.dialogs.port_options import PortOptionsDialog
|
||||
from bedit.gui.dialogs.parameter_options import ParameterOptionsDialog
|
||||
@@ -55,7 +60,9 @@ class MainWindow(QMainWindow):
|
||||
self.log.info("BEdit started")
|
||||
self.settings = application_settings()
|
||||
self._applying_text_definition = False
|
||||
self._simulation_window = SimulationWindow(self)
|
||||
# Keep a Python-owned top-level window. Giving it MainWindow as its Qt
|
||||
# parent makes some Linux window managers inherit the BEdit window icon.
|
||||
self._simulation_window = SimulationWindow()
|
||||
|
||||
self.libraries = LibraryRepository(self)
|
||||
self.simulation = Simulation(
|
||||
@@ -69,6 +76,7 @@ class MainWindow(QMainWindow):
|
||||
)
|
||||
self.document_tree_model = DocumentTreeModel(self.document_controller, self)
|
||||
self._configure_models()
|
||||
QApplication.clipboard().dataChanged.connect(self._update_edit_actions)
|
||||
self._connect_actions()
|
||||
self._populate_view_menu()
|
||||
self._restore_window_geometry()
|
||||
@@ -102,6 +110,10 @@ class MainWindow(QMainWindow):
|
||||
self.ui.documentTreeView.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
self.ui.documentTreeView.customContextMenuRequested.connect(self.show_library_context_menu)
|
||||
self.ui.documentTreeView.doubleClicked.connect(self.activate_tree_component)
|
||||
self.ui.documentTreeView.clicked.connect(
|
||||
lambda _index: self._update_edit_actions()
|
||||
)
|
||||
self.ui.treeView.clicked.connect(lambda _index: self._update_edit_actions())
|
||||
self.document_tree_model.rebuilt.connect(self.ui.documentTreeView.expandAll)
|
||||
self.ui.graphView.set_model(self.document_controller)
|
||||
self.ui.graphView.componentOptionsRequested.connect(self.show_component_options)
|
||||
@@ -155,9 +167,9 @@ class MainWindow(QMainWindow):
|
||||
self.ui.actionUndo.triggered.connect(self.document_controller.undo_stack.undo)
|
||||
self.ui.actionRedo.triggered.connect(self.document_controller.undo_stack.redo)
|
||||
self.ui.actionDelete.triggered.connect(self.ui.graphView.delete_selected)
|
||||
self.ui.actionCopy.triggered.connect(self.ui.graphView.copy_selection)
|
||||
self.ui.actionCut.triggered.connect(self.ui.graphView.cut_selection)
|
||||
self.ui.actionPaste.triggered.connect(self.ui.graphView.paste_selection)
|
||||
self.ui.actionCopy.triggered.connect(self.copy_selection)
|
||||
self.ui.actionCut.triggered.connect(self.cut_selection)
|
||||
self.ui.actionPaste.triggered.connect(self.paste_selection)
|
||||
self.ui.actionSelectAll.triggered.connect(self.ui.graphView.select_all)
|
||||
self.ui.actionRotateClockwise.triggered.connect(self.ui.graphView.rotate_selected)
|
||||
self.addAction(self.ui.actionRotateClockwise)
|
||||
@@ -324,8 +336,24 @@ class MainWindow(QMainWindow):
|
||||
has_selection = bool(
|
||||
self.ui.graphView.scene() and self.ui.graphView.scene().selectedItems()
|
||||
)
|
||||
for action in (self.ui.actionCut, self.ui.actionCopy, self.ui.actionDelete):
|
||||
action.setEnabled(is_graph and has_selection)
|
||||
document_component_selected = bool(
|
||||
self.ui.documentTreeView.currentIndex().data(COMPONENT_ID_ROLE)
|
||||
)
|
||||
library_component_selected = isinstance(
|
||||
self.ui.treeView.currentIndex().data(COMPONENT_ROLE), dict
|
||||
)
|
||||
document_has_focus = self._view_has_focus(self.ui.documentTreeView)
|
||||
library_has_focus = self._view_has_focus(self.ui.treeView)
|
||||
self.ui.actionCopy.setEnabled(
|
||||
(is_graph and has_selection)
|
||||
or (document_has_focus and document_component_selected)
|
||||
or (library_has_focus and library_component_selected)
|
||||
)
|
||||
self.ui.actionCut.setEnabled(
|
||||
(is_graph and has_selection)
|
||||
or (document_has_focus and document_component_selected)
|
||||
)
|
||||
self.ui.actionDelete.setEnabled(is_graph and has_selection)
|
||||
self.ui.actionRotateClockwise.setEnabled(
|
||||
is_graph and self.ui.graphView.has_selected_components()
|
||||
)
|
||||
@@ -333,11 +361,114 @@ class MainWindow(QMainWindow):
|
||||
is_graph and self.ui.graphView.has_selected_components()
|
||||
)
|
||||
self.ui.actionSelectAll.setEnabled(is_graph)
|
||||
self.ui.actionPaste.setEnabled(is_graph)
|
||||
can_paste_component = QApplication.clipboard().mimeData().hasFormat(
|
||||
COMPONENT_MIME_TYPE
|
||||
) or QApplication.clipboard().mimeData().hasFormat(SELECTION_MIME_TYPE)
|
||||
self.ui.actionPaste.setEnabled(
|
||||
(is_graph and not document_has_focus and not library_has_focus)
|
||||
or (
|
||||
document_has_focus
|
||||
and self.document_controller.document is not None
|
||||
and can_paste_component
|
||||
)
|
||||
)
|
||||
self.ui.navigateDownButton.setEnabled(
|
||||
is_graph and self.ui.graphView.has_single_selected_component()
|
||||
)
|
||||
|
||||
def copy_selection(self) -> bool:
|
||||
if self._view_has_focus(self.ui.documentTreeView):
|
||||
return self._copy_tree_component(self.ui.documentTreeView)
|
||||
if self._view_has_focus(self.ui.treeView):
|
||||
return self._copy_tree_component(self.ui.treeView)
|
||||
return self.ui.graphView.copy_selection()
|
||||
|
||||
def cut_selection(self) -> None:
|
||||
if self._view_has_focus(self.ui.documentTreeView):
|
||||
index = self.ui.documentTreeView.currentIndex()
|
||||
component_id = index.data(COMPONENT_ID_ROLE)
|
||||
if component_id and self._copy_tree_component(self.ui.documentTreeView):
|
||||
self.document_controller.delete_component(component_id)
|
||||
return
|
||||
if self._view_has_focus(self.ui.treeView):
|
||||
self._copy_tree_component(self.ui.treeView)
|
||||
return
|
||||
self.ui.graphView.cut_selection()
|
||||
|
||||
def paste_selection(self) -> None:
|
||||
if self._view_has_focus(self.ui.documentTreeView):
|
||||
self._paste_into_document_tree()
|
||||
return
|
||||
self.ui.graphView.paste_selection()
|
||||
|
||||
def _copy_tree_component(self, tree) -> bool:
|
||||
component = tree.currentIndex().data(COMPONENT_ROLE)
|
||||
if not isinstance(component, dict):
|
||||
return False
|
||||
mime_data = QMimeData()
|
||||
mime_data.setData(
|
||||
COMPONENT_MIME_TYPE,
|
||||
QByteArray(json.dumps(component).encode("utf-8")),
|
||||
)
|
||||
QApplication.clipboard().setMimeData(mime_data)
|
||||
return True
|
||||
|
||||
def _paste_into_document_tree(self) -> None:
|
||||
index = self.ui.documentTreeView.currentIndex()
|
||||
kind = index.data(ITEM_KIND_ROLE)
|
||||
owner_id = None
|
||||
if kind == "current-component":
|
||||
component_id = index.data(COMPONENT_ID_ROLE)
|
||||
component = (
|
||||
self.document_controller.document.find_component(component_id)
|
||||
if self.document_controller.document
|
||||
else None
|
||||
)
|
||||
if component is None or component.implementation_kind != "graph":
|
||||
QMessageBox.warning(
|
||||
self, "Cannot Paste", "Select a graph component or the document root."
|
||||
)
|
||||
return
|
||||
owner_id = component.id
|
||||
elif kind != "current-document":
|
||||
QMessageBox.warning(
|
||||
self, "Cannot Paste", "Select a graph component or the document root."
|
||||
)
|
||||
return
|
||||
try:
|
||||
components, connections = self._clipboard_components()
|
||||
self.document_controller.paste_components_to(
|
||||
owner_id, components, connections
|
||||
)
|
||||
except ValueError as error:
|
||||
QMessageBox.warning(self, "Cannot Paste", str(error))
|
||||
|
||||
@staticmethod
|
||||
def _clipboard_components() -> tuple[list[Component], list]:
|
||||
mime_data = QApplication.clipboard().mimeData()
|
||||
try:
|
||||
if mime_data.hasFormat(SELECTION_MIME_TYPE):
|
||||
payload = json.loads(
|
||||
bytes(mime_data.data(SELECTION_MIME_TYPE)).decode("utf-8")
|
||||
)
|
||||
return (
|
||||
[Component.from_dict(item) for item in payload.get("components", [])],
|
||||
[Connection.from_dict(item) for item in payload.get("connections", [])],
|
||||
)
|
||||
if mime_data.hasFormat(COMPONENT_MIME_TYPE):
|
||||
payload = json.loads(
|
||||
bytes(mime_data.data(COMPONENT_MIME_TYPE)).decode("utf-8")
|
||||
)
|
||||
return [Component.from_dict(payload)], []
|
||||
except (KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
|
||||
raise ValueError("The clipboard does not contain a valid component") from error
|
||||
raise ValueError("The clipboard does not contain a BEdit component")
|
||||
|
||||
@staticmethod
|
||||
def _view_has_focus(view) -> bool:
|
||||
focus = QApplication.focusWidget()
|
||||
return focus is view or (focus is not None and view.isAncestorOf(focus))
|
||||
|
||||
@Slot()
|
||||
def navigate_up(self) -> None:
|
||||
if self._resolve_source_edits():
|
||||
@@ -560,6 +691,8 @@ class MainWindow(QMainWindow):
|
||||
def show_library_context_menu(self, position) -> None:
|
||||
tree_view = self.ui.documentTreeView
|
||||
index = tree_view.indexAt(position)
|
||||
if index.isValid():
|
||||
tree_view.setCurrentIndex(index)
|
||||
kind = index.data(ITEM_KIND_ROLE)
|
||||
if kind == "current-component":
|
||||
component_id = index.data(COMPONENT_ID_ROLE)
|
||||
@@ -577,6 +710,11 @@ class MainWindow(QMainWindow):
|
||||
options_action = menu.addAction("Component Options…")
|
||||
ports_action = menu.addAction("Port Options…")
|
||||
parameters_action = menu.addAction("Parameter Options…")
|
||||
menu.addSeparator()
|
||||
copy_action = menu.addAction("Copy")
|
||||
cut_action = menu.addAction("Cut")
|
||||
paste_action = menu.addAction("Paste")
|
||||
paste_action.setEnabled(component.implementation_kind == "graph")
|
||||
delete_action = menu.addAction("Delete")
|
||||
selected = menu.exec(tree_view.viewport().mapToGlobal(position))
|
||||
if graph_action is not None and selected is graph_action:
|
||||
@@ -589,6 +727,13 @@ class MainWindow(QMainWindow):
|
||||
self.show_component_port_options(component_id)
|
||||
elif selected is parameters_action:
|
||||
self.show_component_parameter_options(component_id)
|
||||
elif selected is copy_action:
|
||||
self._copy_tree_component(tree_view)
|
||||
elif selected is cut_action:
|
||||
if self._copy_tree_component(tree_view):
|
||||
self.document_controller.delete_component(component_id)
|
||||
elif selected is paste_action:
|
||||
self._paste_into_document_tree()
|
||||
elif selected is delete_action:
|
||||
answer = QMessageBox.question(
|
||||
self,
|
||||
@@ -604,24 +749,34 @@ class MainWindow(QMainWindow):
|
||||
menu = QMenu(self)
|
||||
graph_action = menu.addAction("New Graph Block")
|
||||
text_action = menu.addAction("New Text Block")
|
||||
menu.addSeparator()
|
||||
paste_action = menu.addAction("Paste")
|
||||
selected = menu.exec(tree_view.viewport().mapToGlobal(position))
|
||||
if selected is graph_action:
|
||||
self.document_controller.add_root("graph")
|
||||
elif selected is text_action:
|
||||
self.document_controller.add_root("text")
|
||||
elif selected is paste_action:
|
||||
self._paste_into_document_tree()
|
||||
|
||||
@Slot(object)
|
||||
def show_external_library_context_menu(self, position) -> None:
|
||||
tree = self.ui.treeView
|
||||
index = tree.indexAt(position)
|
||||
if index.isValid():
|
||||
tree.setCurrentIndex(index)
|
||||
component = index.data(COMPONENT_INSTANCE_ROLE)
|
||||
if not isinstance(component, Component):
|
||||
return
|
||||
menu = QMenu(self)
|
||||
copy_action = menu.addAction("Copy")
|
||||
menu.addSeparator()
|
||||
ports_action = menu.addAction("Port Options…")
|
||||
parameters_action = menu.addAction("Parameter Options…")
|
||||
selected = menu.exec(tree.viewport().mapToGlobal(position))
|
||||
if selected is ports_action:
|
||||
if selected is copy_action:
|
||||
self._copy_tree_component(tree)
|
||||
elif selected is ports_action:
|
||||
dialog = PortOptionsDialog(component, self)
|
||||
if dialog.exec() == dialog.DialogCode.Accepted:
|
||||
old_inputs, old_outputs = deepcopy(component.inputs), deepcopy(component.outputs)
|
||||
@@ -773,6 +928,7 @@ class MainWindow(QMainWindow):
|
||||
event.ignore()
|
||||
return
|
||||
self.settings.setValue("window/geometry", self.saveGeometry())
|
||||
self._simulation_window.close()
|
||||
self.simulation.shutdown()
|
||||
self.log.info("BEdit closed")
|
||||
self.application_logger.removeHandler(self.log_handler)
|
||||
|
||||
Reference in New Issue
Block a user