diff --git a/client/ayon_core/hosts/unreal/api/hierarchy.py b/client/ayon_core/hosts/unreal/api/hierarchy.py index 3fa8658f80..3dd6fe7d72 100644 --- a/client/ayon_core/hosts/unreal/api/hierarchy.py +++ b/client/ayon_core/hosts/unreal/api/hierarchy.py @@ -1,9 +1,20 @@ -import os +from qtpy import QtWidgets, QtCore, QtGui from ayon_api import get_folders_hierarchy +from ayon_core import ( + resources, + style +) from ayon_core.pipeline import get_current_project_name -from ayon_core.tools.utils import show_message_dialog from ayon_core.settings import get_project_settings +from ayon_core.tools.utils import ( + show_message_dialog, + PlaceholderLineEdit, + SquareButton, +) +from ayon_core.tools.ayon_utils.widgets import ( + SimpleFoldersWidget, +) from ayon_core.hosts.unreal.api.pipeline import ( generate_sequence, set_sequence_hierarchy, @@ -12,6 +23,96 @@ from ayon_core.hosts.unreal.api.pipeline import ( import unreal +class ConfirmButton(SquareButton): + def __init__(self, parent=None): + super(ConfirmButton, self).__init__(parent) + self.setText("Confirm") + + +class FolderSelector(QtWidgets.QWidget): + """Widget for selecting a folder from the project hierarchy.""" + + confirm_btn = None + + def __init__(self, controller=None, parent=None, project=None): + if not project: + raise ValueError("Project name not provided.") + + super(FolderSelector, self).__init__(parent) + + icon = QtGui.QIcon(resources.get_ayon_icon_filepath()) + self.setWindowIcon(icon) + self.setWindowTitle("Folder Selector") + self.setFocusPolicy(QtCore.Qt.StrongFocus) + self.setAttribute(QtCore.Qt.WA_DeleteOnClose, False) + + self.setStyleSheet(style.load_stylesheet()) + + # Allow minimize + self.setWindowFlags( + QtCore.Qt.Window + | QtCore.Qt.CustomizeWindowHint + | QtCore.Qt.WindowTitleHint + | QtCore.Qt.WindowMinimizeButtonHint + | QtCore.Qt.WindowCloseButtonHint + ) + + content_body = QtWidgets.QWidget(self) + + # Folders + folders_wrapper = QtWidgets.QWidget(content_body) + + folders_filter_text = PlaceholderLineEdit(folders_wrapper) + folders_filter_text.setPlaceholderText("Filter folders...") + + folders_widget = SimpleFoldersWidget( + controller=None, parent=folders_wrapper) + folders_widget.set_project_name(project_name=project) + + folders_wrapper_layout = QtWidgets.QVBoxLayout(folders_wrapper) + folders_wrapper_layout.setContentsMargins(0, 0, 0, 0) + folders_wrapper_layout.addWidget(folders_filter_text, 0) + folders_wrapper_layout.addWidget(folders_widget, 1) + + # Footer + footer_widget = QtWidgets.QWidget(content_body) + + self.confirm_btn = ConfirmButton(footer_widget) + + footer_layout = QtWidgets.QHBoxLayout(footer_widget) + footer_layout.setContentsMargins(0, 0, 0, 0) + footer_layout.addWidget(self.confirm_btn, 0) + + # Main layout + content_layout = QtWidgets.QVBoxLayout(content_body) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.addWidget(folders_wrapper, 1) + content_layout.addWidget(footer_widget, 0) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(content_body, 1) + + folders_filter_text.textChanged.connect( + self._on_filter_text_changed) + + self._controller = controller + + self._confirm_btn = self.confirm_btn + self._folders_widget = folders_widget + + self.resize(300, 400) + + self.show() + self.raise_() + self.activateWindow() + + def _on_filter_text_changed(self, text): + self._folders_widget.set_name_filter(text) + + def get_selected_folder(self): + return self._folders_widget.get_selected_folder_path() + + def get_default_sequence_path(settings): """Get default render folder from blender settings.""" @@ -43,6 +144,17 @@ def _create_sequence( element, sequence_path, master_level, parent_path="", parents_sequence=[], parents_frame_range=[] ): + """ + Create sequences from the hierarchy element. + + Args: + element (dict): The hierarchy element. + sequence_path (str): The sequence path. + master_level (str): The master level package. + parent_path (str): The parent path. + parents_sequence (list): The list of parent sequences. + parents_frame_range (list): The list of parent frame ranges. + """ name = element["name"] path = f"{parent_path}/{name}" hierarchy_dir = f"{sequence_path}{path}" @@ -79,21 +191,30 @@ def _create_sequence( [level]) -def build_sequence_hierarchy(): +def _find_in_hierarchy(hierarchy, path): """ - Builds the sequence hierarchy by creating sequences from the root element. + Find the hierarchy element from the path. - Raises: - ValueError: If the sequence root element is not found in the hierarchy. + Args: + hierarchy (list): The hierarchy list. + path (str): The path to find. """ - print("Building sequence hierarchy...") + elements = path.split("/") + current_element = elements[0] - project = get_current_project_name() + for element in hierarchy: + if element["name"] == current_element: + if len(elements) == 1: + return element - settings = get_project_settings(project) - sequence_path = get_default_sequence_path(settings) + remaining_path = "/".join(elements[1:]) + return _find_in_hierarchy(element["children"], remaining_path) - sequence_root_name = "shots" + return None + + +def _on_confirm_clicked(selected_root, sequence_path, project): + sequence_root_name = selected_root.lstrip("/") sequence_root = f"{sequence_path}/{sequence_root_name}" asset_content = unreal.EditorAssetLibrary.list_assets( @@ -114,14 +235,10 @@ def build_sequence_hierarchy(): hierarchy = get_folders_hierarchy(project_name=project)["hierarchy"] # Find the sequence root element in the hierarchy - sequence_root = next(( - element - for element in hierarchy - if element["name"] == sequence_root_name - ), None) + hierarchy_element = _find_in_hierarchy(hierarchy, sequence_root_name) # Raise an error if the sequence root element is not found - if not sequence_root: + if not hierarchy_element: raise ValueError(f"Could not find {sequence_root_name} in hierarchy") # Create the master level @@ -130,7 +247,7 @@ def build_sequence_hierarchy(): unreal.EditorLevelLibrary.new_level(master_level_path) # Start creating sequences from the root element - _create_sequence(sequence_root, sequence_path, master_level_package) + _create_sequence(hierarchy_element, sequence_path, master_level_package) # List all the assets in the sequence path and save them asset_content = unreal.EditorAssetLibrary.list_assets( @@ -142,3 +259,25 @@ def build_sequence_hierarchy(): # Load the master level unreal.EditorLevelLibrary.load_level(master_level_package) + + +def build_sequence_hierarchy(): + """ + Builds the sequence hierarchy by creating sequences from the root element. + + Raises: + ValueError: If the sequence root element is not found in the hierarchy. + """ + print("Building sequence hierarchy...") + + project = get_current_project_name() + + settings = get_project_settings(project) + sequence_path = get_default_sequence_path(settings) + + folder_selector = FolderSelector(project=project) + + folder_selector.confirm_btn.clicked.connect( + lambda: _on_confirm_clicked( + folder_selector.get_selected_folder(), sequence_path, project) + )