mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge branch 'develop' of github.com:pypeclub/OpenPype into feature/OP-1117_Disable-workfile-autoload-in-Launcher
This commit is contained in:
commit
90642a191e
75 changed files with 2359 additions and 736 deletions
2
.github/workflows/test_build.yml
vendored
2
.github/workflows/test_build.yml
vendored
|
|
@ -37,6 +37,7 @@ jobs:
|
|||
- name: 🔨 Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
$env:SKIP_THIRD_PARTY_VALIDATION="1"
|
||||
./tools/build.ps1
|
||||
|
||||
Ubuntu-latest:
|
||||
|
|
@ -61,6 +62,7 @@ jobs:
|
|||
|
||||
- name: 🔨 Build
|
||||
run: |
|
||||
export SKIP_THIRD_PARTY_VALIDATION="1"
|
||||
./tools/build.sh
|
||||
|
||||
# MacOS-latest:
|
||||
|
|
|
|||
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,11 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## [3.8.0-nightly.4](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
## [3.8.0-nightly.5](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.7.0...HEAD)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Renamed to proper name [\#2546](https://github.com/pypeclub/OpenPype/pull/2546)
|
||||
- Slack: Add review to notification message [\#2498](https://github.com/pypeclub/OpenPype/pull/2498)
|
||||
|
||||
**🆕 New features**
|
||||
|
|
@ -14,6 +15,8 @@
|
|||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Settings: PathInput strip passed string [\#2550](https://github.com/pypeclub/OpenPype/pull/2550)
|
||||
- General: Validate if current process OpenPype version is requested version [\#2529](https://github.com/pypeclub/OpenPype/pull/2529)
|
||||
- General: Be able to use anatomy data in ffmpeg output arguments [\#2525](https://github.com/pypeclub/OpenPype/pull/2525)
|
||||
- Expose toggle publish plug-in settings for Maya Look Shading Engine Naming [\#2521](https://github.com/pypeclub/OpenPype/pull/2521)
|
||||
- Photoshop: Move implementation to OpenPype [\#2510](https://github.com/pypeclub/OpenPype/pull/2510)
|
||||
|
|
@ -48,6 +51,8 @@
|
|||
|
||||
**Merged pull requests:**
|
||||
|
||||
- General: Fix install thread in igniter [\#2549](https://github.com/pypeclub/OpenPype/pull/2549)
|
||||
- AfterEffects: Move implementation to OpenPype [\#2543](https://github.com/pypeclub/OpenPype/pull/2543)
|
||||
- Fix create zip tool - path argument [\#2522](https://github.com/pypeclub/OpenPype/pull/2522)
|
||||
- General: Modules import function output fix [\#2492](https://github.com/pypeclub/OpenPype/pull/2492)
|
||||
- AE: fix hiding of alert window below Publish [\#2491](https://github.com/pypeclub/OpenPype/pull/2491)
|
||||
|
|
@ -57,10 +62,6 @@
|
|||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.7.0-nightly.14...3.7.0)
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
- General: Default modules hierarchy n2 [\#2368](https://github.com/pypeclub/OpenPype/pull/2368)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- General: Workdir extra folders [\#2462](https://github.com/pypeclub/OpenPype/pull/2462)
|
||||
|
|
@ -79,7 +80,6 @@
|
|||
- Enhancement: Settings: Use project settings values from another project [\#2382](https://github.com/pypeclub/OpenPype/pull/2382)
|
||||
- Blender 3: Support auto install for new blender version [\#2377](https://github.com/pypeclub/OpenPype/pull/2377)
|
||||
- Maya add render image path to settings [\#2375](https://github.com/pypeclub/OpenPype/pull/2375)
|
||||
- Hiero: python3 compatibility [\#2365](https://github.com/pypeclub/OpenPype/pull/2365)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
|
|
@ -97,7 +97,6 @@
|
|||
- hiero: fix workio and flatten [\#2378](https://github.com/pypeclub/OpenPype/pull/2378)
|
||||
- Nuke: fixing menu re-drawing during context change [\#2374](https://github.com/pypeclub/OpenPype/pull/2374)
|
||||
- Webpublisher: Fix assignment of families of TVpaint instances [\#2373](https://github.com/pypeclub/OpenPype/pull/2373)
|
||||
- Nuke: fixing node name based on switched asset name [\#2369](https://github.com/pypeclub/OpenPype/pull/2369)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
|
|
|
|||
|
|
@ -912,7 +912,6 @@ class BootstrapRepos:
|
|||
processed_path = file
|
||||
self._print(f"- processing {processed_path}")
|
||||
|
||||
|
||||
checksums.append(
|
||||
(
|
||||
sha256sum(file.as_posix()),
|
||||
|
|
@ -1544,7 +1543,8 @@ class BootstrapRepos:
|
|||
|
||||
Args:
|
||||
zip_item (Path): Zip file to test.
|
||||
detected_version (OpenPypeVersion): Pype version detected from name.
|
||||
detected_version (OpenPypeVersion): Pype version detected from
|
||||
name.
|
||||
|
||||
Returns:
|
||||
True if it is valid OpenPype version, False otherwise.
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class InstallThread(QThread):
|
|||
# find local version of OpenPype
|
||||
bs = BootstrapRepos(
|
||||
progress_callback=self.set_progress, message=self.message)
|
||||
local_version = bs.get_local_live_version()
|
||||
local_version = OpenPypeVersion.get_installed_version_str()
|
||||
|
||||
# if user did entered nothing, we install OpenPype from local version.
|
||||
# zip content of `repos`, copy it to user data dir and append
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
import importlib
|
||||
from openpype.lib import PreLaunchHook
|
||||
from openpype.lib import PreLaunchHook, ApplicationLaunchFailed
|
||||
from openpype.hosts.fusion.api import utils
|
||||
|
||||
|
||||
|
|
@ -14,25 +14,27 @@ class FusionPrelaunch(PreLaunchHook):
|
|||
def execute(self):
|
||||
# making sure pyton 3.6 is installed at provided path
|
||||
py36_dir = os.path.normpath(self.launch_context.env.get("PYTHON36", ""))
|
||||
assert os.path.isdir(py36_dir), (
|
||||
"Python 3.6 is not installed at the provided folder path. Either "
|
||||
"make sure the `environments\resolve.json` is having correctly "
|
||||
"set `PYTHON36` or make sure Python 3.6 is installed "
|
||||
f"in given path. \nPYTHON36E: `{py36_dir}`"
|
||||
)
|
||||
self.log.info(f"Path to Fusion Python folder: `{py36_dir}`...")
|
||||
if not os.path.isdir(py36_dir):
|
||||
raise ApplicationLaunchFailed(
|
||||
"Python 3.6 is not installed at the provided path.\n"
|
||||
"Either make sure the 'environments/fusion.json' has "
|
||||
"'PYTHON36' set corectly or make sure Python 3.6 is installed "
|
||||
f"in the given path.\n\nPYTHON36: {py36_dir}"
|
||||
)
|
||||
self.log.info(f"Path to Fusion Python folder: '{py36_dir}'...")
|
||||
self.launch_context.env["PYTHON36"] = py36_dir
|
||||
|
||||
# setting utility scripts dir for scripts syncing
|
||||
us_dir = os.path.normpath(
|
||||
self.launch_context.env.get("FUSION_UTILITY_SCRIPTS_DIR", "")
|
||||
)
|
||||
assert os.path.isdir(us_dir), (
|
||||
"Fusion utility script dir does not exists. Either make sure "
|
||||
"the `environments\fusion.json` is having correctly set "
|
||||
"`FUSION_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n"
|
||||
f"FUSION_UTILITY_SCRIPTS_DIR: `{us_dir}`"
|
||||
)
|
||||
if not os.path.isdir(us_dir):
|
||||
raise ApplicationLaunchFailed(
|
||||
"Fusion utility script dir does not exist. Either make sure "
|
||||
"the 'environments/fusion.json' has "
|
||||
"'FUSION_UTILITY_SCRIPTS_DIR' set correctly or reinstall "
|
||||
f"Fusion.\n\nFUSION_UTILITY_SCRIPTS_DIR: '{us_dir}'"
|
||||
)
|
||||
|
||||
try:
|
||||
__import__("avalon.fusion")
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ def add_implementation_envs(env, _app):
|
|||
# Add requirements to NUKE_PATH
|
||||
pype_root = os.environ["OPENPYPE_REPOS_ROOT"]
|
||||
new_nuke_paths = [
|
||||
os.path.join(pype_root, "openpype", "hosts", "nuke", "startup"),
|
||||
os.path.join(
|
||||
pype_root, "repos", "avalon-core", "setup", "nuke", "nuke_path"
|
||||
)
|
||||
os.path.join(pype_root, "openpype", "hosts", "nuke", "startup")
|
||||
]
|
||||
old_nuke_path = env.get("NUKE_PATH") or ""
|
||||
for path in old_nuke_path.split(os.pathsep):
|
||||
|
|
|
|||
|
|
@ -1,130 +1,57 @@
|
|||
import os
|
||||
import nuke
|
||||
from .workio import (
|
||||
file_extensions,
|
||||
has_unsaved_changes,
|
||||
save_file,
|
||||
open_file,
|
||||
current_file,
|
||||
work_root,
|
||||
)
|
||||
|
||||
import avalon.api
|
||||
import pyblish.api
|
||||
import openpype
|
||||
from . import lib, menu
|
||||
from .command import (
|
||||
reset_frame_range,
|
||||
get_handles,
|
||||
reset_resolution,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
log = openpype.api.Logger().get_logger(__name__)
|
||||
from .plugin import OpenPypeCreator
|
||||
from .pipeline import (
|
||||
install,
|
||||
uninstall,
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.nuke.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
|
||||
ls,
|
||||
|
||||
containerise,
|
||||
parse_container,
|
||||
update_container,
|
||||
)
|
||||
from .lib import (
|
||||
maintained_selection
|
||||
)
|
||||
|
||||
|
||||
# registering pyblish gui regarding settings in presets
|
||||
if os.getenv("PYBLISH_GUI", None):
|
||||
pyblish.api.register_gui(os.getenv("PYBLISH_GUI", None))
|
||||
__all__ = (
|
||||
"file_extensions",
|
||||
"has_unsaved_changes",
|
||||
"save_file",
|
||||
"open_file",
|
||||
"current_file",
|
||||
"work_root",
|
||||
|
||||
"reset_frame_range",
|
||||
"get_handles",
|
||||
"reset_resolution",
|
||||
"viewer_update_and_undo_stop",
|
||||
|
||||
def reload_config():
|
||||
"""Attempt to reload pipeline at run-time.
|
||||
"OpenPypeCreator",
|
||||
"install",
|
||||
"uninstall",
|
||||
|
||||
CAUTION: This is primarily for development and debugging purposes.
|
||||
"ls",
|
||||
|
||||
"""
|
||||
"containerise",
|
||||
"parse_container",
|
||||
"update_container",
|
||||
|
||||
import importlib
|
||||
|
||||
for module in (
|
||||
"{}.api".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.actions".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.menu".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.plugin".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.lib".format(AVALON_CONFIG),
|
||||
):
|
||||
log.info("Reloading module: {}...".format(module))
|
||||
|
||||
module = importlib.import_module(module)
|
||||
|
||||
try:
|
||||
importlib.reload(module)
|
||||
except AttributeError as e:
|
||||
from importlib import reload
|
||||
log.warning("Cannot reload module: {}".format(e))
|
||||
reload(module)
|
||||
|
||||
|
||||
def install():
|
||||
''' Installing all requarements for Nuke host
|
||||
'''
|
||||
|
||||
# remove all registred callbacks form avalon.nuke
|
||||
from avalon import pipeline
|
||||
pipeline._registered_event_handlers.clear()
|
||||
|
||||
log.info("Registering Nuke plug-ins..")
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# Register Avalon event for workfiles loading.
|
||||
avalon.api.on("workio.open_file", lib.check_inventory_versions)
|
||||
avalon.api.on("taskChanged", menu.change_context_label)
|
||||
|
||||
pyblish.api.register_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled)
|
||||
workfile_settings = lib.WorkfileSettings()
|
||||
# Disable all families except for the ones we explicitly want to see
|
||||
family_states = [
|
||||
"write",
|
||||
"review",
|
||||
"nukenodes",
|
||||
"model",
|
||||
"gizmo"
|
||||
]
|
||||
|
||||
avalon.api.data["familiesStateDefault"] = False
|
||||
avalon.api.data["familiesStateToggled"] = family_states
|
||||
|
||||
# Set context settings.
|
||||
nuke.addOnCreate(workfile_settings.set_context_settings, nodeClass="Root")
|
||||
nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root")
|
||||
nuke.addOnCreate(lib.process_workfile_builder, nodeClass="Root")
|
||||
nuke.addOnCreate(lib.launch_workfiles_app, nodeClass="Root")
|
||||
menu.install()
|
||||
|
||||
|
||||
def uninstall():
|
||||
'''Uninstalling host's integration
|
||||
'''
|
||||
log.info("Deregistering Nuke plug-ins..")
|
||||
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
|
||||
pyblish.api.deregister_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled)
|
||||
|
||||
reload_config()
|
||||
menu.uninstall()
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
"""Toggle node passthrough states on instance toggles."""
|
||||
|
||||
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
instance, old_value, new_value))
|
||||
|
||||
from avalon.nuke import (
|
||||
viewer_update_and_undo_stop,
|
||||
add_publish_knob
|
||||
)
|
||||
|
||||
# Whether instances should be passthrough based on new value
|
||||
|
||||
with viewer_update_and_undo_stop():
|
||||
n = instance[0]
|
||||
try:
|
||||
n["publish"].value()
|
||||
except ValueError:
|
||||
n = add_publish_knob(n)
|
||||
log.info(" `Publish` knob was added to write node..")
|
||||
|
||||
n["publish"].setValue(new_value)
|
||||
"maintained_selection",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import pyblish.api
|
||||
|
||||
from avalon.nuke.lib import (
|
||||
from openpype.api import get_errored_instances_from_context
|
||||
from .lib import (
|
||||
reset_selection,
|
||||
select_nodes
|
||||
)
|
||||
|
||||
from openpype.api import get_errored_instances_from_context
|
||||
|
||||
|
||||
class SelectInvalidAction(pyblish.api.Action):
|
||||
"""Select invalid nodes in Nuke when plug-in failed.
|
||||
|
|
|
|||
135
openpype/hosts/nuke/api/command.py
Normal file
135
openpype/hosts/nuke/api/command.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import logging
|
||||
import contextlib
|
||||
import nuke
|
||||
|
||||
from avalon 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()
|
||||
|
|
@ -3,15 +3,15 @@ import re
|
|||
import sys
|
||||
import six
|
||||
import platform
|
||||
import contextlib
|
||||
from collections import OrderedDict
|
||||
|
||||
import clique
|
||||
|
||||
import nuke
|
||||
|
||||
from avalon import api, io, lib
|
||||
import avalon.nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import (
|
||||
save_file, open_file
|
||||
)
|
||||
|
||||
from openpype.api import (
|
||||
Logger,
|
||||
Anatomy,
|
||||
|
|
@ -28,21 +28,476 @@ from openpype.lib.path_tools import HostDirmap
|
|||
from openpype.settings import get_project_settings
|
||||
from openpype.modules import ModulesManager
|
||||
|
||||
import nuke
|
||||
from .workio import (
|
||||
save_file,
|
||||
open_file
|
||||
)
|
||||
|
||||
from .utils import set_context_favorites
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
_NODE_TAB_NAME = "{}".format(os.getenv("AVALON_LABEL") or "Avalon")
|
||||
AVALON_LABEL = os.getenv("AVALON_LABEL") or "Avalon"
|
||||
AVALON_TAB = "{}".format(AVALON_LABEL)
|
||||
AVALON_DATA_GROUP = "{}DataGroup".format(AVALON_LABEL.capitalize())
|
||||
EXCLUDED_KNOB_TYPE_ON_READ = (
|
||||
20, # Tab Knob
|
||||
26, # Text Knob (But for backward compatibility, still be read
|
||||
# if value is not an empty string.)
|
||||
)
|
||||
|
||||
opnl = sys.modules[__name__]
|
||||
opnl._project = None
|
||||
opnl.project_name = os.getenv("AVALON_PROJECT")
|
||||
opnl.workfiles_launched = False
|
||||
opnl._node_tab_name = "{}".format(os.getenv("AVALON_LABEL") or "Avalon")
|
||||
|
||||
class Context:
|
||||
main_window = None
|
||||
context_label = None
|
||||
project_name = os.getenv("AVALON_PROJECT")
|
||||
workfiles_launched = False
|
||||
# Seems unused
|
||||
_project_doc = None
|
||||
|
||||
|
||||
class Knobby(object):
|
||||
"""For creating knob which it's type isn't mapped in `create_knobs`
|
||||
|
||||
Args:
|
||||
type (string): Nuke knob type name
|
||||
value: Value to be set with `Knob.setValue`, put `None` if not required
|
||||
flags (list, optional): Knob flags to be set with `Knob.setFlag`
|
||||
*args: Args other than knob name for initializing knob class
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, type, value, flags=None, *args):
|
||||
self.type = type
|
||||
self.value = value
|
||||
self.flags = flags or []
|
||||
self.args = args
|
||||
|
||||
def create(self, name, nice=None):
|
||||
knob_cls = getattr(nuke, self.type)
|
||||
knob = knob_cls(name, nice, *self.args)
|
||||
if self.value is not None:
|
||||
knob.setValue(self.value)
|
||||
for flag in self.flags:
|
||||
knob.setFlag(flag)
|
||||
return knob
|
||||
|
||||
|
||||
def create_knobs(data, tab=None):
|
||||
"""Create knobs by data
|
||||
|
||||
Depending on the type of each dict value and creates the correct Knob.
|
||||
|
||||
Mapped types:
|
||||
bool: nuke.Boolean_Knob
|
||||
int: nuke.Int_Knob
|
||||
float: nuke.Double_Knob
|
||||
list: nuke.Enumeration_Knob
|
||||
six.string_types: nuke.String_Knob
|
||||
|
||||
dict: If it's a nested dict (all values are dict), will turn into
|
||||
A tabs group. Or just a knobs group.
|
||||
|
||||
Args:
|
||||
data (dict): collection of attributes and their value
|
||||
tab (string, optional): Knobs' tab name
|
||||
|
||||
Returns:
|
||||
list: A list of `nuke.Knob` objects
|
||||
|
||||
"""
|
||||
def nice_naming(key):
|
||||
"""Convert camelCase name into UI Display Name"""
|
||||
words = re.findall('[A-Z][^A-Z]*', key[0].upper() + key[1:])
|
||||
return " ".join(words)
|
||||
|
||||
# Turn key-value pairs into knobs
|
||||
knobs = list()
|
||||
|
||||
if tab:
|
||||
knobs.append(nuke.Tab_Knob(tab))
|
||||
|
||||
for key, value in data.items():
|
||||
# Knob name
|
||||
if isinstance(key, tuple):
|
||||
name, nice = key
|
||||
else:
|
||||
name, nice = key, nice_naming(key)
|
||||
|
||||
# Create knob by value type
|
||||
if isinstance(value, Knobby):
|
||||
knobby = value
|
||||
knob = knobby.create(name, nice)
|
||||
|
||||
elif isinstance(value, float):
|
||||
knob = nuke.Double_Knob(name, nice)
|
||||
knob.setValue(value)
|
||||
|
||||
elif isinstance(value, bool):
|
||||
knob = nuke.Boolean_Knob(name, nice)
|
||||
knob.setValue(value)
|
||||
knob.setFlag(nuke.STARTLINE)
|
||||
|
||||
elif isinstance(value, int):
|
||||
knob = nuke.Int_Knob(name, nice)
|
||||
knob.setValue(value)
|
||||
|
||||
elif isinstance(value, six.string_types):
|
||||
knob = nuke.String_Knob(name, nice)
|
||||
knob.setValue(value)
|
||||
|
||||
elif isinstance(value, list):
|
||||
knob = nuke.Enumeration_Knob(name, nice, value)
|
||||
|
||||
elif isinstance(value, dict):
|
||||
if all(isinstance(v, dict) for v in value.values()):
|
||||
# Create a group of tabs
|
||||
begain = nuke.BeginTabGroup_Knob()
|
||||
end = nuke.EndTabGroup_Knob()
|
||||
begain.setName(name)
|
||||
end.setName(name + "_End")
|
||||
knobs.append(begain)
|
||||
for k, v in value.items():
|
||||
knobs += create_knobs(v, tab=k)
|
||||
knobs.append(end)
|
||||
else:
|
||||
# Create a group of knobs
|
||||
knobs.append(nuke.Tab_Knob(
|
||||
name, nice, nuke.TABBEGINCLOSEDGROUP))
|
||||
knobs += create_knobs(value)
|
||||
knobs.append(
|
||||
nuke.Tab_Knob(name + "_End", nice, nuke.TABENDGROUP))
|
||||
continue
|
||||
|
||||
else:
|
||||
raise TypeError("Unsupported type: %r" % type(value))
|
||||
|
||||
knobs.append(knob)
|
||||
|
||||
return knobs
|
||||
|
||||
|
||||
def imprint(node, data, tab=None):
|
||||
"""Store attributes with value on node
|
||||
|
||||
Parse user data into Node knobs.
|
||||
Use `collections.OrderedDict` to ensure knob order.
|
||||
|
||||
Args:
|
||||
node(nuke.Node): node object from Nuke
|
||||
data(dict): collection of attributes and their value
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Examples:
|
||||
```
|
||||
import nuke
|
||||
from avalon.nuke import lib
|
||||
|
||||
node = nuke.createNode("NoOp")
|
||||
data = {
|
||||
# Regular type of attributes
|
||||
"myList": ["x", "y", "z"],
|
||||
"myBool": True,
|
||||
"myFloat": 0.1,
|
||||
"myInt": 5,
|
||||
|
||||
# Creating non-default imprint type of knob
|
||||
"MyFilePath": lib.Knobby("File_Knob", "/file/path"),
|
||||
"divider": lib.Knobby("Text_Knob", ""),
|
||||
|
||||
# Manual nice knob naming
|
||||
("my_knob", "Nice Knob Name"): "some text",
|
||||
|
||||
# dict type will be created as knob group
|
||||
"KnobGroup": {
|
||||
"knob1": 5,
|
||||
"knob2": "hello",
|
||||
"knob3": ["a", "b"],
|
||||
},
|
||||
|
||||
# Nested dict will be created as tab group
|
||||
"TabGroup": {
|
||||
"tab1": {"count": 5},
|
||||
"tab2": {"isGood": True},
|
||||
"tab3": {"direction": ["Left", "Right"]},
|
||||
},
|
||||
}
|
||||
lib.imprint(node, data, tab="Demo")
|
||||
|
||||
```
|
||||
|
||||
"""
|
||||
for knob in create_knobs(data, tab):
|
||||
node.addKnob(knob)
|
||||
|
||||
|
||||
def add_publish_knob(node):
|
||||
"""Add Publish knob to node
|
||||
|
||||
Arguments:
|
||||
node (nuke.Node): nuke node to be processed
|
||||
|
||||
Returns:
|
||||
node (nuke.Node): processed nuke node
|
||||
|
||||
"""
|
||||
if "publish" not in node.knobs():
|
||||
body = OrderedDict()
|
||||
body[("divd", "Publishing")] = Knobby("Text_Knob", '')
|
||||
body["publish"] = True
|
||||
imprint(node, body)
|
||||
return node
|
||||
|
||||
|
||||
def set_avalon_knob_data(node, data=None, prefix="avalon:"):
|
||||
""" Sets data into nodes's avalon knob
|
||||
|
||||
Arguments:
|
||||
node (nuke.Node): Nuke node to imprint with data,
|
||||
data (dict, optional): Data to be imprinted into AvalonTab
|
||||
prefix (str, optional): filtering prefix
|
||||
|
||||
Returns:
|
||||
node (nuke.Node)
|
||||
|
||||
Examples:
|
||||
data = {
|
||||
'asset': 'sq020sh0280',
|
||||
'family': 'render',
|
||||
'subset': 'subsetMain'
|
||||
}
|
||||
"""
|
||||
data = data or dict()
|
||||
create = OrderedDict()
|
||||
|
||||
tab_name = AVALON_TAB
|
||||
editable = ["asset", "subset", "name", "namespace"]
|
||||
|
||||
existed_knobs = node.knobs()
|
||||
|
||||
for key, value in data.items():
|
||||
knob_name = prefix + key
|
||||
gui_name = key
|
||||
|
||||
if knob_name in existed_knobs:
|
||||
# Set value
|
||||
try:
|
||||
node[knob_name].setValue(value)
|
||||
except TypeError:
|
||||
node[knob_name].setValue(str(value))
|
||||
else:
|
||||
# New knob
|
||||
name = (knob_name, gui_name) # Hide prefix on GUI
|
||||
if key in editable:
|
||||
create[name] = value
|
||||
else:
|
||||
create[name] = Knobby("String_Knob",
|
||||
str(value),
|
||||
flags=[nuke.READ_ONLY])
|
||||
if tab_name in existed_knobs:
|
||||
tab_name = None
|
||||
else:
|
||||
tab = OrderedDict()
|
||||
warn = Knobby("Text_Knob", "Warning! Do not change following data!")
|
||||
divd = Knobby("Text_Knob", "")
|
||||
head = [
|
||||
(("warn", ""), warn),
|
||||
(("divd", ""), divd),
|
||||
]
|
||||
tab[AVALON_DATA_GROUP] = OrderedDict(head + list(create.items()))
|
||||
create = tab
|
||||
|
||||
imprint(node, create, tab=tab_name)
|
||||
return node
|
||||
|
||||
|
||||
def get_avalon_knob_data(node, prefix="avalon:"):
|
||||
""" Gets a data from nodes's avalon knob
|
||||
|
||||
Arguments:
|
||||
node (obj): Nuke node to search for data,
|
||||
prefix (str, optional): filtering prefix
|
||||
|
||||
Returns:
|
||||
data (dict)
|
||||
"""
|
||||
|
||||
# check if lists
|
||||
if not isinstance(prefix, list):
|
||||
prefix = list([prefix])
|
||||
|
||||
data = dict()
|
||||
|
||||
# loop prefix
|
||||
for p in prefix:
|
||||
# check if the node is avalon tracked
|
||||
if AVALON_TAB not in node.knobs():
|
||||
continue
|
||||
try:
|
||||
# check if data available on the node
|
||||
test = node[AVALON_DATA_GROUP].value()
|
||||
log.debug("Only testing if data avalable: `{}`".format(test))
|
||||
except NameError as e:
|
||||
# if it doesn't then create it
|
||||
log.debug("Creating avalon knob: `{}`".format(e))
|
||||
node = set_avalon_knob_data(node)
|
||||
return get_avalon_knob_data(node)
|
||||
|
||||
# get data from filtered knobs
|
||||
data.update({k.replace(p, ''): node[k].value()
|
||||
for k in node.knobs().keys()
|
||||
if p in k})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def fix_data_for_node_create(data):
|
||||
"""Fixing data to be used for nuke knobs
|
||||
"""
|
||||
for k, v in data.items():
|
||||
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(
|
||||
"Write",
|
||||
"name {}".format(name))
|
||||
|
||||
w["file"].setValue(kwarg["file"])
|
||||
|
||||
for k, v in kwarg.items():
|
||||
if "frame_range" in k:
|
||||
continue
|
||||
log.info([k, v])
|
||||
try:
|
||||
w[k].setValue(v)
|
||||
except KeyError as e:
|
||||
log.debug(e)
|
||||
continue
|
||||
|
||||
if frame_range:
|
||||
w["use_limit"].setValue(True)
|
||||
w["first"].setValue(frame_range[0])
|
||||
w["last"].setValue(frame_range[1])
|
||||
|
||||
return w
|
||||
|
||||
|
||||
def read(node):
|
||||
"""Return user-defined knobs from given `node`
|
||||
|
||||
Args:
|
||||
node (nuke.Node): Nuke node object
|
||||
|
||||
Returns:
|
||||
list: A list of nuke.Knob object
|
||||
|
||||
"""
|
||||
def compat_prefixed(knob_name):
|
||||
if knob_name.startswith("avalon:"):
|
||||
return knob_name[len("avalon:"):]
|
||||
elif knob_name.startswith("ak:"):
|
||||
return knob_name[len("ak:"):]
|
||||
else:
|
||||
return knob_name
|
||||
|
||||
data = dict()
|
||||
|
||||
pattern = ("(?<=addUserKnob {)"
|
||||
"([0-9]*) (\\S*)" # Matching knob type and knob name
|
||||
"(?=[ |}])")
|
||||
tcl_script = node.writeKnobs(nuke.WRITE_USER_KNOB_DEFS)
|
||||
result = re.search(pattern, tcl_script)
|
||||
|
||||
if result:
|
||||
first_user_knob = result.group(2)
|
||||
# Collect user knobs from the end of the knob list
|
||||
for knob in reversed(node.allKnobs()):
|
||||
knob_name = knob.name()
|
||||
if not knob_name:
|
||||
# Ignore unnamed knob
|
||||
continue
|
||||
|
||||
knob_type = nuke.knob(knob.fullyQualifiedName(), type=True)
|
||||
value = knob.value()
|
||||
|
||||
if (
|
||||
knob_type not in EXCLUDED_KNOB_TYPE_ON_READ or
|
||||
# For compating read-only string data that imprinted
|
||||
# by `nuke.Text_Knob`.
|
||||
(knob_type == 26 and value)
|
||||
):
|
||||
key = compat_prefixed(knob_name)
|
||||
data[key] = value
|
||||
|
||||
if knob_name == first_user_knob:
|
||||
break
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_node_path(path, padding=4):
|
||||
"""Get filename for the Nuke write with padded number as '#'
|
||||
|
||||
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)
|
||||
|
||||
# Find a final number group
|
||||
if '%' in filename:
|
||||
match = re.match('.*?(%[0-9]+d)$', filename)
|
||||
if match:
|
||||
padding = int(match.group(1).replace('%', '').replace('d', ''))
|
||||
# remove number from end since fusion
|
||||
# will swap it with the frame number
|
||||
filename = filename.replace(match.group(1), '')
|
||||
elif '#' in filename:
|
||||
match = re.match('.*?(#+)$', filename)
|
||||
|
||||
if match:
|
||||
padding = len(match.group(1))
|
||||
# remove number from end since fusion
|
||||
# will swap it with the frame number
|
||||
filename = filename.replace(match.group(1), '')
|
||||
|
||||
return filename, padding, ext
|
||||
|
||||
|
||||
def get_nuke_imageio_settings():
|
||||
return get_anatomy_settings(opnl.project_name)["imageio"]["nuke"]
|
||||
return get_anatomy_settings(Context.project_name)["imageio"]["nuke"]
|
||||
|
||||
|
||||
def get_created_node_imageio_setting(**kwarg):
|
||||
|
|
@ -103,14 +558,15 @@ def check_inventory_versions():
|
|||
and check if the node is having actual version. If not then it will color
|
||||
it to red.
|
||||
"""
|
||||
from .pipeline import parse_container
|
||||
|
||||
# get all Loader nodes by avalon attribute metadata
|
||||
for each in nuke.allNodes():
|
||||
container = avalon.nuke.parse_container(each)
|
||||
container = parse_container(each)
|
||||
|
||||
if container:
|
||||
node = nuke.toNode(container["objectName"])
|
||||
avalon_knob_data = avalon.nuke.read(
|
||||
node)
|
||||
avalon_knob_data = read(node)
|
||||
|
||||
# get representation from io
|
||||
representation = io.find_one({
|
||||
|
|
@ -163,11 +619,10 @@ def writes_version_sync():
|
|||
|
||||
for each in nuke.allNodes(filter="Write"):
|
||||
# check if the node is avalon tracked
|
||||
if opnl._node_tab_name not in each.knobs():
|
||||
if _NODE_TAB_NAME not in each.knobs():
|
||||
continue
|
||||
|
||||
avalon_knob_data = avalon.nuke.read(
|
||||
each)
|
||||
avalon_knob_data = read(each)
|
||||
|
||||
try:
|
||||
if avalon_knob_data['families'] not in ["render"]:
|
||||
|
|
@ -209,14 +664,14 @@ def check_subsetname_exists(nodes, subset_name):
|
|||
bool: True of False
|
||||
"""
|
||||
return next((True for n in nodes
|
||||
if subset_name in avalon.nuke.read(n).get("subset", "")),
|
||||
if subset_name in read(n).get("subset", "")),
|
||||
False)
|
||||
|
||||
|
||||
def get_render_path(node):
|
||||
''' Generate Render path from presets regarding avalon knob data
|
||||
'''
|
||||
data = {'avalon': avalon.nuke.read(node)}
|
||||
data = {'avalon': read(node)}
|
||||
data_preset = {
|
||||
"nodeclass": data['avalon']['family'],
|
||||
"families": [data['avalon']['families']],
|
||||
|
|
@ -385,7 +840,7 @@ def create_write_node(name, data, input=None, prenodes=None,
|
|||
for knob in imageio_writes["knobs"]:
|
||||
_data.update({knob["name"]: knob["value"]})
|
||||
|
||||
_data = anlib.fix_data_for_node_create(_data)
|
||||
_data = fix_data_for_node_create(_data)
|
||||
|
||||
log.debug("_data: `{}`".format(_data))
|
||||
|
||||
|
|
@ -466,7 +921,7 @@ def create_write_node(name, data, input=None, prenodes=None,
|
|||
prev_node = now_node
|
||||
|
||||
# creating write node
|
||||
write_node = now_node = anlib.add_write_node(
|
||||
write_node = now_node = add_write_node(
|
||||
"inside_{}".format(name),
|
||||
**_data
|
||||
)
|
||||
|
|
@ -484,8 +939,8 @@ def create_write_node(name, data, input=None, prenodes=None,
|
|||
now_node.setInput(0, prev_node)
|
||||
|
||||
# imprinting group node
|
||||
anlib.set_avalon_knob_data(GN, data["avalon"])
|
||||
anlib.add_publish_knob(GN)
|
||||
set_avalon_knob_data(GN, data["avalon"])
|
||||
add_publish_knob(GN)
|
||||
add_rendering_knobs(GN, farm)
|
||||
|
||||
if review:
|
||||
|
|
@ -537,7 +992,7 @@ def create_write_node(name, data, input=None, prenodes=None,
|
|||
add_deadline_tab(GN)
|
||||
|
||||
# open the our Tab as default
|
||||
GN[opnl._node_tab_name].setFlag(0)
|
||||
GN[_NODE_TAB_NAME].setFlag(0)
|
||||
|
||||
# set tile color
|
||||
tile_color = _data.get("tile_color", "0xff0000ff")
|
||||
|
|
@ -663,7 +1118,7 @@ class WorkfileSettings(object):
|
|||
root_node=None,
|
||||
nodes=None,
|
||||
**kwargs):
|
||||
opnl._project = kwargs.get(
|
||||
Context._project_doc = kwargs.get(
|
||||
"project") or io.find_one({"type": "project"})
|
||||
self._asset = kwargs.get("asset_name") or api.Session["AVALON_ASSET"]
|
||||
self._asset_entity = get_asset(self._asset)
|
||||
|
|
@ -804,8 +1259,6 @@ class WorkfileSettings(object):
|
|||
''' Adds correct colorspace to write node dict
|
||||
|
||||
'''
|
||||
from avalon.nuke import read
|
||||
|
||||
for node in nuke.allNodes(filter="Group"):
|
||||
|
||||
# get data from avalon knob
|
||||
|
|
@ -1005,7 +1458,7 @@ class WorkfileSettings(object):
|
|||
node['frame_range_lock'].setValue(True)
|
||||
|
||||
# adding handle_start/end to root avalon knob
|
||||
if not anlib.set_avalon_knob_data(self._root_node, {
|
||||
if not set_avalon_knob_data(self._root_node, {
|
||||
"handleStart": int(handle_start),
|
||||
"handleEnd": int(handle_end)
|
||||
}):
|
||||
|
|
@ -1089,6 +1542,8 @@ class WorkfileSettings(object):
|
|||
self.set_colorspace()
|
||||
|
||||
def set_favorites(self):
|
||||
from .utils import set_context_favorites
|
||||
|
||||
work_dir = os.getenv("AVALON_WORKDIR")
|
||||
asset = os.getenv("AVALON_ASSET")
|
||||
favorite_items = OrderedDict()
|
||||
|
|
@ -1096,9 +1551,9 @@ class WorkfileSettings(object):
|
|||
# project
|
||||
# get project's root and split to parts
|
||||
projects_root = os.path.normpath(work_dir.split(
|
||||
opnl.project_name)[0])
|
||||
Context.project_name)[0])
|
||||
# add project name
|
||||
project_dir = os.path.join(projects_root, opnl.project_name) + "/"
|
||||
project_dir = os.path.join(projects_root, Context.project_name) + "/"
|
||||
# add to favorites
|
||||
favorite_items.update({"Project dir": project_dir.replace("\\", "/")})
|
||||
|
||||
|
|
@ -1145,8 +1600,7 @@ def get_write_node_template_attr(node):
|
|||
'''
|
||||
# get avalon data from node
|
||||
data = dict()
|
||||
data['avalon'] = avalon.nuke.read(
|
||||
node)
|
||||
data['avalon'] = read(node)
|
||||
data_preset = {
|
||||
"nodeclass": data['avalon']['family'],
|
||||
"families": [data['avalon']['families']],
|
||||
|
|
@ -1167,7 +1621,7 @@ def get_write_node_template_attr(node):
|
|||
if k not in ["_id", "_previous"]}
|
||||
|
||||
# fix badly encoded data
|
||||
return anlib.fix_data_for_node_create(correct_data)
|
||||
return fix_data_for_node_create(correct_data)
|
||||
|
||||
|
||||
def get_dependent_nodes(nodes):
|
||||
|
|
@ -1274,13 +1728,53 @@ def find_free_space_to_paste_nodes(
|
|||
return xpos, ypos
|
||||
|
||||
|
||||
@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
|
||||
finally:
|
||||
# unselect all selection in case there is some
|
||||
current_seletion = nuke.selectedNodes()
|
||||
[n['selected'].setValue(False) for n in current_seletion]
|
||||
# and select all previously selected nodes
|
||||
if previous_selection:
|
||||
[n['selected'].setValue(True) for n in previous_selection]
|
||||
|
||||
|
||||
def reset_selection():
|
||||
"""Deselect all selected nodes"""
|
||||
for node in nuke.selectedNodes():
|
||||
node["selected"].setValue(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 launch_workfiles_app():
|
||||
'''Function letting start workfiles after start of host
|
||||
'''
|
||||
from openpype.lib import (
|
||||
env_value_to_bool
|
||||
)
|
||||
from avalon.nuke.pipeline import get_main_window
|
||||
from .pipeline import get_main_window
|
||||
|
||||
# get all imortant settings
|
||||
open_at_start = env_value_to_bool(
|
||||
|
|
@ -1291,8 +1785,8 @@ def launch_workfiles_app():
|
|||
if not open_at_start:
|
||||
return
|
||||
|
||||
if not opnl.workfiles_launched:
|
||||
opnl.workfiles_launched = True
|
||||
if not Context.workfiles_launched:
|
||||
Context.workfiles_launched = True
|
||||
main_window = get_main_window()
|
||||
host_tools.show_workfiles(parent=main_window)
|
||||
|
||||
|
|
@ -1378,7 +1872,7 @@ def recreate_instance(origin_node, avalon_data=None):
|
|||
knobs_wl = ["render", "publish", "review", "ypos",
|
||||
"use_limit", "first", "last"]
|
||||
# get data from avalon knobs
|
||||
data = anlib.get_avalon_knob_data(
|
||||
data = get_avalon_knob_data(
|
||||
origin_node)
|
||||
|
||||
# add input data to avalon data
|
||||
|
|
@ -1494,3 +1988,45 @@ def dirmap_file_name_filter(file_name):
|
|||
if os.path.exists(dirmap_processor.file_name):
|
||||
return dirmap_processor.file_name
|
||||
return file_name
|
||||
|
||||
|
||||
# ------------------------------------
|
||||
# This function seems to be deprecated
|
||||
# ------------------------------------
|
||||
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)
|
||||
dirpath = 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(dirpath)
|
||||
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(dirpath, file).replace("\\", "/"),
|
||||
"frames": collections[0].format("[{ranges}]")
|
||||
}
|
||||
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -1,166 +0,0 @@
|
|||
import os
|
||||
import nuke
|
||||
from avalon.nuke.pipeline import get_main_window
|
||||
|
||||
from .lib import WorkfileSettings
|
||||
from openpype.api import Logger, BuildWorkfile, get_current_project_settings
|
||||
from openpype.tools.utils import host_tools
|
||||
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
menu_label = os.environ["AVALON_LABEL"]
|
||||
context_label = None
|
||||
|
||||
|
||||
def change_context_label(*args):
|
||||
global context_label
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.findItem(menu_label)
|
||||
|
||||
label = "{0}, {1}".format(
|
||||
os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"]
|
||||
)
|
||||
|
||||
rm_item = [
|
||||
(i, item) for i, item in enumerate(menu.items())
|
||||
if context_label in item.name()
|
||||
][0]
|
||||
|
||||
menu.removeItem(rm_item[1].name())
|
||||
|
||||
context_action = menu.addCommand(
|
||||
label,
|
||||
index=(rm_item[0])
|
||||
)
|
||||
context_action.setEnabled(False)
|
||||
|
||||
log.info("Task label changed from `{}` to `{}`".format(
|
||||
context_label, label))
|
||||
|
||||
context_label = label
|
||||
|
||||
|
||||
|
||||
def install():
|
||||
from openpype.hosts.nuke.api import reload_config
|
||||
|
||||
global context_label
|
||||
|
||||
# uninstall original avalon menu
|
||||
uninstall()
|
||||
|
||||
main_window = get_main_window()
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.addMenu(menu_label)
|
||||
|
||||
label = "{0}, {1}".format(
|
||||
os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"]
|
||||
)
|
||||
context_label = label
|
||||
context_action = menu.addCommand(label)
|
||||
context_action.setEnabled(False)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Work Files...",
|
||||
lambda: host_tools.show_workfiles(parent=main_window)
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Create...",
|
||||
lambda: host_tools.show_creator(parent=main_window)
|
||||
)
|
||||
menu.addCommand(
|
||||
"Load...",
|
||||
lambda: host_tools.show_loader(
|
||||
parent=main_window,
|
||||
use_context=True
|
||||
)
|
||||
)
|
||||
menu.addCommand(
|
||||
"Publish...",
|
||||
lambda: host_tools.show_publish(parent=main_window)
|
||||
)
|
||||
menu.addCommand(
|
||||
"Manage...",
|
||||
lambda: host_tools.show_scene_inventory(parent=main_window)
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Set Resolution",
|
||||
lambda: WorkfileSettings().reset_resolution()
|
||||
)
|
||||
menu.addCommand(
|
||||
"Set Frame Range",
|
||||
lambda: WorkfileSettings().reset_frame_range_handles()
|
||||
)
|
||||
menu.addCommand(
|
||||
"Set Colorspace",
|
||||
lambda: WorkfileSettings().set_colorspace()
|
||||
)
|
||||
menu.addCommand(
|
||||
"Apply All Settings",
|
||||
lambda: WorkfileSettings().set_context_settings()
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Build Workfile",
|
||||
lambda: BuildWorkfile().process()
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Experimental tools...",
|
||||
lambda: host_tools.show_experimental_tools_dialog(parent=main_window)
|
||||
)
|
||||
|
||||
# add reload pipeline only in debug mode
|
||||
if bool(os.getenv("NUKE_DEBUG")):
|
||||
menu.addSeparator()
|
||||
menu.addCommand("Reload Pipeline", reload_config)
|
||||
|
||||
# adding shortcuts
|
||||
add_shortcuts_from_presets()
|
||||
|
||||
|
||||
def uninstall():
|
||||
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.findItem(menu_label)
|
||||
|
||||
for item in menu.items():
|
||||
log.info("Removing menu item: {}".format(item.name()))
|
||||
menu.removeItem(item.name())
|
||||
|
||||
|
||||
def add_shortcuts_from_presets():
|
||||
menubar = nuke.menu("Nuke")
|
||||
nuke_presets = get_current_project_settings()["nuke"]["general"]
|
||||
|
||||
if nuke_presets.get("menu"):
|
||||
menu_label_mapping = {
|
||||
"manage": "Manage...",
|
||||
"create": "Create...",
|
||||
"load": "Load...",
|
||||
"build_workfile": "Build Workfile",
|
||||
"publish": "Publish..."
|
||||
}
|
||||
|
||||
for command_name, shortcut_str in nuke_presets.get("menu").items():
|
||||
log.info("menu_name `{}` | menu_label `{}`".format(
|
||||
command_name, menu_label
|
||||
))
|
||||
log.info("Adding Shortcut `{}` to `{}`".format(
|
||||
shortcut_str, command_name
|
||||
))
|
||||
try:
|
||||
menu = menubar.findItem(menu_label)
|
||||
item_label = menu_label_mapping[command_name]
|
||||
menuitem = menu.findItem(item_label)
|
||||
menuitem.setShortcut(shortcut_str)
|
||||
except AttributeError as e:
|
||||
log.error(e)
|
||||
421
openpype/hosts/nuke/api/pipeline.py
Normal file
421
openpype/hosts/nuke/api/pipeline.py
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
import os
|
||||
import importlib
|
||||
from collections import OrderedDict
|
||||
|
||||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
import avalon.api
|
||||
from avalon import pipeline
|
||||
|
||||
import openpype
|
||||
from openpype.api import (
|
||||
Logger,
|
||||
BuildWorkfile,
|
||||
get_current_project_settings
|
||||
)
|
||||
from openpype.tools.utils import host_tools
|
||||
|
||||
from .command import viewer_update_and_undo_stop
|
||||
from .lib import (
|
||||
add_publish_knob,
|
||||
WorkfileSettings,
|
||||
process_workfile_builder,
|
||||
launch_workfiles_app,
|
||||
check_inventory_versions,
|
||||
set_avalon_knob_data,
|
||||
read,
|
||||
Context
|
||||
)
|
||||
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.nuke.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
|
||||
|
||||
MENU_LABEL = os.environ["AVALON_LABEL"]
|
||||
|
||||
|
||||
# registering pyblish gui regarding settings in presets
|
||||
if os.getenv("PYBLISH_GUI", None):
|
||||
pyblish.api.register_gui(os.getenv("PYBLISH_GUI", None))
|
||||
|
||||
|
||||
def get_main_window():
|
||||
"""Acquire Nuke's main window"""
|
||||
if Context.main_window is None:
|
||||
from Qt import QtWidgets
|
||||
|
||||
top_widgets = QtWidgets.QApplication.topLevelWidgets()
|
||||
name = "Foundry::UI::DockMainWindow"
|
||||
for widget in top_widgets:
|
||||
if (
|
||||
widget.inherits("QMainWindow")
|
||||
and widget.metaObject().className() == name
|
||||
):
|
||||
Context.main_window = widget
|
||||
break
|
||||
return Context.main_window
|
||||
|
||||
|
||||
def reload_config():
|
||||
"""Attempt to reload pipeline at run-time.
|
||||
|
||||
CAUTION: This is primarily for development and debugging purposes.
|
||||
|
||||
"""
|
||||
|
||||
for module in (
|
||||
"{}.api".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.actions".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.menu".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.plugin".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.lib".format(AVALON_CONFIG),
|
||||
):
|
||||
log.info("Reloading module: {}...".format(module))
|
||||
|
||||
module = importlib.import_module(module)
|
||||
|
||||
try:
|
||||
importlib.reload(module)
|
||||
except AttributeError as e:
|
||||
from importlib import reload
|
||||
log.warning("Cannot reload module: {}".format(e))
|
||||
reload(module)
|
||||
|
||||
|
||||
def install():
|
||||
''' Installing all requarements for Nuke host
|
||||
'''
|
||||
|
||||
pyblish.api.register_host("nuke")
|
||||
|
||||
log.info("Registering Nuke plug-ins..")
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# Register Avalon event for workfiles loading.
|
||||
avalon.api.on("workio.open_file", check_inventory_versions)
|
||||
avalon.api.on("taskChanged", change_context_label)
|
||||
|
||||
pyblish.api.register_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled)
|
||||
workfile_settings = WorkfileSettings()
|
||||
# Disable all families except for the ones we explicitly want to see
|
||||
family_states = [
|
||||
"write",
|
||||
"review",
|
||||
"nukenodes",
|
||||
"model",
|
||||
"gizmo"
|
||||
]
|
||||
|
||||
avalon.api.data["familiesStateDefault"] = False
|
||||
avalon.api.data["familiesStateToggled"] = family_states
|
||||
|
||||
# Set context settings.
|
||||
nuke.addOnCreate(workfile_settings.set_context_settings, nodeClass="Root")
|
||||
nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root")
|
||||
nuke.addOnCreate(process_workfile_builder, nodeClass="Root")
|
||||
nuke.addOnCreate(launch_workfiles_app, nodeClass="Root")
|
||||
_install_menu()
|
||||
|
||||
|
||||
def uninstall():
|
||||
'''Uninstalling host's integration
|
||||
'''
|
||||
log.info("Deregistering Nuke plug-ins..")
|
||||
pyblish.deregister_host("nuke")
|
||||
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
|
||||
pyblish.api.deregister_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled)
|
||||
|
||||
reload_config()
|
||||
_uninstall_menu()
|
||||
|
||||
|
||||
def _install_menu():
|
||||
# uninstall original avalon menu
|
||||
main_window = get_main_window()
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.addMenu(MENU_LABEL)
|
||||
|
||||
label = "{0}, {1}".format(
|
||||
os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"]
|
||||
)
|
||||
Context.context_label = label
|
||||
context_action = menu.addCommand(label)
|
||||
context_action.setEnabled(False)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Work Files...",
|
||||
lambda: host_tools.show_workfiles(parent=main_window)
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Create...",
|
||||
lambda: host_tools.show_creator(parent=main_window)
|
||||
)
|
||||
menu.addCommand(
|
||||
"Load...",
|
||||
lambda: host_tools.show_loader(
|
||||
parent=main_window,
|
||||
use_context=True
|
||||
)
|
||||
)
|
||||
menu.addCommand(
|
||||
"Publish...",
|
||||
lambda: host_tools.show_publish(parent=main_window)
|
||||
)
|
||||
menu.addCommand(
|
||||
"Manage...",
|
||||
lambda: host_tools.show_scene_inventory(parent=main_window)
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Set Resolution",
|
||||
lambda: WorkfileSettings().reset_resolution()
|
||||
)
|
||||
menu.addCommand(
|
||||
"Set Frame Range",
|
||||
lambda: WorkfileSettings().reset_frame_range_handles()
|
||||
)
|
||||
menu.addCommand(
|
||||
"Set Colorspace",
|
||||
lambda: WorkfileSettings().set_colorspace()
|
||||
)
|
||||
menu.addCommand(
|
||||
"Apply All Settings",
|
||||
lambda: WorkfileSettings().set_context_settings()
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Build Workfile",
|
||||
lambda: BuildWorkfile().process()
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Experimental tools...",
|
||||
lambda: host_tools.show_experimental_tools_dialog(parent=main_window)
|
||||
)
|
||||
|
||||
# add reload pipeline only in debug mode
|
||||
if bool(os.getenv("NUKE_DEBUG")):
|
||||
menu.addSeparator()
|
||||
menu.addCommand("Reload Pipeline", reload_config)
|
||||
|
||||
# adding shortcuts
|
||||
add_shortcuts_from_presets()
|
||||
|
||||
|
||||
def _uninstall_menu():
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.findItem(MENU_LABEL)
|
||||
|
||||
for item in menu.items():
|
||||
log.info("Removing menu item: {}".format(item.name()))
|
||||
menu.removeItem(item.name())
|
||||
|
||||
|
||||
def change_context_label(*args):
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.findItem(MENU_LABEL)
|
||||
|
||||
label = "{0}, {1}".format(
|
||||
os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"]
|
||||
)
|
||||
|
||||
rm_item = [
|
||||
(i, item) for i, item in enumerate(menu.items())
|
||||
if Context.context_label in item.name()
|
||||
][0]
|
||||
|
||||
menu.removeItem(rm_item[1].name())
|
||||
|
||||
context_action = menu.addCommand(
|
||||
label,
|
||||
index=(rm_item[0])
|
||||
)
|
||||
context_action.setEnabled(False)
|
||||
|
||||
log.info("Task label changed from `{}` to `{}`".format(
|
||||
Context.context_label, label))
|
||||
|
||||
|
||||
def add_shortcuts_from_presets():
|
||||
menubar = nuke.menu("Nuke")
|
||||
nuke_presets = get_current_project_settings()["nuke"]["general"]
|
||||
|
||||
if nuke_presets.get("menu"):
|
||||
menu_label_mapping = {
|
||||
"manage": "Manage...",
|
||||
"create": "Create...",
|
||||
"load": "Load...",
|
||||
"build_workfile": "Build Workfile",
|
||||
"publish": "Publish..."
|
||||
}
|
||||
|
||||
for command_name, shortcut_str in nuke_presets.get("menu").items():
|
||||
log.info("menu_name `{}` | menu_label `{}`".format(
|
||||
command_name, MENU_LABEL
|
||||
))
|
||||
log.info("Adding Shortcut `{}` to `{}`".format(
|
||||
shortcut_str, command_name
|
||||
))
|
||||
try:
|
||||
menu = menubar.findItem(MENU_LABEL)
|
||||
item_label = menu_label_mapping[command_name]
|
||||
menuitem = menu.findItem(item_label)
|
||||
menuitem.setShortcut(shortcut_str)
|
||||
except AttributeError as e:
|
||||
log.error(e)
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
"""Toggle node passthrough states on instance toggles."""
|
||||
|
||||
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
instance, old_value, new_value))
|
||||
|
||||
# Whether instances should be passthrough based on new value
|
||||
|
||||
with viewer_update_and_undo_stop():
|
||||
n = instance[0]
|
||||
try:
|
||||
n["publish"].value()
|
||||
except ValueError:
|
||||
n = add_publish_knob(n)
|
||||
log.info(" `Publish` knob was added to write node..")
|
||||
|
||||
n["publish"].setValue(new_value)
|
||||
|
||||
|
||||
def containerise(node,
|
||||
name,
|
||||
namespace,
|
||||
context,
|
||||
loader=None,
|
||||
data=None):
|
||||
"""Bundle `node` into an assembly and imprint it with metadata
|
||||
|
||||
Containerisation enables a tracking of version, author and origin
|
||||
for loaded assets.
|
||||
|
||||
Arguments:
|
||||
node (nuke.Node): Nuke's node object to imprint as container
|
||||
name (str): Name of resulting assembly
|
||||
namespace (str): Namespace under which to host container
|
||||
context (dict): Asset information
|
||||
loader (str, optional): Name of node used to produce this container.
|
||||
|
||||
Returns:
|
||||
node (nuke.Node): containerised nuke's node object
|
||||
|
||||
"""
|
||||
data = OrderedDict(
|
||||
[
|
||||
("schema", "openpype:container-2.0"),
|
||||
("id", pipeline.AVALON_CONTAINER_ID),
|
||||
("name", name),
|
||||
("namespace", namespace),
|
||||
("loader", str(loader)),
|
||||
("representation", context["representation"]["_id"]),
|
||||
],
|
||||
|
||||
**data or dict()
|
||||
)
|
||||
|
||||
set_avalon_knob_data(node, data)
|
||||
|
||||
return node
|
||||
|
||||
|
||||
def parse_container(node):
|
||||
"""Returns containerised data of a node
|
||||
|
||||
Reads the imprinted data from `containerise`.
|
||||
|
||||
Arguments:
|
||||
node (nuke.Node): Nuke's node object to read imprinted data
|
||||
|
||||
Returns:
|
||||
dict: The container schema data for this container node.
|
||||
|
||||
"""
|
||||
data = read(node)
|
||||
|
||||
# (TODO) Remove key validation when `ls` has re-implemented.
|
||||
#
|
||||
# If not all required data return the empty container
|
||||
required = ["schema", "id", "name",
|
||||
"namespace", "loader", "representation"]
|
||||
if not all(key in data for key in required):
|
||||
return
|
||||
|
||||
# Store the node's name
|
||||
data["objectName"] = node["name"].value()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def update_container(node, keys=None):
|
||||
"""Returns node with updateted containder data
|
||||
|
||||
Arguments:
|
||||
node (nuke.Node): The node in Nuke to imprint as container,
|
||||
keys (dict, optional): data which should be updated
|
||||
|
||||
Returns:
|
||||
node (nuke.Node): nuke node with updated container data
|
||||
|
||||
Raises:
|
||||
TypeError on given an invalid container node
|
||||
|
||||
"""
|
||||
keys = keys or dict()
|
||||
|
||||
container = parse_container(node)
|
||||
if not container:
|
||||
raise TypeError("Not a valid container node.")
|
||||
|
||||
container.update(keys)
|
||||
node = set_avalon_knob_data(node, container)
|
||||
|
||||
return node
|
||||
|
||||
|
||||
def ls():
|
||||
"""List available containers.
|
||||
|
||||
This function is used by the Container Manager in Nuke. You'll
|
||||
need to implement a for-loop that then *yields* one Container at
|
||||
a time.
|
||||
|
||||
See the `container.json` schema for details on how it should look,
|
||||
and the Maya equivalent, which is in `avalon.maya.pipeline`
|
||||
"""
|
||||
all_nodes = nuke.allNodes(recurseGroups=False)
|
||||
|
||||
# TODO: add readgeo, readcamera, readimage
|
||||
nodes = [n for n in all_nodes]
|
||||
|
||||
for n in nodes:
|
||||
log.debug("name: `{}`".format(n.name()))
|
||||
container = parse_container(n)
|
||||
if container:
|
||||
yield container
|
||||
|
|
@ -2,23 +2,30 @@ import os
|
|||
import random
|
||||
import string
|
||||
|
||||
import avalon.nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon import api
|
||||
import nuke
|
||||
|
||||
import avalon.api
|
||||
|
||||
from openpype.api import (
|
||||
get_current_project_settings,
|
||||
PypeCreatorMixin
|
||||
)
|
||||
from .lib import check_subsetname_exists
|
||||
import nuke
|
||||
from .lib import (
|
||||
Knobby,
|
||||
check_subsetname_exists,
|
||||
reset_selection,
|
||||
maintained_selection,
|
||||
set_avalon_knob_data,
|
||||
add_publish_knob
|
||||
)
|
||||
|
||||
|
||||
class PypeCreator(PypeCreatorMixin, avalon.nuke.pipeline.Creator):
|
||||
"""Pype Nuke Creator class wrapper
|
||||
"""
|
||||
class OpenPypeCreator(PypeCreatorMixin, avalon.api.Creator):
|
||||
"""Pype Nuke Creator class wrapper"""
|
||||
node_color = "0xdfea5dff"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PypeCreator, self).__init__(*args, **kwargs)
|
||||
super(OpenPypeCreator, self).__init__(*args, **kwargs)
|
||||
self.presets = get_current_project_settings()["nuke"]["create"].get(
|
||||
self.__class__.__name__, {}
|
||||
)
|
||||
|
|
@ -31,6 +38,38 @@ class PypeCreator(PypeCreatorMixin, avalon.nuke.pipeline.Creator):
|
|||
raise NameError("`{0}: {1}".format(__name__, msg))
|
||||
return
|
||||
|
||||
def process(self):
|
||||
from nukescripts import autoBackdrop
|
||||
|
||||
instance = None
|
||||
|
||||
if (self.options or {}).get("useSelection"):
|
||||
|
||||
nodes = nuke.selectedNodes()
|
||||
if not nodes:
|
||||
nuke.message("Please select nodes that you "
|
||||
"wish to add to a container")
|
||||
return
|
||||
|
||||
elif len(nodes) == 1:
|
||||
# only one node is selected
|
||||
instance = nodes[0]
|
||||
|
||||
if not instance:
|
||||
# Not using selection or multiple nodes selected
|
||||
bckd_node = autoBackdrop()
|
||||
bckd_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
bckd_node["note_font_size"].setValue(24)
|
||||
bckd_node["label"].setValue("[{}]".format(self.name))
|
||||
|
||||
instance = bckd_node
|
||||
|
||||
# add avalon knobs
|
||||
set_avalon_knob_data(instance, self.data)
|
||||
add_publish_knob(instance)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
def get_review_presets_config():
|
||||
settings = get_current_project_settings()
|
||||
|
|
@ -48,7 +87,7 @@ def get_review_presets_config():
|
|||
return [str(name) for name, _prop in outputs.items()]
|
||||
|
||||
|
||||
class NukeLoader(api.Loader):
|
||||
class NukeLoader(avalon.api.Loader):
|
||||
container_id_knob = "containerId"
|
||||
container_id = None
|
||||
|
||||
|
|
@ -74,7 +113,7 @@ class NukeLoader(api.Loader):
|
|||
node[self.container_id_knob].setValue(source_id)
|
||||
else:
|
||||
HIDEN_FLAG = 0x00040000
|
||||
_knob = anlib.Knobby(
|
||||
_knob = Knobby(
|
||||
"String_Knob",
|
||||
self.container_id,
|
||||
flags=[
|
||||
|
|
@ -183,7 +222,7 @@ class ExporterReview(object):
|
|||
Returns:
|
||||
nuke.Node: copy node of Input Process node
|
||||
"""
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
ipn_orig = None
|
||||
for v in nuke.allNodes(filter="Viewer"):
|
||||
ip = v["input_process"].getValue()
|
||||
|
|
@ -196,7 +235,7 @@ class ExporterReview(object):
|
|||
# copy selected to clipboard
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
# reset selection
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
# paste node and selection is on it only
|
||||
nuke.nodePaste("%clipboard%")
|
||||
# assign to variable
|
||||
|
|
@ -396,7 +435,7 @@ class ExporterReviewMov(ExporterReview):
|
|||
|
||||
def save_file(self):
|
||||
import shutil
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
self.log.info("Saving nodes as file... ")
|
||||
# create nk path
|
||||
path = os.path.splitext(self.path)[0] + ".nk"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
|
||||
from openpype.api import resources
|
||||
from .lib import maintained_selection
|
||||
|
||||
|
||||
def set_context_favorites(favorites=None):
|
||||
|
|
@ -48,14 +49,16 @@ def gizmo_is_nuke_default(gizmo):
|
|||
return gizmo.filename().startswith(plug_dir)
|
||||
|
||||
|
||||
def bake_gizmos_recursively(in_group=nuke.Root()):
|
||||
def bake_gizmos_recursively(in_group=None):
|
||||
"""Converting a gizmo to group
|
||||
|
||||
Argumets:
|
||||
is_group (nuke.Node)[optonal]: group node or all nodes
|
||||
"""
|
||||
if in_group is None:
|
||||
in_group = nuke.Root()
|
||||
# preserve selection after all is done
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# jump to the group
|
||||
with in_group:
|
||||
for node in nuke.allNodes():
|
||||
|
|
|
|||
55
openpype/hosts/nuke/api/workio.py
Normal file
55
openpype/hosts/nuke/api/workio.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
"""Host API required Work Files tool"""
|
||||
import os
|
||||
import nuke
|
||||
import avalon.api
|
||||
|
||||
|
||||
def file_extensions():
|
||||
return avalon.api.HOST_WORKFILE_EXTENSIONS["nuke"]
|
||||
|
||||
|
||||
def has_unsaved_changes():
|
||||
return nuke.root().modified()
|
||||
|
||||
|
||||
def save_file(filepath):
|
||||
path = filepath.replace("\\", "/")
|
||||
nuke.scriptSaveAs(path)
|
||||
nuke.Root()["name"].setValue(path)
|
||||
nuke.Root()["project_directory"].setValue(os.path.dirname(path))
|
||||
nuke.Root().setModified(False)
|
||||
|
||||
|
||||
def open_file(filepath):
|
||||
filepath = filepath.replace("\\", "/")
|
||||
|
||||
# To remain in the same window, we have to clear the script and read
|
||||
# in the contents of the workfile.
|
||||
nuke.scriptClear()
|
||||
nuke.scriptReadFile(filepath)
|
||||
nuke.Root()["name"].setValue(filepath)
|
||||
nuke.Root()["project_directory"].setValue(os.path.dirname(filepath))
|
||||
nuke.Root().setModified(False)
|
||||
return True
|
||||
|
||||
|
||||
def current_file():
|
||||
current_file = nuke.root().name()
|
||||
|
||||
# Unsaved current file
|
||||
if current_file == 'Root':
|
||||
return None
|
||||
|
||||
return os.path.normpath(current_file).replace("\\", "/")
|
||||
|
||||
|
||||
def work_root(session):
|
||||
|
||||
work_dir = session["AVALON_WORKDIR"]
|
||||
scene_dir = session.get("AVALON_SCENEDIR")
|
||||
if scene_dir:
|
||||
path = os.path.join(work_dir, scene_dir)
|
||||
else:
|
||||
path = work_dir
|
||||
|
||||
return os.path.normpath(path).replace("\\", "/")
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
select_nodes,
|
||||
set_avalon_knob_data
|
||||
)
|
||||
|
||||
|
||||
class CreateBackdrop(plugin.PypeCreator):
|
||||
class CreateBackdrop(plugin.OpenPypeCreator):
|
||||
"""Add Publishable Backdrop"""
|
||||
|
||||
name = "nukenodes"
|
||||
|
|
@ -25,14 +28,14 @@ class CreateBackdrop(plugin.PypeCreator):
|
|||
nodes = self.nodes
|
||||
|
||||
if len(nodes) >= 1:
|
||||
anlib.select_nodes(nodes)
|
||||
select_nodes(nodes)
|
||||
bckd_node = autoBackdrop()
|
||||
bckd_node["name"].setValue("{}_BDN".format(self.name))
|
||||
bckd_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
bckd_node["note_font_size"].setValue(24)
|
||||
bckd_node["label"].setValue("[{}]".format(self.name))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(bckd_node, self.data)
|
||||
instance = set_avalon_knob_data(bckd_node, self.data)
|
||||
|
||||
return instance
|
||||
else:
|
||||
|
|
@ -48,6 +51,6 @@ class CreateBackdrop(plugin.PypeCreator):
|
|||
bckd_node["note_font_size"].setValue(24)
|
||||
bckd_node["label"].setValue("[{}]".format(self.name))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(bckd_node, self.data)
|
||||
instance = set_avalon_knob_data(bckd_node, self.data)
|
||||
|
||||
return instance
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
set_avalon_knob_data
|
||||
)
|
||||
|
||||
|
||||
class CreateCamera(plugin.PypeCreator):
|
||||
class CreateCamera(plugin.OpenPypeCreator):
|
||||
"""Add Publishable Backdrop"""
|
||||
|
||||
name = "camera"
|
||||
|
|
@ -36,7 +38,7 @@ class CreateCamera(plugin.PypeCreator):
|
|||
# change node color
|
||||
n["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
anlib.set_avalon_knob_data(n, data)
|
||||
set_avalon_knob_data(n, data)
|
||||
return True
|
||||
else:
|
||||
msg = str("Please select nodes you "
|
||||
|
|
@ -49,5 +51,5 @@ class CreateCamera(plugin.PypeCreator):
|
|||
camera_node = nuke.createNode("Camera2")
|
||||
camera_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(camera_node, self.data)
|
||||
instance = set_avalon_knob_data(camera_node, self.data)
|
||||
return instance
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
maintained_selection,
|
||||
select_nodes,
|
||||
set_avalon_knob_data
|
||||
)
|
||||
|
||||
class CreateGizmo(plugin.PypeCreator):
|
||||
|
||||
class CreateGizmo(plugin.OpenPypeCreator):
|
||||
"""Add Publishable "gizmo" group
|
||||
|
||||
The name is symbolically gizmo as presumably
|
||||
|
|
@ -28,13 +33,13 @@ class CreateGizmo(plugin.PypeCreator):
|
|||
nodes = self.nodes
|
||||
self.log.info(len(nodes))
|
||||
if len(nodes) == 1:
|
||||
anlib.select_nodes(nodes)
|
||||
select_nodes(nodes)
|
||||
node = nodes[-1]
|
||||
# check if Group node
|
||||
if node.Class() in "Group":
|
||||
node["name"].setValue("{}_GZM".format(self.name))
|
||||
node["tile_color"].setValue(int(self.node_color, 16))
|
||||
return anlib.set_avalon_knob_data(node, self.data)
|
||||
return set_avalon_knob_data(node, self.data)
|
||||
else:
|
||||
msg = ("Please select a group node "
|
||||
"you wish to publish as the gizmo")
|
||||
|
|
@ -42,7 +47,7 @@ class CreateGizmo(plugin.PypeCreator):
|
|||
nuke.message(msg)
|
||||
|
||||
if len(nodes) >= 2:
|
||||
anlib.select_nodes(nodes)
|
||||
select_nodes(nodes)
|
||||
nuke.makeGroup()
|
||||
gizmo_node = nuke.selectedNode()
|
||||
gizmo_node["name"].setValue("{}_GZM".format(self.name))
|
||||
|
|
@ -57,16 +62,15 @@ class CreateGizmo(plugin.PypeCreator):
|
|||
"- create User knobs on the group")
|
||||
|
||||
# add avalon knobs
|
||||
return anlib.set_avalon_knob_data(gizmo_node, self.data)
|
||||
return set_avalon_knob_data(gizmo_node, self.data)
|
||||
|
||||
else:
|
||||
msg = ("Please select nodes you "
|
||||
"wish to add to the gizmo")
|
||||
msg = "Please select nodes you wish to add to the gizmo"
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
else:
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
gizmo_node = nuke.createNode("Group")
|
||||
gizmo_node["name"].setValue("{}_GZM".format(self.name))
|
||||
gizmo_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
|
@ -80,4 +84,4 @@ class CreateGizmo(plugin.PypeCreator):
|
|||
"- create User knobs on the group")
|
||||
|
||||
# add avalon knobs
|
||||
return anlib.set_avalon_knob_data(gizmo_node, self.data)
|
||||
return set_avalon_knob_data(gizmo_node, self.data)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
set_avalon_knob_data
|
||||
)
|
||||
|
||||
|
||||
class CreateModel(plugin.PypeCreator):
|
||||
class CreateModel(plugin.OpenPypeCreator):
|
||||
"""Add Publishable Model Geometry"""
|
||||
|
||||
name = "model"
|
||||
|
|
@ -68,7 +70,7 @@ class CreateModel(plugin.PypeCreator):
|
|||
# change node color
|
||||
n["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
anlib.set_avalon_knob_data(n, data)
|
||||
set_avalon_knob_data(n, data)
|
||||
return True
|
||||
else:
|
||||
msg = str("Please select nodes you "
|
||||
|
|
@ -81,5 +83,5 @@ class CreateModel(plugin.PypeCreator):
|
|||
model_node = nuke.createNode("WriteGeo")
|
||||
model_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(model_node, self.data)
|
||||
instance = set_avalon_knob_data(model_node, self.data)
|
||||
return instance
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
from collections import OrderedDict
|
||||
import avalon.api
|
||||
import avalon.nuke
|
||||
from openpype import api as pype
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
|
||||
import nuke
|
||||
|
||||
import avalon.api
|
||||
from openpype import api as pype
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
set_avalon_knob_data
|
||||
)
|
||||
|
||||
class CrateRead(plugin.PypeCreator):
|
||||
|
||||
class CrateRead(plugin.OpenPypeCreator):
|
||||
# change this to template preset
|
||||
name = "ReadCopy"
|
||||
label = "Create Read Copy"
|
||||
|
|
@ -45,7 +48,7 @@ class CrateRead(plugin.PypeCreator):
|
|||
continue
|
||||
avalon_data = self.data
|
||||
avalon_data['subset'] = "{}".format(self.name)
|
||||
avalon.nuke.lib.set_avalon_knob_data(node, avalon_data)
|
||||
set_avalon_knob_data(node, avalon_data)
|
||||
node['tile_color'].setValue(16744935)
|
||||
count_reads += 1
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
from collections import OrderedDict
|
||||
from openpype.hosts.nuke.api import (
|
||||
plugin,
|
||||
lib)
|
||||
|
||||
import nuke
|
||||
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import create_write_node
|
||||
|
||||
class CreateWritePrerender(plugin.PypeCreator):
|
||||
|
||||
class CreateWritePrerender(plugin.OpenPypeCreator):
|
||||
# change this to template preset
|
||||
name = "WritePrerender"
|
||||
label = "Create Write Prerender"
|
||||
|
|
@ -98,7 +99,7 @@ class CreateWritePrerender(plugin.PypeCreator):
|
|||
|
||||
self.log.info("write_data: {}".format(write_data))
|
||||
|
||||
write_node = lib.create_write_node(
|
||||
write_node = create_write_node(
|
||||
self.data["subset"],
|
||||
write_data,
|
||||
input=selected_node,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
from collections import OrderedDict
|
||||
from openpype.hosts.nuke.api import (
|
||||
plugin,
|
||||
lib)
|
||||
|
||||
import nuke
|
||||
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import create_write_node
|
||||
|
||||
class CreateWriteRender(plugin.PypeCreator):
|
||||
|
||||
class CreateWriteRender(plugin.OpenPypeCreator):
|
||||
# change this to template preset
|
||||
name = "WriteRender"
|
||||
label = "Create Write Render"
|
||||
|
|
@ -119,7 +120,7 @@ class CreateWriteRender(plugin.PypeCreator):
|
|||
}
|
||||
]
|
||||
|
||||
write_node = lib.create_write_node(
|
||||
write_node = create_write_node(
|
||||
self.data["subset"],
|
||||
write_data,
|
||||
input=selected_node,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
from collections import OrderedDict
|
||||
from openpype.hosts.nuke.api import (
|
||||
plugin,
|
||||
lib)
|
||||
|
||||
import nuke
|
||||
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import create_write_node
|
||||
|
||||
class CreateWriteStill(plugin.PypeCreator):
|
||||
|
||||
class CreateWriteStill(plugin.OpenPypeCreator):
|
||||
# change this to template preset
|
||||
name = "WriteStillFrame"
|
||||
label = "Create Write Still Image"
|
||||
|
|
@ -108,7 +109,7 @@ class CreateWriteStill(plugin.PypeCreator):
|
|||
}
|
||||
]
|
||||
|
||||
write_node = lib.create_write_node(
|
||||
write_node = create_write_node(
|
||||
self.name,
|
||||
write_data,
|
||||
input=selected_node,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from avalon import api, style
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.api import (
|
||||
Logger)
|
||||
from openpype.api import Logger
|
||||
from openpype.hosts.nuke.api.lib import set_avalon_knob_data
|
||||
|
||||
|
||||
class RepairOldLoaders(api.InventoryAction):
|
||||
|
|
@ -10,7 +9,7 @@ class RepairOldLoaders(api.InventoryAction):
|
|||
icon = "gears"
|
||||
color = style.colors.alert
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
def process(self, containers):
|
||||
import nuke
|
||||
|
|
@ -34,4 +33,4 @@ class RepairOldLoaders(api.InventoryAction):
|
|||
})
|
||||
node["name"].setValue(new_name)
|
||||
# get data from avalon knob
|
||||
anlib.set_avalon_knob_data(node, cdata)
|
||||
set_avalon_knob_data(node, cdata)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from avalon import api
|
||||
from openpype.hosts.nuke.api.commands import viewer_update_and_undo_stop
|
||||
|
||||
|
||||
class SelectContainers(api.InventoryAction):
|
||||
|
|
@ -9,11 +10,10 @@ class SelectContainers(api.InventoryAction):
|
|||
|
||||
def process(self, containers):
|
||||
import nuke
|
||||
import avalon.nuke
|
||||
|
||||
nodes = [nuke.toNode(i["objectName"]) for i in containers]
|
||||
|
||||
with avalon.nuke.viewer_update_and_undo_stop():
|
||||
with viewer_update_and_undo_stop():
|
||||
# clear previous_selection
|
||||
[n['selected'].setValue(False) for n in nodes]
|
||||
# Select tool
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
import nukescripts
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
find_free_space_to_paste_nodes,
|
||||
maintained_selection,
|
||||
reset_selection,
|
||||
select_nodes,
|
||||
get_avalon_knob_data,
|
||||
set_avalon_knob_data
|
||||
)
|
||||
from openpype.hosts.nuke.api.commands import viewer_update_and_undo_stop
|
||||
from openpype.hosts.nuke.api import containerise, update_container
|
||||
|
||||
|
||||
class LoadBackdropNodes(api.Loader):
|
||||
"""Loading Published Backdrop nodes (workfile, nukenodes)"""
|
||||
|
|
@ -66,12 +75,12 @@ class LoadBackdropNodes(api.Loader):
|
|||
# Get mouse position
|
||||
n = nuke.createNode("NoOp")
|
||||
xcursor, ycursor = (n.xpos(), n.ypos())
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
nuke.delete(n)
|
||||
|
||||
bdn_frame = 50
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
|
@ -81,11 +90,13 @@ class LoadBackdropNodes(api.Loader):
|
|||
nodes = nuke.selectedNodes()
|
||||
|
||||
# get pointer position in DAG
|
||||
xpointer, ypointer = pnlib.find_free_space_to_paste_nodes(nodes, direction="right", offset=200+bdn_frame)
|
||||
xpointer, ypointer = find_free_space_to_paste_nodes(
|
||||
nodes, direction="right", offset=200 + bdn_frame
|
||||
)
|
||||
|
||||
# reset position to all nodes and replace inputs and output
|
||||
for n in nodes:
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
xpos = (n.xpos() - xcursor) + xpointer
|
||||
ypos = (n.ypos() - ycursor) + ypointer
|
||||
n.setXYpos(xpos, ypos)
|
||||
|
|
@ -108,7 +119,7 @@ class LoadBackdropNodes(api.Loader):
|
|||
d.setInput(index, dot)
|
||||
|
||||
# remove Input node
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
nuke.delete(n)
|
||||
continue
|
||||
|
||||
|
|
@ -127,15 +138,15 @@ class LoadBackdropNodes(api.Loader):
|
|||
dot.setInput(0, dep)
|
||||
|
||||
# remove Input node
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
nuke.delete(n)
|
||||
continue
|
||||
else:
|
||||
new_nodes.append(n)
|
||||
|
||||
# reselect nodes with new Dot instead of Inputs and Output
|
||||
anlib.reset_selection()
|
||||
anlib.select_nodes(new_nodes)
|
||||
reset_selection()
|
||||
select_nodes(new_nodes)
|
||||
# place on backdrop
|
||||
bdn = nukescripts.autoBackdrop()
|
||||
|
||||
|
|
@ -208,16 +219,16 @@ class LoadBackdropNodes(api.Loader):
|
|||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
xpos = GN.xpos()
|
||||
ypos = GN.ypos()
|
||||
avalon_data = anlib.get_avalon_knob_data(GN)
|
||||
avalon_data = get_avalon_knob_data(GN)
|
||||
nuke.delete(GN)
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
anlib.set_avalon_knob_data(GN, avalon_data)
|
||||
set_avalon_knob_data(GN, avalon_data)
|
||||
GN.setXYpos(xpos, ypos)
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
|
|
@ -243,7 +254,6 @@ class LoadBackdropNodes(api.Loader):
|
|||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
from avalon import api, io
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
import nuke
|
||||
|
||||
from avalon import api, io
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
maintained_selection
|
||||
)
|
||||
|
||||
|
||||
class AlembicCameraLoader(api.Loader):
|
||||
"""
|
||||
|
|
@ -43,7 +50,7 @@ class AlembicCameraLoader(api.Loader):
|
|||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
camera_node = nuke.createNode(
|
||||
"Camera2",
|
||||
"name {} file {} read_from_file True".format(
|
||||
|
|
@ -122,7 +129,7 @@ class AlembicCameraLoader(api.Loader):
|
|||
# getting file path
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
camera_node = nuke.toNode(object_name)
|
||||
camera_node['selected'].setValue(True)
|
||||
|
||||
|
|
@ -181,7 +188,6 @@ class AlembicCameraLoader(api.Loader):
|
|||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ from avalon.vendor import qargparse
|
|||
from avalon import api, io
|
||||
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace
|
||||
get_imageio_input_colorspace,
|
||||
maintained_selection
|
||||
)
|
||||
from avalon.nuke import (
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop,
|
||||
maintained_selection
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
|
||||
|
|
@ -280,9 +280,6 @@ class LoadClip(plugin.NukeLoader):
|
|||
self.set_as_member(read_node)
|
||||
|
||||
def remove(self, container):
|
||||
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
|
||||
read_node = nuke.toNode(container['objectName'])
|
||||
assert read_node.Class() == "Read", "Must be Read"
|
||||
|
||||
|
|
@ -378,4 +375,4 @@ class LoadClip(plugin.NukeLoader):
|
|||
"class_name": self.__class__.__name__
|
||||
}
|
||||
|
||||
return self.node_name_template.format(**name_data)
|
||||
return self.node_name_template.format(**name_data)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
import nuke
|
||||
from avalon import api, style, io
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
|
||||
class LoadEffects(api.Loader):
|
||||
|
|
@ -30,9 +35,6 @@ class LoadEffects(api.Loader):
|
|||
Returns:
|
||||
nuke node: containerised nuke node object
|
||||
"""
|
||||
# import dependencies
|
||||
from avalon.nuke import containerise
|
||||
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
|
@ -138,10 +140,6 @@ class LoadEffects(api.Loader):
|
|||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
# get main variables
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
|
|
@ -338,7 +336,6 @@ class LoadEffects(api.Loader):
|
|||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
|
||||
import nuke
|
||||
|
||||
from avalon import api, style, io
|
||||
from openpype.hosts.nuke.api import lib
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
|
||||
class LoadEffectsInputProcess(api.Loader):
|
||||
|
|
@ -30,8 +37,6 @@ class LoadEffectsInputProcess(api.Loader):
|
|||
Returns:
|
||||
nuke node: containerised nuke node object
|
||||
"""
|
||||
# import dependencies
|
||||
from avalon.nuke import containerise
|
||||
|
||||
# get main variables
|
||||
version = context['version']
|
||||
|
|
@ -142,9 +147,6 @@ class LoadEffectsInputProcess(api.Loader):
|
|||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
# get main variables
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
|
|
@ -355,7 +357,6 @@ class LoadEffectsInputProcess(api.Loader):
|
|||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
from avalon import api, style, io
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
maintained_selection,
|
||||
get_avalon_knob_data,
|
||||
set_avalon_knob_data
|
||||
)
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
|
||||
class LoadGizmo(api.Loader):
|
||||
|
|
@ -61,7 +69,7 @@ class LoadGizmo(api.Loader):
|
|||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
|
|
@ -122,16 +130,16 @@ class LoadGizmo(api.Loader):
|
|||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
xpos = GN.xpos()
|
||||
ypos = GN.ypos()
|
||||
avalon_data = anlib.get_avalon_knob_data(GN)
|
||||
avalon_data = get_avalon_knob_data(GN)
|
||||
nuke.delete(GN)
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
anlib.set_avalon_knob_data(GN, avalon_data)
|
||||
set_avalon_knob_data(GN, avalon_data)
|
||||
GN.setXYpos(xpos, ypos)
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
|
|
@ -157,7 +165,6 @@ class LoadGizmo(api.Loader):
|
|||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
maintained_selection,
|
||||
create_backdrop,
|
||||
get_avalon_knob_data,
|
||||
set_avalon_knob_data
|
||||
)
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
|
||||
class LoadGizmoInputProcess(api.Loader):
|
||||
|
|
@ -62,7 +70,7 @@ class LoadGizmoInputProcess(api.Loader):
|
|||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
|
|
@ -128,16 +136,16 @@ class LoadGizmoInputProcess(api.Loader):
|
|||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
xpos = GN.xpos()
|
||||
ypos = GN.ypos()
|
||||
avalon_data = anlib.get_avalon_knob_data(GN)
|
||||
avalon_data = get_avalon_knob_data(GN)
|
||||
nuke.delete(GN)
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
anlib.set_avalon_knob_data(GN, avalon_data)
|
||||
set_avalon_knob_data(GN, avalon_data)
|
||||
GN.setXYpos(xpos, ypos)
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
|
|
@ -197,8 +205,12 @@ class LoadGizmoInputProcess(api.Loader):
|
|||
viewer["input_process_node"].setValue(group_node_name)
|
||||
|
||||
# put backdrop under
|
||||
pnlib.create_backdrop(label="Input Process", layer=2,
|
||||
nodes=[viewer, group_node], color="0x7c7faaff")
|
||||
create_backdrop(
|
||||
label="Input Process",
|
||||
layer=2,
|
||||
nodes=[viewer, group_node],
|
||||
color="0x7c7faaff"
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
|
@ -234,7 +246,6 @@ class LoadGizmoInputProcess(api.Loader):
|
|||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ from avalon import api, io
|
|||
from openpype.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace
|
||||
)
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
|
||||
class LoadImage(api.Loader):
|
||||
|
|
@ -46,10 +51,6 @@ class LoadImage(api.Loader):
|
|||
return cls.representations + cls._representations
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
from avalon.nuke import (
|
||||
containerise,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
self.log.info("__ options: `{}`".format(options))
|
||||
frame_number = options.get("frame_number", 1)
|
||||
|
||||
|
|
@ -154,11 +155,6 @@ class LoadImage(api.Loader):
|
|||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
node = nuke.toNode(container["objectName"])
|
||||
frame_number = node["first"].value()
|
||||
|
||||
|
|
@ -234,9 +230,6 @@ class LoadImage(api.Loader):
|
|||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def remove(self, container):
|
||||
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
assert node.Class() == "Read", "Must be Read"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
from avalon import api, io
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
import nuke
|
||||
from avalon import api, io
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
|
||||
class AlembicModelLoader(api.Loader):
|
||||
|
|
@ -43,7 +47,7 @@ class AlembicModelLoader(api.Loader):
|
|||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
model_node = nuke.createNode(
|
||||
"ReadGeo2",
|
||||
"name {} file {} ".format(
|
||||
|
|
@ -122,7 +126,7 @@ class AlembicModelLoader(api.Loader):
|
|||
# getting file path
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
model_node = nuke.toNode(object_name)
|
||||
model_node['selected'].setValue(True)
|
||||
|
||||
|
|
@ -181,7 +185,6 @@ class AlembicModelLoader(api.Loader):
|
|||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
from avalon import api, style, io
|
||||
from avalon.nuke import get_avalon_knob_data
|
||||
import nuke
|
||||
from avalon import api, style, io
|
||||
from openpype.hosts.nuke.api.lib import get_avalon_knob_data
|
||||
from openpype.hosts.nuke.api import (
|
||||
containerise,
|
||||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
|
||||
class LinkAsGroup(api.Loader):
|
||||
|
|
@ -15,8 +20,6 @@ class LinkAsGroup(api.Loader):
|
|||
color = style.colors.alert
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from avalon.nuke import containerise
|
||||
# for k, v in context.items():
|
||||
# log.info("key: `{}`, value: {}\n".format(k, v))
|
||||
version = context['version']
|
||||
|
|
@ -103,11 +106,6 @@ class LinkAsGroup(api.Loader):
|
|||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
|
||||
root = api.get_representation_path(representation).replace("\\", "/")
|
||||
|
|
@ -155,7 +153,6 @@ class LinkAsGroup(api.Loader):
|
|||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
import nuke
|
||||
import os
|
||||
|
||||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
maintained_selection,
|
||||
reset_selection,
|
||||
select_nodes
|
||||
)
|
||||
|
||||
|
||||
class ExtractBackdropNode(openpype.api.Extractor):
|
||||
"""Extracting content of backdrop nodes
|
||||
|
|
@ -27,7 +34,7 @@ class ExtractBackdropNode(openpype.api.Extractor):
|
|||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# all connections outside of backdrop
|
||||
connections_in = instance.data["nodeConnectionsIn"]
|
||||
connections_out = instance.data["nodeConnectionsOut"]
|
||||
|
|
@ -44,7 +51,7 @@ class ExtractBackdropNode(openpype.api.Extractor):
|
|||
nodes.append(inpn)
|
||||
tmp_nodes.append(inpn)
|
||||
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
|
||||
# connect output node
|
||||
for n, output in connections_out.items():
|
||||
|
|
@ -58,11 +65,11 @@ class ExtractBackdropNode(openpype.api.Extractor):
|
|||
opn.autoplace()
|
||||
nodes.append(opn)
|
||||
tmp_nodes.append(opn)
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
|
||||
# select nodes to copy
|
||||
anlib.reset_selection()
|
||||
anlib.select_nodes(nodes)
|
||||
reset_selection()
|
||||
select_nodes(nodes)
|
||||
# create tmp nk file
|
||||
# save file to the path
|
||||
nuke.nodeCopy(path)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import nuke
|
||||
import os
|
||||
import math
|
||||
from pprint import pformat
|
||||
|
||||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from pprint import pformat
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
class ExtractCamera(openpype.api.Extractor):
|
||||
|
|
@ -52,7 +54,7 @@ class ExtractCamera(openpype.api.Extractor):
|
|||
filename = subset + ".{}".format(extension)
|
||||
file_path = os.path.join(staging_dir, filename).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# bake camera with axeses onto word coordinate XYZ
|
||||
rm_n = bakeCameraWithAxeses(
|
||||
nuke.toNode(instance.data["name"]), output_range)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import utils as pnutils
|
||||
import nuke
|
||||
import os
|
||||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api import utils as pnutils
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
maintained_selection,
|
||||
reset_selection,
|
||||
select_nodes
|
||||
)
|
||||
|
||||
|
||||
class ExtractGizmo(openpype.api.Extractor):
|
||||
|
|
@ -26,17 +32,17 @@ class ExtractGizmo(openpype.api.Extractor):
|
|||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
orig_grpn_name = orig_grpn.name()
|
||||
tmp_grpn_name = orig_grpn_name + "_tmp"
|
||||
# select original group node
|
||||
anlib.select_nodes([orig_grpn])
|
||||
select_nodes([orig_grpn])
|
||||
|
||||
# copy to clipboard
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
|
||||
# reset selection to none
|
||||
anlib.reset_selection()
|
||||
reset_selection()
|
||||
|
||||
# paste clipboard
|
||||
nuke.nodePaste("%clipboard%")
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import nuke
|
||||
import os
|
||||
from pprint import pformat
|
||||
import nuke
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from pprint import pformat
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
maintained_selection,
|
||||
select_nodes
|
||||
)
|
||||
|
||||
|
||||
class ExtractModel(openpype.api.Extractor):
|
||||
|
|
@ -49,9 +52,9 @@ class ExtractModel(openpype.api.Extractor):
|
|||
filename = subset + ".{}".format(extension)
|
||||
file_path = os.path.join(staging_dir, filename).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# select model node
|
||||
anlib.select_nodes([model_node])
|
||||
select_nodes([model_node])
|
||||
|
||||
# create write geo node
|
||||
wg_n = nuke.createNode("WriteGeo")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
from avalon.nuke import maintained_selection
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
class CreateOutputNode(pyblish.api.ContextPlugin):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
class ExtractReviewDataLut(openpype.api.Extractor):
|
||||
|
|
@ -37,7 +37,7 @@ class ExtractReviewDataLut(openpype.api.Extractor):
|
|||
"StagingDir `{0}`...".format(instance.data["stagingDir"]))
|
||||
|
||||
# generate data
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
exporter = plugin.ExporterReviewLut(
|
||||
self, instance
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
class ExtractReviewDataMov(openpype.api.Extractor):
|
||||
|
|
@ -41,7 +41,7 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
self.log.info(self.outputs)
|
||||
|
||||
# generate data
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
generated_repres = []
|
||||
for o_name, o_data in self.outputs.items():
|
||||
f_families = o_data["filter"]["families"]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
import nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
import pyblish.api
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
class ExtractSlateFrame(openpype.api.Extractor):
|
||||
|
|
@ -25,7 +25,7 @@ class ExtractSlateFrame(openpype.api.Extractor):
|
|||
else:
|
||||
self.viewer_lut_raw = False
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
self.log.debug("instance: {}".format(instance))
|
||||
self.log.debug("instance.data[families]: {}".format(
|
||||
instance.data["families"]))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import sys
|
||||
import os
|
||||
import nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
import pyblish.api
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
|
|
@ -30,7 +30,7 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
if "render.farm" in instance.data["families"]:
|
||||
return
|
||||
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
self.log.debug("instance: {}".format(instance))
|
||||
self.log.debug("instance.data[families]: {}".format(
|
||||
instance.data["families"]))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
from avalon import io, api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
add_publish_knob,
|
||||
get_avalon_knob_data
|
||||
)
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
|
|
@ -39,7 +42,7 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin):
|
|||
self.log.warning(E)
|
||||
|
||||
# get data from avalon knob
|
||||
avalon_knob_data = anlib.get_avalon_knob_data(
|
||||
avalon_knob_data = get_avalon_knob_data(
|
||||
node, ["avalon:", "ak:"])
|
||||
|
||||
self.log.debug("avalon_knob_data: {}".format(avalon_knob_data))
|
||||
|
|
@ -115,7 +118,7 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin):
|
|||
|
||||
# get publish knob value
|
||||
if "publish" not in node.knobs():
|
||||
anlib.add_publish_knob(node)
|
||||
add_publish_knob(node)
|
||||
|
||||
# sync workfile version
|
||||
_families_test = [family] + families
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
import os
|
||||
|
||||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api as pype
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
add_publish_knob,
|
||||
get_avalon_knob_data
|
||||
)
|
||||
|
||||
|
||||
class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||
|
|
@ -17,9 +22,9 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
|
|||
|
||||
current_file = os.path.normpath(nuke.root().name())
|
||||
|
||||
knob_data = anlib.get_avalon_knob_data(root)
|
||||
knob_data = get_avalon_knob_data(root)
|
||||
|
||||
anlib.add_publish_knob(root)
|
||||
add_publish_knob(root)
|
||||
|
||||
family = "workfile"
|
||||
task = os.getenv("AVALON_TASK", None)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish
|
||||
from avalon.nuke import lib as anlib
|
||||
import nuke
|
||||
import pyblish
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
class SelectCenterInNodeGraph(pyblish.api.Action):
|
||||
|
|
@ -28,7 +28,7 @@ class SelectCenterInNodeGraph(pyblish.api.Action):
|
|||
all_yC = list()
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# collect all failed nodes xpos and ypos
|
||||
for instance in instances:
|
||||
bdn = instance[0]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish
|
||||
from avalon.nuke import lib as anlib
|
||||
import nuke
|
||||
import pyblish
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
|
||||
|
||||
class OpenFailedGroupNode(pyblish.api.Action):
|
||||
|
|
@ -25,7 +25,7 @@ class OpenFailedGroupNode(pyblish.api.Action):
|
|||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
with maintained_selection():
|
||||
# collect all failed nodes xpos and ypos
|
||||
for instance in instances:
|
||||
grpn = instance[0]
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@ import nuke
|
|||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import avalon.nuke.lib
|
||||
import openpype.hosts.nuke.api as nuke_api
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
recreate_instance,
|
||||
reset_selection,
|
||||
select_nodes
|
||||
)
|
||||
|
||||
|
||||
class SelectInvalidInstances(pyblish.api.Action):
|
||||
|
|
@ -47,12 +50,12 @@ class SelectInvalidInstances(pyblish.api.Action):
|
|||
self.deselect()
|
||||
|
||||
def select(self, instances):
|
||||
avalon.nuke.lib.select_nodes(
|
||||
select_nodes(
|
||||
[nuke.toNode(str(x)) for x in instances]
|
||||
)
|
||||
|
||||
def deselect(self):
|
||||
avalon.nuke.lib.reset_selection()
|
||||
reset_selection()
|
||||
|
||||
|
||||
class RepairSelectInvalidInstances(pyblish.api.Action):
|
||||
|
|
@ -82,7 +85,7 @@ class RepairSelectInvalidInstances(pyblish.api.Action):
|
|||
context_asset = context.data["assetEntity"]["name"]
|
||||
for instance in instances:
|
||||
origin_node = instance[0]
|
||||
nuke_api.lib.recreate_instance(
|
||||
recreate_instance(
|
||||
origin_node, avalon_data={"asset": context_asset}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import toml
|
||||
import os
|
||||
import toml
|
||||
|
||||
import nuke
|
||||
|
||||
from avalon import api
|
||||
import re
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from avalon.nuke import get_avalon_knob_data
|
||||
from openpype.hosts.nuke.api.lib import get_avalon_knob_data
|
||||
|
||||
|
||||
class ValidateWriteLegacy(pyblish.api.InstancePlugin):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
import openpype.utils
|
||||
import openpype.hosts.nuke.lib as nukelib
|
||||
import avalon.nuke
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
get_write_node_template_attr,
|
||||
get_node_path
|
||||
)
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class RepairNukeWriteNodeAction(pyblish.api.Action):
|
||||
|
|
@ -15,7 +18,7 @@ class RepairNukeWriteNodeAction(pyblish.api.Action):
|
|||
|
||||
for instance in instances:
|
||||
node = instance[1]
|
||||
correct_data = nukelib.get_write_node_template_attr(node)
|
||||
correct_data = get_write_node_template_attr(node)
|
||||
for k, v in correct_data.items():
|
||||
node[k].setValue(v)
|
||||
self.log.info("Node attributes were fixed")
|
||||
|
|
@ -34,14 +37,14 @@ class ValidateNukeWriteNode(pyblish.api.InstancePlugin):
|
|||
def process(self, instance):
|
||||
|
||||
node = instance[1]
|
||||
correct_data = nukelib.get_write_node_template_attr(node)
|
||||
correct_data = get_write_node_template_attr(node)
|
||||
|
||||
check = []
|
||||
for k, v in correct_data.items():
|
||||
if k is 'file':
|
||||
padding = len(v.split('#'))
|
||||
ref_path = avalon.nuke.lib.get_node_path(v, padding)
|
||||
n_path = avalon.nuke.lib.get_node_path(node[k].value(), padding)
|
||||
ref_path = get_node_path(v, padding)
|
||||
n_path = get_node_path(node[k].value(), padding)
|
||||
isnt = False
|
||||
for i, p in enumerate(ref_path):
|
||||
if str(n_path[i]) not in str(p):
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
import nuke
|
||||
|
||||
# default write mov
|
||||
nuke.knobDefault('Write.mov.colorspace', 'sRGB')
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
import nuke
|
||||
import avalon.api
|
||||
|
||||
from openpype.api import Logger
|
||||
from openpype.hosts.nuke import api
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
on_script_load,
|
||||
check_inventory_versions,
|
||||
WorkfileSettings
|
||||
WorkfileSettings,
|
||||
dirmap_file_name_filter
|
||||
)
|
||||
|
||||
import nuke
|
||||
from openpype.api import Logger
|
||||
from openpype.hosts.nuke.api.lib import dirmap_file_name_filter
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
avalon.api.install(api)
|
||||
|
||||
# fix ffmpeg settings on script
|
||||
nuke.addOnScriptLoad(on_script_load)
|
||||
|
|
|
|||
|
|
@ -168,9 +168,14 @@ from .editorial import (
|
|||
make_sequence_collection
|
||||
)
|
||||
|
||||
from .pype_info import (
|
||||
from .openpype_version import (
|
||||
op_version_control_available,
|
||||
get_openpype_version,
|
||||
get_build_version
|
||||
get_build_version,
|
||||
get_expected_version,
|
||||
is_running_from_build,
|
||||
is_running_staging,
|
||||
is_current_version_studio_latest
|
||||
)
|
||||
|
||||
terminal = Terminal
|
||||
|
|
@ -302,6 +307,11 @@ __all__ = [
|
|||
"create_workdir_extra_folders",
|
||||
"get_project_basic_paths",
|
||||
|
||||
"op_version_control_available",
|
||||
"get_openpype_version",
|
||||
"get_build_version",
|
||||
"get_expected_version",
|
||||
"is_running_from_build",
|
||||
"is_running_staging",
|
||||
"is_current_version_studio_latest",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -9,9 +9,69 @@ OpenPype version located in build but versions available in remote versions
|
|||
repository or locally available.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import openpype.version
|
||||
|
||||
from .python_module_tools import import_filepath
|
||||
|
||||
|
||||
# ----------------------------------------
|
||||
# Functions independent on OpenPypeVersion
|
||||
# ----------------------------------------
|
||||
def get_openpype_version():
|
||||
"""Version of pype that is currently used."""
|
||||
return openpype.version.__version__
|
||||
|
||||
|
||||
def get_build_version():
|
||||
"""OpenPype version of build."""
|
||||
# Return OpenPype version if is running from code
|
||||
if not is_running_from_build():
|
||||
return get_openpype_version()
|
||||
|
||||
# Import `version.py` from build directory
|
||||
version_filepath = os.path.join(
|
||||
os.environ["OPENPYPE_ROOT"],
|
||||
"openpype",
|
||||
"version.py"
|
||||
)
|
||||
if not os.path.exists(version_filepath):
|
||||
return None
|
||||
|
||||
module = import_filepath(version_filepath, "openpype_build_version")
|
||||
return getattr(module, "__version__", None)
|
||||
|
||||
|
||||
def is_running_from_build():
|
||||
"""Determine if current process is running from build or code.
|
||||
|
||||
Returns:
|
||||
bool: True if running from build.
|
||||
"""
|
||||
executable_path = os.environ["OPENPYPE_EXECUTABLE"]
|
||||
executable_filename = os.path.basename(executable_path)
|
||||
if "python" in executable_filename.lower():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_running_staging():
|
||||
"""Currently used OpenPype is staging version.
|
||||
|
||||
Returns:
|
||||
bool: True if openpype version containt 'staging'.
|
||||
"""
|
||||
if "staging" in get_openpype_version():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# ----------------------------------------
|
||||
# Functions dependent on OpenPypeVersion
|
||||
# - Make sense to call only in OpenPype process
|
||||
# ----------------------------------------
|
||||
def get_OpenPypeVersion():
|
||||
"""Access to OpenPypeVersion class stored in sys modules."""
|
||||
return sys.modules.get("OpenPypeVersion")
|
||||
|
|
@ -71,15 +131,67 @@ def get_remote_versions(*args, **kwargs):
|
|||
return None
|
||||
|
||||
|
||||
def get_latest_version(*args, **kwargs):
|
||||
def get_latest_version(staging=None, local=None, remote=None):
|
||||
"""Get latest version from repository path."""
|
||||
if staging is None:
|
||||
staging = is_running_staging()
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_latest_version(*args, **kwargs)
|
||||
return get_OpenPypeVersion().get_latest_version(
|
||||
staging=staging,
|
||||
local=local,
|
||||
remote=remote
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_expected_studio_version(staging=False):
|
||||
def get_expected_studio_version(staging=None):
|
||||
"""Expected production or staging version in studio."""
|
||||
if staging is None:
|
||||
staging = is_running_staging()
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_expected_studio_version(staging)
|
||||
return None
|
||||
|
||||
|
||||
def get_expected_version(staging=None):
|
||||
expected_version = get_expected_studio_version(staging)
|
||||
if expected_version is None:
|
||||
# Look for latest if expected version is not set in settings
|
||||
expected_version = get_latest_version(
|
||||
staging=staging,
|
||||
remote=True
|
||||
)
|
||||
return expected_version
|
||||
|
||||
|
||||
def is_current_version_studio_latest():
|
||||
"""Is currently running OpenPype version which is defined by studio.
|
||||
|
||||
It is not recommended to ask in each process as there may be situations
|
||||
when older OpenPype should be used. For example on farm. But it does make
|
||||
sense in processes that can run for a long time.
|
||||
|
||||
Returns:
|
||||
None: Can't determine. e.g. when running from code or the build is
|
||||
too old.
|
||||
bool: True when is using studio
|
||||
"""
|
||||
output = None
|
||||
# Skip if is not running from build or build does not support version
|
||||
# control or path to folder with zip files is not accessible
|
||||
if (
|
||||
not is_running_from_build()
|
||||
or not op_version_control_available()
|
||||
or not openpype_path_is_accessible()
|
||||
):
|
||||
return output
|
||||
|
||||
# Get OpenPypeVersion class
|
||||
OpenPypeVersion = get_OpenPypeVersion()
|
||||
# Convert current version to OpenPypeVersion object
|
||||
current_version = OpenPypeVersion(version=get_openpype_version())
|
||||
|
||||
# Get expected version (from settings)
|
||||
expected_version = get_expected_version()
|
||||
# Check if current version is expected version
|
||||
return current_version == expected_version
|
||||
|
|
|
|||
|
|
@ -5,68 +5,13 @@ import platform
|
|||
import getpass
|
||||
import socket
|
||||
|
||||
import openpype.version
|
||||
from openpype.settings.lib import get_local_settings
|
||||
from .execute import get_openpype_execute_args
|
||||
from .local_settings import get_local_site_id
|
||||
from .python_module_tools import import_filepath
|
||||
|
||||
|
||||
def get_openpype_version():
|
||||
"""Version of pype that is currently used."""
|
||||
return openpype.version.__version__
|
||||
|
||||
|
||||
def get_pype_version():
|
||||
"""Backwards compatibility. Remove when 100% not used."""
|
||||
print((
|
||||
"Using deprecated function 'openpype.lib.pype_info.get_pype_version'"
|
||||
" replace with 'openpype.lib.pype_info.get_openpype_version'."
|
||||
))
|
||||
return get_openpype_version()
|
||||
|
||||
|
||||
def get_build_version():
|
||||
"""OpenPype version of build."""
|
||||
# Return OpenPype version if is running from code
|
||||
if not is_running_from_build():
|
||||
return get_openpype_version()
|
||||
|
||||
# Import `version.py` from build directory
|
||||
version_filepath = os.path.join(
|
||||
os.environ["OPENPYPE_ROOT"],
|
||||
"openpype",
|
||||
"version.py"
|
||||
)
|
||||
if not os.path.exists(version_filepath):
|
||||
return None
|
||||
|
||||
module = import_filepath(version_filepath, "openpype_build_version")
|
||||
return getattr(module, "__version__", None)
|
||||
|
||||
|
||||
def is_running_from_build():
|
||||
"""Determine if current process is running from build or code.
|
||||
|
||||
Returns:
|
||||
bool: True if running from build.
|
||||
"""
|
||||
executable_path = os.environ["OPENPYPE_EXECUTABLE"]
|
||||
executable_filename = os.path.basename(executable_path)
|
||||
if "python" in executable_filename.lower():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_running_staging():
|
||||
"""Currently used OpenPype is staging version.
|
||||
|
||||
Returns:
|
||||
bool: True if openpype version containt 'staging'.
|
||||
"""
|
||||
if "staging" in get_openpype_version():
|
||||
return True
|
||||
return False
|
||||
from .openpype_version import (
|
||||
is_running_from_build,
|
||||
get_openpype_version
|
||||
)
|
||||
|
||||
|
||||
def get_pype_info():
|
||||
|
|
|
|||
|
|
@ -163,24 +163,27 @@ class DeleteAssetSubset(BaseAction):
|
|||
|
||||
if not selected_av_entities:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Didn't found entities in avalon"
|
||||
"success": True,
|
||||
"message": (
|
||||
"Didn't found entities in avalon."
|
||||
" You can use Ftrack's Delete button for the selection."
|
||||
)
|
||||
}
|
||||
|
||||
# Remove cached action older than 2 minutes
|
||||
old_action_ids = []
|
||||
for id, data in self.action_data_by_id.items():
|
||||
for action_id, data in self.action_data_by_id.items():
|
||||
created_at = data.get("created_at")
|
||||
if not created_at:
|
||||
old_action_ids.append(id)
|
||||
old_action_ids.append(action_id)
|
||||
continue
|
||||
cur_time = datetime.now()
|
||||
existing_in_sec = (created_at - cur_time).total_seconds()
|
||||
if existing_in_sec > 60 * 2:
|
||||
old_action_ids.append(id)
|
||||
old_action_ids.append(action_id)
|
||||
|
||||
for id in old_action_ids:
|
||||
self.action_data_by_id.pop(id, None)
|
||||
for action_id in old_action_ids:
|
||||
self.action_data_by_id.pop(action_id, None)
|
||||
|
||||
# Store data for action id
|
||||
action_id = str(uuid.uuid1())
|
||||
|
|
@ -439,7 +442,11 @@ class DeleteAssetSubset(BaseAction):
|
|||
subsets_to_delete = to_delete.get("subsets") or []
|
||||
|
||||
# Convert asset ids to ObjectId obj
|
||||
assets_to_delete = [ObjectId(id) for id in assets_to_delete if id]
|
||||
assets_to_delete = [
|
||||
ObjectId(asset_id)
|
||||
for asset_id in assets_to_delete
|
||||
if asset_id
|
||||
]
|
||||
|
||||
subset_ids_by_parent = spec_data["subset_ids_by_parent"]
|
||||
subset_ids_by_name = spec_data["subset_ids_by_name"]
|
||||
|
|
@ -468,9 +475,8 @@ class DeleteAssetSubset(BaseAction):
|
|||
if not ftrack_id:
|
||||
ftrack_id = asset["data"].get("ftrackId")
|
||||
|
||||
if not ftrack_id:
|
||||
continue
|
||||
ftrack_ids_to_delete.append(ftrack_id)
|
||||
if ftrack_id:
|
||||
ftrack_ids_to_delete.append(ftrack_id)
|
||||
|
||||
children_queue = collections.deque()
|
||||
for mongo_id in assets_to_delete:
|
||||
|
|
@ -569,12 +575,12 @@ class DeleteAssetSubset(BaseAction):
|
|||
exc_info=True
|
||||
)
|
||||
|
||||
if not_deleted_entities_id:
|
||||
joined_not_deleted = ", ".join([
|
||||
if not_deleted_entities_id and asset_names_to_delete:
|
||||
joined_not_deleted = ",".join([
|
||||
"\"{}\"".format(ftrack_id)
|
||||
for ftrack_id in not_deleted_entities_id
|
||||
])
|
||||
joined_asset_names = ", ".join([
|
||||
joined_asset_names = ",".join([
|
||||
"\"{}\"".format(name)
|
||||
for name in asset_names_to_delete
|
||||
])
|
||||
|
|
@ -613,6 +619,25 @@ class DeleteAssetSubset(BaseAction):
|
|||
joined_ids_to_delete
|
||||
)
|
||||
).all()
|
||||
# Find all children entities and add them to list
|
||||
# - Delete tasks first then their parents and continue
|
||||
parent_ids_to_delete = [
|
||||
entity["id"]
|
||||
for entity in to_delete_entities
|
||||
]
|
||||
while parent_ids_to_delete:
|
||||
joined_parent_ids_to_delete = ",".join([
|
||||
"\"{}\"".format(ftrack_id)
|
||||
for ftrack_id in parent_ids_to_delete
|
||||
])
|
||||
_to_delete = session.query((
|
||||
"select id, link from TypedContext where parent_id in ({})"
|
||||
).format(joined_parent_ids_to_delete)).all()
|
||||
parent_ids_to_delete = []
|
||||
for entity in _to_delete:
|
||||
parent_ids_to_delete.append(entity["id"])
|
||||
to_delete_entities.append(entity)
|
||||
|
||||
entities_by_link_len = collections.defaultdict(list)
|
||||
for entity in to_delete_entities:
|
||||
entities_by_link_len[len(entity["link"])].append(entity)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import os
|
||||
import time
|
||||
import subprocess
|
||||
from operator import itemgetter
|
||||
from openpype.lib import ApplicationManager
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
|
|
@ -23,15 +25,25 @@ class DJVViewAction(BaseAction):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.djv_path = self.find_djv_path()
|
||||
self.application_manager = ApplicationManager()
|
||||
self._last_check = time.time()
|
||||
self._check_interval = 10
|
||||
|
||||
def preregister(self):
|
||||
if self.djv_path is None:
|
||||
return (
|
||||
'DJV View is not installed'
|
||||
' or paths in presets are not set correctly'
|
||||
)
|
||||
return True
|
||||
def _get_djv_apps(self):
|
||||
app_group = self.application_manager.app_groups["djvview"]
|
||||
|
||||
output = []
|
||||
for app in app_group:
|
||||
executable = app.find_executable()
|
||||
if executable is not None:
|
||||
output.append(app)
|
||||
return output
|
||||
|
||||
def get_djv_apps(self):
|
||||
cur_time = time.time()
|
||||
if (cur_time - self._last_check) > self._check_interval:
|
||||
self.application_manager.refresh()
|
||||
return self._get_djv_apps()
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
"""Return available actions based on *event*. """
|
||||
|
|
@ -40,15 +52,13 @@ class DJVViewAction(BaseAction):
|
|||
return False
|
||||
|
||||
entityType = selection[0].get("entityType", None)
|
||||
if entityType in ["assetversion", "task"]:
|
||||
if entityType not in ["assetversion", "task"]:
|
||||
return False
|
||||
|
||||
if self.get_djv_apps():
|
||||
return True
|
||||
return False
|
||||
|
||||
def find_djv_path(self):
|
||||
for path in (os.environ.get("DJV_PATH") or "").split(os.pathsep):
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
def interface(self, session, entities, event):
|
||||
if event['data'].get('values', {}):
|
||||
return
|
||||
|
|
@ -88,7 +98,37 @@ class DJVViewAction(BaseAction):
|
|||
'message': 'There are no Asset Versions to open.'
|
||||
}
|
||||
|
||||
items = []
|
||||
# TODO sort them (somehow?)
|
||||
enum_items = []
|
||||
first_value = None
|
||||
for app in self.get_djv_apps():
|
||||
if first_value is None:
|
||||
first_value = app.full_name
|
||||
enum_items.append({
|
||||
"value": app.full_name,
|
||||
"label": app.full_label
|
||||
})
|
||||
|
||||
if not enum_items:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Couldn't find DJV executable."
|
||||
}
|
||||
|
||||
items = [
|
||||
{
|
||||
"type": "enumerator",
|
||||
"label": "DJV version:",
|
||||
"name": "djv_app_name",
|
||||
"data": enum_items,
|
||||
"value": first_value
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"value": "---"
|
||||
}
|
||||
]
|
||||
version_items = []
|
||||
base_label = "v{0} - {1} - {2}"
|
||||
default_component = None
|
||||
last_available = None
|
||||
|
|
@ -115,11 +155,11 @@ class DJVViewAction(BaseAction):
|
|||
last_available = file_path
|
||||
if component['name'] == default_component:
|
||||
select_value = file_path
|
||||
items.append(
|
||||
version_items.append(
|
||||
{'label': label, 'value': file_path}
|
||||
)
|
||||
|
||||
if len(items) == 0:
|
||||
if len(version_items) == 0:
|
||||
return {
|
||||
'success': False,
|
||||
'message': (
|
||||
|
|
@ -132,7 +172,7 @@ class DJVViewAction(BaseAction):
|
|||
'type': 'enumerator',
|
||||
'name': 'path',
|
||||
'data': sorted(
|
||||
items,
|
||||
version_items,
|
||||
key=itemgetter('label'),
|
||||
reverse=True
|
||||
)
|
||||
|
|
@ -142,21 +182,37 @@ class DJVViewAction(BaseAction):
|
|||
else:
|
||||
item['value'] = last_available
|
||||
|
||||
return {'items': [item]}
|
||||
items.append(item)
|
||||
|
||||
return {'items': items}
|
||||
|
||||
def launch(self, session, entities, event):
|
||||
"""Callback method for DJVView action."""
|
||||
|
||||
# Launching application
|
||||
if "values" not in event["data"]:
|
||||
event_data = event["data"]
|
||||
if "values" not in event_data:
|
||||
return
|
||||
filpath = event['data']['values']['path']
|
||||
|
||||
djv_app_name = event_data["djv_app_name"]
|
||||
app = self.applicaion_manager.applications.get(djv_app_name)
|
||||
executable = None
|
||||
if app is not None:
|
||||
executable = app.find_executable()
|
||||
|
||||
if not executable:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Couldn't find DJV executable."
|
||||
}
|
||||
|
||||
filpath = os.path.normpath(event_data["values"]["path"])
|
||||
|
||||
cmd = [
|
||||
# DJV path
|
||||
os.path.normpath(self.djv_path),
|
||||
executable,
|
||||
# PATH TO COMPONENT
|
||||
os.path.normpath(filpath)
|
||||
filpath
|
||||
]
|
||||
|
||||
try:
|
||||
|
|
@ -164,8 +220,8 @@ class DJVViewAction(BaseAction):
|
|||
subprocess.Popen(cmd)
|
||||
except FileNotFoundError:
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'File "{}" was not found.'.format(
|
||||
"success": False,
|
||||
"message": "File \"{}\" was not found.".format(
|
||||
os.path.basename(filpath)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,14 @@ from openpype_modules.ftrack.ftrack_server.lib import (
|
|||
TOPIC_STATUS_SERVER_RESULT
|
||||
)
|
||||
from openpype.api import Logger
|
||||
from openpype.lib import (
|
||||
is_current_version_studio_latest,
|
||||
is_running_from_build,
|
||||
get_expected_version,
|
||||
get_openpype_version
|
||||
)
|
||||
|
||||
log = Logger().get_logger("Event storer")
|
||||
log = Logger.get_logger("Event storer")
|
||||
action_identifier = (
|
||||
"event.server.status" + os.environ["FTRACK_EVENT_SUB_ID"]
|
||||
)
|
||||
|
|
@ -203,8 +209,57 @@ class StatusFactory:
|
|||
})
|
||||
return items
|
||||
|
||||
def openpype_version_items(self):
|
||||
items = []
|
||||
is_latest = is_current_version_studio_latest()
|
||||
items.append({
|
||||
"type": "label",
|
||||
"value": "# OpenPype version"
|
||||
})
|
||||
if not is_running_from_build():
|
||||
items.append({
|
||||
"type": "label",
|
||||
"value": (
|
||||
"OpenPype event server is running from code <b>{}</b>."
|
||||
).format(str(get_openpype_version()))
|
||||
})
|
||||
|
||||
elif is_latest is None:
|
||||
items.append({
|
||||
"type": "label",
|
||||
"value": (
|
||||
"Can't determine if OpenPype version is outdated"
|
||||
" <b>{}</b>. OpenPype build version should be updated."
|
||||
).format(str(get_openpype_version()))
|
||||
})
|
||||
elif is_latest:
|
||||
items.append({
|
||||
"type": "label",
|
||||
"value": "OpenPype version is up to date <b>{}</b>.".format(
|
||||
str(get_openpype_version())
|
||||
)
|
||||
})
|
||||
else:
|
||||
items.append({
|
||||
"type": "label",
|
||||
"value": (
|
||||
"Using <b>outdated</b> OpenPype version <b>{}</b>."
|
||||
" Expected version is <b>{}</b>."
|
||||
"<br/>- Please restart event server for automatic"
|
||||
" updates or update manually."
|
||||
).format(
|
||||
str(get_openpype_version()),
|
||||
str(get_expected_version())
|
||||
)
|
||||
})
|
||||
|
||||
items.append({"type": "label", "value": "---"})
|
||||
|
||||
return items
|
||||
|
||||
def items(self):
|
||||
items = []
|
||||
items.extend(self.openpype_version_items())
|
||||
items.append(self.note_item)
|
||||
items.extend(self.bool_items())
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,28 @@ class ExtractOTIOReview(openpype.api.Extractor):
|
|||
for index, r_otio_cl in enumerate(otio_review_clips):
|
||||
# QUESTION: what if transition on clip?
|
||||
|
||||
# check if resolution is the same
|
||||
width = self.to_width
|
||||
height = self.to_height
|
||||
otio_media = r_otio_cl.media_reference
|
||||
media_metadata = otio_media.metadata
|
||||
|
||||
# get from media reference metadata source
|
||||
if media_metadata.get("openpype.source.width"):
|
||||
width = int(media_metadata.get("openpype.source.width"))
|
||||
if media_metadata.get("openpype.source.height"):
|
||||
height = int(media_metadata.get("openpype.source.height"))
|
||||
|
||||
# compare and reset
|
||||
if width != self.to_width:
|
||||
self.to_width = width
|
||||
if height != self.to_height:
|
||||
self.to_height = height
|
||||
|
||||
self.log.debug("> self.to_width x self.to_height: {} x {}".format(
|
||||
self.to_width, self.to_height
|
||||
))
|
||||
|
||||
# get frame range values
|
||||
src_range = r_otio_cl.source_range
|
||||
start = src_range.start_time.value
|
||||
|
|
|
|||
|
|
@ -292,6 +292,21 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
temp_data["frame_start"],
|
||||
temp_data["frame_end"])
|
||||
|
||||
# create or update outputName
|
||||
output_name = new_repre.get("outputName", "")
|
||||
output_ext = new_repre["ext"]
|
||||
if output_name:
|
||||
output_name += "_"
|
||||
output_name += output_def["filename_suffix"]
|
||||
if temp_data["without_handles"]:
|
||||
output_name += "_noHandles"
|
||||
|
||||
# add outputName to anatomy format fill_data
|
||||
fill_data.update({
|
||||
"output": output_name,
|
||||
"ext": output_ext
|
||||
})
|
||||
|
||||
try: # temporary until oiiotool is supported cross platform
|
||||
ffmpeg_args = self._ffmpeg_arguments(
|
||||
output_def, instance, new_repre, temp_data, fill_data
|
||||
|
|
@ -317,14 +332,6 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
for f in files_to_clean:
|
||||
os.unlink(f)
|
||||
|
||||
output_name = new_repre.get("outputName", "")
|
||||
output_ext = new_repre["ext"]
|
||||
if output_name:
|
||||
output_name += "_"
|
||||
output_name += output_def["filename_suffix"]
|
||||
if temp_data["without_handles"]:
|
||||
output_name += "_noHandles"
|
||||
|
||||
new_repre.update({
|
||||
"name": "{}_{}".format(output_name, output_ext),
|
||||
"outputName": output_name,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
from openpype.lib.pype_info import is_running_staging
|
||||
from openpype.lib.openpype_version import is_running_staging
|
||||
|
||||
RESOURCES_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"admin_password": "",
|
||||
"production_version": "",
|
||||
"staging_version": "",
|
||||
"version_check_interval": 5,
|
||||
"environment": {
|
||||
"__environment_keys__": {
|
||||
"global": []
|
||||
|
|
|
|||
|
|
@ -469,6 +469,17 @@ class PathInput(InputEntity):
|
|||
# GUI attributes
|
||||
self.placeholder_text = self.schema_data.get("placeholder")
|
||||
|
||||
def set(self, value):
|
||||
# Strip value
|
||||
super(PathInput, self).set(value.strip())
|
||||
|
||||
def set_override_state(self, state, ignore_missing_defaults):
|
||||
super(PathInput, self).set_override_state(
|
||||
state, ignore_missing_defaults
|
||||
)
|
||||
# Strip current value
|
||||
self._current_value = self._current_value.strip()
|
||||
|
||||
|
||||
class RawJsonEntity(InputEntity):
|
||||
schema_types = ["raw-json"]
|
||||
|
|
|
|||
|
|
@ -47,6 +47,19 @@
|
|||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Trigger validation if running OpenPype is using studio defined version each 'n' <b>minutes</b>. Validation happens in OpenPype tray application."
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "version_check_interval",
|
||||
"label": "Version check interval",
|
||||
"minimum": 0
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"key": "environment",
|
||||
"label": "Environment",
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@
|
|||
"border-hover": "rgba(168, 175, 189, .3)",
|
||||
"border-focus": "rgb(92, 173, 214)",
|
||||
|
||||
"restart-btn-bg": "#458056",
|
||||
|
||||
"delete-btn-bg": "rgb(201, 54, 54)",
|
||||
"delete-btn-bg-disabled": "rgba(201, 54, 54, 64)",
|
||||
|
||||
|
|
|
|||
|
|
@ -1228,6 +1228,11 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
background: #21252B;
|
||||
}
|
||||
|
||||
/* Tray */
|
||||
#TrayRestartButton {
|
||||
background: {color:restart-btn-bg};
|
||||
}
|
||||
|
||||
/* Globally used names */
|
||||
#Separator {
|
||||
background: {color:bg-menu-separator};
|
||||
|
|
|
|||
BIN
openpype/tools/tray/images/gifts.png
Normal file
BIN
openpype/tools/tray/images/gifts.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
|
|
@ -14,7 +14,15 @@ from openpype.api import (
|
|||
resources,
|
||||
get_system_settings
|
||||
)
|
||||
from openpype.lib import get_openpype_execute_args
|
||||
from openpype.lib import (
|
||||
get_openpype_execute_args,
|
||||
op_version_control_available,
|
||||
is_current_version_studio_latest,
|
||||
is_running_from_build,
|
||||
is_running_staging,
|
||||
get_expected_version,
|
||||
get_openpype_version
|
||||
)
|
||||
from openpype.modules import TrayModulesManager
|
||||
from openpype import style
|
||||
from openpype.settings import (
|
||||
|
|
@ -22,29 +30,177 @@ from openpype.settings import (
|
|||
ProjectSettings,
|
||||
DefaultsNotDefined
|
||||
)
|
||||
from openpype.tools.utils import (
|
||||
WrappedCallbackItem,
|
||||
paint_image_with_color
|
||||
)
|
||||
|
||||
from .pype_info_widget import PypeInfoWidget
|
||||
|
||||
|
||||
# TODO PixmapLabel should be moved to 'utils' in other future PR so should be
|
||||
# imported from there
|
||||
class PixmapLabel(QtWidgets.QLabel):
|
||||
"""Label resizing image to height of font."""
|
||||
def __init__(self, pixmap, parent):
|
||||
super(PixmapLabel, self).__init__(parent)
|
||||
self._empty_pixmap = QtGui.QPixmap(0, 0)
|
||||
self._source_pixmap = pixmap
|
||||
|
||||
def set_source_pixmap(self, pixmap):
|
||||
"""Change source image."""
|
||||
self._source_pixmap = pixmap
|
||||
self._set_resized_pix()
|
||||
|
||||
def _get_pix_size(self):
|
||||
size = self.fontMetrics().height() * 3
|
||||
return size, size
|
||||
|
||||
def _set_resized_pix(self):
|
||||
if self._source_pixmap is None:
|
||||
self.setPixmap(self._empty_pixmap)
|
||||
return
|
||||
width, height = self._get_pix_size()
|
||||
self.setPixmap(
|
||||
self._source_pixmap.scaled(
|
||||
width,
|
||||
height,
|
||||
QtCore.Qt.KeepAspectRatio,
|
||||
QtCore.Qt.SmoothTransformation
|
||||
)
|
||||
)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
self._set_resized_pix()
|
||||
super(PixmapLabel, self).resizeEvent(event)
|
||||
|
||||
|
||||
class VersionDialog(QtWidgets.QDialog):
|
||||
restart_requested = QtCore.Signal()
|
||||
ignore_requested = QtCore.Signal()
|
||||
|
||||
_min_width = 400
|
||||
_min_height = 130
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(VersionDialog, self).__init__(parent)
|
||||
self.setWindowTitle("OpenPype update is needed")
|
||||
icon = QtGui.QIcon(resources.get_openpype_icon_filepath())
|
||||
self.setWindowIcon(icon)
|
||||
self.setWindowFlags(
|
||||
self.windowFlags()
|
||||
| QtCore.Qt.WindowStaysOnTopHint
|
||||
)
|
||||
|
||||
self.setMinimumWidth(self._min_width)
|
||||
self.setMinimumHeight(self._min_height)
|
||||
|
||||
top_widget = QtWidgets.QWidget(self)
|
||||
|
||||
gift_pixmap = self._get_gift_pixmap()
|
||||
gift_icon_label = PixmapLabel(gift_pixmap, top_widget)
|
||||
|
||||
label_widget = QtWidgets.QLabel(top_widget)
|
||||
label_widget.setWordWrap(True)
|
||||
|
||||
top_layout = QtWidgets.QHBoxLayout(top_widget)
|
||||
# top_layout.setContentsMargins(0, 0, 0, 0)
|
||||
top_layout.setSpacing(10)
|
||||
top_layout.addWidget(gift_icon_label, 0, QtCore.Qt.AlignCenter)
|
||||
top_layout.addWidget(label_widget, 1)
|
||||
|
||||
ignore_btn = QtWidgets.QPushButton("Later", self)
|
||||
restart_btn = QtWidgets.QPushButton("Restart && Update", self)
|
||||
restart_btn.setObjectName("TrayRestartButton")
|
||||
|
||||
btns_layout = QtWidgets.QHBoxLayout()
|
||||
btns_layout.addStretch(1)
|
||||
btns_layout.addWidget(ignore_btn, 0)
|
||||
btns_layout.addWidget(restart_btn, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(top_widget, 0)
|
||||
layout.addStretch(1)
|
||||
layout.addLayout(btns_layout, 0)
|
||||
|
||||
ignore_btn.clicked.connect(self._on_ignore)
|
||||
restart_btn.clicked.connect(self._on_reset)
|
||||
|
||||
self._label_widget = label_widget
|
||||
self._restart_accepted = False
|
||||
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
def _get_gift_pixmap(self):
|
||||
image_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"images",
|
||||
"gifts.png"
|
||||
)
|
||||
src_image = QtGui.QImage(image_path)
|
||||
colors = style.get_objected_colors()
|
||||
color_value = colors["font"]
|
||||
|
||||
return paint_image_with_color(
|
||||
src_image,
|
||||
color_value.get_qcolor()
|
||||
)
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
self._restart_accepted = False
|
||||
|
||||
def closeEvent(self, event):
|
||||
super().closeEvent(event)
|
||||
if not self._restart_accepted:
|
||||
self.ignore_requested.emit()
|
||||
|
||||
def update_versions(self, current_version, expected_version):
|
||||
message = (
|
||||
"Running OpenPype version is <b>{}</b>."
|
||||
" Your production has been updated to version <b>{}</b>."
|
||||
).format(str(current_version), str(expected_version))
|
||||
self._label_widget.setText(message)
|
||||
|
||||
def _on_ignore(self):
|
||||
self.reject()
|
||||
|
||||
def _on_reset(self):
|
||||
self._restart_accepted = True
|
||||
self.restart_requested.emit()
|
||||
self.accept()
|
||||
|
||||
|
||||
class TrayManager:
|
||||
"""Cares about context of application.
|
||||
|
||||
Load submenus, actions, separators and modules into tray's context.
|
||||
"""
|
||||
|
||||
def __init__(self, tray_widget, main_window):
|
||||
self.tray_widget = tray_widget
|
||||
self.main_window = main_window
|
||||
self.pype_info_widget = None
|
||||
self._restart_action = None
|
||||
|
||||
self.log = Logger.get_logger(self.__class__.__name__)
|
||||
|
||||
self.module_settings = get_system_settings()["modules"]
|
||||
system_settings = get_system_settings()
|
||||
self.module_settings = system_settings["modules"]
|
||||
|
||||
version_check_interval = system_settings["general"].get(
|
||||
"version_check_interval"
|
||||
)
|
||||
if version_check_interval is None:
|
||||
version_check_interval = 5
|
||||
self._version_check_interval = version_check_interval * 60 * 1000
|
||||
|
||||
self.modules_manager = TrayModulesManager()
|
||||
|
||||
self.errors = []
|
||||
|
||||
self._version_check_timer = None
|
||||
self._version_dialog = None
|
||||
|
||||
self.main_thread_timer = None
|
||||
self._main_thread_callbacks = collections.deque()
|
||||
self._execution_in_progress = None
|
||||
|
|
@ -61,21 +217,73 @@ class TrayManager:
|
|||
if callback:
|
||||
self.execute_in_main_thread(callback)
|
||||
|
||||
def execute_in_main_thread(self, callback):
|
||||
self._main_thread_callbacks.append(callback)
|
||||
def _on_version_check_timer(self):
|
||||
# Check if is running from build and stop future validations if yes
|
||||
if not is_running_from_build() or not op_version_control_available():
|
||||
self._version_check_timer.stop()
|
||||
return
|
||||
|
||||
self.validate_openpype_version()
|
||||
|
||||
def validate_openpype_version(self):
|
||||
using_requested = is_current_version_studio_latest()
|
||||
self._restart_action.setVisible(not using_requested)
|
||||
if using_requested:
|
||||
if (
|
||||
self._version_dialog is not None
|
||||
and self._version_dialog.isVisible()
|
||||
):
|
||||
self._version_dialog.close()
|
||||
return
|
||||
|
||||
if self._version_dialog is None:
|
||||
self._version_dialog = VersionDialog()
|
||||
self._version_dialog.restart_requested.connect(
|
||||
self._restart_and_install
|
||||
)
|
||||
self._version_dialog.ignore_requested.connect(
|
||||
self._outdated_version_ignored
|
||||
)
|
||||
|
||||
expected_version = get_expected_version()
|
||||
current_version = get_openpype_version()
|
||||
self._version_dialog.update_versions(
|
||||
current_version, expected_version
|
||||
)
|
||||
self._version_dialog.show()
|
||||
self._version_dialog.raise_()
|
||||
self._version_dialog.activateWindow()
|
||||
|
||||
def _restart_and_install(self):
|
||||
self.restart()
|
||||
|
||||
def _outdated_version_ignored(self):
|
||||
self.show_tray_message(
|
||||
"OpenPype version is outdated",
|
||||
(
|
||||
"Please update your OpenPype as soon as possible."
|
||||
" To update, restart OpenPype Tray application."
|
||||
)
|
||||
)
|
||||
|
||||
def execute_in_main_thread(self, callback, *args, **kwargs):
|
||||
if isinstance(callback, WrappedCallbackItem):
|
||||
item = callback
|
||||
else:
|
||||
item = WrappedCallbackItem(callback, *args, **kwargs)
|
||||
|
||||
self._main_thread_callbacks.append(item)
|
||||
|
||||
return item
|
||||
|
||||
def _main_thread_execution(self):
|
||||
if self._execution_in_progress:
|
||||
return
|
||||
self._execution_in_progress = True
|
||||
while self._main_thread_callbacks:
|
||||
try:
|
||||
callback = self._main_thread_callbacks.popleft()
|
||||
callback()
|
||||
except:
|
||||
self.log.warning(
|
||||
"Failed to execute {} in main thread".format(callback),
|
||||
exc_info=True)
|
||||
for _ in range(len(self._main_thread_callbacks)):
|
||||
if self._main_thread_callbacks:
|
||||
item = self._main_thread_callbacks.popleft()
|
||||
item.execute()
|
||||
|
||||
self._execution_in_progress = False
|
||||
|
||||
|
|
@ -119,6 +327,13 @@ class TrayManager:
|
|||
|
||||
self.main_thread_timer = main_thread_timer
|
||||
|
||||
version_check_timer = QtCore.QTimer()
|
||||
version_check_timer.timeout.connect(self._on_version_check_timer)
|
||||
if self._version_check_interval > 0:
|
||||
version_check_timer.setInterval(self._version_check_interval)
|
||||
version_check_timer.start()
|
||||
self._version_check_timer = version_check_timer
|
||||
|
||||
# For storing missing settings dialog
|
||||
self._settings_validation_dialog = None
|
||||
|
||||
|
|
@ -200,24 +415,47 @@ class TrayManager:
|
|||
|
||||
version_action = QtWidgets.QAction(version_string, self.tray_widget)
|
||||
version_action.triggered.connect(self._on_version_action)
|
||||
|
||||
restart_action = QtWidgets.QAction(
|
||||
"Restart && Update", self.tray_widget
|
||||
)
|
||||
restart_action.triggered.connect(self._on_restart_action)
|
||||
restart_action.setVisible(False)
|
||||
|
||||
self.tray_widget.menu.addAction(version_action)
|
||||
self.tray_widget.menu.addAction(restart_action)
|
||||
self.tray_widget.menu.addSeparator()
|
||||
|
||||
def restart(self):
|
||||
self._restart_action = restart_action
|
||||
|
||||
def _on_restart_action(self):
|
||||
self.restart()
|
||||
|
||||
def restart(self, reset_version=True):
|
||||
"""Restart Tray tool.
|
||||
|
||||
First creates new process with same argument and close current tray.
|
||||
"""
|
||||
args = get_openpype_execute_args()
|
||||
kwargs = {
|
||||
"env": dict(os.environ.items())
|
||||
}
|
||||
|
||||
# Create a copy of sys.argv
|
||||
additional_args = list(sys.argv)
|
||||
# Check last argument from `get_openpype_execute_args`
|
||||
# - when running from code it is the same as first from sys.argv
|
||||
if args[-1] == additional_args[0]:
|
||||
additional_args.pop(0)
|
||||
args.extend(additional_args)
|
||||
|
||||
kwargs = {}
|
||||
# Pop OPENPYPE_VERSION
|
||||
if reset_version:
|
||||
# Add staging flag if was running from staging
|
||||
if is_running_staging():
|
||||
args.append("--use-staging")
|
||||
kwargs["env"].pop("OPENPYPE_VERSION", None)
|
||||
|
||||
args.extend(additional_args)
|
||||
if platform.system().lower() == "windows":
|
||||
flags = (
|
||||
subprocess.CREATE_NEW_PROCESS_GROUP
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ from .widgets import (
|
|||
)
|
||||
|
||||
from .error_dialog import ErrorMessageBox
|
||||
from .lib import (
|
||||
WrappedCallbackItem,
|
||||
paint_image_with_color
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
|
|
@ -14,5 +18,8 @@ __all__ = (
|
|||
"ClickableFrame",
|
||||
"ExpandBtn",
|
||||
|
||||
"ErrorMessageBox"
|
||||
"ErrorMessageBox",
|
||||
|
||||
"WrappedCallbackItem",
|
||||
"paint_image_with_color",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import avalon.api
|
|||
from avalon import style
|
||||
from avalon.vendor import qtawesome
|
||||
|
||||
from openpype.api import get_project_settings
|
||||
from openpype.api import (
|
||||
get_project_settings,
|
||||
Logger
|
||||
)
|
||||
from openpype.lib import filter_profiles
|
||||
|
||||
|
||||
|
|
@ -598,3 +601,68 @@ def is_remove_site_loader(loader):
|
|||
|
||||
def is_add_site_loader(loader):
|
||||
return hasattr(loader, "add_site_to_representation")
|
||||
|
||||
|
||||
class WrappedCallbackItem:
|
||||
"""Structure to store information about callback and args/kwargs for it.
|
||||
|
||||
Item can be used to execute callback in main thread which may be needed
|
||||
for execution of Qt objects.
|
||||
|
||||
Item store callback (callable variable), arguments and keyword arguments
|
||||
for the callback. Item hold information about it's process.
|
||||
"""
|
||||
not_set = object()
|
||||
_log = None
|
||||
|
||||
def __init__(self, callback, *args, **kwargs):
|
||||
self._done = False
|
||||
self._exception = self.not_set
|
||||
self._result = self.not_set
|
||||
self._callback = callback
|
||||
self._args = args
|
||||
self._kwargs = kwargs
|
||||
|
||||
def __call__(self):
|
||||
self.execute()
|
||||
|
||||
@property
|
||||
def log(self):
|
||||
cls = self.__class__
|
||||
if cls._log is None:
|
||||
cls._log = Logger.get_logger(cls.__name__)
|
||||
return cls._log
|
||||
|
||||
@property
|
||||
def done(self):
|
||||
return self._done
|
||||
|
||||
@property
|
||||
def exception(self):
|
||||
return self._exception
|
||||
|
||||
@property
|
||||
def result(self):
|
||||
return self._result
|
||||
|
||||
def execute(self):
|
||||
"""Execute callback and store it's result.
|
||||
|
||||
Method must be called from main thread. Item is marked as `done`
|
||||
when callback execution finished. Store output of callback of exception
|
||||
information when callback raise one.
|
||||
"""
|
||||
if self.done:
|
||||
self.log.warning("- item is already processed")
|
||||
return
|
||||
|
||||
self.log.debug("Running callback: {}".format(str(self._callback)))
|
||||
try:
|
||||
result = self._callback(*self._args, **self._kwargs)
|
||||
self._result = result
|
||||
|
||||
except Exception as exc:
|
||||
self._exception = exc
|
||||
|
||||
finally:
|
||||
self._done = True
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.8.0-nightly.4"
|
||||
__version__ = "3.8.0-nightly.5"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.8.0-nightly.4" # OpenPype
|
||||
version = "3.8.0-nightly.5" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
license = "MIT License"
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ Burnin version (usually .mp4) is preferred if present.
|
|||
|
||||
Please be sure that this configuration is viable for your use case. In case of uploading large reviews to Slack,
|
||||
all publishes will be slowed down and you might hit a file limit on Slack pretty soon (it is 5GB for Free version of Slack, any file cannot be bigger than 1GB).
|
||||
You might try to add `{review_link}` to message content. This link might help users to find review easier on their machines.
|
||||
You might try to add `{review_filepath}` to message content. This link might help users to find review easier on their machines.
|
||||
(It won't show a playable preview though!)
|
||||
|
||||
#### Message
|
||||
|
|
|
|||
|
|
@ -2250,9 +2250,9 @@ bail@^1.0.0:
|
|||
integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base16@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
|
@ -4136,9 +4136,9 @@ glob-to-regexp@^0.4.1:
|
|||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.1.3:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
|
|
@ -4825,6 +4825,13 @@ is-core-module@^2.2.0:
|
|||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-core-module@^2.8.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
|
||||
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-data-descriptor@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
||||
|
|
@ -6167,7 +6174,7 @@ path-key@^3.0.0, path-key@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
path-parse@^1.0.6:
|
||||
path-parse@^1.0.6, path-parse@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
|
@ -7208,7 +7215,16 @@ resolve-url@^0.2.1:
|
|||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
|
||||
|
||||
resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2:
|
||||
resolve@^1.1.6:
|
||||
version "1.21.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f"
|
||||
integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==
|
||||
dependencies:
|
||||
is-core-module "^2.8.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
resolve@^1.14.2, resolve@^1.3.2:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
|
|
@ -7533,9 +7549,9 @@ shell-quote@1.7.2:
|
|||
integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
|
||||
|
||||
shelljs@^0.8.4:
|
||||
version "0.8.4"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2"
|
||||
integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==
|
||||
version "0.8.5"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
|
||||
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
|
||||
dependencies:
|
||||
glob "^7.0.0"
|
||||
interpret "^1.0.0"
|
||||
|
|
@ -7896,6 +7912,11 @@ supports-color@^7.0.0, supports-color@^7.1.0:
|
|||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
supports-preserve-symlinks-flag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
svg-parser@^2.0.2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue