Skip to content

Commit

Permalink
Merge pull request getavalon#484 from davidlatwe/porting#438
Browse files Browse the repository at this point in the history
Porting#438
  • Loading branch information
davidlatwe authored Dec 24, 2019
2 parents a5b2733 + 544c4df commit d380d88
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 126 deletions.
4 changes: 4 additions & 0 deletions avalon/nuke/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from .lib import (
maintained_selection,
imprint,
read,

add_publish_knob,
Expand Down Expand Up @@ -33,6 +34,7 @@
containerise,
parse_container,
update_container,
get_handles,

# Experimental
viewer_update_and_undo_stop,
Expand All @@ -59,7 +61,9 @@
"containerise",
"parse_container",
"update_container",
"get_handles",

"imprint",
"read",

# Experimental
Expand Down
136 changes: 136 additions & 0 deletions avalon/nuke/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@

import logging
import contextlib
import nuke

from .. import api, io


log = logging.getLogger(__name__)


def reset_frame_range():
""" Set frame range to current asset
Also it will set a Viewer range with
displayed handles
"""

fps = float(api.Session.get("AVALON_FPS", 25))

nuke.root()["fps"].setValue(fps)
name = api.Session["AVALON_ASSET"]
asset = io.find_one({"name": name, "type": "asset"})
asset_data = asset["data"]

handles = get_handles(asset)

frame_start = int(asset_data.get(
"frameStart",
asset_data.get("edit_in")))

frame_end = int(asset_data.get(
"frameEnd",
asset_data.get("edit_out")))

if not all([frame_start, frame_end]):
missing = ", ".join(["frame_start", "frame_end"])
msg = "'{}' are not set for asset '{}'!".format(missing, name)
log.warning(msg)
nuke.message(msg)
return

frame_start -= handles
frame_end += handles

nuke.root()["first_frame"].setValue(frame_start)
nuke.root()["last_frame"].setValue(frame_end)

# setting active viewers
vv = nuke.activeViewer().node()
vv["frame_range_lock"].setValue(True)
vv["frame_range"].setValue("{0}-{1}".format(
int(asset_data["frameStart"]),
int(asset_data["frameEnd"]))
)


def get_handles(asset):
""" Gets handles data
Arguments:
asset (dict): avalon asset entity
Returns:
handles (int)
"""
data = asset["data"]
if "handles" in data and data["handles"] is not None:
return int(data["handles"])

parent_asset = None
if "visualParent" in data:
vp = data["visualParent"]
if vp is not None:
parent_asset = io.find_one({"_id": io.ObjectId(vp)})

if parent_asset is None:
parent_asset = io.find_one({"_id": io.ObjectId(asset["parent"])})

if parent_asset is not None:
return get_handles(parent_asset)
else:
return 0


def reset_resolution():
"""Set resolution to project resolution."""
project = io.find_one({"type": "project"})
p_data = project["data"]

width = p_data.get("resolution_width",
p_data.get("resolutionWidth"))
height = p_data.get("resolution_height",
p_data.get("resolutionHeight"))

if not all([width, height]):
missing = ", ".join(["width", "height"])
msg = "No resolution information `{0}` found for '{1}'.".format(
missing,
project["name"])
log.warning(msg)
nuke.message(msg)
return

current_width = nuke.root()["format"].value().width()
current_height = nuke.root()["format"].value().height()

if width != current_width or height != current_height:

fmt = None
for f in nuke.formats():
if f.width() == width and f.height() == height:
fmt = f.name()

if not fmt:
nuke.addFormat(
"{0} {1} {2}".format(int(width), int(height), project["name"])
)
fmt = project["name"]

nuke.root()["format"].setValue(fmt)


@contextlib.contextmanager
def viewer_update_and_undo_stop():
"""Lock viewer from updating and stop recording undo steps"""
try:
# stop active viewer to update any change
viewer = nuke.activeViewer()
if viewer:
viewer.stop()
else:
log.warning("No available active Viewer")
nuke.Undo.disable()
yield
finally:
nuke.Undo.enable()
113 changes: 90 additions & 23 deletions avalon/nuke/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@
import logging
from collections import OrderedDict

from ..vendor import six
from ..vendor import six, clique

log = logging.getLogger(__name__)


@contextlib.contextmanager
def maintained_selection():
"""Maintain selection during context
Example:
>>> with maintained_selection():
... node['selected'].setValue(True)
>>> print(node['selected'].value())
False
"""
previous_selection = nuke.selectedNodes()
try:
yield
Expand All @@ -24,6 +32,25 @@ def maintained_selection():
[n['selected'].setValue(True) for n in previous_selection]


def reset_selection():
"""Deselect all selected nodes
"""
for node in nuke.selectedNodes():
node["selected"] = False


def select_nodes(nodes):
"""Selects all inputed nodes
Arguments:
nodes (list): nuke nodes to be selected
"""
assert isinstance(nodes, (list, tuple)), "nodes has to be list or tuple"

for node in nodes:
node["selected"].setValue(True)


def imprint(node, data, tab=None):
"""Store attributes with value on node
Expand Down Expand Up @@ -357,20 +384,26 @@ def get_avalon_knob_data(node, prefix="avalon:"):


def fix_data_for_node_create(data):
"""Fixing data to be used for nuke knobs
"""
for k, v in data.items():

data[k] = str(v)

if "True" in v:
data[k] = True
if "False" in v:
data[k] = False
if "0x" in v:
if isinstance(v, six.text_type):
data[k] = str(v)
if str(v).startswith("0x"):
data[k] = int(v, 16)
return data


def add_write_node(name, **kwarg):
"""Adding nuke write node
Arguments:
name (str): nuke node name
kwarg (attrs): data for nuke knobs
Returns:
node (obj): nuke write node
"""
frame_range = kwarg.get("frame_range", None)

w = nuke.createNode(
Expand All @@ -394,31 +427,30 @@ def add_write_node(name, **kwarg):
w["first"].setValue(frame_range[0])
w["last"].setValue(frame_range[1])

log.info(w)
return w


def get_node_path(path, padding=4):
"""Get filename for the Nuke write with padded number as '#'
>>> get_frame_path("test.exr")
('test', 4, '.exr')
>>> get_frame_path("filename.#####.tif")
('filename.', 5, '.tif')
>>> get_frame_path("foobar##.tif")
('foobar', 2, '.tif')
>>> get_frame_path("foobar_%08d.tif")
('foobar_', 8, '.tif')
Args:
Arguments:
path (str): The path to render to.
Returns:
tuple: head, padding, tail (extension)
Examples:
>>> get_frame_path("test.exr")
('test', 4, '.exr')
>>> get_frame_path("filename.#####.tif")
('filename.', 5, '.tif')
>>> get_frame_path("foobar##.tif")
('foobar', 2, '.tif')
>>> get_frame_path("foobar_%08d.tif")
('foobar_', 8, '.tif')
"""
filename, ext = os.path.splitext(path)

Expand All @@ -440,3 +472,38 @@ def get_node_path(path, padding=4):
filename = filename.replace(match.group(1), '')

return filename, padding, ext


def ls_img_sequence(path):
"""Listing all available coherent image sequence from path
Arguments:
path (str): A nuke's node object
Returns:
data (dict): with nuke formated path and frameranges
"""
file = os.path.basename(path)
dir = os.path.dirname(path)
base, ext = os.path.splitext(file)
name, padding = os.path.splitext(base)

# populate list of files
files = [f for f in os.listdir(dir)
if name in f
if ext in f]

# create collection from list of files
collections, reminder = clique.assemble(files)

if len(collections) > 0:
head = collections[0].format("{head}")
padding = collections[0].format("{padding}") % 1
padding = "#" * len(padding)
tail = collections[0].format("{tail}")
file = head + padding + tail

return {"path": os.path.join(dir, file).replace("\\", "/"),
"frames": collections[0].format("[{ranges}]")}
else:
return False
Loading

0 comments on commit d380d88

Please sign in to comment.