From 0e5f5e7f8a4b411ab510aa5b854a11e30bc5e2b1 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher
Date: Mon, 10 Jun 2024 00:20:15 +0200 Subject: [PATCH] wip 2 - TscatRootModel works as before with paths --- tscat_gui/__init__.py | 16 +- tscat_gui/state.py | 10 +- tscat_gui/tscat_driver/nodes.py | 3 - tscat_gui/tscat_driver/tscat_root_model.py | 235 +++++++++++---------- tscat_gui/undo.py | 8 +- 5 files changed, 149 insertions(+), 123 deletions(-) diff --git a/tscat_gui/__init__.py b/tscat_gui/__init__.py index 6146998..de56ccd 100644 --- a/tscat_gui/__init__.py +++ b/tscat_gui/__init__.py @@ -15,7 +15,7 @@ from tscat import _Catalogue, _Event from .edit import EntityEditView -from .model_base.constants import UUIDDataRole +from .model_base.constants import UUIDDataRole, EntityRole from .state import AppState from .tscat_driver.actions import Action, AddEventsToCatalogueAction, CanonicalizeImportAction, CreateEntityAction, \ DeleteAttributeAction, MoveToTrashAction, SaveAction, SetAttributeAction @@ -165,9 +165,17 @@ def __current_event_changed(self, _: QtCore.QModelIndex, __: QtCore.QModelIndex) def __catalogue_selection_changed(self, _: QtCore.QItemSelection, __: QtCore.QItemSelection) -> None: if not self.programmatic_select: - uuids = [index.data(UUIDDataRole) for index in self.catalogues_view.selectedIndexes()] - self.state.updated('active_select', _Catalogue, uuids) - self.catalogues_selected.emit(uuids) + catalogue_uuids = [] + selected_indexes = self.catalogues_view.selectedIndexes() + for index in selected_indexes: + if index.data(EntityRole) is not None: + catalogue_uuids.append(index.data(UUIDDataRole)) + + if selected_indexes: + self.state.set_catalogue_path(self.catalogue_model.current_path(selected_indexes[-1])) + if catalogue_uuids: + self.state.updated('active_select', _Catalogue, catalogue_uuids) + self.catalogues_selected.emit(catalogue_uuids) def __create_undo_redo_action_menu_on_toolbutton(self, index_range: range, diff --git a/tscat_gui/state.py b/tscat_gui/state.py index b3333ce..47f28b2 100644 --- a/tscat_gui/state.py +++ b/tscat_gui/state.py @@ -15,6 +15,7 @@ class SelectState: selected: List[str] type: Union[Type[tscat._Catalogue], Type[tscat._Event]] selected_catalogues: List[str] + catalogue_path: List[str] class AppState(QtCore.QObject): @@ -24,7 +25,7 @@ class AppState(QtCore.QObject): def __init__(self) -> None: super().__init__() - self._select_state = SelectState([], tscat._Catalogue, []) + self._select_state = SelectState([], tscat._Catalogue, [], []) self._undo_stack = QtGui.QUndoStack() self._undo_stack.cleanChanged.connect(lambda x: self.undo_stack_clean_changed.emit(x)) # type: ignore @@ -58,3 +59,10 @@ def updated(self, action: str, ty: Union[Type[tscat._Catalogue], Type[tscat._Eve log.debug(f'app-state-updated action:{action}, type:{ty}, uuids:{uuids}') self.state_changed.emit(action, ty, uuids) + + def set_catalogue_path(self, path: List[str]) -> None: + print("setting path", path) + self._select_state.catalogue_path = path + + def current_catalogue_path(self) -> List[str]: + return self._select_state.catalogue_path diff --git a/tscat_gui/tscat_driver/nodes.py b/tscat_gui/tscat_driver/nodes.py index ad14618..a48431c 100644 --- a/tscat_gui/tscat_driver/nodes.py +++ b/tscat_gui/tscat_driver/nodes.py @@ -157,6 +157,3 @@ def uuid(self) -> str: @property def name(self) -> str: return self._name - - def flags(self): - return QtCore.Qt.ItemIsEnabled # super().flags() | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled # type: ignore diff --git a/tscat_gui/tscat_driver/tscat_root_model.py b/tscat_gui/tscat_driver/tscat_root_model.py index 747a577..1aca6fe 100644 --- a/tscat_gui/tscat_driver/tscat_root_model.py +++ b/tscat_gui/tscat_driver/tscat_root_model.py @@ -4,7 +4,6 @@ import tempfile from typing import Any, Dict, List, Sequence, Union, cast -import itertools from PySide6.QtCore import QAbstractItemModel, QMimeData, QModelIndex, QPersistentModelIndex, QUrl, Qt, Signal from tscat import _Catalogue @@ -38,6 +37,45 @@ def __init__(self) -> None: def _trash_index(self) -> QModelIndex: return self.index(0, 0, QModelIndex()) + def _node_from_catalogue_path(self, c: _Catalogue) -> Node: + root = self._root + if hasattr(c, 'Path') and isinstance(c.Path, list) and all(isinstance(x, str) for x in c.Path): + for folder in c.Path: + for child in root.children: + if isinstance(child, FolderNode) and child.name == folder: + root = child + break + else: + new_folder = FolderNode(folder) + root.append_child(new_folder) + root = new_folder + + return root + + def _node_from_uuid(self, uuid: str) -> CatalogNode: + stack = [self._root] + + while stack: + node = stack.pop() + if isinstance(node, CatalogNode) and node.uuid == uuid: + return node + + # we do not want to search in Catalogue's children, they are events + if not isinstance(node, CatalogNode): + stack.extend(node.children) + + raise ValueError(f'Node with uuid {uuid} not found') + + def _index_from_node(self, node: Node) -> QModelIndex: + if node.parent is None: + return QModelIndex() + + return self.index(node.row, 0, self._index_from_node(node.parent)) + + def index_from_uuid(self, uuid: str) -> QModelIndex: + node = self._node_from_uuid(uuid) + return self._index_from_node(node) + def _driver_action_done(self, action: Action) -> None: if isinstance(action, GetCataloguesAction): if action.removed_items: @@ -53,111 +91,75 @@ def _driver_action_done(self, action: Action) -> None: self._root.set_children([self._trash]) for c in action.catalogues: - root = self._root - if hasattr(c, 'Path') and isinstance(c.Path, list) and all(isinstance(x, str) for x in c.Path): - for folder in c.Path: - for child in root.children: - if isinstance(child, FolderNode) and child.name == folder: - root = child - break - else: - new_folder = FolderNode(folder) - root.append_child(new_folder) - root = new_folder - - root.append_child(CatalogNode(c)) + parent_node = self._node_from_catalogue_path(c) + parent_node.append_child(CatalogNode(c)) self.endResetModel() - # elif isinstance(action, GetCatalogueAction): - # for row, child in enumerate(self._root.children): - # if child.uuid == action.uuid: - # index = self.index(row, 0, QModelIndex()) - # self.dataChanged.emit(index, index) # type: ignore - # return - -# for row, child in enumerate(self._trash.children): -# if child.uuid == action.uuid: -# index = self.index(row, 0, self._trash_index()) -# self.dataChanged.emit(index, index) # type: ignore - -# elif isinstance(action, CreateEntityAction): -# if isinstance(action.entity, _Catalogue): -# self.beginInsertRows(QModelIndex(), len(self._root.children), len(self._root.children)) -# node = CatalogNode(action.entity) -# self._root.append_child(node) -# self.endInsertRows() - -# elif isinstance(action, (RemoveEntitiesAction, DeletePermanentlyAction)): -# for row, c in reversed(list(enumerate(self._root.children))): -# if c.uuid in action.uuids: -# self.beginRemoveRows(QModelIndex(), row, row) -# self._root.remove_child(c) -# self.endRemoveRows() -# if c.uuid in self._catalogues: -# del self._catalogues[c.uuid] - -# for row, c in reversed(list(enumerate(self._trash.children))): -# if c.uuid in action.uuids: -# self.beginRemoveRows(self._trash_index(), row, row) -# self._trash.remove_child(c) -# self.endRemoveRows() - -# elif isinstance(action, MoveToTrashAction): -# for row, c in reversed(list(enumerate(self._root.children))): -# if c.uuid in action.uuids: -# self.beginRemoveRows(QModelIndex(), row, row) -# self._root.remove_child(c) -# self.endRemoveRows() - -# self.beginInsertRows(self._trash_index(), len(self._trash.children), len(self._trash.children)) -# self._trash.append_child(c) -# self.endInsertRows() - -# elif isinstance(action, RestoreFromTrashAction): -# for row, c in reversed(list(enumerate(self._trash.children))): -# if c.uuid in action.uuids: -# self.beginRemoveRows(self._trash_index(), row, row) -# self._trash.remove_child(c) -# self.endRemoveRows() - -# self.beginInsertRows(QModelIndex(), len(self._root.children), len(self._root.children)) -# self._root.append_child(c) -# self.endInsertRows() - -# elif isinstance(action, RestorePermanentlyDeletedAction): -# for e in action.deleted_entities: -# if isinstance(e.restored_entity, _Catalogue): -# node = CatalogNode(e.restored_entity) -# if e.restored_entity.is_removed(): -# self.beginInsertRows(self._trash_index(), len(self._trash.children), len(self._trash.children)) -# self._trash.append_child(node) -# self.endInsertRows() -# else: -# self.beginInsertRows(QModelIndex(), len(self._root.children), len(self._root.children)) -# self._root.append_child(node) -# self.endInsertRows() - -# elif isinstance(action, (SetAttributeAction, DeleteAttributeAction)): -# for c in filter(lambda x: isinstance(x, _Catalogue), action.entities): -# assert isinstance(c, _Catalogue) -# for row, child in enumerate(self._root.children): -# if isinstance(child, CatalogNode) and child.uuid == c.uuid: -# child.node = c -# index = self.index(row, 0, QModelIndex()) -# self.dataChanged.emit(index, index) # type: ignore - -# for row, child in enumerate(self._trash.children): -# if isinstance(child, CatalogNode) and child.uuid == c.uuid: -# child.node = c -# index = self.index(row, 0, self._trash_index()) -# self.dataChanged.emit(index, index) # type: ignore - -# elif isinstance(action, ImportCanonicalizedDictAction): -# self.beginInsertRows(QModelIndex(), -# len(self._root.children), -# len(self._root.children) + len(action.catalogues) - 1) -# self._root.append_children(list(map(CatalogNode, action.catalogues))) -# self.endInsertRows() + elif isinstance(action, GetCatalogueAction): + # also search in Trash + node = self._node_from_uuid(action.uuid) + index = self._index_from_node(node) + self.dataChanged.emit(index, index) # type: ignore + + elif isinstance(action, CreateEntityAction): + if isinstance(action.entity, _Catalogue): + node = CatalogNode(action.entity) + self._insert_catalogue_node_at_node_path_or_trash(node) + + elif isinstance(action, (RemoveEntitiesAction, DeletePermanentlyAction)): + for uuid in action.uuids: + node = self._node_from_uuid(uuid) + index = self._index_from_node(node) + + self.beginRemoveRows(index.parent(), index.row(), index.row()) + node.parent.remove_child(node) + self.endRemoveRows() + + # in case a catalogueModel was created for this catalogue, remove it + if uuid in self._catalogues: + del self._catalogues[uuid] + + elif isinstance(action, (MoveToTrashAction, RestoreFromTrashAction)): # Move to Trash exists only for Catalogs + for uuid, entity in zip(action.uuids, action.entities): + node = self._node_from_uuid(uuid) + index = self._index_from_node(node) + + self.beginRemoveRows(index.parent(), index.row(), index.row()) + node.parent.remove_child(node) + self.endRemoveRows() + + node.node = entity # update the node's entity to the new one with the removed flag set + + self._insert_catalogue_node_at_node_path_or_trash(node) + + elif isinstance(action, RestorePermanentlyDeletedAction): + for e in action.deleted_entities: + if isinstance(e.restored_entity, _Catalogue): + node = CatalogNode(e.restored_entity) + self._insert_catalogue_node_at_node_path_or_trash(node) + + elif isinstance(action, (SetAttributeAction, DeleteAttributeAction)): + for c in filter(lambda x: isinstance(x, _Catalogue), action.entities): + node = self._node_from_uuid(c.uuid) + node.node = c + + index = self._index_from_node(node) + self.dataChanged.emit(index, index) # type: ignore + + elif isinstance(action, ImportCanonicalizedDictAction): + for node in list(map(CatalogNode, action.catalogues)): + self._insert_catalogue_node_at_node_path_or_trash(node) + + def _insert_catalogue_node_at_node_path_or_trash(self, node: CatalogNode) -> None: + if node.node.is_removed(): # undelete to Trash + parent_node = self._trash + else: + parent_node = self._node_from_catalogue_path(node.node) + parent_index = self._index_from_node(parent_node) + + self.beginInsertRows(parent_index, len(parent_node.children), len(parent_node.children)) + parent_node.append_child(node) + self.endInsertRows() def catalog(self, uuid: str) -> CatalogModel: if uuid not in self._catalogues: @@ -177,16 +179,20 @@ def catalog(self, uuid: str) -> CatalogModel: return self._catalogues[uuid] - def index_from_uuid(self, uuid: str, parent=QModelIndex()) -> QModelIndex: - for i in range(self.rowCount(parent)): - index = self.index(i, 0, parent) - if self.data(index, UUIDDataRole) == uuid: - return index - if self.rowCount(index) > 0: - result = self.index_from_uuid(uuid, index) - if result != QModelIndex(): - return result - return QModelIndex() + def current_path(self, index: QModelIndex) -> List[str]: + if not index.isValid(): + return [] + + # get the path starting with the parent if the index is a catalogue else starting with the index + if index.data(EntityRole) is not None: + index = index.parent() + + path = [] + + while index.isValid(): + path.append(index.data()) + index = index.parent() + return path[::-1] def index(self, row: int, column: int, parent: Union[QModelIndex, QPersistentModelIndex] = QModelIndex()) -> QModelIndex: @@ -241,6 +247,7 @@ def data(self, index: Union[QModelIndex, QPersistentModelIndex], elif role == EntityRole: if isinstance(item, CatalogNode): return item.node + return None def flags(self, index: Union[QModelIndex, QPersistentModelIndex]) -> Qt.ItemFlag: if index.isValid(): diff --git a/tscat_gui/undo.py b/tscat_gui/undo.py index a6265f8..a105b0f 100644 --- a/tscat_gui/undo.py +++ b/tscat_gui/undo.py @@ -34,7 +34,10 @@ def _select(self, uuids: List[str], if type == tscat._Event: self.state.updated('passive_select', tscat._Catalogue, self._select_state.selected_catalogues) + self.state.set_catalogue_path(self._select_state.catalogue_path) self.state.updated('active_select', type, uuids) + if type == tscat._Catalogue: + print("TODO update path in select_state") def redo(self) -> None: self._redo() @@ -157,17 +160,20 @@ def __init__(self, state: AppState, parent=None) -> None: def _redo(self) -> None: def creation_callback(action: CreateEntityAction) -> None: + print("New Catalogue created", action.entity) assert action.entity is not None self.uuid = action.entity.uuid assert self.uuid is not None self._select([self.uuid], tscat._Catalogue) + print(self.state.current_catalogue_path()) from .tscat_driver.model import tscat_model tscat_model.do(CreateEntityAction(creation_callback, tscat._Catalogue, { 'name': "New Catalogue", 'author': os.getlogin(), - 'uuid': self.uuid + 'uuid': self.uuid, + 'Path': self.state.current_catalogue_path() })) def _undo(self) -> None: