def load(self, context, name=None, namespace=None, data=None): # Import template. temp_dir = tempfile.mkdtemp() zip_file = api.get_representation_path(context["representation"]) template_path = os.path.join(temp_dir, "temp.tpl") with zipfile.ZipFile(zip_file, "r") as zip_ref: zip_ref.extractall(template_path) sig = harmony.signature("paste") func = """function %s(args) { var template_path = args[0]; var drag_object = copyPaste.pasteTemplateIntoGroup( template_path, "Top", 1 ); } %s """ % (sig, sig) harmony.send({"function": func, "args": [template_path]}) shutil.rmtree(temp_dir) subset_name = context["subset"]["name"] return harmony.containerise(subset_name, namespace, subset_name, context, self.__class__.__name__) def update(self, container, representation): pass def remove(self, container): pass
def set_scene_settings(settings): func = """function func(args) { if (args[0]["fps"]) { scene.setFrameRate(args[0]["fps"]); } if (args[0]["frameStart"] && args[0]["frameEnd"]) { var duration = args[0]["frameEnd"] - args[0]["frameStart"] + 1 if (frame.numberOf() < duration) { frame.insert( duration, duration - frame.numberOf() ); } scene.setStartFrame(1); scene.setStopFrame(duration); } if (args[0]["resolutionWidth"] && args[0]["resolutionHeight"]) { scene.setDefaultResolution( args[0]["resolutionWidth"], args[0]["resolutionHeight"], 41.112 ) } } func """ harmony.send({"function": func, "args": [settings]})
def process(self, context): nodes = harmony.send({ "function": "node.subNodes", "args": ["Top"] })["result"] for node in nodes: data = harmony.read(node) # Skip non-tagged nodes. if not data: continue # Skip containers. if "container" in data["id"]: continue instance = context.create_instance(node.split("/")[-1]) instance.append(node) instance.data.update(data) instance.data["publish"] = harmony.send({ "function": "node.getEnable", "args": [node] })["result"] instance.data["families"] = self.families_mapping[data["family"]] # Produce diagnostic message for any graphical # user interface interested in visualising it. self.log.info("Found: \"{0}\": \n{1}".format( instance.data["name"], json.dumps(instance.data, indent=4)))
def _set_red(self, node): """Set node color to red `rgba(255, 0, 0, 255)`.""" harmony.send( { "function": "PypeHarmony.setColor", "args": [node, [255, 0, 0, 255]] })
def _set_green(self, node): """Set node color to green `rgba(0, 255, 0, 255)`.""" harmony.send( { "function": "PypeHarmony.setColor", "args": [node, [0, 255, 0, 255]] })
def setup_node(self, node): """Set render node.""" self_name = self.__class__.__name__ path = "render/{0}/{0}.".format(node.split("/")[-1]) harmony.send({ "function": f"PypeHarmony.Creators.{self_name}.create", "args": [node, path] })
def remove(self, container): """Remove container. Args: container (dict): container definition. """ node = harmony.find_node_by_name(container["name"], "GROUP") harmony.send({"function": "PypeHarmony.deleteNode", "args": [node]})
def remove(self, container): node = container.pop("node") func = """function deleteNode(_node) { node.deleteNode(_node, true, true); } deleteNode """ harmony.send({"function": func, "args": [node]})
def on_pyblish_instance_toggled(instance, old_value, new_value): """Toggle node enabling on instance toggles.""" func = """function func(args) { node.setEnable(args[0], args[1]) } func """ harmony.send({"function": func, "args": [instance[0], new_value]})
def remove(self, container): """Remove loaded container. Args: container (dict): Container data. """ node = container.get("nodes").pop() harmony.send({"function": "PypeHarmony.deleteNode", "args": [node]}) harmony.imprint(node, {}, remove=True)
def remove(self, container): node = harmony.find_node_by_name(container["name"], "READ") func = """function deleteNode(_node) { node.deleteNode(_node, true, true); } deleteNode """ harmony.send({"function": func, "args": [node]})
def setup_node(self, node): """Set render node.""" path = "render/{0}/{0}.".format(node.split("/")[-1]) harmony.send({ "function": f"PypeHarmony.Creators.CreateRender.create", "args": [node, path] }) harmony.send({ "function": f"PypeHarmony.color", "args": [[0.9, 0.75, 0.3, 1.0]] })
def load(self, context, name=None, namespace=None, data=None): wav_file = api.get_representation_path(context["representation"]) harmony.send({ "function": func, "args": [context["subset"]["name"], wav_file] }) subset_name = context["subset"]["name"] return harmony.containerise(subset_name, namespace, subset_name, context, self.__class__.__name__)
def setup_node(self, node): func = """function func(args) { node.setTextAttr(args[0], "DRAWING_TYPE", 1, "PNG4"); node.setTextAttr(args[0], "DRAWING_NAME", 1, args[1]); node.setTextAttr(args[0], "MOVIE_PATH", 1, args[1]); } func """ path = "{0}/{0}".format(node.split("/")[-1]) harmony.send({"function": func, "args": [node, path]})
def remove(self, container): for node in container.get("nodes"): func = """function deleteNode(_node) { node.deleteNode(_node, true, true); } deleteNode """ harmony.send({"function": func, "args": [node]}) harmony.imprint(container['name'], {}, remove=True)
def process(self, context): """Plugin entry point.""" result = harmony.send({ f"function": "PypeHarmony.getSceneSettings", "args": [] })["result"] context.data["applicationPath"] = result[0] context.data["scenePath"] = os.path.join(result[1], result[2] + ".xstage") context.data["frameRate"] = result[3] context.data["frameStartHandle"] = result[4] context.data["frameEndHandle"] = result[5] context.data["audioPath"] = result[6] context.data["resolutionWidth"] = result[7] context.data["resolutionHeight"] = result[8] context.data["FOV"] = result[9] # harmony always starts from 1. frame # 1001 - 10010 >> 1 - 10 # frameStart, frameEnd already collected by global plugin offset = context.data["frameStart"] - 1 frame_start = context.data["frameStart"] - offset frames_count = context.data["frameEnd"] - \ context.data["frameStart"] + 1 # increase by handleStart - real frame range # frameStart != frameStartHandle with handle presence context.data["frameStart"] = int(frame_start) + \ context.data["handleStart"] context.data["frameEnd"] = int(frames_count) + \ context.data["frameStart"] - 1 all_nodes = harmony.send({ "function": "node.subNodes", "args": ["Top"] })["result"] context.data["allNodes"] = all_nodes # collect all write nodes to be able disable them in Deadline all_write_nodes = harmony.send({ "function": "node.getNodes", "args": ["WRITE"] })["result"] context.data["all_write_nodes"] = all_write_nodes result = harmony.send({ f"function": "PypeHarmony.getVersion", "args": [] })["result"] context.data["harmonyVersion"] = "{}.{}".format(result[0], result[1])
def check_inventory(): if not lib.any_outdated(): return host = avalon.api.registered_host() outdated_containers = [] for container in host.ls(): representation = container['representation'] representation_doc = io.find_one( { "_id": io.ObjectId(representation), "type": "representation" }, projection={"parent": True} ) if representation_doc and not lib.is_latest(representation_doc): outdated_containers.append(container) # Colour nodes. func = """function func(args){ for( var i =0; i <= args[0].length - 1; ++i) { var red_color = new ColorRGBA(255, 0, 0, 255); node.setColor(args[0][i], red_color); } } func """ outdated_nodes = [] for container in outdated_containers: if container["loader"] == "ImageSequenceLoader": outdated_nodes.append( harmony.find_node_by_name(container["name"], "READ") ) harmony.send({"function": func, "args": [outdated_nodes]}) # Warn about outdated containers. print("Starting new QApplication..") app = Qt.QtWidgets.QApplication(sys.argv) message_box = Qt.QtWidgets.QMessageBox() message_box.setIcon(Qt.QtWidgets.QMessageBox.Warning) msg = "There are outdated containers in the scene." message_box.setText(msg) message_box.exec_() # Garbage collect QApplication. del app
def process(self, context): func = """function func() { var palette_list = PaletteObjectManager.getScenePaletteList(); var palettes = {}; for(var i=0; i < palette_list.numPalettes; ++i) { var palette = palette_list.getPaletteByIndex(i); palettes[palette.getName()] = palette.id; } return palettes; } func """ palettes = harmony.send({"function": func})["result"] for name, id in palettes.items(): instance = context.create_instance(name) instance.data.update({ "id": id, "family": "harmony.palette", "asset": os.environ["AVALON_ASSET"], "subset": "palette" + name }) self.log.info( "Created instance:\n" + json.dumps( instance.data, sort_keys=True, indent=4 ) )
def process(self, context): """Inject the current working file.""" self_name = self.__class__.__name__ current_file = harmony.send( {"function": f"PypeHarmony.Publish.{self_name}.collect"})["result"] context.data["currentFile"] = os.path.normpath(current_file)
def process(self, context): """Collector entry point.""" self_name = self.__class__.__name__ palettes = harmony.send({ "function": f"PypeHarmony.Publish.{self_name}.getPalettes", })["result"] # skip collecting if not in allowed task if self.allowed_tasks: task_name = context.data["anatomyData"]["task"].lower() if (not any([ re.search(pattern, task_name) for pattern in self.allowed_tasks ])): return for name, id in palettes.items(): instance = context.create_instance(name) instance.data.update({ "id": id, "family": "harmony.palette", 'families': [], "asset": os.environ["AVALON_ASSET"], "subset": "{}{}".format("palette", name) }) self.log.info("Created instance:\n" + json.dumps(instance.data, sort_keys=True, indent=4))
def load(self, context, name=None, namespace=None, data=None): """Plugin entry point. Args: context (:class:`pyblish.api.Context`): Context. name (str, optional): Container name. namespace (str, optional): Container namespace. data (dict, optional): Additional data passed into loader. """ # Load template. self_name = self.__class__.__name__ temp_dir = tempfile.mkdtemp() zip_file = api.get_representation_path(context["representation"]) template_path = os.path.join(temp_dir, "temp.tpl") with zipfile.ZipFile(zip_file, "r") as zip_ref: zip_ref.extractall(template_path) group_id = "{}".format(uuid.uuid4()) container_group = harmony.send({ "function": f"PypeHarmony.Loaders.{self_name}.loadContainer", "args": [ template_path, context["asset"]["name"], context["subset"]["name"], group_id ] })["result"] # Cleanup the temp directory shutil.rmtree(temp_dir) # We must validate the group_node return harmony.containerise(name, namespace, container_group, context, self_name)
def load(self, context, name=None, namespace=None, data=None): collections, remainder = clique.assemble( os.listdir(os.path.dirname(self.fname))) files = [] if collections: for f in list(collections[0]): files.append( os.path.join(os.path.dirname(self.fname), f).replace("\\", "/")) else: files.append( os.path.join(os.path.dirname(self.fname), remainder[0]).replace("\\", "/")) name = context["subset"]["name"] name += "_{}".format(uuid.uuid4()) read_node = harmony.send({ "function": copy_files + import_files, "args": ["Top", files, name, 1] })["result"] return harmony.containerise(name, namespace, read_node, context, self.__class__.__name__, nodes=[read_node])
def get_dependencies(self, node: str, dependencies: list = None) -> list: """Get node dependencies. This will return recursive dependency list of given node. Args: node (str): Path to the node. dependencies (list, optional): existing dependency list. Returns: list: List of dependent nodes. """ current_dependencies = harmony.send({ "function": "PypeHarmony.getDependencies", "args": node })["result"] for dependency in current_dependencies: if not dependency: continue if dependency in dependencies: continue dependencies.append(dependency) self.get_dependencies(dependency, dependencies)
def export_template(backdrops, nodes, filepath): func = """function func(args) { var temp_node = node.add("Top", "temp_note", "NOTE", 0, 0, 0); var template_group = node.createGroup(temp_node, "temp_group"); node.deleteNode( template_group + "/temp_note" ); selection.clearSelection(); for (var f = 0; f < args[1].length; f++) { selection.addNodeToSelection(args[1][f]); } Action.perform("copy()", "Node View"); selection.clearSelection(); selection.addNodeToSelection(template_group); Action.perform("onActionEnterGroup()", "Node View"); Action.perform("paste()", "Node View"); // Recreate backdrops in group. for (var i = 0 ; i < args[0].length; i++) { MessageLog.trace(args[0][i]); Backdrop.addBackdrop(template_group, args[0][i]); }; Action.perform( "selectAll()", "Node View" ); copyPaste.createTemplateFromSelection(args[2], args[3]); // Unfocus the group in Node view, delete all nodes and backdrops // created during the process. Action.perform("onActionUpToParent()", "Node View"); node.deleteNode(template_group, true, true); } func """ harmony.send({ "function": func, "args": [ backdrops, nodes, os.path.basename(filepath), os.path.dirname(filepath) ] })
def process(self, instance): """Plugin entry point.""" self_name = self.__class__.__name__ result = harmony.send({ "function": f"PypeHarmony.Publish.{self_name}.getPalette", "args": instance.data["id"] })["result"] if not isinstance(result, list): self.log.error(f"Invalid reply: {result}") raise AssertionError("Invalid reply from server.") palette_name = result[0] palette_file = result[1] self.log.info(f"Got palette named {palette_name} " f"and file {palette_file}.") tmp_thumb_path = os.path.join( os.path.dirname(palette_file), os.path.basename(palette_file).split(".plt")[0] + "_swatches.png") self.log.info(f"Temporary thumbnail path {tmp_thumb_path}") palette_version = str(instance.data.get("version")).zfill(3) self.log.info(f"Palette version {palette_version}") if not instance.data.get("representations"): instance.data["representations"] = [] try: thumbnail_path = self.create_palette_thumbnail( palette_name, palette_version, palette_file, tmp_thumb_path) except OSError as e: # FIXME: this happens on Mac where PIL cannot access fonts # for some reason. self.log.warning("Thumbnail generation failed") self.log.warning(e) except ValueError: self.log.error("Unsupported palette type for thumbnail.") else: thumbnail = { "name": "thumbnail", "ext": "png", "files": os.path.basename(thumbnail_path), "stagingDir": os.path.dirname(thumbnail_path), "tags": ["thumbnail"] } instance.data["representations"].append(thumbnail) representation = { "name": "plt", "ext": "plt", "files": os.path.basename(palette_file), "stagingDir": os.path.dirname(palette_file) } instance.data["representations"].append(representation)
def process(self, context): """Plugin entry point. Args: context (:class:`pyblish.api.Context`): Context data. """ nodes = harmony.send({ "function": "node.subNodes", "args": ["Top"] })["result"] for node in nodes: data = harmony.read(node) # Skip non-tagged nodes. if not data: continue # Skip containers. if "container" in data["id"]: continue # skip render farm family as it is collected separately if data["family"] == "renderFarm": continue instance = context.create_instance(node.split("/")[-1]) instance.data.update(data) instance.data["setMembers"] = [node] instance.data["publish"] = harmony.send({ "function": "node.getEnable", "args": [node] })["result"] instance.data["families"] = self.families_mapping[data["family"]] # If set in plugin, pair the scene Version in ftrack with # thumbnails and review media. if (self.pair_media and instance.data["family"] == "scene"): context.data["scene_instance"] = instance # Produce diagnostic message for any graphical # user interface interested in visualising it. self.log.info("Found: \"{0}\": \n{1}".format( instance.data["name"], json.dumps(instance.data, indent=4)))
def update(self, container, representation): node = container.pop("node") collections, remainder = clique.assemble( os.listdir( os.path.dirname(api.get_representation_path(representation)))) files = [] for f in list(collections[0]): files.append( os.path.join(os.path.dirname(self.fname), f).replace("\\", "/")) harmony.send({ "function": copy_files + replace_files, "args": [files, node, 1] }) harmony.imprint(node, {"representation": str(representation["_id"])})
def update(self, container, representation): """Update loaded containers. Args: container (dict): Container data. representation (dict): Representation data. """ node_name = container["name"] node = harmony.find_node_by_name(node_name, "GROUP") self_name = self.__class__.__name__ update_and_replace = False if pype.lib.is_latest(representation): self._set_green(node) else: self._set_red(node) update_and_replace = harmony.send( { "function": f"PypeHarmony.Loaders.{self_name}." "askForColumnsUpdate", "args": [] } )["result"] if update_and_replace: # FIXME: This won't work, need to implement it. harmony.send( { "function": f"PypeHarmony.Loaders.{self_name}." "replaceNode", "args": [] } ) else: self.load( container["context"], container["name"], None, container["data"]) harmony.imprint( node, {"representation": str(representation["_id"])} )
def load_palette(self, representation): subset_name = representation["context"]["subset"] name = subset_name.replace("palette", "") # Overwrite palette on disk. scene_path = harmony.send({"function": "scene.currentProjectPath"})["result"] src = api.get_representation_path(representation) dst = os.path.join(scene_path, "palette-library", "{}.plt".format(name)) shutil.copy(src, dst) harmony.save_scene() msg = "Updated {}.".format(subset_name) msg += " You need to reload the scene to see the changes." harmony.send({"function": "PypeHarmony.message", "args": msg}) return name
def export_template(backdrops, nodes, filepath): func = """function func(args) { // Add an extra node just so a new group can be created. var temp_node = node.add("Top", "temp_note", "NOTE", 0, 0, 0); var template_group = node.createGroup(temp_node, "temp_group"); node.deleteNode( template_group + "/temp_note" ); // This will make Node View to focus on the new group. selection.clearSelection(); selection.addNodeToSelection(template_group); Action.perform("onActionEnterGroup()", "Node View"); // Recreate backdrops in group. for (var i = 0 ; i < args[0].length; i++) { Backdrop.addBackdrop(template_group, args[0][i]); }; // Copy-paste the selected nodes into the new group. var drag_object = copyPaste.copy(args[1], 1, frame.numberOf, ""); copyPaste.pasteNewNodes(drag_object, template_group, ""); // Select all nodes within group and export as template. Action.perform( "selectAll()", "Node View" ); copyPaste.createTemplateFromSelection(args[2], args[3]); // Unfocus the group in Node view, delete all nodes and backdrops // created during the process. Action.perform("onActionUpToParent()", "Node View"); node.deleteNode(template_group, true, true); } func """ harmony.send({ "function": func, "args": [ backdrops, nodes, os.path.basename(filepath), os.path.dirname(filepath) ] })