diff --git a/client/ayon_core/tools/subsetmanager/README.md b/client/ayon_core/tools/subsetmanager/README.md deleted file mode 100644 index 35b80ea114..0000000000 --- a/client/ayon_core/tools/subsetmanager/README.md +++ /dev/null @@ -1,19 +0,0 @@ -Subset manager --------------- - -Simple UI showing list of created subset that will be published via Pyblish. -Useful for applications (Photoshop, AfterEffects, TVPaint, Harmony) which are -storing metadata about instance hidden from user. - -This UI allows listing all created subset and removal of them if needed ( -in case use doesn't want to publish anymore, its using workfile as a starting -file for different task and instances should be completely different etc. -) - -Host is expected to implemented: -- `list_instances` - returning list of dictionaries (instances), must contain - unique uuid field - example: - ```[{"uuid":"15","active":true,"subset":"imageBG","family":"image","id":"ayon.create.instance","asset":"Town"}]``` -- `remove_instance(instance)` - removes instance from file's metadata - instance is a dictionary, with uuid field \ No newline at end of file diff --git a/client/ayon_core/tools/subsetmanager/__init__.py b/client/ayon_core/tools/subsetmanager/__init__.py deleted file mode 100644 index 6cfca7db66..0000000000 --- a/client/ayon_core/tools/subsetmanager/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .window import ( - show, - SubsetManagerWindow -) - -__all__ = ( - "show", - "SubsetManagerWindow" -) diff --git a/client/ayon_core/tools/subsetmanager/model.py b/client/ayon_core/tools/subsetmanager/model.py deleted file mode 100644 index 4964abd86d..0000000000 --- a/client/ayon_core/tools/subsetmanager/model.py +++ /dev/null @@ -1,56 +0,0 @@ -import uuid - -from qtpy import QtCore, QtGui - -from ayon_core.pipeline import registered_host - -ITEM_ID_ROLE = QtCore.Qt.UserRole + 1 - - -class InstanceModel(QtGui.QStandardItemModel): - def __init__(self, *args, **kwargs): - super(InstanceModel, self).__init__(*args, **kwargs) - self._instances_by_item_id = {} - - def get_instance_by_id(self, item_id): - return self._instances_by_item_id.get(item_id) - - def refresh(self): - self.clear() - - self._instances_by_item_id = {} - - instances = None - host = registered_host() - list_instances = getattr(host, "list_instances", None) - if list_instances: - instances = list_instances() - - if not instances: - return - - items = [] - for instance_data in instances: - item_id = str(uuid.uuid4()) - product_name = ( - instance_data.get("productName") - or instance_data.get("subset") - ) - label = instance_data.get("label") or product_name - item = QtGui.QStandardItem(label) - item.setEnabled(True) - item.setEditable(False) - item.setData(item_id, ITEM_ID_ROLE) - items.append(item) - self._instances_by_item_id[item_id] = instance_data - - if items: - self.invisibleRootItem().appendRows(items) - - def headerData(self, section, orientation, role): - if role == QtCore.Qt.DisplayRole and section == 0: - return "Instance" - - return super(InstanceModel, self).headerData( - section, orientation, role - ) diff --git a/client/ayon_core/tools/subsetmanager/widgets.py b/client/ayon_core/tools/subsetmanager/widgets.py deleted file mode 100644 index 1067474c44..0000000000 --- a/client/ayon_core/tools/subsetmanager/widgets.py +++ /dev/null @@ -1,110 +0,0 @@ -import json -from qtpy import QtWidgets, QtCore - - -class InstanceDetail(QtWidgets.QWidget): - save_triggered = QtCore.Signal() - - def __init__(self, parent=None): - super(InstanceDetail, self).__init__(parent) - - details_widget = QtWidgets.QPlainTextEdit(self) - details_widget.setObjectName("SubsetManagerDetailsText") - - save_btn = QtWidgets.QPushButton("Save", self) - - self._block_changes = False - self._editable = False - self._item_id = None - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(details_widget, 1) - layout.addWidget(save_btn, 0, QtCore.Qt.AlignRight) - - save_btn.clicked.connect(self._on_save_clicked) - details_widget.textChanged.connect(self._on_text_change) - - self._details_widget = details_widget - self._save_btn = save_btn - - self.set_editable(False) - - def _on_save_clicked(self): - if self.is_valid(): - self.save_triggered.emit() - - def set_editable(self, enabled=True): - self._editable = enabled - self.update_state() - - def update_state(self, valid=None): - editable = self._editable - if not self._item_id: - editable = False - - self._save_btn.setVisible(editable) - self._details_widget.setReadOnly(not editable) - if valid is None: - valid = self.is_valid() - - self._save_btn.setEnabled(valid) - self._set_invalid_detail(valid) - - def _set_invalid_detail(self, valid): - state = "" - if not valid: - state = "invalid" - - current_state = self._details_widget.property("state") - if current_state != state: - self._details_widget.setProperty("state", state) - self._details_widget.style().polish(self._details_widget) - - def set_details(self, container, item_id): - self._item_id = item_id - - text = "Nothing selected" - if item_id: - try: - text = json.dumps(container, indent=4) - except Exception: - text = str(container) - - self._block_changes = True - self._details_widget.setPlainText(text) - self._block_changes = False - - self.update_state() - - def instance_data_from_text(self): - try: - jsoned = json.loads(self._details_widget.toPlainText()) - except Exception: - jsoned = None - return jsoned - - def item_id(self): - return self._item_id - - def is_valid(self): - if not self._item_id: - return True - - value = self._details_widget.toPlainText() - valid = False - try: - jsoned = json.loads(value) - if jsoned and isinstance(jsoned, dict): - valid = True - - except Exception: - pass - return valid - - def _on_text_change(self): - if self._block_changes or not self._item_id: - return - - valid = self.is_valid() - self.update_state(valid) diff --git a/client/ayon_core/tools/subsetmanager/window.py b/client/ayon_core/tools/subsetmanager/window.py deleted file mode 100644 index 164ffa95a7..0000000000 --- a/client/ayon_core/tools/subsetmanager/window.py +++ /dev/null @@ -1,218 +0,0 @@ -import os -import sys - -from qtpy import QtWidgets, QtCore -import qtawesome - -from ayon_core import style -from ayon_core.pipeline import registered_host -from ayon_core.tools.utils import PlaceholderLineEdit -from ayon_core.tools.utils.lib import ( - iter_model_rows, - qt_app_context -) -from ayon_core.tools.utils.models import RecursiveSortFilterProxyModel -from .model import ( - InstanceModel, - ITEM_ID_ROLE -) -from .widgets import InstanceDetail - - -module = sys.modules[__name__] -module.window = None - - -class SubsetManagerWindow(QtWidgets.QDialog): - def __init__(self, parent=None): - super(SubsetManagerWindow, self).__init__(parent=parent) - self.setWindowTitle("Subset Manager 0.1") - self.setObjectName("SubsetManager") - if not parent: - self.setWindowFlags( - self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint - ) - - self.resize(780, 430) - - # Trigger refresh on first called show - self._first_show = True - - left_side_widget = QtWidgets.QWidget(self) - - # Header part - header_widget = QtWidgets.QWidget(left_side_widget) - - # Filter input - filter_input = PlaceholderLineEdit(header_widget) - filter_input.setPlaceholderText("Filter products..") - - # Refresh button - icon = qtawesome.icon("fa.refresh", color="white") - refresh_btn = QtWidgets.QPushButton(header_widget) - refresh_btn.setIcon(icon) - - header_layout = QtWidgets.QHBoxLayout(header_widget) - header_layout.setContentsMargins(0, 0, 0, 0) - header_layout.addWidget(filter_input) - header_layout.addWidget(refresh_btn) - - # Instances view - view = QtWidgets.QTreeView(left_side_widget) - view.setIndentation(0) - view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - - model = InstanceModel(view) - proxy = RecursiveSortFilterProxyModel() - proxy.setSourceModel(model) - proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - - view.setModel(proxy) - - left_side_layout = QtWidgets.QVBoxLayout(left_side_widget) - left_side_layout.setContentsMargins(0, 0, 0, 0) - left_side_layout.addWidget(header_widget) - left_side_layout.addWidget(view) - - details_widget = InstanceDetail(self) - - layout = QtWidgets.QHBoxLayout(self) - layout.addWidget(left_side_widget, 0) - layout.addWidget(details_widget, 1) - - filter_input.textChanged.connect(proxy.setFilterFixedString) - refresh_btn.clicked.connect(self._on_refresh_clicked) - view.clicked.connect(self._on_activated) - view.customContextMenuRequested.connect(self.on_context_menu) - details_widget.save_triggered.connect(self._on_save) - - self._model = model - self._proxy = proxy - self._view = view - self._details_widget = details_widget - self._refresh_btn = refresh_btn - - def _on_refresh_clicked(self): - self.refresh() - - def _on_activated(self, index): - container = None - item_id = None - if index.isValid(): - item_id = index.data(ITEM_ID_ROLE) - container = self._model.get_instance_by_id(item_id) - - self._details_widget.set_details(container, item_id) - - def _on_save(self): - host = registered_host() - if not hasattr(host, "save_instances"): - print("BUG: Host does not have \"save_instances\" method") - return - - current_index = self._view.selectionModel().currentIndex() - if not current_index.isValid(): - return - - item_id = current_index.data(ITEM_ID_ROLE) - if item_id != self._details_widget.item_id(): - return - - item_data = self._details_widget.instance_data_from_text() - new_instances = [] - for index in iter_model_rows(self._model, 0): - _item_id = index.data(ITEM_ID_ROLE) - if _item_id == item_id: - instance_data = item_data - else: - instance_data = self._model.get_instance_by_id(item_id) - new_instances.append(instance_data) - - host.save_instances(new_instances) - - def on_context_menu(self, point): - point_index = self._view.indexAt(point) - item_id = point_index.data(ITEM_ID_ROLE) - instance_data = self._model.get_instance_by_id(item_id) - if instance_data is None: - return - - # Prepare menu - menu = QtWidgets.QMenu(self) - actions = [] - host = registered_host() - if hasattr(host, "remove_instance"): - action = QtWidgets.QAction("Remove instance", menu) - action.setData(host.remove_instance) - actions.append(action) - - if hasattr(host, "select_instance"): - action = QtWidgets.QAction("Select instance", menu) - action.setData(host.select_instance) - actions.append(action) - - if not actions: - actions.append(QtWidgets.QAction("* Nothing to do", menu)) - - for action in actions: - menu.addAction(action) - - # Show menu under mouse - global_point = self._view.mapToGlobal(point) - action = menu.exec_(global_point) - if not action or not action.data(): - return - - # Process action - # TODO catch exceptions - function = action.data() - function(instance_data) - - # Reset modified data - self.refresh() - - def refresh(self): - self._details_widget.set_details(None, None) - self._model.refresh() - - host = registered_host() - dev_mode = os.environ.get("AVALON_DEVELOP_MODE") or "" - editable = False - if dev_mode.lower() in ("1", "yes", "true", "on"): - editable = hasattr(host, "save_instances") - self._details_widget.set_editable(editable) - - def showEvent(self, *args, **kwargs): - super(SubsetManagerWindow, self).showEvent(*args, **kwargs) - if self._first_show: - self._first_show = False - self.setStyleSheet(style.load_stylesheet()) - self.refresh() - - -def show(root=None, debug=False, parent=None): - """Display Scene Inventory GUI - - Arguments: - debug (bool, optional): Run in debug-mode, - defaults to False - parent (QtCore.QObject, optional): When provided parent the interface - to this QObject. - - """ - - try: - module.window.close() - del module.window - except (RuntimeError, AttributeError): - pass - - with qt_app_context(): - window = SubsetManagerWindow(parent) - window.show() - - module.window = window - - # Pull window to the front. - module.window.raise_() - module.window.activateWindow() diff --git a/client/ayon_core/tools/utils/host_tools.py b/client/ayon_core/tools/utils/host_tools.py index 3d356555f3..94e3c946c5 100644 --- a/client/ayon_core/tools/utils/host_tools.py +++ b/client/ayon_core/tools/utils/host_tools.py @@ -33,7 +33,6 @@ class HostToolsHelper: self._loader_tool = None self._creator_tool = None self._publisher_tool = None - self._subset_manager_tool = None self._scene_inventory_tool = None self._experimental_tools_dialog = None @@ -117,28 +116,6 @@ class HostToolsHelper: creator_tool.raise_() creator_tool.activateWindow() - def get_subset_manager_tool(self, parent): - """Create, cache and return subset manager tool window.""" - if self._subset_manager_tool is None: - from ayon_core.tools.subsetmanager import SubsetManagerWindow - - subset_manager_window = SubsetManagerWindow( - parent=parent or self._parent - ) - self._subset_manager_tool = subset_manager_window - - return self._subset_manager_tool - - def show_subset_manager(self, parent=None): - """Show tool display/remove existing created instances.""" - with qt_app_context(): - subset_manager_tool = self.get_subset_manager_tool(parent) - subset_manager_tool.show() - - # Pull window to the front. - subset_manager_tool.raise_() - subset_manager_tool.activateWindow() - def get_scene_inventory_tool(self, parent): """Create, cache and return scene inventory tool window.""" if self._scene_inventory_tool is None: @@ -270,9 +247,6 @@ class HostToolsHelper: elif tool_name == "creator": return self.get_creator_tool(parent, *args, **kwargs) - elif tool_name == "subsetmanager": - return self.get_subset_manager_tool(parent, *args, **kwargs) - elif tool_name == "sceneinventory": return self.get_scene_inventory_tool(parent, *args, **kwargs) @@ -308,9 +282,6 @@ class HostToolsHelper: elif tool_name == "creator": self.show_creator(parent, *args, **kwargs) - elif tool_name == "subsetmanager": - self.show_subset_manager(parent, *args, **kwargs) - elif tool_name == "sceneinventory": self.show_scene_inventory(parent, *args, **kwargs) @@ -383,10 +354,6 @@ def show_creator(parent=None): _SingletonPoint.show_tool_by_name("creator", parent) -def show_subset_manager(parent=None): - _SingletonPoint.show_tool_by_name("subsetmanager", parent) - - def show_scene_inventory(parent=None): _SingletonPoint.show_tool_by_name("sceneinventory", parent)