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 savers = [ tool for tool in instance if getattr(tool, "ID", None) == "Saver" ] if not savers: return # Whether instances should be passthrough based on new value passthrough = not new_value with comp_lock_and_undo_chunk(comp, undo_queue_name="Change instance " "active state"): for tool in savers: attrs = tool.GetAttrs() current = attrs["TOOLB_PassThrough"] if current != passthrough: tool.SetAttrs({"TOOLB_PassThrough": passthrough})
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'] # Use the first file for now path = self._get_first_image(self.fname) # Create the Loader with the filename path set comp = get_current_comp() with comp_lock_and_undo_chunk(comp, "Create Loader"): args = (-32768, -32768) tool = comp.AddTool("Loader", *args) tool["Clip"] = path # Set global in point to start frame (if in version.data) start = context["version"]["data"].get("startFrame", None) if start is not None: loader_shift(tool, start, relative=False) imprint_container(tool, name=name, namespace=namespace, context=context, loader=self.__class__.__name__)
def duplicate_with_input_connections(): """Duplicate selected tools with incoming connections.""" original_tools = comp.GetToolList(True).values() if not original_tools: return # nothing selected with comp_lock_and_undo_chunk(comp, "Duplicate With Input Connections"): # Generate duplicates comp.Copy() comp.SetActiveTool() comp.Paste() duplicate_tools = comp.GetToolList(True).values() # Copy connections for original, new in zip(original_tools, duplicate_tools): original_inputs = original.GetInputList().values() new_inputs = new.GetInputList().values() assert len(original_inputs) == len(new_inputs) for original_input, new_input in zip(original_inputs, new_inputs): if is_connected(original_input): if is_connected(new_input): # Already connected if it is between the copied tools continue new_input.ConnectTo(original_input.GetConnectedOutput()) assert is_connected(new_input), "Must be connected now"
def process(self): file_format = "TiffFormat" comp = fusion.get_current_comp() # todo: improve method of getting current environment # todo: pref avalon.Session over os.environ workdir = os.path.normpath(os.environ["AVALON_WORKDIR"]) filename = "{}..tiff".format(self.name) filepath = os.path.join(workdir, "render", "preview", filename) with fusion.comp_lock_and_undo_chunk(comp): args = (-32768, -32768) # Magical position numbers saver = comp.AddTool("Saver", *args) saver.SetAttrs({"TOOLS_Name": self.name}) # Setting input attributes is different from basic attributes # Not confused with "MainInputAttributes" which saver["Clip"] = filepath saver["OutputFormat"] = file_format # # # Set standard TIFF settings if saver[file_format] is None: raise RuntimeError("File format is not set to TiffFormat, " "this is a bug") # Set file format attributes saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other saver[file_format]["SaveAlpha"] = 0
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() with comp_lock_and_undo_chunk(comp, "Remove Loader"): tool.Delete()
def process(self, instance): context = instance.context key = "__hasRun{}".format(self.__class__.__name__) if context.data.get(key, False): return else: context.data[key] = True current_comp = context.data["currentComp"] frame_start = current_comp.GetAttrs("COMPN_RenderStart") frame_end = current_comp.GetAttrs("COMPN_RenderEnd") path = instance.data["path"] output_dir = instance.data["outputDir"] ext = os.path.splitext(os.path.basename(path))[-1] self.log.info("Starting render") 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): result = current_comp.Render() if "representations" not in instance.data: instance.data["representations"] = [] collected_frames = os.listdir(output_dir) repre = { 'name': ext[1:], 'ext': ext[1:], 'frameStart': "%0{}d".format(len(str(frame_end))) % frame_start, 'files': collected_frames, "stagingDir": output_dir, } instance.data["representations"].append(repre) # review representation repre_preview = repre.copy() repre_preview["name"] = repre_preview["ext"] = "mp4" repre_preview["tags"] = ["review", "preview", "ftrackreview", "delete"] instance.data["representations"].append(repre_preview) self.log.debug(f"_ instance.data: {pformat(instance.data)}") if not result: raise RuntimeError("Comp render failed")
def update_loader_ranges(): comp = fusion.get_current_comp() with fusion.comp_lock_and_undo_chunk(comp, "Reload clip time ranges"): tools = comp.GetToolList(True, "Loader").values() for tool in tools: # Get tool attributes tool_a = tool.GetAttrs() clipTable = tool_a['TOOLST_Clip_Name'] altclipTable = tool_a['TOOLST_AltClip_Name'] startTime = tool_a['TOOLNT_Clip_Start'] old_global_in = tool.GlobalIn[comp.CurrentTime] # Reapply for index, _ in clipTable.items(): time = startTime[index] tool.Clip[time] = tool.Clip[time] for index, _ in altclipTable.items(): time = startTime[index] tool.ProxyFilename[time] = tool.ProxyFilename[time] tool.GlobalIn[comp.CurrentTime] = old_global_in
def process(self, instance): # This should be a ContextPlugin, but this is a workaround # for a bug in pyblish to run once for a family: issue #250 context = instance.context key = "__hasRun{}".format(self.__class__.__name__) if context.data.get(key, False): return else: context.data[key] = True current_comp = context.data["currentComp"] start_frame = current_comp.GetAttrs("COMPN_RenderStart") end_frame = current_comp.GetAttrs("COMPN_RenderEnd") self.log.info("Starting render") self.log.info("Start frame: {}".format(start_frame)) self.log.info("End frame: {}".format(end_frame)) with fusion.comp_lock_and_undo_chunk(current_comp): result = current_comp.Render() if not result: raise RuntimeError("Comp render failed")
def update(self, container, representation): """Update the Loader's path Fusion automatically tries to reset some variables when changing the loader's path to a new file. These automatic changes are to its inputs: - ClipTimeStart: Fusion reset to 0 if duration changes - We keep the trim in as close as possible to the previous value. When there are less frames then the amount of trim we reduce it accordingly. - ClipTimeEnd: Fusion reset to 0 if duration changes - We keep the trim out as close as possible to the previous value within new amount of frames after trim in (ClipTimeStart) has been set. - GlobalIn: Fusion reset to comp's global in if duration changes - We change it to the "startFrame" - GlobalEnd: Fusion resets to globalIn + length if duration changes - We do the same like Fusion - allow fusion to take control. - HoldFirstFrame: Fusion resets this to 0 - We preverse the value. - HoldLastFrame: Fusion resets this to 0 - We preverse the value. - Reverse: Fusion resets to disabled if "Loop" is not enabled. - We preserve the value. - Depth: Fusion resets to "Format" - We preverse the value. - KeyCode: Fusion resets to "" - We preverse the value. - TimeCodeOffset: Fusion resets to 0 - We preverse the value. """ 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) path = self._get_first_image(root) # Get start frame from version data version = io.find_one({ "type": "version", "_id": representation["parent"] }) start = version["data"].get("startFrame") if start is None: self.log.warning("Missing start frame for updated version" "assuming starts at frame 0 for: " "{} ({})".format(tool.Name, representation)) start = 0 with comp_lock_and_undo_chunk(comp, "Update Loader"): # Update the loader's path whilst preserving some values with preserve_trim(tool, log=self.log): with preserve_inputs(tool, inputs=("HoldFirstFrame", "HoldLastFrame", "Reverse", "Depth", "KeyCode", "TimeCodeOffset")): tool["Clip"] = path # Set the global in to the start frame of the sequence global_in_changed = loader_shift(tool, start, relative=False) if global_in_changed: # Log this change to the user self.log.debug("Changed '%s' global in: %d" % (tool.Name, start)) # Update the imprinted representation tool.SetData("avalon.representation", str(representation["_id"]))
def main(): """Set all loaders to 32 bit""" with comp_lock_and_undo_chunk(comp, 'Loaders to 32bit'): tools = comp.GetToolList(False, "Loader").values() for tool in tools: tool.Depth = 5
def main(): """Set all backgrounds to 32 bit""" with comp_lock_and_undo_chunk(comp, 'Backgrounds to 32bit'): tools = comp.GetToolList(False, "Background").values() for tool in tools: tool.Depth = 5
"""Forces Fusion to 'retrigger' the Loader to update. Warning: This might change settings like 'Reverse', 'Loop', trims and other settings of the Loader. So use this at your own risk. """ from avalon.fusion import comp_lock_and_undo_chunk with comp_lock_and_undo_chunk(comp, "Reload clip time ranges"): tools = comp.GetToolList(True, "Loader").values() for tool in tools: # Get tool attributes tool_a = tool.GetAttrs() clipTable = tool_a['TOOLST_Clip_Name'] altclipTable = tool_a['TOOLST_AltClip_Name'] startTime = tool_a['TOOLNT_Clip_Start'] old_global_in = tool.GlobalIn[comp.CurrentTime] # Reapply for index, _ in clipTable.items(): time = startTime[index] tool.Clip[time] = tool.Clip[time] for index, _ in altclipTable.items(): time = startTime[index] tool.ProxyFilename[time] = tool.ProxyFilename[time] tool.GlobalIn[comp.CurrentTime] = old_global_in