mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into bugfix/OP-1594_Flame-client_bugs_catches
This commit is contained in:
commit
9dad0599e3
57 changed files with 676 additions and 429 deletions
|
|
@ -19,6 +19,7 @@ class CreateRender(openpype.api.Creator):
|
|||
name = "renderDefault"
|
||||
label = "Render on Farm"
|
||||
family = "render"
|
||||
defaults = ["Main"]
|
||||
|
||||
def process(self):
|
||||
stub = get_stub() # only after After Effects is up
|
||||
|
|
|
|||
|
|
@ -131,6 +131,8 @@ def deselect_all():
|
|||
|
||||
class Creator(PypeCreatorMixin, avalon.api.Creator):
|
||||
"""Base class for Creator plug-ins."""
|
||||
defaults = ['Main']
|
||||
|
||||
def process(self):
|
||||
collection = bpy.data.collections.new(name=self.data["subset"])
|
||||
bpy.context.scene.collection.children.link(collection)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,27 @@
|
|||
from .pipeline import (
|
||||
install,
|
||||
uninstall
|
||||
uninstall,
|
||||
|
||||
ls,
|
||||
|
||||
imprint_container,
|
||||
parse_container,
|
||||
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
from .utils import (
|
||||
setup
|
||||
from .workio import (
|
||||
open_file,
|
||||
save_file,
|
||||
current_file,
|
||||
has_unsaved_changes,
|
||||
file_extensions,
|
||||
work_root
|
||||
)
|
||||
|
||||
|
||||
from .lib import (
|
||||
maintained_selection,
|
||||
get_additional_data,
|
||||
update_frame_range
|
||||
)
|
||||
|
|
@ -20,11 +33,24 @@ __all__ = [
|
|||
# pipeline
|
||||
"install",
|
||||
"uninstall",
|
||||
"ls",
|
||||
|
||||
# utils
|
||||
"setup",
|
||||
"imprint_container",
|
||||
"parse_container",
|
||||
|
||||
"get_current_comp",
|
||||
"comp_lock_and_undo_chunk",
|
||||
|
||||
# workio
|
||||
"open_file",
|
||||
"save_file",
|
||||
"current_file",
|
||||
"has_unsaved_changes",
|
||||
"file_extensions",
|
||||
"work_root",
|
||||
|
||||
# lib
|
||||
"maintained_selection",
|
||||
"get_additional_data",
|
||||
"update_frame_range",
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import contextlib
|
||||
|
||||
from Qt import QtGui
|
||||
import avalon.fusion
|
||||
|
||||
import avalon.api
|
||||
from avalon import io
|
||||
from .pipeline import get_current_comp, comp_lock_and_undo_chunk
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self._project = None
|
||||
|
|
@ -24,7 +29,7 @@ def update_frame_range(start, end, comp=None, set_render_range=True):
|
|||
"""
|
||||
|
||||
if not comp:
|
||||
comp = avalon.fusion.get_current_comp()
|
||||
comp = get_current_comp()
|
||||
|
||||
attrs = {
|
||||
"COMPN_GlobalStart": start,
|
||||
|
|
@ -37,7 +42,7 @@ def update_frame_range(start, end, comp=None, set_render_range=True):
|
|||
"COMPN_RenderEnd": end
|
||||
})
|
||||
|
||||
with avalon.fusion.comp_lock_and_undo_chunk(comp):
|
||||
with comp_lock_and_undo_chunk(comp):
|
||||
comp.SetAttrs(attrs)
|
||||
|
||||
|
||||
|
|
@ -140,3 +145,51 @@ def switch_item(container,
|
|||
avalon.api.switch(container, representation)
|
||||
|
||||
return representation
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def maintained_selection():
|
||||
comp = get_current_comp()
|
||||
previous_selection = comp.GetToolList(True).values()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
flow = comp.CurrentFrame.FlowView
|
||||
flow.Select() # No args equals clearing selection
|
||||
if previous_selection:
|
||||
for tool in previous_selection:
|
||||
flow.Select(tool, True)
|
||||
|
||||
|
||||
def get_frame_path(path):
|
||||
"""Get filename for the Fusion Saver with padded number as '#'
|
||||
|
||||
>>> get_frame_path("C:/test.exr")
|
||||
('C:/test', 4, '.exr')
|
||||
|
||||
>>> get_frame_path("filename.00.tif")
|
||||
('filename.', 2, '.tif')
|
||||
|
||||
>>> get_frame_path("foobar35.tif")
|
||||
('foobar', 2, '.tif')
|
||||
|
||||
Args:
|
||||
path (str): The path to render to.
|
||||
|
||||
Returns:
|
||||
tuple: head, padding, tail (extension)
|
||||
|
||||
"""
|
||||
filename, ext = os.path.splitext(path)
|
||||
|
||||
# Find a final number group
|
||||
match = re.match('.*?([0-9]+)$', filename)
|
||||
if match:
|
||||
padding = len(match.group(1))
|
||||
# remove number from end since fusion
|
||||
# will swap it with the frame number
|
||||
filename = filename[:-padding]
|
||||
else:
|
||||
padding = 4 # default Fusion padding
|
||||
|
||||
return filename, padding, ext
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import sys
|
|||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
from openpype import style
|
||||
from openpype.tools.utils import host_tools
|
||||
|
||||
from openpype.hosts.fusion.scripts import (
|
||||
|
|
@ -58,7 +59,7 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
create_btn = QtWidgets.QPushButton("Create...", self)
|
||||
publish_btn = QtWidgets.QPushButton("Publish...", self)
|
||||
load_btn = QtWidgets.QPushButton("Load...", self)
|
||||
inventory_btn = QtWidgets.QPushButton("Inventory...", self)
|
||||
manager_btn = QtWidgets.QPushButton("Manage...", self)
|
||||
libload_btn = QtWidgets.QPushButton("Library...", self)
|
||||
rendermode_btn = QtWidgets.QPushButton("Set render mode...", self)
|
||||
duplicate_with_inputs_btn = QtWidgets.QPushButton(
|
||||
|
|
@ -75,7 +76,7 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
layout.addWidget(create_btn)
|
||||
layout.addWidget(publish_btn)
|
||||
layout.addWidget(load_btn)
|
||||
layout.addWidget(inventory_btn)
|
||||
layout.addWidget(manager_btn)
|
||||
|
||||
layout.addWidget(Spacer(15, self))
|
||||
|
||||
|
|
@ -96,7 +97,7 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
create_btn.clicked.connect(self.on_create_clicked)
|
||||
publish_btn.clicked.connect(self.on_publish_clicked)
|
||||
load_btn.clicked.connect(self.on_load_clicked)
|
||||
inventory_btn.clicked.connect(self.on_inventory_clicked)
|
||||
manager_btn.clicked.connect(self.on_manager_clicked)
|
||||
libload_btn.clicked.connect(self.on_libload_clicked)
|
||||
rendermode_btn.clicked.connect(self.on_rendernode_clicked)
|
||||
duplicate_with_inputs_btn.clicked.connect(
|
||||
|
|
@ -119,8 +120,8 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
print("Clicked Load")
|
||||
host_tools.show_loader(use_context=True)
|
||||
|
||||
def on_inventory_clicked(self):
|
||||
print("Clicked Inventory")
|
||||
def on_manager_clicked(self):
|
||||
print("Clicked Manager")
|
||||
host_tools.show_scene_inventory()
|
||||
|
||||
def on_libload_clicked(self):
|
||||
|
|
@ -128,7 +129,6 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
host_tools.show_library_loader()
|
||||
|
||||
def on_rendernode_clicked(self):
|
||||
from avalon import style
|
||||
print("Clicked Set Render Mode")
|
||||
if self.render_mode_widget is None:
|
||||
window = set_rendermode.SetRenderMode()
|
||||
|
|
|
|||
|
|
@ -2,9 +2,14 @@
|
|||
Basic avalon integration
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import contextlib
|
||||
|
||||
import pyblish.api
|
||||
import avalon.api
|
||||
from avalon.pipeline import AVALON_CONTAINER_ID
|
||||
|
||||
from avalon import api as avalon
|
||||
from pyblish import api as pyblish
|
||||
from openpype.api import Logger
|
||||
import openpype.hosts.fusion
|
||||
|
||||
|
|
@ -19,6 +24,14 @@ CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
|||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
|
||||
|
||||
|
||||
class CompLogHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
entry = self.format(record)
|
||||
comp = get_current_comp()
|
||||
if comp:
|
||||
comp.Print(entry)
|
||||
|
||||
|
||||
def install():
|
||||
"""Install fusion-specific functionality of avalon-core.
|
||||
|
||||
|
|
@ -30,18 +43,32 @@ def install():
|
|||
See the Maya equivalent for inspiration on how to implement this.
|
||||
|
||||
"""
|
||||
# Remove all handlers associated with the root logger object, because
|
||||
# that one sometimes logs as "warnings" incorrectly.
|
||||
for handler in logging.root.handlers[:]:
|
||||
logging.root.removeHandler(handler)
|
||||
|
||||
# Attach default logging handler that prints to active comp
|
||||
logger = logging.getLogger()
|
||||
formatter = logging.Formatter(fmt="%(message)s\n")
|
||||
handler = CompLogHandler()
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
log.info("openpype.hosts.fusion installed")
|
||||
|
||||
pyblish.register_host("fusion")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
pyblish.api.register_host("fusion")
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
log.info("Registering Fusion plug-ins..")
|
||||
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_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)
|
||||
|
||||
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
pyblish.api.register_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled
|
||||
)
|
||||
|
||||
|
||||
def uninstall():
|
||||
|
|
@ -55,22 +82,23 @@ def uninstall():
|
|||
modifying the menu or registered families.
|
||||
|
||||
"""
|
||||
pyblish.deregister_host("fusion")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
pyblish.api.deregister_host("fusion")
|
||||
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
|
||||
log.info("Deregistering Fusion plug-ins..")
|
||||
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
avalon.api.deregister_plugin_path(
|
||||
avalon.api.InventoryAction, INVENTORY_PATH
|
||||
)
|
||||
|
||||
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
pyblish.api.deregister_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled
|
||||
)
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, new_value, old_value):
|
||||
"""Toggle saver tool passthrough states on instance toggles."""
|
||||
|
||||
from avalon.fusion import comp_lock_and_undo_chunk
|
||||
|
||||
comp = instance.context.data.get("currentComp")
|
||||
if not comp:
|
||||
return
|
||||
|
|
@ -90,3 +118,106 @@ def on_pyblish_instance_toggled(instance, new_value, old_value):
|
|||
current = attrs["TOOLB_PassThrough"]
|
||||
if current != passthrough:
|
||||
tool.SetAttrs({"TOOLB_PassThrough": passthrough})
|
||||
|
||||
|
||||
def ls():
|
||||
"""List containers from active Fusion scene
|
||||
|
||||
This is the host-equivalent of api.ls(), but instead of listing
|
||||
assets on disk, it lists assets already loaded in Fusion; once loaded
|
||||
they are called 'containers'
|
||||
|
||||
Yields:
|
||||
dict: container
|
||||
|
||||
"""
|
||||
|
||||
comp = get_current_comp()
|
||||
tools = comp.GetToolList(False, "Loader").values()
|
||||
|
||||
for tool in tools:
|
||||
container = parse_container(tool)
|
||||
if container:
|
||||
yield container
|
||||
|
||||
|
||||
def imprint_container(tool,
|
||||
name,
|
||||
namespace,
|
||||
context,
|
||||
loader=None):
|
||||
"""Imprint a Loader with metadata
|
||||
|
||||
Containerisation enables a tracking of version, author and origin
|
||||
for loaded assets.
|
||||
|
||||
Arguments:
|
||||
tool (object): The node in Fusion to imprint as container, usually a
|
||||
Loader.
|
||||
name (str): Name of resulting assembly
|
||||
namespace (str): Namespace under which to host container
|
||||
context (dict): Asset information
|
||||
loader (str, optional): Name of loader used to produce this container.
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
|
||||
data = [
|
||||
("schema", "openpype:container-2.0"),
|
||||
("id", AVALON_CONTAINER_ID),
|
||||
("name", str(name)),
|
||||
("namespace", str(namespace)),
|
||||
("loader", str(loader)),
|
||||
("representation", str(context["representation"]["_id"])),
|
||||
]
|
||||
|
||||
for key, value in data:
|
||||
tool.SetData("avalon.{}".format(key), value)
|
||||
|
||||
|
||||
def parse_container(tool):
|
||||
"""Returns imprinted container data of a tool
|
||||
|
||||
This reads the imprinted data from `imprint_container`.
|
||||
|
||||
"""
|
||||
|
||||
data = tool.GetData('avalon')
|
||||
if not isinstance(data, dict):
|
||||
return
|
||||
|
||||
# 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
|
||||
|
||||
container = {key: data[key] for key in required}
|
||||
|
||||
# Store the tool's name
|
||||
container["objectName"] = tool.Name
|
||||
|
||||
# Store reference to the tool object
|
||||
container["_tool"] = tool
|
||||
|
||||
return container
|
||||
|
||||
|
||||
def get_current_comp():
|
||||
"""Hack to get current comp in this session"""
|
||||
fusion = getattr(sys.modules["__main__"], "fusion", None)
|
||||
return fusion.CurrentComp if fusion else None
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def comp_lock_and_undo_chunk(comp, undo_queue_name="Script CMD"):
|
||||
"""Lock comp and open an undo chunk during the context"""
|
||||
try:
|
||||
comp.Lock()
|
||||
comp.StartUndo(undo_queue_name)
|
||||
yield
|
||||
finally:
|
||||
comp.Unlock()
|
||||
comp.EndUndo()
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
#! python3
|
||||
|
||||
"""
|
||||
Fusion tools for setting environment
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from openpype.api import Logger
|
||||
import openpype.hosts.fusion
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
def _sync_utility_scripts(env=None):
|
||||
""" Synchronizing basic utlility scripts for resolve.
|
||||
|
||||
To be able to run scripts from inside `Fusion/Workspace/Scripts` menu
|
||||
all scripts has to be accessible from defined folder.
|
||||
"""
|
||||
if not env:
|
||||
env = os.environ
|
||||
|
||||
# initiate inputs
|
||||
scripts = {}
|
||||
us_env = env.get("FUSION_UTILITY_SCRIPTS_SOURCE_DIR")
|
||||
us_dir = env.get("FUSION_UTILITY_SCRIPTS_DIR", "")
|
||||
us_paths = [os.path.join(
|
||||
os.path.dirname(os.path.abspath(openpype.hosts.fusion.__file__)),
|
||||
"utility_scripts"
|
||||
)]
|
||||
|
||||
# collect script dirs
|
||||
if us_env:
|
||||
log.info(f"Utility Scripts Env: `{us_env}`")
|
||||
us_paths = us_env.split(
|
||||
os.pathsep) + us_paths
|
||||
|
||||
# collect scripts from dirs
|
||||
for path in us_paths:
|
||||
scripts.update({path: os.listdir(path)})
|
||||
|
||||
log.info(f"Utility Scripts Dir: `{us_paths}`")
|
||||
log.info(f"Utility Scripts: `{scripts}`")
|
||||
|
||||
# make sure no script file is in folder
|
||||
if next((s for s in os.listdir(us_dir)), None):
|
||||
for s in os.listdir(us_dir):
|
||||
path = os.path.normpath(
|
||||
os.path.join(us_dir, s))
|
||||
log.info(f"Removing `{path}`...")
|
||||
|
||||
# remove file or directory if not in our folders
|
||||
if not os.path.isdir(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
shutil.rmtree(path)
|
||||
|
||||
# copy scripts into Resolve's utility scripts dir
|
||||
for d, sl in scripts.items():
|
||||
# directory and scripts list
|
||||
for s in sl:
|
||||
# script in script list
|
||||
src = os.path.normpath(os.path.join(d, s))
|
||||
dst = os.path.normpath(os.path.join(us_dir, s))
|
||||
|
||||
log.info(f"Copying `{src}` to `{dst}`...")
|
||||
|
||||
# copy file or directory from our folders to fusion's folder
|
||||
if not os.path.isdir(src):
|
||||
shutil.copy2(src, dst)
|
||||
else:
|
||||
shutil.copytree(src, dst)
|
||||
|
||||
|
||||
def setup(env=None):
|
||||
""" Wrapper installer started from pype.hooks.fusion.FusionPrelaunch()
|
||||
"""
|
||||
if not env:
|
||||
env = os.environ
|
||||
|
||||
# synchronize resolve utility scripts
|
||||
_sync_utility_scripts(env)
|
||||
|
||||
log.info("Fusion Pype wrapper has been installed")
|
||||
45
openpype/hosts/fusion/api/workio.py
Normal file
45
openpype/hosts/fusion/api/workio.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
"""Host API required Work Files tool"""
|
||||
import sys
|
||||
import os
|
||||
from avalon import api
|
||||
from .pipeline import get_current_comp
|
||||
|
||||
|
||||
def file_extensions():
|
||||
return api.HOST_WORKFILE_EXTENSIONS["fusion"]
|
||||
|
||||
|
||||
def has_unsaved_changes():
|
||||
comp = get_current_comp()
|
||||
return comp.GetAttrs()["COMPB_Modified"]
|
||||
|
||||
|
||||
def save_file(filepath):
|
||||
comp = get_current_comp()
|
||||
comp.Save(filepath)
|
||||
|
||||
|
||||
def open_file(filepath):
|
||||
# Hack to get fusion, see
|
||||
# openpype.hosts.fusion.api.pipeline.get_current_comp()
|
||||
fusion = getattr(sys.modules["__main__"], "fusion", None)
|
||||
|
||||
return fusion.LoadComp(filepath)
|
||||
|
||||
|
||||
def current_file():
|
||||
comp = get_current_comp()
|
||||
current_filepath = comp.GetAttrs()["COMPS_FileName"]
|
||||
if not current_filepath:
|
||||
return None
|
||||
|
||||
return current_filepath
|
||||
|
||||
|
||||
def work_root(session):
|
||||
work_dir = session["AVALON_WORKDIR"]
|
||||
scene_dir = session.get("AVALON_SCENEDIR")
|
||||
if scene_dir:
|
||||
return os.path.join(work_dir, scene_dir)
|
||||
else:
|
||||
return work_dir
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import importlib
|
||||
import shutil
|
||||
|
||||
import openpype.hosts.fusion
|
||||
from openpype.lib import PreLaunchHook, ApplicationLaunchFailed
|
||||
from openpype.hosts.fusion.api import utils
|
||||
|
||||
|
||||
class FusionPrelaunch(PreLaunchHook):
|
||||
|
|
@ -13,40 +14,101 @@ class FusionPrelaunch(PreLaunchHook):
|
|||
|
||||
def execute(self):
|
||||
# making sure python 3.6 is installed at provided path
|
||||
py36_dir = os.path.normpath(self.launch_context.env.get("PYTHON36", ""))
|
||||
py36_dir = self.launch_context.env.get("PYTHON36")
|
||||
if not py36_dir:
|
||||
raise ApplicationLaunchFailed(
|
||||
"Required environment variable \"PYTHON36\" is not set."
|
||||
"\n\nFusion implementation requires to have"
|
||||
" installed Python 3.6"
|
||||
)
|
||||
|
||||
py36_dir = os.path.normpath(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}"
|
||||
"Either make sure the environments in fusion settings 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
|
||||
|
||||
utility_dir = self.launch_context.env.get("FUSION_UTILITY_SCRIPTS_DIR")
|
||||
if not utility_dir:
|
||||
raise ApplicationLaunchFailed(
|
||||
"Required Fusion utility script dir environment variable"
|
||||
" \"FUSION_UTILITY_SCRIPTS_DIR\" is not set."
|
||||
)
|
||||
|
||||
# setting utility scripts dir for scripts syncing
|
||||
us_dir = os.path.normpath(
|
||||
self.launch_context.env.get("FUSION_UTILITY_SCRIPTS_DIR", "")
|
||||
)
|
||||
if not os.path.isdir(us_dir):
|
||||
utility_dir = os.path.normpath(utility_dir)
|
||||
if not os.path.isdir(utility_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}'"
|
||||
"the environments in fusion settings has"
|
||||
" 'FUSION_UTILITY_SCRIPTS_DIR' set correctly or reinstall "
|
||||
f"Fusion.\n\nFUSION_UTILITY_SCRIPTS_DIR: '{utility_dir}'"
|
||||
)
|
||||
|
||||
try:
|
||||
__import__("avalon.fusion")
|
||||
__import__("pyblish")
|
||||
self._sync_utility_scripts(self.launch_context.env)
|
||||
self.log.info("Fusion Pype wrapper has been installed")
|
||||
|
||||
except ImportError:
|
||||
self.log.warning(
|
||||
"pyblish: Could not load Fusion integration.",
|
||||
exc_info=True
|
||||
)
|
||||
def _sync_utility_scripts(self, env):
|
||||
""" Synchronizing basic utlility scripts for resolve.
|
||||
|
||||
else:
|
||||
# Resolve Setup integration
|
||||
importlib.reload(utils)
|
||||
utils.setup(self.launch_context.env)
|
||||
To be able to run scripts from inside `Fusion/Workspace/Scripts` menu
|
||||
all scripts has to be accessible from defined folder.
|
||||
"""
|
||||
if not env:
|
||||
env = {k: v for k, v in os.environ.items()}
|
||||
|
||||
# initiate inputs
|
||||
scripts = {}
|
||||
us_env = env.get("FUSION_UTILITY_SCRIPTS_SOURCE_DIR")
|
||||
us_dir = env.get("FUSION_UTILITY_SCRIPTS_DIR", "")
|
||||
us_paths = [os.path.join(
|
||||
os.path.dirname(os.path.abspath(openpype.hosts.fusion.__file__)),
|
||||
"utility_scripts"
|
||||
)]
|
||||
|
||||
# collect script dirs
|
||||
if us_env:
|
||||
self.log.info(f"Utility Scripts Env: `{us_env}`")
|
||||
us_paths = us_env.split(
|
||||
os.pathsep) + us_paths
|
||||
|
||||
# collect scripts from dirs
|
||||
for path in us_paths:
|
||||
scripts.update({path: os.listdir(path)})
|
||||
|
||||
self.log.info(f"Utility Scripts Dir: `{us_paths}`")
|
||||
self.log.info(f"Utility Scripts: `{scripts}`")
|
||||
|
||||
# make sure no script file is in folder
|
||||
if next((s for s in os.listdir(us_dir)), None):
|
||||
for s in os.listdir(us_dir):
|
||||
path = os.path.normpath(
|
||||
os.path.join(us_dir, s))
|
||||
self.log.info(f"Removing `{path}`...")
|
||||
|
||||
# remove file or directory if not in our folders
|
||||
if not os.path.isdir(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
shutil.rmtree(path)
|
||||
|
||||
# copy scripts into Resolve's utility scripts dir
|
||||
for d, sl in scripts.items():
|
||||
# directory and scripts list
|
||||
for s in sl:
|
||||
# script in script list
|
||||
src = os.path.normpath(os.path.join(d, s))
|
||||
dst = os.path.normpath(os.path.join(us_dir, s))
|
||||
|
||||
self.log.info(f"Copying `{src}` to `{dst}`...")
|
||||
|
||||
# copy file or directory from our folders to fusion's folder
|
||||
if not os.path.isdir(src):
|
||||
shutil.copy2(src, dst)
|
||||
else:
|
||||
shutil.copytree(src, dst)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import os
|
||||
|
||||
import openpype.api
|
||||
from avalon import fusion
|
||||
from openpype.hosts.fusion.api import (
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
|
||||
class CreateOpenEXRSaver(openpype.api.Creator):
|
||||
|
|
@ -10,12 +13,13 @@ class CreateOpenEXRSaver(openpype.api.Creator):
|
|||
label = "Create OpenEXR Saver"
|
||||
hosts = ["fusion"]
|
||||
family = "render"
|
||||
defaults = ["Main"]
|
||||
|
||||
def process(self):
|
||||
|
||||
file_format = "OpenEXRFormat"
|
||||
|
||||
comp = fusion.get_current_comp()
|
||||
comp = get_current_comp()
|
||||
|
||||
# todo: improve method of getting current environment
|
||||
# todo: pref avalon.Session over os.environ
|
||||
|
|
@ -25,7 +29,7 @@ class CreateOpenEXRSaver(openpype.api.Creator):
|
|||
filename = "{}..tiff".format(self.name)
|
||||
filepath = os.path.join(workdir, "render", filename)
|
||||
|
||||
with fusion.comp_lock_and_undo_chunk(comp):
|
||||
with comp_lock_and_undo_chunk(comp):
|
||||
args = (-32768, -32768) # Magical position numbers
|
||||
saver = comp.AddTool("Saver", *args)
|
||||
saver.SetAttrs({"TOOLS_Name": self.name})
|
||||
|
|
|
|||
|
|
@ -8,15 +8,17 @@ class FusionSelectContainers(api.InventoryAction):
|
|||
color = "#d8d8d8"
|
||||
|
||||
def process(self, containers):
|
||||
|
||||
import avalon.fusion
|
||||
from openpype.hosts.fusion.api import (
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
tools = [i["_tool"] for i in containers]
|
||||
|
||||
comp = avalon.fusion.get_current_comp()
|
||||
comp = get_current_comp()
|
||||
flow = comp.CurrentFrame.FlowView
|
||||
|
||||
with avalon.fusion.comp_lock_and_undo_chunk(comp, self.label):
|
||||
with comp_lock_and_undo_chunk(comp, self.label):
|
||||
# Clear selection
|
||||
flow.Select()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
from avalon import api, style
|
||||
from avalon import api
|
||||
from Qt import QtGui, QtWidgets
|
||||
|
||||
import avalon.fusion
|
||||
from openpype import style
|
||||
from openpype.hosts.fusion.api import (
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
|
||||
class FusionSetToolColor(api.InventoryAction):
|
||||
|
|
@ -16,7 +20,7 @@ class FusionSetToolColor(api.InventoryAction):
|
|||
"""Color all selected tools the selected colors"""
|
||||
|
||||
result = []
|
||||
comp = avalon.fusion.get_current_comp()
|
||||
comp = get_current_comp()
|
||||
|
||||
# Get tool color
|
||||
first = containers[0]
|
||||
|
|
@ -33,7 +37,7 @@ class FusionSetToolColor(api.InventoryAction):
|
|||
if not picked_color:
|
||||
return
|
||||
|
||||
with avalon.fusion.comp_lock_and_undo_chunk(comp):
|
||||
with comp_lock_and_undo_chunk(comp):
|
||||
for container in containers:
|
||||
# Convert color to RGB 0-1 floats
|
||||
rgb_f = picked_color.getRgbF()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ class FusionSetFrameRangeLoader(api.Loader):
|
|||
"camera",
|
||||
"imagesequence",
|
||||
"yeticache",
|
||||
"pointcache"]
|
||||
"pointcache",
|
||||
"render"]
|
||||
representations = ["*"]
|
||||
|
||||
label = "Set frame range"
|
||||
|
|
@ -45,7 +46,8 @@ class FusionSetFrameRangeWithHandlesLoader(api.Loader):
|
|||
"camera",
|
||||
"imagesequence",
|
||||
"yeticache",
|
||||
"pointcache"]
|
||||
"pointcache",
|
||||
"render"]
|
||||
representations = ["*"]
|
||||
|
||||
label = "Set frame range (with handles)"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import os
|
||||
import contextlib
|
||||
|
||||
from avalon import api
|
||||
import avalon.io as io
|
||||
from avalon import api, io
|
||||
|
||||
from avalon import fusion
|
||||
from openpype.hosts.fusion.api import (
|
||||
imprint_container,
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
comp = fusion.get_current_comp()
|
||||
comp = get_current_comp()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
|
@ -117,7 +120,7 @@ def loader_shift(loader, frame, relative=True):
|
|||
class FusionLoadSequence(api.Loader):
|
||||
"""Load image sequence into Fusion"""
|
||||
|
||||
families = ["imagesequence", "review"]
|
||||
families = ["imagesequence", "review", "render"]
|
||||
representations = ["*"]
|
||||
|
||||
label = "Load sequence"
|
||||
|
|
@ -126,13 +129,6 @@ class FusionLoadSequence(api.Loader):
|
|||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from avalon.fusion import (
|
||||
imprint_container,
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
# Fallback to asset name when namespace is None
|
||||
if namespace is None:
|
||||
namespace = context['asset']['name']
|
||||
|
|
@ -204,13 +200,11 @@ class FusionLoadSequence(api.Loader):
|
|||
|
||||
"""
|
||||
|
||||
from avalon.fusion import comp_lock_and_undo_chunk
|
||||
|
||||
tool = container["_tool"]
|
||||
assert tool.ID == "Loader", "Must be Loader"
|
||||
comp = tool.Comp()
|
||||
|
||||
root = api.get_representation_path(representation)
|
||||
root = os.path.dirname(api.get_representation_path(representation))
|
||||
path = self._get_first_image(root)
|
||||
|
||||
# Get start frame from version data
|
||||
|
|
@ -247,9 +241,6 @@ class FusionLoadSequence(api.Loader):
|
|||
tool.SetData("avalon.representation", str(representation["_id"]))
|
||||
|
||||
def remove(self, container):
|
||||
|
||||
from avalon.fusion import comp_lock_and_undo_chunk
|
||||
|
||||
tool = container["_tool"]
|
||||
assert tool.ID == "Loader", "Must be Loader"
|
||||
comp = tool.Comp()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from avalon import fusion
|
||||
from openpype.hosts.fusion.api import get_current_comp
|
||||
|
||||
|
||||
class CollectCurrentCompFusion(pyblish.api.ContextPlugin):
|
||||
|
|
@ -15,7 +15,7 @@ class CollectCurrentCompFusion(pyblish.api.ContextPlugin):
|
|||
def process(self, context):
|
||||
"""Collect all image sequence tools"""
|
||||
|
||||
current_comp = fusion.get_current_comp()
|
||||
current_comp = get_current_comp()
|
||||
assert current_comp, "Must have active Fusion composition"
|
||||
context.data["currentComp"] = current_comp
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
def process(self, context):
|
||||
"""Collect all image sequence tools"""
|
||||
|
||||
from avalon.fusion.lib import get_frame_path
|
||||
from openpype.hosts.fusion.api.lib import get_frame_path
|
||||
|
||||
comp = context.data["currentComp"]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
|
||||
import avalon.fusion as fusion
|
||||
from pprint import pformat
|
||||
|
||||
import pyblish.api
|
||||
from openpype.hosts.fusion.api import comp_lock_and_undo_chunk
|
||||
|
||||
|
||||
class Fusionlocal(pyblish.api.InstancePlugin):
|
||||
"""Render the current Fusion composition locally.
|
||||
|
|
@ -39,7 +39,7 @@ class Fusionlocal(pyblish.api.InstancePlugin):
|
|||
self.log.info("Start frame: {}".format(frame_start))
|
||||
self.log.info("End frame: {}".format(frame_end))
|
||||
|
||||
with fusion.comp_lock_and_undo_chunk(current_comp):
|
||||
with comp_lock_and_undo_chunk(current_comp):
|
||||
result = current_comp.Render()
|
||||
|
||||
if "representations" not in instance.data:
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import os
|
|||
import json
|
||||
import getpass
|
||||
|
||||
import requests
|
||||
|
||||
from avalon import api
|
||||
from avalon.vendor import requests
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ class FusionSubmitDeadline(pyblish.api.InstancePlugin):
|
|||
else:
|
||||
context.data[key] = True
|
||||
|
||||
from avalon.fusion.lib import get_frame_path
|
||||
from openpype.hosts.fusion.api.lib import get_frame_path
|
||||
|
||||
deadline_url = (
|
||||
context.data["system_settings"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
from avalon import fusion
|
||||
from openpype.hosts.fusion.api import (
|
||||
comp_lock_and_undo_chunk,
|
||||
get_current_comp
|
||||
)
|
||||
|
||||
|
||||
def is_connected(input):
|
||||
|
|
@ -9,12 +12,12 @@ def is_connected(input):
|
|||
def duplicate_with_input_connections():
|
||||
"""Duplicate selected tools with incoming connections."""
|
||||
|
||||
comp = fusion.get_current_comp()
|
||||
comp = get_current_comp()
|
||||
original_tools = comp.GetToolList(True).values()
|
||||
if not original_tools:
|
||||
return # nothing selected
|
||||
|
||||
with fusion.comp_lock_and_undo_chunk(
|
||||
with comp_lock_and_undo_chunk(
|
||||
comp, "Duplicate With Input Connections"):
|
||||
|
||||
# Generate duplicates
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import sys
|
|||
import logging
|
||||
|
||||
# Pipeline imports
|
||||
from avalon import api, io, pipeline
|
||||
import avalon.fusion
|
||||
import avalon.api
|
||||
from avalon import io, pipeline
|
||||
|
||||
# Config imports
|
||||
import openpype.lib as pype
|
||||
import openpype.hosts.fusion.api.lib as fusion_lib
|
||||
from openpype.lib import version_up
|
||||
from openpype.hosts.fusion import api
|
||||
from openpype.hosts.fusion.api import lib
|
||||
|
||||
log = logging.getLogger("Update Slap Comp")
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ def _format_filepath(session):
|
|||
|
||||
# Create new unique filepath
|
||||
if os.path.exists(new_filepath):
|
||||
new_filepath = pype.version_up(new_filepath)
|
||||
new_filepath = version_up(new_filepath)
|
||||
|
||||
return new_filepath
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ def _update_savers(comp, session):
|
|||
|
||||
comp.Print("New renders to: %s\n" % renders)
|
||||
|
||||
with avalon.fusion.comp_lock_and_undo_chunk(comp):
|
||||
with api.comp_lock_and_undo_chunk(comp):
|
||||
savers = comp.GetToolList(False, "Saver").values()
|
||||
for saver in savers:
|
||||
filepath = saver.GetAttrs("TOOLST_Clip_Name")[1.0]
|
||||
|
|
@ -185,7 +185,7 @@ def update_frame_range(comp, representations):
|
|||
start = min(v["data"]["frameStart"] for v in versions)
|
||||
end = max(v["data"]["frameEnd"] for v in versions)
|
||||
|
||||
fusion_lib.update_frame_range(start, end, comp=comp)
|
||||
lib.update_frame_range(start, end, comp=comp)
|
||||
|
||||
|
||||
def switch(asset_name, filepath=None, new=True):
|
||||
|
|
@ -215,11 +215,11 @@ def switch(asset_name, filepath=None, new=True):
|
|||
|
||||
# Get current project
|
||||
self._project = io.find_one({"type": "project",
|
||||
"name": api.Session["AVALON_PROJECT"]})
|
||||
"name": avalon.api.Session["AVALON_PROJECT"]})
|
||||
|
||||
# Go to comp
|
||||
if not filepath:
|
||||
current_comp = avalon.fusion.get_current_comp()
|
||||
current_comp = api.get_current_comp()
|
||||
assert current_comp is not None, "Could not find current comp"
|
||||
else:
|
||||
fusion = _get_fusion_instance()
|
||||
|
|
@ -227,14 +227,14 @@ def switch(asset_name, filepath=None, new=True):
|
|||
assert current_comp is not None, (
|
||||
"Fusion could not load '{}'").format(filepath)
|
||||
|
||||
host = api.registered_host()
|
||||
host = avalon.api.registered_host()
|
||||
containers = list(host.ls())
|
||||
assert containers, "Nothing to update"
|
||||
|
||||
representations = []
|
||||
for container in containers:
|
||||
try:
|
||||
representation = fusion_lib.switch_item(
|
||||
representation = lib.switch_item(
|
||||
container,
|
||||
asset_name=asset_name)
|
||||
representations.append(representation)
|
||||
|
|
@ -246,7 +246,7 @@ def switch(asset_name, filepath=None, new=True):
|
|||
current_comp.Print(message)
|
||||
|
||||
# Build the session to switch to
|
||||
switch_to_session = api.Session.copy()
|
||||
switch_to_session = avalon.api.Session.copy()
|
||||
switch_to_session["AVALON_ASSET"] = asset['name']
|
||||
|
||||
if new:
|
||||
|
|
@ -255,7 +255,7 @@ def switch(asset_name, filepath=None, new=True):
|
|||
# Update savers output based on new session
|
||||
_update_savers(current_comp, switch_to_session)
|
||||
else:
|
||||
comp_path = pype.version_up(filepath)
|
||||
comp_path = version_up(filepath)
|
||||
|
||||
current_comp.Print(comp_path)
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ if __name__ == '__main__':
|
|||
|
||||
args, unknown = parser.parse_args()
|
||||
|
||||
api.install(avalon.fusion)
|
||||
avalon.api.install(api)
|
||||
switch(args.asset_name, args.file_path)
|
||||
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from Qt import QtWidgets
|
||||
from avalon.vendor import qtawesome
|
||||
import avalon.fusion as avalon
|
||||
from openpype.hosts.fusion.api import get_current_comp
|
||||
|
||||
|
||||
_help = {"local": "Render the comp on your own machine and publish "
|
||||
|
|
@ -14,7 +14,7 @@ class SetRenderMode(QtWidgets.QWidget):
|
|||
def __init__(self, parent=None):
|
||||
QtWidgets.QWidget.__init__(self, parent)
|
||||
|
||||
self._comp = avalon.get_current_comp()
|
||||
self._comp = get_current_comp()
|
||||
self._comp_name = self._get_comp_name()
|
||||
|
||||
self.setWindowTitle("Set Render Mode")
|
||||
|
|
@ -79,7 +79,7 @@ class SetRenderMode(QtWidgets.QWidget):
|
|||
def update(self):
|
||||
"""Update all information in the UI"""
|
||||
|
||||
self._comp = avalon.get_current_comp()
|
||||
self._comp = get_current_comp()
|
||||
self._comp_name = self._get_comp_name()
|
||||
self.comp_information.setText(self._comp_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from avalon.fusion import comp_lock_and_undo_chunk
|
||||
|
||||
from avalon import fusion
|
||||
comp = fusion.get_current_comp()
|
||||
from openpype.hosts.fusion.api import (
|
||||
comp_lock_and_undo_chunk,
|
||||
get_current_comp
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
comp = get_current_comp()
|
||||
"""Set all selected backgrounds to 32 bit"""
|
||||
with comp_lock_and_undo_chunk(comp, 'Selected Backgrounds to 32bit'):
|
||||
tools = comp.GetToolList(True, "Background").values()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from avalon.fusion import comp_lock_and_undo_chunk
|
||||
from avalon import fusion
|
||||
comp = fusion.get_current_comp()
|
||||
from openpype.hosts.fusion.api import (
|
||||
comp_lock_and_undo_chunk,
|
||||
get_current_comp
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
comp = get_current_comp()
|
||||
"""Set all backgrounds to 32 bit"""
|
||||
with comp_lock_and_undo_chunk(comp, 'Backgrounds to 32bit'):
|
||||
tools = comp.GetToolList(False, "Background").values()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from avalon.fusion import comp_lock_and_undo_chunk
|
||||
from avalon import fusion
|
||||
comp = fusion.get_current_comp()
|
||||
from openpype.hosts.fusion.api import (
|
||||
comp_lock_and_undo_chunk,
|
||||
get_current_comp
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
comp = get_current_comp()
|
||||
"""Set all selected loaders to 32 bit"""
|
||||
with comp_lock_and_undo_chunk(comp, 'Selected Loaders to 32bit'):
|
||||
tools = comp.GetToolList(True, "Loader").values()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from avalon.fusion import comp_lock_and_undo_chunk
|
||||
from avalon import fusion
|
||||
comp = fusion.get_current_comp()
|
||||
from openpype.hosts.fusion.api import (
|
||||
comp_lock_and_undo_chunk,
|
||||
get_current_comp
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
comp = get_current_comp()
|
||||
"""Set all loaders to 32 bit"""
|
||||
with comp_lock_and_undo_chunk(comp, 'Loaders to 32bit'):
|
||||
tools = comp.GetToolList(False, "Loader").values()
|
||||
|
|
|
|||
|
|
@ -8,13 +8,15 @@ log = Logger().get_logger(__name__)
|
|||
|
||||
|
||||
def main(env):
|
||||
import avalon.api
|
||||
from openpype.hosts.fusion import api
|
||||
from openpype.hosts.fusion.api import menu
|
||||
import avalon.fusion
|
||||
|
||||
# Registers pype's Global pyblish plugins
|
||||
openpype.install()
|
||||
|
||||
# activate resolve from pype
|
||||
avalon.api.install(avalon.fusion)
|
||||
avalon.api.install(api)
|
||||
|
||||
log.info(f"Avalon registered hosts: {avalon.api.registered_host()}")
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@ import logging
|
|||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
import avalon.io as io
|
||||
import avalon.api as api
|
||||
import avalon.pipeline as pipeline
|
||||
import avalon.fusion
|
||||
import avalon.style as style
|
||||
import avalon.api
|
||||
from avalon import io, pipeline
|
||||
from avalon.vendor import qtawesome as qta
|
||||
|
||||
from openpype import style
|
||||
from openpype.hosts.fusion import api
|
||||
|
||||
log = logging.getLogger("Fusion Switch Shot")
|
||||
|
||||
|
|
@ -150,7 +149,7 @@ class App(QtWidgets.QWidget):
|
|||
if not self._use_current.isChecked():
|
||||
file_name = self._comps.itemData(self._comps.currentIndex())
|
||||
else:
|
||||
comp = avalon.fusion.get_current_comp()
|
||||
comp = api.get_current_comp()
|
||||
file_name = comp.GetAttrs("COMPS_FileName")
|
||||
|
||||
asset = self._assets.currentText()
|
||||
|
|
@ -161,11 +160,11 @@ class App(QtWidgets.QWidget):
|
|||
def _get_context_directory(self):
|
||||
|
||||
project = io.find_one({"type": "project",
|
||||
"name": api.Session["AVALON_PROJECT"]},
|
||||
"name": avalon.api.Session["AVALON_PROJECT"]},
|
||||
projection={"config": True})
|
||||
|
||||
template = project["config"]["template"]["work"]
|
||||
dir = pipeline._format_work_template(template, api.Session)
|
||||
dir = pipeline._format_work_template(template, avalon.api.Session)
|
||||
|
||||
return dir
|
||||
|
||||
|
|
@ -174,7 +173,7 @@ class App(QtWidgets.QWidget):
|
|||
return items
|
||||
|
||||
def collect_assets(self):
|
||||
return list(io.find({"type": "asset", "silo": "film"}))
|
||||
return list(io.find({"type": "asset"}, {"name": True}))
|
||||
|
||||
def populate_comp_box(self, files):
|
||||
"""Ensure we display the filename only but the path is stored as well
|
||||
|
|
@ -193,7 +192,7 @@ class App(QtWidgets.QWidget):
|
|||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
api.install(avalon.fusion)
|
||||
avalon.api.install(api)
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = App()
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@ Warning:
|
|||
settings of the Loader. So use this at your own risk.
|
||||
|
||||
"""
|
||||
from avalon import fusion
|
||||
from openpype.hosts.fusion.api.pipeline import (
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
|
||||
def update_loader_ranges():
|
||||
comp = fusion.get_current_comp()
|
||||
with fusion.comp_lock_and_undo_chunk(comp, "Reload clip time ranges"):
|
||||
comp = get_current_comp()
|
||||
with comp_lock_and_undo_chunk(comp, "Reload clip time ranges"):
|
||||
tools = comp.GetToolList(True, "Loader").values()
|
||||
for tool in tools:
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class Creator(PypeCreatorMixin, avalon.api.Creator):
|
|||
created node.
|
||||
"""
|
||||
|
||||
defaults = ["Main"]
|
||||
node_type = "COMPOSITE"
|
||||
|
||||
def setup_node(self, node):
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class Creator(PypeCreatorMixin, avalon.api.Creator):
|
|||
the node.
|
||||
|
||||
"""
|
||||
defaults = ['Main']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Creator, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ def get_reference_node_parents(ref):
|
|||
|
||||
|
||||
class Creator(PypeCreatorMixin, api.Creator):
|
||||
defaults = ['Main']
|
||||
|
||||
def process(self):
|
||||
nodes = list()
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ class CreateAnimation(plugin.Creator):
|
|||
label = "Animation"
|
||||
family = "animation"
|
||||
icon = "male"
|
||||
defaults = ['Main']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateAnimation, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class CreateAss(plugin.Creator):
|
|||
label = "Ass StandIn"
|
||||
family = "ass"
|
||||
icon = "cube"
|
||||
defaults = ['Main']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateAss, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -8,4 +8,3 @@ class CreateAssembly(plugin.Creator):
|
|||
label = "Assembly"
|
||||
family = "assembly"
|
||||
icon = "cubes"
|
||||
defaults = ['Main']
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ class CreateCamera(plugin.Creator):
|
|||
label = "Camera"
|
||||
family = "camera"
|
||||
icon = "video-camera"
|
||||
defaults = ['Main']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateCamera, self).__init__(*args, **kwargs)
|
||||
|
|
@ -33,4 +32,3 @@ class CreateCameraRig(plugin.Creator):
|
|||
label = "Camera Rig"
|
||||
family = "camerarig"
|
||||
icon = "video-camera"
|
||||
defaults = ['Main']
|
||||
|
|
|
|||
|
|
@ -8,4 +8,3 @@ class CreateLayout(plugin.Creator):
|
|||
label = "Layout"
|
||||
family = "layout"
|
||||
icon = "cubes"
|
||||
defaults = ["Main"]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ class CreateLook(plugin.Creator):
|
|||
label = "Look"
|
||||
family = "look"
|
||||
icon = "paint-brush"
|
||||
defaults = ['Main']
|
||||
make_tx = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -8,4 +8,3 @@ class CreateMayaScene(plugin.Creator):
|
|||
label = "Maya Scene"
|
||||
family = "mayaScene"
|
||||
icon = "file-archive-o"
|
||||
defaults = ['Main']
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ class CreatePointCache(plugin.Creator):
|
|||
label = "Point Cache"
|
||||
family = "pointcache"
|
||||
icon = "gears"
|
||||
defaults = ['Main']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreatePointCache, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ class CreateRender(plugin.Creator):
|
|||
label = "Render"
|
||||
family = "rendering"
|
||||
icon = "eye"
|
||||
defaults = ["Main"]
|
||||
|
||||
_token = None
|
||||
_user = None
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ class CreateReview(plugin.Creator):
|
|||
label = "Review"
|
||||
family = "review"
|
||||
icon = "video-camera"
|
||||
defaults = ['Main']
|
||||
keepImages = False
|
||||
isolate = False
|
||||
imagePlane = True
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class CreateRig(plugin.Creator):
|
|||
label = "Rig"
|
||||
family = "rig"
|
||||
icon = "wheelchair"
|
||||
defaults = ['Main']
|
||||
|
||||
def process(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -8,4 +8,3 @@ class CreateXgen(plugin.Creator):
|
|||
label = "Xgen Interactive"
|
||||
family = "xgen"
|
||||
icon = "pagelines"
|
||||
defaults = ['Main']
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class CreateYetiCache(plugin.Creator):
|
|||
label = "Yeti Cache"
|
||||
family = "yeticache"
|
||||
icon = "pagelines"
|
||||
defaults = ["Main"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateYetiCache, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ class CreateYetiRig(plugin.Creator):
|
|||
label = "Yeti Rig"
|
||||
family = "yetiRig"
|
||||
icon = "usb"
|
||||
defaults = ["Main"]
|
||||
|
||||
def process(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import openpype.api
|
|||
|
||||
class Creator(openpype.api.Creator):
|
||||
"""This serves as skeleton for future OpenPype specific functionality"""
|
||||
pass
|
||||
defaults = ['Main']
|
||||
|
||||
|
||||
class Loader(api.Loader):
|
||||
|
|
|
|||
|
|
@ -694,13 +694,13 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
audio_args_dentifiers = ["-af", "-filter:a"]
|
||||
for arg in tuple(output_args):
|
||||
for identifier in video_args_dentifiers:
|
||||
if identifier in arg:
|
||||
if arg.startswith("{} ".format(identifier)):
|
||||
output_args.remove(arg)
|
||||
arg = arg.replace(identifier, "").strip()
|
||||
video_filters.append(arg)
|
||||
|
||||
for identifier in audio_args_dentifiers:
|
||||
if identifier in arg:
|
||||
if arg.startswith("{} ".format(identifier)):
|
||||
output_args.remove(arg)
|
||||
arg = arg.replace(identifier, "").strip()
|
||||
audio_filters.append(arg)
|
||||
|
|
|
|||
|
|
@ -713,15 +713,29 @@
|
|||
"OPENPYPE_LOG_NO_COLORS": "Yes"
|
||||
},
|
||||
"variants": {
|
||||
"16": {
|
||||
"enabled": true,
|
||||
"variant_label": "16",
|
||||
"use_python_2": false,
|
||||
"17": {
|
||||
"executables": {
|
||||
"windows": [
|
||||
"C:\\Program Files\\Blackmagic Design\\Fusion 17\\Fusion.exe"
|
||||
],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"arguments": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"environment": {}
|
||||
},
|
||||
"16": {
|
||||
"executables": {
|
||||
"windows": [
|
||||
"C:\\Program Files\\Blackmagic Design\\Fusion 16\\Fusion.exe"
|
||||
],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"arguments": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
|
|
@ -730,9 +744,6 @@
|
|||
"environment": {}
|
||||
},
|
||||
"9": {
|
||||
"enabled": true,
|
||||
"variant_label": "9",
|
||||
"use_python_2": false,
|
||||
"executables": {
|
||||
"windows": [
|
||||
"C:\\Program Files\\Blackmagic Design\\Fusion 9\\Fusion.exe"
|
||||
|
|
@ -961,8 +972,12 @@
|
|||
"enabled": true,
|
||||
"variant_label": "21",
|
||||
"executables": {
|
||||
"windows": ["c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 21 Premium\\win64\\bin\\HarmonyPremium.exe"],
|
||||
"darwin": ["/Applications/Toon Boom Harmony 21 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium"],
|
||||
"windows": [
|
||||
"c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 21 Premium\\win64\\bin\\HarmonyPremium.exe"
|
||||
],
|
||||
"darwin": [
|
||||
"/Applications/Toon Boom Harmony 21 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium"
|
||||
],
|
||||
"linux": []
|
||||
},
|
||||
"arguments": {
|
||||
|
|
@ -976,8 +991,12 @@
|
|||
"enabled": true,
|
||||
"variant_label": "20",
|
||||
"executables": {
|
||||
"windows": ["c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 20 Premium\\win64\\bin\\HarmonyPremium.exe"],
|
||||
"darwin": ["/Applications/Toon Boom Harmony 20 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium"],
|
||||
"windows": [
|
||||
"c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 20 Premium\\win64\\bin\\HarmonyPremium.exe"
|
||||
],
|
||||
"darwin": [
|
||||
"/Applications/Toon Boom Harmony 20 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium"
|
||||
],
|
||||
"linux": []
|
||||
},
|
||||
"arguments": {
|
||||
|
|
@ -991,7 +1010,9 @@
|
|||
"enabled": true,
|
||||
"variant_label": "17",
|
||||
"executables": {
|
||||
"windows": ["c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 17 Premium\\win64\\bin\\HarmonyPremium.exe"],
|
||||
"windows": [
|
||||
"c:\\Program Files (x86)\\Toon Boom Animation\\Toon Boom Harmony 17 Premium\\win64\\bin\\HarmonyPremium.exe"
|
||||
],
|
||||
"darwin": [
|
||||
"/Applications/Toon Boom Harmony 17 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -20,24 +20,21 @@
|
|||
"type": "raw-json"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"type": "dict-modifiable",
|
||||
"key": "variants",
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_host_variant",
|
||||
"template_data": [
|
||||
{
|
||||
"app_variant_label": "16",
|
||||
"app_variant": "16"
|
||||
},
|
||||
{
|
||||
"app_variant_label": "9",
|
||||
"app_variant": "9"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"collapsible_key": true,
|
||||
"use_label_wrap": false,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_host_variant_items",
|
||||
"skip_paths": ["use_python_2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -396,9 +396,7 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
self._versionschanged()
|
||||
return
|
||||
|
||||
selected_subsets = self._subsets_widget.selected_subsets(
|
||||
_merged=True, _other=False
|
||||
)
|
||||
selected_subsets = self._subsets_widget.get_selected_merge_items()
|
||||
|
||||
asset_colors = {}
|
||||
asset_ids = []
|
||||
|
|
@ -423,35 +421,14 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
self._versionschanged()
|
||||
|
||||
def _versionschanged(self):
|
||||
selection = self._subsets_widget.view.selectionModel()
|
||||
|
||||
# Active must be in the selected rows otherwise we
|
||||
# assume it's not actually an "active" current index.
|
||||
version_docs = None
|
||||
items = self._subsets_widget.get_selected_subsets()
|
||||
version_doc = None
|
||||
active = selection.currentIndex()
|
||||
rows = selection.selectedRows(column=active.column())
|
||||
if active and active in rows:
|
||||
item = active.data(self._subsets_widget.model.ItemRole)
|
||||
if (
|
||||
item is not None
|
||||
and not (item.get("isGroup") or item.get("isMerged"))
|
||||
):
|
||||
version_doc = item["version_document"]
|
||||
|
||||
if rows:
|
||||
version_docs = []
|
||||
for index in rows:
|
||||
if not index or not index.isValid():
|
||||
continue
|
||||
item = index.data(self._subsets_widget.model.ItemRole)
|
||||
if (
|
||||
item is None
|
||||
or item.get("isGroup")
|
||||
or item.get("isMerged")
|
||||
):
|
||||
continue
|
||||
version_docs.append(item["version_document"])
|
||||
version_docs = []
|
||||
for item in items:
|
||||
doc = item["version_document"]
|
||||
version_docs.append(doc)
|
||||
if version_doc is None:
|
||||
version_doc = doc
|
||||
|
||||
self._version_info_widget.set_version(version_doc)
|
||||
|
||||
|
|
|
|||
|
|
@ -287,9 +287,7 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
on selection change so they match current selection.
|
||||
"""
|
||||
# TODO do not touch inner attributes of asset widget
|
||||
last_asset_ids = self.data["state"]["assetIds"]
|
||||
if last_asset_ids:
|
||||
self._assets_widget.clear_underlines()
|
||||
self._assets_widget.clear_underlines()
|
||||
|
||||
def _assetschanged(self):
|
||||
"""Selected assets have changed"""
|
||||
|
|
@ -328,12 +326,11 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
asset_ids = self.data["state"]["assetIds"]
|
||||
# Skip setting colors if not asset multiselection
|
||||
if not asset_ids or len(asset_ids) < 2:
|
||||
self.clear_assets_underlines()
|
||||
self._versionschanged()
|
||||
return
|
||||
|
||||
selected_subsets = self._subsets_widget.selected_subsets(
|
||||
_merged=True, _other=False
|
||||
)
|
||||
selected_subsets = self._subsets_widget.get_selected_merge_items()
|
||||
|
||||
asset_colors = {}
|
||||
asset_ids = []
|
||||
|
|
@ -358,37 +355,16 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
self._versionschanged()
|
||||
|
||||
def _versionschanged(self):
|
||||
subsets = self._subsets_widget
|
||||
selection = subsets.view.selectionModel()
|
||||
|
||||
# Active must be in the selected rows otherwise we
|
||||
# assume it's not actually an "active" current index.
|
||||
items = self._subsets_widget.get_selected_subsets()
|
||||
version_doc = None
|
||||
active = selection.currentIndex()
|
||||
rows = selection.selectedRows(column=active.column())
|
||||
if active:
|
||||
if active in rows:
|
||||
item = active.data(subsets.model.ItemRole)
|
||||
if (
|
||||
item is not None and
|
||||
not (item.get("isGroup") or item.get("isMerged"))
|
||||
):
|
||||
version_doc = item["version_document"]
|
||||
self._version_info_widget.set_version(version_doc)
|
||||
|
||||
version_docs = []
|
||||
if rows:
|
||||
for index in rows:
|
||||
if not index or not index.isValid():
|
||||
continue
|
||||
item = index.data(subsets.model.ItemRole)
|
||||
if item is None:
|
||||
continue
|
||||
if item.get("isGroup") or item.get("isMerged"):
|
||||
for child in item.children():
|
||||
version_docs.append(child["version_document"])
|
||||
else:
|
||||
version_docs.append(item["version_document"])
|
||||
for item in items:
|
||||
doc = item["version_document"]
|
||||
version_docs.append(doc)
|
||||
if version_doc is None:
|
||||
version_doc = doc
|
||||
|
||||
self._version_info_widget.set_version(version_doc)
|
||||
|
||||
thumbnail_src_ids = [
|
||||
version_doc["_id"]
|
||||
|
|
@ -480,18 +456,7 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
self.echo("Grouping not enabled.")
|
||||
return
|
||||
|
||||
selected = []
|
||||
merged_items = []
|
||||
for item in subsets.selected_subsets(_merged=True):
|
||||
if item.get("isMerged"):
|
||||
merged_items.append(item)
|
||||
else:
|
||||
selected.append(item)
|
||||
|
||||
for merged_item in merged_items:
|
||||
for child_item in merged_item.children():
|
||||
selected.append(child_item)
|
||||
|
||||
selected = self._subsets_widget.get_selected_subsets()
|
||||
if not selected:
|
||||
self.echo("No selected subset.")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -18,26 +18,6 @@ def change_visibility(model, view, column_name, visible):
|
|||
view.setColumnHidden(index, not visible)
|
||||
|
||||
|
||||
def get_selected_items(rows, item_role):
|
||||
items = []
|
||||
for row_index in rows:
|
||||
item = row_index.data(item_role)
|
||||
if item.get("isGroup"):
|
||||
continue
|
||||
|
||||
elif item.get("isMerged"):
|
||||
for idx in range(row_index.model().rowCount(row_index)):
|
||||
child_index = row_index.child(idx, 0)
|
||||
item = child_index.data(item_role)
|
||||
if item not in items:
|
||||
items.append(item)
|
||||
|
||||
else:
|
||||
if item not in items:
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
|
||||
def get_options(action, loader, parent, repre_contexts):
|
||||
"""Provides dialog to select value from loader provided options.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import copy
|
||||
import re
|
||||
import math
|
||||
from uuid import uuid4
|
||||
|
||||
from avalon import (
|
||||
style,
|
||||
|
|
@ -22,6 +23,8 @@ from openpype.tools.utils.constants import (
|
|||
REMOTE_AVAILABILITY_ROLE
|
||||
)
|
||||
|
||||
ITEM_ID_ROLE = QtCore.Qt.UserRole + 90
|
||||
|
||||
|
||||
def is_filtering_recursible():
|
||||
"""Does Qt binding support recursive filtering for QSortFilterProxyModel?
|
||||
|
|
@ -179,6 +182,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
self._icons = {
|
||||
"subset": qtawesome.icon("fa.file-o", color=style.colors.default)
|
||||
}
|
||||
self._items_by_id = {}
|
||||
|
||||
self._doc_fetching_thread = None
|
||||
self._doc_fetching_stop = False
|
||||
|
|
@ -188,6 +192,15 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
|
||||
self.refresh()
|
||||
|
||||
def get_item_by_id(self, item_id):
|
||||
return self._items_by_id.get(item_id)
|
||||
|
||||
def add_child(self, new_item, *args, **kwargs):
|
||||
item_id = str(uuid4())
|
||||
new_item["id"] = item_id
|
||||
self._items_by_id[item_id] = new_item
|
||||
super(SubsetsModel, self).add_child(new_item, *args, **kwargs)
|
||||
|
||||
def set_assets(self, asset_ids):
|
||||
self._asset_ids = asset_ids
|
||||
self.refresh()
|
||||
|
|
@ -486,7 +499,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
def refresh(self):
|
||||
self.stop_fetch_thread()
|
||||
self.clear()
|
||||
|
||||
self._items_by_id = {}
|
||||
self.reset_sync_server()
|
||||
|
||||
if not self._asset_ids:
|
||||
|
|
@ -497,6 +510,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
|
||||
def on_doc_fetched(self):
|
||||
self.clear()
|
||||
self._items_by_id = {}
|
||||
self.beginResetModel()
|
||||
|
||||
asset_docs_by_id = self._doc_payload.get(
|
||||
|
|
@ -524,9 +538,13 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
return
|
||||
|
||||
self._fill_subset_items(
|
||||
asset_docs_by_id, subset_docs_by_id, last_versions_by_subset_id,
|
||||
asset_docs_by_id,
|
||||
subset_docs_by_id,
|
||||
last_versions_by_subset_id,
|
||||
repre_info_by_version_id
|
||||
)
|
||||
self.endResetModel()
|
||||
self.refreshed.emit(True)
|
||||
|
||||
def create_multiasset_group(
|
||||
self, subset_name, asset_ids, subset_counter, parent_item=None
|
||||
|
|
@ -538,7 +556,6 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
merge_group.update({
|
||||
"subset": "{} ({})".format(subset_name, len(asset_ids)),
|
||||
"isMerged": True,
|
||||
"childRow": 0,
|
||||
"subsetColor": subset_color,
|
||||
"assetIds": list(asset_ids),
|
||||
"icon": qtawesome.icon(
|
||||
|
|
@ -547,7 +564,6 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
)
|
||||
})
|
||||
|
||||
subset_counter += 1
|
||||
self.add_child(merge_group, parent_item)
|
||||
|
||||
return merge_group
|
||||
|
|
@ -567,8 +583,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
group_item = Item()
|
||||
group_item.update({
|
||||
"subset": group_name,
|
||||
"isGroup": True,
|
||||
"childRow": 0
|
||||
"isGroup": True
|
||||
})
|
||||
group_item.update(group_data)
|
||||
|
||||
|
|
@ -666,14 +681,14 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
|
|||
index = self.index(item.row(), 0, parent_index)
|
||||
self.set_version(index, last_version)
|
||||
|
||||
self.endResetModel()
|
||||
self.refreshed.emit(True)
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid():
|
||||
return
|
||||
|
||||
item = index.internalPointer()
|
||||
if role == ITEM_ID_ROLE:
|
||||
return item["id"]
|
||||
|
||||
if role == self.SortDescendingRole:
|
||||
if item.get("isGroup"):
|
||||
# Ensure groups be on top when sorting by descending order
|
||||
|
|
@ -1139,7 +1154,6 @@ class RepresentationModel(TreeModel, BaseRepresentationModel):
|
|||
"_id": doc["_id"],
|
||||
"name": doc["name"],
|
||||
"isMerged": True,
|
||||
"childRow": 0,
|
||||
"active_site_name": self.active_site,
|
||||
"remote_site_name": self.remote_site,
|
||||
"icon": qtawesome.icon(
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ from .model import (
|
|||
SubsetFilterProxyModel,
|
||||
FamiliesFilterProxyModel,
|
||||
RepresentationModel,
|
||||
RepresentationSortProxyModel
|
||||
RepresentationSortProxyModel,
|
||||
ITEM_ID_ROLE
|
||||
)
|
||||
from . import lib
|
||||
|
||||
|
|
@ -351,6 +352,59 @@ class SubsetWidget(QtWidgets.QWidget):
|
|||
|
||||
lib.change_visibility(self.model, self.view, "repre_info", enabled)
|
||||
|
||||
def get_selected_items(self):
|
||||
selection_model = self.view.selectionModel()
|
||||
indexes = selection_model.selectedIndexes()
|
||||
|
||||
item_ids = set()
|
||||
for index in indexes:
|
||||
item_id = index.data(ITEM_ID_ROLE)
|
||||
if item_id is not None:
|
||||
item_ids.add(item_id)
|
||||
|
||||
output = []
|
||||
for item_id in item_ids:
|
||||
item = self.model.get_item_by_id(item_id)
|
||||
if item is not None:
|
||||
output.append(item)
|
||||
return output
|
||||
|
||||
def get_selected_merge_items(self):
|
||||
output = []
|
||||
items = collections.deque(self.get_selected_items())
|
||||
|
||||
item_ids = set()
|
||||
while items:
|
||||
item = items.popleft()
|
||||
if item.get("isGroup"):
|
||||
for child in item.children():
|
||||
items.appendleft(child)
|
||||
|
||||
elif item.get("isMerged"):
|
||||
item_id = item["id"]
|
||||
if item_id not in item_ids:
|
||||
item_ids.add(item_id)
|
||||
output.append(item)
|
||||
|
||||
return output
|
||||
|
||||
def get_selected_subsets(self):
|
||||
output = []
|
||||
items = collections.deque(self.get_selected_items())
|
||||
|
||||
item_ids = set()
|
||||
while items:
|
||||
item = items.popleft()
|
||||
if item.get("isGroup") or item.get("isMerged"):
|
||||
for child in item.children():
|
||||
items.appendleft(child)
|
||||
else:
|
||||
item_id = item["id"]
|
||||
if item_id not in item_ids:
|
||||
item_ids.add(item_id)
|
||||
output.append(item)
|
||||
return output
|
||||
|
||||
def on_context_menu(self, point):
|
||||
"""Shows menu with loader actions on Right-click.
|
||||
|
||||
|
|
@ -367,10 +421,7 @@ class SubsetWidget(QtWidgets.QWidget):
|
|||
return
|
||||
|
||||
# Get selected subsets without groups
|
||||
selection = self.view.selectionModel()
|
||||
rows = selection.selectedRows(column=0)
|
||||
|
||||
items = lib.get_selected_items(rows, self.model.ItemRole)
|
||||
items = self.get_selected_subsets()
|
||||
|
||||
# Get all representation->loader combinations available for the
|
||||
# index under the cursor, so we can list the user the options.
|
||||
|
|
@ -539,35 +590,6 @@ class SubsetWidget(QtWidgets.QWidget):
|
|||
box = LoadErrorMessageBox(error_info, self)
|
||||
box.show()
|
||||
|
||||
def selected_subsets(self, _groups=False, _merged=False, _other=True):
|
||||
selection = self.view.selectionModel()
|
||||
rows = selection.selectedRows(column=0)
|
||||
|
||||
subsets = list()
|
||||
if not any([_groups, _merged, _other]):
|
||||
self.echo((
|
||||
"This is a BUG: Selected_subsets args must contain"
|
||||
" at least one value set to True"
|
||||
))
|
||||
return subsets
|
||||
|
||||
for row in rows:
|
||||
item = row.data(self.model.ItemRole)
|
||||
if item.get("isGroup"):
|
||||
if not _groups:
|
||||
continue
|
||||
|
||||
elif item.get("isMerged"):
|
||||
if not _merged:
|
||||
continue
|
||||
else:
|
||||
if not _other:
|
||||
continue
|
||||
|
||||
subsets.append(item)
|
||||
|
||||
return subsets
|
||||
|
||||
def group_subsets(self, name, asset_ids, items):
|
||||
field = "data.subsetGroup"
|
||||
|
||||
|
|
@ -1279,7 +1301,7 @@ class RepresentationWidget(QtWidgets.QWidget):
|
|||
selection = self.tree_view.selectionModel()
|
||||
rows = selection.selectedRows(column=0)
|
||||
|
||||
items = lib.get_selected_items(rows, self.model.ItemRole)
|
||||
items = self.get_selected_subsets()
|
||||
|
||||
selected_side = self._get_selected_side(point_index, rows)
|
||||
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ class AssetModel(QtGui.QStandardItemModel):
|
|||
|
||||
self._doc_fetched.connect(self._on_docs_fetched)
|
||||
|
||||
self._items_with_color_by_id = {}
|
||||
self._item_ids_with_color = set()
|
||||
self._items_by_asset_id = {}
|
||||
|
||||
self._last_project_name = None
|
||||
|
|
@ -382,9 +382,11 @@ class AssetModel(QtGui.QStandardItemModel):
|
|||
self._stop_fetch_thread()
|
||||
|
||||
def clear_underlines(self):
|
||||
for asset_id in tuple(self._items_with_color_by_id.keys()):
|
||||
item = self._items_with_color_by_id.pop(asset_id)
|
||||
item.setData(None, ASSET_UNDERLINE_COLORS_ROLE)
|
||||
for asset_id in set(self._item_ids_with_color):
|
||||
self._item_ids_with_color.remove(asset_id)
|
||||
item = self._items_by_asset_id.get(asset_id)
|
||||
if item is not None:
|
||||
item.setData(None, ASSET_UNDERLINE_COLORS_ROLE)
|
||||
|
||||
def set_underline_colors(self, colors_by_asset_id):
|
||||
self.clear_underlines()
|
||||
|
|
@ -394,12 +396,13 @@ class AssetModel(QtGui.QStandardItemModel):
|
|||
if item is None:
|
||||
continue
|
||||
item.setData(colors, ASSET_UNDERLINE_COLORS_ROLE)
|
||||
self._item_ids_with_color.add(asset_id)
|
||||
|
||||
def _clear_items(self):
|
||||
root_item = self.invisibleRootItem()
|
||||
root_item.removeRows(0, root_item.rowCount())
|
||||
self._items_by_asset_id = {}
|
||||
self._items_with_color_by_id = {}
|
||||
self._item_ids_with_color = set()
|
||||
|
||||
def _on_docs_fetched(self):
|
||||
# Make sure refreshing did not change
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ Structure:
|
|||
|
||||
How to run:
|
||||
----------
|
||||
- single test class could be run by PyCharm and its pytest runner directly
|
||||
- OR
|
||||
- use Openpype command 'runtests' from command line (`.venv` in ${OPENPYPE_ROOT} must be activated to use configured Python!)
|
||||
-- `${OPENPYPE_ROOT}/python start.py runtests`
|
||||
|
||||
|
|
|
|||
29
tests/unit/openpype/plugins/publish/test_extract_review.py
Normal file
29
tests/unit/openpype/plugins/publish/test_extract_review.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
from openpype.plugins.publish.extract_review import ExtractReview
|
||||
|
||||
|
||||
def test_fix_ffmpeg_full_args_filters():
|
||||
"""Tests because of wrong resolution of audio filters."""
|
||||
plugin = ExtractReview()
|
||||
output_arg = "c:/test-afbdc"
|
||||
ret = plugin.ffmpeg_full_args([], [], [], [output_arg])
|
||||
assert len(ret) == 2, "Parsed wrong"
|
||||
assert ret[-1] == output_arg
|
||||
|
||||
ret = plugin.ffmpeg_full_args([], [], ["adeclick"], [output_arg])
|
||||
assert len(ret) == 4, "Parsed wrong"
|
||||
assert ret[-1] == output_arg
|
||||
assert ret[-2] == '"adeclick"'
|
||||
assert ret[-3] == "-filter:a"
|
||||
|
||||
ret = plugin.ffmpeg_full_args([], [], [], [output_arg, "-af adeclick"])
|
||||
assert len(ret) == 4, "Parsed wrong"
|
||||
assert ret[-1] == output_arg
|
||||
assert ret[-2] == '"adeclick"'
|
||||
assert ret[-3] == "-filter:a"
|
||||
|
||||
ret = plugin.ffmpeg_full_args([], [], ["adeclick"],
|
||||
[output_arg, "-af adeclick"])
|
||||
assert len(ret) == 4, "Parsed wrong"
|
||||
assert ret[-1] == output_arg
|
||||
assert ret[-2] == '"adeclick,adeclick"' # TODO fix this duplication
|
||||
assert ret[-3] == "-filter:a"
|
||||
Loading…
Add table
Add a link
Reference in a new issue