Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Nuke: Push to project - AY-742 #6245

Merged
merged 7 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions openpype/hosts/nuke/api/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
current_file
)
from .constants import ASSIST
from . import push_to_project

log = Logger.get_logger(__name__)

Expand Down Expand Up @@ -337,6 +338,10 @@ def _install_menu():
"Experimental tools...",
lambda: host_tools.show_experimental_tools_dialog(parent=main_window)
)
menu.addCommand(
"Push to Project",
lambda: push_to_project.main()
)
menu.addSeparator()
# add reload pipeline only in debug mode
if bool(os.getenv("NUKE_DEBUG")):
Expand Down
116 changes: 116 additions & 0 deletions openpype/hosts/nuke/api/push_to_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from collections import defaultdict
import shutil
import os

from openpype.client import get_project, get_asset_by_id
from openpype.settings import get_system_settings, get_project_settings
from openpype.pipeline import Anatomy, registered_host
from openpype.pipeline.template_data import get_template_data
from openpype.pipeline.workfile import get_workdir_with_workdir_data
from openpype.tools.push_to_project.app import show

from .utils import bake_gizmos_recursively

import nuke


def bake_container(container):
"""Bake containers to read nodes."""

node = container["node"]

# Fetch knobs to remove in order.
knobs_to_remove = []
remove = False
for count in range(0, node.numKnobs()):
knob = node.knob(count)

# All knobs from "OpenPype" tab knob onwards.
if knob.name() == "OpenPype":
remove = True

if remove:
knobs_to_remove.append(knob)

# Dont remove knobs from "containerId" onwards.
if knob.name() == "containerId":
remove = False

# Knobs needs to be remove in reverse order, because child knobs needs to
# be remove first.
for knob in reversed(knobs_to_remove):
node.removeKnob(knob)

node["tile_color"].setValue(0)


def main():
context = show("", "", False, True)

if context is None:
return

# Get workfile path to save to.
project_name = context["project_name"]
project_doc = get_project(project_name)
asset_doc = get_asset_by_id(project_name, context["asset_id"])
task_name = context["task_name"]
host = registered_host()
system_settings = get_system_settings()
project_settings = get_project_settings(project_name)
anatomy = Anatomy(project_name)

workdir_data = get_template_data(
project_doc, asset_doc, task_name, host.name, system_settings
)

workdir = get_workdir_with_workdir_data(
workdir_data,
project_name,
anatomy,
project_settings=project_settings
)

# Save current workfile.
current_file = host.current_file()
host.save_file(current_file)

for container in host.ls():
bake_container(container)

# Bake gizmos.
bake_gizmos_recursively()

# Copy all read node files to "resources" folder next to workfile and
# change file path.
first_frame = int(nuke.root()["first_frame"].value())
last_frame = int(nuke.root()["last_frame"].value())
files_by_node_name = defaultdict(set)
nodes_by_name = {}
for count in range(first_frame, last_frame + 1):
nuke.frame(count)
for node in nuke.allNodes(filter="Read"):
files_by_node_name[node.name()].add(
nuke.filename(node, nuke.REPLACE)
)
nodes_by_name[node.name()] = node

resources_dir = os.path.join(workdir, "resources")
for name, files in files_by_node_name.items():
dir = os.path.join(resources_dir, name)
if not os.path.exists(dir):
os.makedirs(dir)

for f in files:
shutil.copy(f, os.path.join(dir, os.path.basename(f)))

node = nodes_by_name[name]
path = node["file"].value().replace(os.path.dirname(f), dir)
node["file"].setValue(path.replace("\\", "/"))

# Save current workfile to new context.
basename = os.path.basename(current_file)
host.save_file(os.path.join(workdir, basename))

# Open current contex workfile.
host.open_file(current_file)
26 changes: 18 additions & 8 deletions openpype/tools/push_to_project/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@
from openpype.tools.push_to_project.window import PushToContextSelectWindow


def show(project, version, library_filter, context_only):
window = PushToContextSelectWindow(
library_filter=library_filter, context_only=context_only
)
window.show()
window.controller.set_source(project, version)

if __name__ == "__main__":
app = get_openpype_qt_app()
app.exec_()
else:
window.exec_()

return window.context


@click.command()
@click.option("--project", help="Source project name")
@click.option("--version", help="Source version id")
Expand All @@ -13,15 +29,9 @@ def main(project, version):
Args:
project (str): Source project name.
version (str): Version id.
version (bool): Filter to library projects only.
"""

app = get_openpype_qt_app()

window = PushToContextSelectWindow()
window.show()
window.controller.set_source(project, version)

app.exec_()
show(project, version, True, False)


if __name__ == "__main__":
Expand Down
18 changes: 13 additions & 5 deletions openpype/tools/push_to_project/control_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,13 @@ def from_asset_doc(cls, asset_doc, project_doc):


class EntitiesModel:
def __init__(self, event_system):
def __init__(self, event_system, library_filter=True):
self._event_system = event_system
self._project_names = None
self._project_docs_by_name = {}
self._assets_by_project = {}
self._tasks_by_asset_id = collections.defaultdict(dict)
self.library_filter = library_filter

def has_cached_projects(self):
return self._project_names is None
Expand Down Expand Up @@ -135,7 +136,7 @@ def refresh_projects(self, force=False):
project_docs_by_name = {}
for project_doc in get_projects():
library_project = project_doc["data"].get("library_project")
if not library_project:
if not library_project and self.library_filter:
continue
project_name = project_doc["name"]
project_names.append(project_name)
Expand Down Expand Up @@ -366,15 +367,19 @@ def set_comment(self, comment):


class PushToContextController:
def __init__(self, project_name=None, version_id=None):
def __init__(
self, project_name=None, version_id=None, library_filter=True
):
self._src_project_name = None
self._src_version_id = None
self._src_asset_doc = None
self._src_subset_doc = None
self._src_version_doc = None

event_system = EventSystem()
entities_model = EntitiesModel(event_system)
entities_model = EntitiesModel(
event_system, library_filter=library_filter
)
selection_model = SelectionModel(event_system)
user_values = UserPublishValues(event_system)

Expand Down Expand Up @@ -629,13 +634,16 @@ def get_selected_asset_name(self):
return asset_item.name
return None

def submit(self, wait=True):
def submit(self, wait=True, context_only=False):
if not self.submission_enabled:
return

if self._process_thread is not None:
return

if context_only:
return

item = ProjectPushItem(
self.src_project_name,
self.src_version_id,
Expand Down
39 changes: 29 additions & 10 deletions openpype/tools/push_to_project/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,16 @@ def _refresh(self, new_project_name):
self.items_changed.emit()


class PushToContextSelectWindow(QtWidgets.QWidget):
def __init__(self, controller=None):
class PushToContextSelectWindow(QtWidgets.QDialog):
def __init__(
self, controller=None, library_filter=True, context_only=False
):
super(PushToContextSelectWindow, self).__init__()
if controller is None:
controller = PushToContextController()
controller = PushToContextController(library_filter=library_filter)
self._controller = controller
self.context_only = context_only
self.context = None

self.setWindowTitle("Push to project (select context)")
self.setWindowIcon(QtGui.QIcon(get_app_icon_path()))
Expand Down Expand Up @@ -455,13 +459,13 @@ def __init__(self, controller=None):
# --- Buttons widget ---
btns_widget = QtWidgets.QWidget(self)
cancel_btn = QtWidgets.QPushButton("Cancel", btns_widget)
publish_btn = QtWidgets.QPushButton("Publish", btns_widget)
push_btn = QtWidgets.QPushButton("Push", btns_widget)

btns_layout = QtWidgets.QHBoxLayout(btns_widget)
btns_layout.setContentsMargins(0, 0, 0, 0)
btns_layout.addStretch(1)
btns_layout.addWidget(cancel_btn, 0)
btns_layout.addWidget(publish_btn, 0)
btns_layout.addWidget(push_btn, 0)

sep_1 = SeparatorWidget(parent=main_context_widget)
sep_2 = SeparatorWidget(parent=main_context_widget)
Expand Down Expand Up @@ -535,7 +539,7 @@ def __init__(self, controller=None):
self._on_task_change
)
task_model.items_changed.connect(self._on_task_model_change)
publish_btn.clicked.connect(self._on_select_click)
push_btn.clicked.connect(self._on_select_click)
cancel_btn.clicked.connect(self._on_close_click)
overlay_close_btn.clicked.connect(self._on_close_click)
overlay_try_btn.clicked.connect(self._on_try_again_click)
Expand Down Expand Up @@ -588,7 +592,7 @@ def __init__(self, controller=None):
self._asset_name_input = asset_name_input
self._comment_input = comment_input

self._publish_btn = publish_btn
self._push_btn = push_btn

self._overlay_widget = overlay_widget
self._overlay_close_btn = overlay_close_btn
Expand All @@ -612,7 +616,7 @@ def __init__(self, controller=None):
self._last_submit_message = None
self._process_item = None

publish_btn.setEnabled(False)
push_btn.setEnabled(False)
overlay_close_btn.setVisible(False)
overlay_try_btn.setVisible(False)

Expand Down Expand Up @@ -774,13 +778,28 @@ def _on_task_change(self):
self._controller.selection_model.select_task(task_name)

def _on_submission_change(self, event):
self._publish_btn.setEnabled(event["enabled"])
self._push_btn.setEnabled(event["enabled"])

def _on_close_click(self):
self.close()

def _on_select_click(self):
self._process_item = self._controller.submit(wait=False)
result = self._controller.submit(
wait=True, context_only=self.context_only
)

if self.context_only:
self.context = {
"project_name": self._controller.selection_model.project_name,
"asset_id": self._controller.selection_model.asset_id,
"task_name": self._controller.selection_model.task_name,
"variant": self._controller.user_values.variant,
"comment": self._controller.user_values.comment,
"asset_name": self._controller.user_values.new_asset_name
}
self.close()

self._process_item = result

def _on_try_again_click(self):
self._process_item = None
Expand Down
Loading