moved fusion implementation from avalon to openpype

This commit is contained in:
Jakub Trllo 2022-02-14 12:23:36 +01:00
parent c6c73de82c
commit c70d0c25ec
23 changed files with 382 additions and 109 deletions

View file

@ -1,9 +1,27 @@
from .pipeline import (
install,
uninstall
uninstall,
ls,
imprint_container,
parse_container,
get_current_comp,
comp_lock_and_undo_chunk
)
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
)
@ -15,9 +33,24 @@ __all__ = [
# pipeline
"install",
"uninstall",
"ls",
"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",

View file

@ -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

View file

@ -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 (
@ -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()

View file

@ -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,25 +43,39 @@ 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)
# Disable all families except for the ones we explicitly want to see
family_states = ["imagesequence",
"camera",
"pointcache"]
avalon.data["familiesStateDefault"] = False
avalon.data["familiesStateToggled"] = family_states
avalon.api.data["familiesStateDefault"] = False
avalon.api.data["familiesStateToggled"] = family_states
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():
@ -62,22 +89,19 @@ 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
@ -97,3 +121,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()

View 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

View file

@ -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):
@ -15,7 +18,7 @@ class CreateOpenEXRSaver(openpype.api.Creator):
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 +28,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})

View file

@ -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()

View file

@ -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()

View file

@ -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
@ -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,8 +200,6 @@ 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()
@ -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()

View file

@ -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

View file

@ -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"]

View file

@ -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:

View file

@ -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"]

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()}")

View file

@ -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()

View file

@ -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: