From 3e743ac33addb3a405c24e4c47a36e2700d0e8a1 Mon Sep 17 00:00:00 2001 From: Aleks Katunar Date: Thu, 13 Apr 2023 09:22:08 +0200 Subject: [PATCH 001/109] openrv integration --- openpype/hosts/openrv/__init__.py | 10 + openpype/hosts/openrv/addon.py | 35 ++ openpype/hosts/openrv/api/__init__.py | 10 + openpype/hosts/openrv/api/commands.py | 43 +++ openpype/hosts/openrv/api/lib.py | 24 ++ openpype/hosts/openrv/api/pipeline.py | 195 +++++++++++ openpype/hosts/openrv/api/review.py | 45 +++ .../hosts/openrv/hooks/pre_config_setup.py | 51 +++ openpype/hosts/openrv/hooks/pre_debughooks.py | 38 +++ openpype/hosts/openrv/hooks/pre_ftrackdata.py | 26 ++ .../openrv/plugins/create/create_debug.py | 46 +++ .../hosts/openrv/plugins/load/load_frames.py | 63 ++++ .../hosts/openrv/plugins/load/load_mov.py | 65 ++++ .../plugins/publish/collect_annotations.py | 115 +++++++ .../publish/collect_loaded_containers.py | 47 +++ .../plugins/publish/collect_workfile.py | 57 ++++ .../plugins/publish/extract_annotations.py | 53 +++ .../startup/pkgs_source/comments/PACKAGE | 17 + .../startup/pkgs_source/comments/comments.py | 304 +++++++++++++++++ .../pkgs_source/openpype_menus-1.0/PACKAGE | 17 + .../openpype_menus-1.0/openpype_menus.py | 66 ++++ .../action_review_openrv.py | 322 ++++++++++++++++++ .../ftrack/event_handlers_user/action_rv.py | 57 +++- openpype/resources/app_icons/openrv.png | Bin 0 -> 10126 bytes .../defaults/project_settings/openrv.json | 39 +++ .../system_settings/applications.json | 35 +- .../schema_project_openrv.json | 66 ++++ .../schemas/schema_openrv_create.json | 18 + .../schemas/schema_openrv_publish.json | 50 +++ 29 files changed, 1896 insertions(+), 18 deletions(-) create mode 100644 openpype/hosts/openrv/__init__.py create mode 100644 openpype/hosts/openrv/addon.py create mode 100644 openpype/hosts/openrv/api/__init__.py create mode 100644 openpype/hosts/openrv/api/commands.py create mode 100644 openpype/hosts/openrv/api/lib.py create mode 100644 openpype/hosts/openrv/api/pipeline.py create mode 100644 openpype/hosts/openrv/api/review.py create mode 100644 openpype/hosts/openrv/hooks/pre_config_setup.py create mode 100644 openpype/hosts/openrv/hooks/pre_debughooks.py create mode 100644 openpype/hosts/openrv/hooks/pre_ftrackdata.py create mode 100644 openpype/hosts/openrv/plugins/create/create_debug.py create mode 100644 openpype/hosts/openrv/plugins/load/load_frames.py create mode 100644 openpype/hosts/openrv/plugins/load/load_mov.py create mode 100644 openpype/hosts/openrv/plugins/publish/collect_annotations.py create mode 100644 openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py create mode 100644 openpype/hosts/openrv/plugins/publish/collect_workfile.py create mode 100644 openpype/hosts/openrv/plugins/publish/extract_annotations.py create mode 100644 openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE create mode 100644 openpype/hosts/openrv/startup/pkgs_source/comments/comments.py create mode 100644 openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/PACKAGE create mode 100644 openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py create mode 100644 openpype/modules/ftrack/event_handlers_user/action_review_openrv.py create mode 100644 openpype/resources/app_icons/openrv.png create mode 100644 openpype/settings/defaults/project_settings/openrv.json create mode 100644 openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json create mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json create mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json diff --git a/openpype/hosts/openrv/__init__.py b/openpype/hosts/openrv/__init__.py new file mode 100644 index 00000000000..11ee189fce7 --- /dev/null +++ b/openpype/hosts/openrv/__init__.py @@ -0,0 +1,10 @@ +from .addon import ( + OpenRVAddon, + OPENRV_ROOT_DIR +) + + +__all__ = ( + "OpenRVAddon", + "OPENRV_ROOT_DIR" +) diff --git a/openpype/hosts/openrv/addon.py b/openpype/hosts/openrv/addon.py new file mode 100644 index 00000000000..eb2b449004b --- /dev/null +++ b/openpype/hosts/openrv/addon.py @@ -0,0 +1,35 @@ +import os +import sys +from openpype.modules import OpenPypeModule +from openpype.modules.interfaces import IHostAddon + +OPENRV_ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class OpenRVAddon(OpenPypeModule, IHostAddon): + name = "openrv" + host_name = "openrv" + + def initialize(self, module_settings): + self.enabled = True + + def add_implementation_envs(self, env, app): + """Modify environments to contain all required for implementation.""" + sys.path.insert(0, os.path.join(os.getenv("OPENPYPE_ROOT"), "vendor", "python")) + # Set default environments if are not set via settings + defaults = { + "OPENPYPE_LOG_NO_COLORS": "True" + } + for key, value in defaults.items(): + if not env.get(key): + env[key] = value + + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(OPENRV_ROOT_DIR, "hooks") + ] + + def get_workfile_extensions(self): + return [".rv"] diff --git a/openpype/hosts/openrv/api/__init__.py b/openpype/hosts/openrv/api/__init__.py new file mode 100644 index 00000000000..e002b5459bd --- /dev/null +++ b/openpype/hosts/openrv/api/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +"""OpenRV OpenPype host API.""" + +from .pipeline import ( + OpenRVHost +) + +__all__ = [ + "OpenRVHost" +] diff --git a/openpype/hosts/openrv/api/commands.py b/openpype/hosts/openrv/api/commands.py new file mode 100644 index 00000000000..8ed87c6d731 --- /dev/null +++ b/openpype/hosts/openrv/api/commands.py @@ -0,0 +1,43 @@ +import logging +import os + +import rv +from openpype.pipeline.context_tools import get_current_project_asset + +log = logging.getLogger(__name__) + + +def reset_frame_range(): + """ Set timeline frame range. + """ + asset_doc = get_current_project_asset() + asset_data = asset_doc["data"] + + frame_start = int(asset_data.get( + "frameStart", + asset_data.get("edit_in"))) + + frame_end = int(asset_data.get( + "frameEnd", + asset_data.get("edit_out"))) + + rv.commands.setFrameStart(frame_start) + rv.commands.setFrameEnd(frame_end) + rv.commands.setFrame(frame_start) + + log.info("Project frame range set") + + +def set_session_fps(): + """ Set session fps. + """ + asset_doc = get_current_project_asset() + asset_data = asset_doc["data"] + fps = float(asset_data.get("fps", 25)) + rv.commands.setFPS(fps) + + + +def create_support_ticket(): + import webbrowser + webbrowser.open("http://localhost:5400/tickets/create_tickets") diff --git a/openpype/hosts/openrv/api/lib.py b/openpype/hosts/openrv/api/lib.py new file mode 100644 index 00000000000..15e4797f5e4 --- /dev/null +++ b/openpype/hosts/openrv/api/lib.py @@ -0,0 +1,24 @@ +import contextlib + + +@contextlib.contextmanager +def maintained_selection(): + return + + +@contextlib.contextmanager +def command_batch(name): + return + + +def group_member_of_type(group_node, member_type): + """ + usage layout_stack = group_member_of_type(sequence_layout, "RVStack") + :param group_node: + :param member_type: + :return: + """ + for node in rv.commands.nodesInGroup(group_node): + if rv.commands.nodeType(node) == member_type: + return node + diff --git a/openpype/hosts/openrv/api/pipeline.py b/openpype/hosts/openrv/api/pipeline.py new file mode 100644 index 00000000000..45c6090a707 --- /dev/null +++ b/openpype/hosts/openrv/api/pipeline.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +import contextlib +import os +from collections import OrderedDict + +import pyblish +import rv + +from openpype.host import HostBase, ILoadHost, IWorkfileHost +from openpype.hosts.openrv import OPENRV_ROOT_DIR +from openpype.pipeline import ( + register_loader_plugin_path, + register_inventory_action_path, + register_creator_plugin_path, + AVALON_CONTAINER_ID, +) +from . import lib + +PLUGINS_DIR = os.path.join(OPENRV_ROOT_DIR, "plugins") +PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") +LOAD_PATH = os.path.join(PLUGINS_DIR, "load") +CREATE_PATH = os.path.join(PLUGINS_DIR, "create") +INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") + +OPENPYPE_ATTR_PREFIX = "openpype" + + +class OpenRVHost(HostBase, IWorkfileHost, ILoadHost): + name = "openrv" + + def __init__(self): + super(OpenRVHost, self).__init__() + self._op_events = {} + + def install(self): + pyblish.api.register_plugin_path(PUBLISH_PATH) + pyblish.api.register_host("openrv") + + register_loader_plugin_path(LOAD_PATH) + register_creator_plugin_path(CREATE_PATH) + register_inventory_action_path(INVENTORY_PATH) + + def open_workfile(self, filepath): + return rv.commands.addSources([filepath]) + + def save_workfile(self, filepath=None): + return rv.commands.saveSession(filepath) + + def work_root(self, session): + work_dir = session.get("AVALON_WORKDIR") + scene_dir = session.get("AVALON_SCENEDIR") + if scene_dir: + return os.path.join(work_dir, scene_dir) + else: + return work_dir + + def get_current_workfile(self): + print("filename", rv.commands.sessionFileName()) + return rv.commands.sessionFileName() + + def workfile_has_unsaved_changes(self): + # dont ask to save if we are on the startup scene without a name ¬ set to untitled project and return False + print("filename", rv.commands.sessionFileName()) + return False + + def get_workfile_extensions(self): + return [".rv"] + + + def get_containers(self): + """Get containers. + """ + all_items = gather_containers() + for rvnode in all_items: + parsed = parse_container(rvnode) + yield parsed + + @contextlib.contextmanager + def maintained_selection(self): + with lib.maintained_selection(): + yield + + +def imprint(node, data): + """Store string attributes with value on a node + + Args: + node (object): The node to imprint data on. + data (dict): Key value pairs of attributes to create. + group (str): The Group to add the attributes to. + + Returns: + None + + """ + for attr, value in data.items(): + # Create and set the attribute + prop = node + "." + OPENPYPE_ATTR_PREFIX + "." + attr + if (not rv.commands.propertyExists(prop)): + rv.commands.newProperty(prop, rv.commands.StringType, 1) + rv.commands.setStringProperty(prop, [str(value)], True) + + return + + +def imprint_container(node, name, namespace, context, loader): + """Imprint `node` with container metadata. + + Arguments: + node (object): The node to containerise. + name (str): Name of resulting assembly + namespace (str): Namespace under which to host container + context (dict): Asset information + loader (str): Name of loader used to produce this container. + + Returns: + None + + """ + + data = [ + ("schema", "openpype:container-2.0"), + ("id", AVALON_CONTAINER_ID), + ("name", name), + ("namespace", namespace), + ("loader", loader), + ("representation", context["representation"]["_id"]) + ] + + # We use an OrderedDict to make sure the attributes + # are always created in the same order. This is solely + # to make debugging easier when reading the values in + # the attribute editor. + imprint(node, OrderedDict(data)) + + +def parse_container(node): + """Returns imprinted container data of a tool + + This reads the imprinted data from `imprint_container`. + + """ + # If not all required data return None + required = ['id', 'schema', 'name', + 'namespace', 'loader', 'representation'] + + data = {} + for key in required: + attr = node + "." + OPENPYPE_ATTR_PREFIX + "." + key + if not rv.commands.propertyExists(attr): + return + + value = rv.commands.getStringProperty(attr)[0] + data[key] = value + + # Store the node's name + data["objectName"] = str(node) + + # Store reference to the node object + data["node"] = node + + return data + + +def gather_containers(): + """gathers all rv nodes list + """ + all_files = [] + all_nodes = rv.commands.nodes() + for node in all_nodes: + prop = node + "." + OPENPYPE_ATTR_PREFIX + ".schema" + if rv.commands.propertyExists(prop): + #print("FOUND PROPERTY ON {}".format(node)) + all_files.append(node) + return set(all_files) + + +def get_containers(): + """Get containers. + """ + all_items = gather_containers() + for rvnode in all_items: + parsed = parse_container(rvnode) + yield parsed + + +@contextlib.contextmanager +def openrv_project_file_lock_and_undo_chunk(openrv_project_file, undo_queue_name="Script CMD"): + """Lock rv session and open an undo chunk during the context""" + pass + + +def get_path_workfile(): + print("filename", rv.commands.sessionFileName()) + return rv.commands.sessionFileName() \ No newline at end of file diff --git a/openpype/hosts/openrv/api/review.py b/openpype/hosts/openrv/api/review.py new file mode 100644 index 00000000000..9b37c7ebd1f --- /dev/null +++ b/openpype/hosts/openrv/api/review.py @@ -0,0 +1,45 @@ +# review code +import os +import sys + +import rv + +def get_path_annotated_frame(frame=None, asset=None, asset_folder=None): + """Get path for annotations + """ + filename = os.path.normpath("{}/pyblish/exports/annotated_frames/annotate_{}_{}.jpg".format( + str(asset_folder), + str(asset), + str(frame) + ) + ) + return filename + +def extract_annotated_frame(filepath=None): + """Export frame to file + """ + if filepath: + return rv.commands.exportCurrentFrame(filepath) + + +def review_attributes(node=None): + prop_status = node + ".openpype" + ".review_status" + prop_comment = node + ".openpype" + ".review_comment" + + +def get_review_attribute(node=None, attribute=None): + attr = node + ".openpype" + "." + attribute + return rv.commands.getStringProperty(attr)[0] + + +def write_review_attribute(node=None, attribute=None, att_value=None): + att_prop = node + ".openpype" + ".{}".format(attribute) + if not rv.commands.propertyExists(att_prop): + rv.commands.newProperty(att_prop, rv.commands.StringType, 1) + rv.commands.setStringProperty(att_prop, [str(att_value)], True) + + +def export_current_view_frame(frame=None, export_path=None): + rv.commands.setFrame(int(frame)) + rv.commands.exportCurrentFrame(export_path) + diff --git a/openpype/hosts/openrv/hooks/pre_config_setup.py b/openpype/hosts/openrv/hooks/pre_config_setup.py new file mode 100644 index 00000000000..45cd69023f9 --- /dev/null +++ b/openpype/hosts/openrv/hooks/pre_config_setup.py @@ -0,0 +1,51 @@ +import os +import platform + +from openpype.lib import PreLaunchHook +from openpype.settings import ( + get_project_settings +) + + +SET_CONFIG_FILE_PATH = None +SET_OCIO_ENV_VAR = None +SET_USERS_CONFIG = None +SET_AOV_CONFIGS = None +SET_STARTUP_PROJECT = None + + +def show_env(launch_env=None): + project_name = launch_env.env.get("AVALON_PROJECT") + workdir = launch_env.env.get("AVALON_WORKDIR") + print("OVERRIDES {}".format(project_name)) + + for k in launch_env.env.keys(): + print(k, launch_env.env[k]) + + +class PreConfigSetups(PreLaunchHook): + app_groups = ["openrv"] + + def execute(self): + self.log.info("PRE-SETUP OPENRV CONFIGS HOOK") + + self.project_name = self.launch_context.env.get("AVALON_PROJECT") + if not self.project_name: + self.project_name = os.environ.get("AVALON_PROJECT") + + print("Loaded project", self.project_name) + + self.load_config = get_project_settings(self.project_name)["openrv"]["imageio"]["workfile"]["OCIO_config"] + print(self.load_config) + + if "srgb" in self.load_config.lower(): + print("OCIO Disabled.") + os.environ["OCIO"] = "" + self.launch_context.env["OCIO"] = "" + else: + print("OCIO Enabled.") + ocio_config_path = get_project_settings(self.project_name)["openrv"]["imageio"]["workfile"]["customOCIOConfigPath"][str(platform.system().lower())][0] + os.environ["OCIO"] = str(ocio_config_path) + self.launch_context.env["OCIO"] = str(ocio_config_path) + print(os.environ["OCIO"]) + diff --git a/openpype/hosts/openrv/hooks/pre_debughooks.py b/openpype/hosts/openrv/hooks/pre_debughooks.py new file mode 100644 index 00000000000..9e4a1c5cae5 --- /dev/null +++ b/openpype/hosts/openrv/hooks/pre_debughooks.py @@ -0,0 +1,38 @@ +import os + +from openpype.lib import PreLaunchHook + + +class PreDebugHook(PreLaunchHook): + """DEBUG hook for openrv + """ + app_groups = ["openrv"] + + def show_env(launch_env=None): + project_name = launch_env.env.get("AVALON_PROJECT") + workdir = launch_env.env.get("AVALON_WORKDIR") + print("------- project_name {}".format(project_name)) + print("------- LAUNCHDATA", launch_env.data) + + for k in launch_env.env.keys(): + print(k, launch_env.env[k]) + + def execute(self): + print("----- ---- ---- ---- ---") + print("------- OPENRV PREHOOK DEBUG ") + print("RV_SUPPORT_PATH", self.launch_context.env.get("RV_SUPPORT_PATH")) + print("HOME", self.launch_context.env.get("HOME")) + #print("PYTHONPATH", self.launch_context.env.get("PYTHONPATH")) + print("RV_PREFS_OVERRIDE_PATH", self.launch_context.env.get("RV_PREFS_OVERRIDE_PATH")) + print("----- ---- ---- ---- ---") + project_name = self.launch_context.env.get("AVALON_PROJECT") + workdir = self.launch_context.env.get("AVALON_WORKDIR") + if not workdir: + self.log.warning("BUG: Workdir is not filled.") + return + #self.log.info("OpenPype: Setting up config files") + #self.log.info("Workdir {}".format(workdir)) + #self.log.info("Project name {}".format(project_name)) + #print(self.data) + # print("self.launch_context", self.launch_context.env) + # print("self.launch_context.data", self.launch_context.data) diff --git a/openpype/hosts/openrv/hooks/pre_ftrackdata.py b/openpype/hosts/openrv/hooks/pre_ftrackdata.py new file mode 100644 index 00000000000..f2e7faa1a39 --- /dev/null +++ b/openpype/hosts/openrv/hooks/pre_ftrackdata.py @@ -0,0 +1,26 @@ +import json +import tempfile + +from openpype.lib import PreLaunchHook + + +class PreFtrackData(PreLaunchHook): + """Pre-hook for openrv/ftrack + """ + app_groups = ["openrv"] + + def execute(self): + print("----- ---- ---- ---- ---") + print("------- OPENRV PreFtrackData ") + print("----- ---- ---- ---- ---") + + + representations = self.data.get("extra", None) + if representations: + payload = {"representations": representations} + repr_file = tempfile.NamedTemporaryFile(mode="w+", delete=False) + json.dump(payload, repr_file) + repr_file.flush() + + self.launch_context.env["OPENPYPE_LOADER_REPRESENTATIONS"] = str(repr_file.name) + diff --git a/openpype/hosts/openrv/plugins/create/create_debug.py b/openpype/hosts/openrv/plugins/create/create_debug.py new file mode 100644 index 00000000000..63d4e929804 --- /dev/null +++ b/openpype/hosts/openrv/plugins/create/create_debug.py @@ -0,0 +1,46 @@ +from openpype.pipeline.context_tools import get_current_project_asset +from openpype.pipeline.create import ( + LegacyCreator +) + +class CreateDebugLogs(LegacyCreator): + """Creates a quick debuger for avalon asset""" + + label = "Show Debug info in the Log" + family = "*" + icon = "gears" + defaults = ["Main"] + + def __init__(self, *args, **kwargs): + super(CreateDebugLogs, self).__init__(*args, **kwargs) + + # Remove the active, we are checking the bypass flag of the nodes + self.data.pop("active", None) + + + def process(self): + """Creator main entry point. + + Args: + instance + + """ + asset_doc = get_current_project_asset() + asset_data = asset_doc["data"] + + print("Asset doc") + print(asset_doc) + print(type(asset_doc)) + print("Asset_data") + print(type(asset_data)) + for d in asset_data: + print(d, asset_data[d]) + + print(self.name) + + + print("-----done ") + + + + diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py new file mode 100644 index 00000000000..f8c37ce6a74 --- /dev/null +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -0,0 +1,63 @@ +from openpype.pipeline import ( + load, + get_representation_path +) +from openpype.hosts.openrv.api.pipeline import imprint_container +from openpype.hosts.openrv.api.commands import set_session_fps +from openpype.hosts.openrv.api.commands import reset_frame_range + +import rv + +class FramesLoader(load.LoaderPlugin): + """Load frames into OpenRV""" + + label = "Load Frames" + families = ["*"] + representations = ["exr"] + order = 0 + + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + + filepath = self.fname + # Command fails on unicode so we must force it to be strings + filepath = str(filepath) + + node_name = "{}_{}".format(namespace, name) if namespace else name + namespace = namespace if namespace else context["asset"]["name"] + + set_session_fps() + reset_frame_range() + loaded_node = rv.commands.addSourceVerbose([filepath]) + print("loaded_node", loaded_node) + imprint_container( + loaded_node, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__ + ) + + + + def update(self, container, representation): + node = container["node"] + filepath = get_representation_path(representation) + filepath = str(filepath) + set_session_fps() + reset_frame_range() + # change path + rv.commands.setSourceMedia(node, [filepath]) + rv.commands.setStringProperty(node + ".media.name", ["newname"], True) + rv.commands.setStringProperty(node + ".media.repName", ["repname"], True) + rv.commands.setStringProperty(node + ".openpype.representation", [str(representation["_id"])], True) + + + + def remove(self, container): + node = container["node"] + # not implemented + return + diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py new file mode 100644 index 00000000000..3a30e5350dd --- /dev/null +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -0,0 +1,65 @@ +from openpype.hosts.openrv.api.commands import set_session_fps, reset_frame_range +from openpype.pipeline import ( + load, + get_representation_path +) +from openpype.hosts.openrv.api.pipeline import imprint_container + +import rv + +class MovLoader(load.LoaderPlugin): + """Load mov into OpenRV""" + + label = "Load MOV" + families = ["*"] + representations = ["mov"] + order = 0 + + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + + filepath = self.fname + # Command fails on unicode so we must force it to be strings + filepath = str(filepath) + + node_name = "{}_{}".format(namespace, name) if namespace else name + namespace = namespace if namespace else context["asset"]["name"] + + set_session_fps() + reset_frame_range() + + loaded_node = rv.commands.addSourceVerbose([filepath]) + print("PROPS") + for prop in rv.commands.properties(loaded_node): + print(prop) + imprint_container( + loaded_node, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__ + ) + + + + def update(self, container, representation): + node = container["node"] + filepath = get_representation_path(representation) + filepath = str(filepath) + set_session_fps() + reset_frame_range() + # change path + update_node = rv.commands.setSourceMedia(node, [filepath]) + # update name + rv.commands.setStringProperty(node + ".media.name", ["newname"], True) + rv.commands.setStringProperty(node + ".media.repName", ["repname"], True) + rv.commands.setStringProperty(node + ".openpype.representation", [str(representation["_id"])], True) + + + def remove(self, container): + node = container["node"] + # not implemented + return + diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py new file mode 100644 index 00000000000..4f1b4a51797 --- /dev/null +++ b/openpype/hosts/openrv/plugins/publish/collect_annotations.py @@ -0,0 +1,115 @@ +import os +import time +import pyblish.api + +from openpype.client import get_representation_parents, get_representations +from openpype.hosts.openrv.api.pipeline import gather_containers +from openpype.pipeline import ( + legacy_io +) + + + + + +class CollectSessionAnnotations(pyblish.api.ContextPlugin): + """Collect session Annotations + """ + + order = pyblish.api.CollectorOrder - 0.02 + label = "Collect Session Annotations" + hosts = ["openrv"] + family = "annotation" + + def process(self, context): + import rv + """Inject collection of annotated frames""" + + project_name = legacy_io.Session["AVALON_PROJECT"] + source_groups = [] + all_nodes = gather_containers() + for container in all_nodes: + print("container-------- ", container) + print(rv.commands.properties(container)) + prop_namespace = container + ".openpype.namespace" + prop_representation = container + ".openpype.representation" + data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] + data_prop_representation_id = rv.commands.getStringProperty(prop_representation)[0] + + representations = get_representations(project_name, + representation_ids=[data_prop_representation_id]) + list_representation = [x for x in representations] + + source_representation_project = list_representation[0]["context"]["project"]["name"] + source_representation_asset = list_representation[0]["context"]["asset"] + source_representation_task = list_representation[0]["context"]["task"]["name"] + source_representation_subset = list_representation[0]["context"]["subset"] + source_group = rv.commands.nodeGroup(container) + print("SOURCE GROUP ", source_group) + source_groups.append(source_group) + rv.commands.setViewNode(source_group) + rv.commands.redraw() + + marked_frames = rv.commands.markedFrames() + annotated_frames = rv.extra_commands.findAnnotatedFrames() + + asset_folder, file = os.path.split(rv.commands.sessionFileName()) + + for marked in marked_frames: + print("MARKED ------------ ", container, marked, source_group) + + + for noted_frame in annotated_frames: + print("NOTED ------- ", container, noted_frame, source_group) + rv.commands.setFrame(int(noted_frame)) + rv.commands.redraw() + + print(os.getenv("AVALON_ASSET", None)) + instance = context.create_instance(name=str(container)) + item_name = "note_" + str(data_prop_namespace) + "_" + str(noted_frame) + data = {} + + # annotation_representation = { + # "tags": ["review", "ftrackreview"], + # "name": "thumbnail", + # "ext": "jpg", + # "files": "frames_1001.jpg", + # "stagingDir": "X:\\projects\\Sync\\DaliesPrep\\work\\prepDaily", + # #"thumbnail": True, + # #"comment": "NEW COMMENT FROM UI" + # "frameStart": noted_frame, + # "frameEnd": noted_frame, + # "fps": "25", + # } + + data.update({ + #"subset": source_representation_subset + "_review_{}".format(noted_frame), + "subset": "annotation_{}".format(str(noted_frame)), + "tags": ["review", "ftrackreview"], + "asset": source_representation_asset, + "task": source_representation_task, + "label": str(item_name), + "publish": True, + "review": True, + "family": "annotation", + #"setMembers": [""], + "asset_folder_path": str(asset_folder), + "annotated_frame": str(noted_frame), + "comment": "NEW COMMENT FROM UI {}".format(noted_frame), + }) + + instance.data.update(data) + # + # if "representations" not in instance.data: + # instance.data["representations"] = [] + # + # instance.data["representations"].append(annotation_representation) + + + view_node = rv.commands.viewNode() + intent = context.data.get("intent") + print("------- intent", intent) + + print("VIEWNODE:", view_node) + #print(context.data) + diff --git a/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py b/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py new file mode 100644 index 00000000000..726638e6060 --- /dev/null +++ b/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py @@ -0,0 +1,47 @@ +import os +import pyblish.api +from openpype.hosts.openrv.api.pipeline import get_containers +from openpype.pipeline import ( + legacy_io +) + + + + +class CollectSessionContainers(pyblish.api.ContextPlugin): + """Collect session containers + """ + + order = pyblish.api.CollectorOrder - 0.01 + label = "Collect Session Containers" + hosts = ["openrv"] + family = "containers" + + def process(self, context): + """Inject the current camera output and file""" + + task = legacy_io.Session["AVALON_TASK"] + item_collection = get_containers() + + # create instances + for item in item_collection: + print(item) + item_name = item["namespace"] + instance = context.create_instance(name=str(item_name)) + subset = 'container' + task.capitalize() + + data = {} + data.update({ + "subset": subset, + "asset": os.getenv("AVALON_ASSET", None), + "label": str(item_name), + "publish": False, + "family": 'containers', + "setMembers": [""], + "frameStart": context.data['frameStart'], + "frameEnd": context.data['frameEnd'], + "handleStart": context.data['handleStart'], + "handleEnd": context.data['handleEnd'], + }) + + instance.data.update(data) \ No newline at end of file diff --git a/openpype/hosts/openrv/plugins/publish/collect_workfile.py b/openpype/hosts/openrv/plugins/publish/collect_workfile.py new file mode 100644 index 00000000000..e58dfd63785 --- /dev/null +++ b/openpype/hosts/openrv/plugins/publish/collect_workfile.py @@ -0,0 +1,57 @@ +import os +import pyblish.api +from openpype.hosts.openrv.api.pipeline import get_path_workfile +from openpype.pipeline import ( + legacy_io, + registered_host +) + + +class CollectWorkfile(pyblish.api.ContextPlugin): + """Inject the current working file into context""" + + order = pyblish.api.CollectorOrder - 0.01 + label = "OpenRV Session Workfile" + hosts = ['openrv'] + + def process(self, context): + """Inject the current working file""" + host = registered_host() + current_file = get_path_workfile() + if not current_file: + self.log.error("No current filepath detected") + + folder, file = os.path.split(current_file) + filename, ext = os.path.splitext(file) + + task = legacy_io.Session["AVALON_TASK"] + + # create instance + instance = context.create_instance(name=filename) + subset = 'workfile' + task.capitalize() + + context.data['currentFile'] = current_file + + data = {} + data.update({ + "subset": subset, + "asset": os.getenv("AVALON_ASSET", None), + "label": subset, + "publish": True, + "family": 'workfile', + "families": ['workfile'], + "setMembers": [current_file], + "frameStart": context.data['frameStart'], + "frameEnd": context.data['frameEnd'], + "handleStart": context.data['handleStart'], + "handleEnd": context.data['handleEnd'] + }) + + data['representations'] = [{ + 'name': ext.lstrip("."), + 'ext': ext.lstrip("."), + 'files': file, + "stagingDir": folder, + }] + + instance.data.update(data) \ No newline at end of file diff --git a/openpype/hosts/openrv/plugins/publish/extract_annotations.py b/openpype/hosts/openrv/plugins/publish/extract_annotations.py new file mode 100644 index 00000000000..f8212ed2fb5 --- /dev/null +++ b/openpype/hosts/openrv/plugins/publish/extract_annotations.py @@ -0,0 +1,53 @@ +import os +from pprint import pformat +import pyblish.api + +from openpype.pipeline import publish +from openpype.hosts.openrv.api.review import get_path_annotated_frame, extract_annotated_frame + +import rv + +class ExtractOpenRVAnnotatedFrames(publish.Extractor): + + order = pyblish.api.ExtractorOrder + label = "Extract Annotations from Session" + hosts = ["openrv"] + families = ["annotation"] + + def process(self, instance): + + self.log.info("instance.data: `{}`".format( + pformat(instance.data))) + #self.log.info("workfile", str(instance.data['asset_folder'])) + asset_folder = instance.data['asset_folder_path'] + asset = instance.data['asset'] + annotated_frame = instance.data['annotated_frame'] + + annotated_frame_path = get_path_annotated_frame(frame=annotated_frame, + asset_folder=asset_folder) + print(annotated_frame_path) + annotated_frame_folder, file = os.path.split(annotated_frame_path) + + if not os.path.isdir(annotated_frame_folder): + os.makedirs(annotated_frame_folder) + # + # # save the frame + # + # #extract_annotated_frame(filepath=annotated_frame) + # + # assert os.path.isfile(annotated_frame) + # + # folder, file = os.path.split(annotated_frame) + # filename, ext = os.path.splitext(file) + # + # representation = { + # "name": ext.lstrip("."), + # "ext": ext.lstrip("."), + # "files": file, + # "stagingDir": folder, + # } + # + # if "representations" not in instance.data: + # instance.data["representations"] = [] + # + # instance.data["representations"].append(representation) \ No newline at end of file diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE b/openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE new file mode 100644 index 00000000000..7b8e56e20a2 --- /dev/null +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE @@ -0,0 +1,17 @@ +package: comments +author: Aleks Katunar +organization: Artisan software Dobro +version: 1.0 +rv: 3.12 +openrv: 1.0.0 +requires: '' +optional: true + +modes: + - file: comments + load: immediate + +description: | + +

Adds COmments to OpenRV

+ diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py new file mode 100644 index 00000000000..25b94a450e1 --- /dev/null +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -0,0 +1,304 @@ +# review code +import os +import sys +from PySide2 import QtWidgets, QtGui +from PySide2.QtCore import QRect +from PySide2.QtWidgets import QApplication + +from rv.qtutils import * +from rv.rvtypes import * + +from rv.commands import * +from rv.extra_commands import * + + +class ReviewMenu(MinorMode): + def __init__(self): + MinorMode.__init__(self) + self.init("py-ReviewMenu-mode", None, None, [("-= OpenPype =-", [("Review", self.runme, None, None)])]) + + # spacers + self.verticalSpacer = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, + QtWidgets.QSizePolicy.Expanding) + self.verticalSpacerMin = QtWidgets.QSpacerItem(2, 2, QtWidgets.QSizePolicy.Minimum, + QtWidgets.QSizePolicy.Minimum) + self.horizontalSpacer = QtWidgets.QSpacerItem(40, 10, QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Minimum) + self.customDockWidget = QtWidgets.QWidget() + + # data + self.current_loaded_viewnode = None + self.review_main_layout = QtWidgets.QVBoxLayout() + self.rev_head_label = QtWidgets.QLabel("Shot Review") + self.set_item_font(self.rev_head_label, size=16) + self.rev_head_name = QtWidgets.QLabel("Shot Name") + self.current_loaded_shot = QtWidgets.QLabel("") + self.current_shot_status = QtWidgets.QComboBox() + self.current_shot_status.addItems(["In Review", "Ready For Review", "Reviewed", "Approved", "Deliver"]) + self.current_shot_comment = QtWidgets.QPlainTextEdit() + self.current_shot_comment.setStyleSheet("color: white; background-color: black") + + self.review_main_layout_head = QtWidgets.QVBoxLayout() + self.review_main_layout_head.addWidget(self.rev_head_label) + self.review_main_layout_head.addWidget(self.rev_head_name) + self.review_main_layout_head.addWidget(self.current_loaded_shot) + self.review_main_layout_head.addWidget(self.current_shot_status) + self.review_main_layout_head.addWidget(self.current_shot_comment) + + self.get_view_image = QtWidgets.QPushButton("Get image") + self.review_main_layout_head.addWidget(self.get_view_image) + + self.remove_cmnt_status_btn = QtWidgets.QPushButton("Remove comment and status") + self.review_main_layout_head.addWidget(self.remove_cmnt_status_btn) + + # annotations controls + self.notes_layout = QtWidgets.QVBoxLayout() + self.notes_layout_label = QtWidgets.QLabel("Annotations") + self.btn_note_prev = QtWidgets.QPushButton("Previous Annotation") + self.btn_note_next = QtWidgets.QPushButton("Next Annotation") + self.notes_layout.addWidget(self.notes_layout_label) + self.notes_layout.addWidget(self.btn_note_prev) + self.notes_layout.addWidget(self.btn_note_next) + + + self.review_main_layout.addLayout(self.review_main_layout_head) + self.review_main_layout.addLayout(self.notes_layout) + self.review_main_layout.addStretch(1) + self.customDockWidget.setLayout(self.review_main_layout) + + # signals + self.current_shot_status.currentTextChanged.connect(self.setup_combo_status) + self.current_shot_comment.textChanged.connect(self.comment_update) + self.get_view_image.clicked.connect(self.get_gui_image) + self.remove_cmnt_status_btn.clicked.connect(self.clean_cmnt_status) + self.btn_note_prev.clicked.connect(self.annotate_prev) + self.btn_note_next.clicked.connect(self.annotate_next) + + self.runme() + + + def runme(self, arg1=None, arg2=None): + self.rvWindow = rv.qtutils.sessionWindow() + + # Create DockWidget and add the Custom Widget to it + self.dockWidget = QDockWidget("OpenPype Review", self.rvWindow) + self.dockWidget.setWidget(self.customDockWidget) + + # Dock widget to the RV MainWindow + self.rvWindow.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget) + self.setup_listeners() + + + + def set_item_font(self, item, size=14, noweight=False, bold=True): + font = QtGui.QFont() + if bold: + font.setFamily("Arial Bold") + else: + font.setFamily("Arial") + font.setPointSize(size) + font.setBold(True) + if not noweight: + font.setWeight(75) + item.setFont(font) + + + def setup_listeners(self): + # new-source, graph-state-change, after-progressive-loading, media-relocated + rv.commands.bind("default", "global", "source-media-set", self.graph_change, "Doc string") + rv.commands.bind("default", "global", "after-graph-view-change", self.graph_change, "Doc string") + + + def graph_change(self, event): + print("image structure change") + print(event.contents()) + print(rv.commands.sourcesAtFrame(rv.commands.frame())) + print(rv.commands.frame()) + # update the view + self.get_view_source() + + + def get_view_source(self): + self.current_loaded_viewnode = rv.commands.sourcesAtFrame(rv.commands.frame())[0] + self.update_ui_attribs() + + + def update_ui_attribs(self): + node = self.current_loaded_viewnode + # representation + prop_representation = node + ".openpype.representation" + prop_namespace = node + ".openpype.namespace" + data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] + data_prop_representation_id = rv.commands.getStringProperty(prop_representation)[0] + print("data_prop_namespace", data_prop_namespace) + self.current_loaded_shot.setText(data_prop_namespace) + #print(self.echo_change_update()) + self.setup_properties() + self.get_comment() + + + + def setup_combo_status(self): + # setup properties + node = self.current_loaded_viewnode + print(rv.commands.properties(node)) + att_prop = node + ".openpype_review.task_status" + status = self.current_shot_status.currentText() + rv.commands.setStringProperty(att_prop, [str(status)], True) + self.current_shot_status.setCurrentText(status) + + + def setup_properties(self): + # setup properties + node = self.current_loaded_viewnode + print(rv.commands.properties(node)) + att_prop = node + ".openpype_review.task_status" + + if not rv.commands.propertyExists(att_prop): + status = "In Review" + rv.commands.newProperty(att_prop, rv.commands.StringType, 1) + rv.commands.setStringProperty(att_prop, [str(status)], True) + self.current_shot_status.setCurrentIndex(0) + else: + status = rv.commands.getStringProperty(att_prop)[0] + self.current_shot_status.setCurrentText(status) + + + def comment_update(self): + node = self.current_loaded_viewnode + comment = self.current_shot_comment.toPlainText() + att_prop = node + ".openpype_review.task_comment" + rv.commands.newProperty(att_prop, rv.commands.StringType, 1) + rv.commands.setStringProperty(att_prop, [str(comment)], True) + + + def get_comment(self): + node = self.current_loaded_viewnode + att_prop = node + ".openpype_review.task_comment" + if not rv.commands.propertyExists(att_prop): + rv.commands.newProperty(att_prop, rv.commands.StringType, 1) + rv.commands.setStringProperty(att_prop, [""], True) + else: + status = rv.commands.getStringProperty(att_prop)[0] + self.current_shot_comment.setPlainText(status) + + + def clean_cmnt_status(self): + attribs = [] + node = self.current_loaded_viewnode + att_prop_cmnt = node + ".openpype_review.task_comment" + att_prop_status = node + ".openpype_review.task_status" + attribs.append(att_prop_cmnt) + attribs.append(att_prop_status) + + for prop in attribs: + if not rv.commands.propertyExists(prop): + rv.commands.newProperty(prop, rv.commands.StringType, 1) + rv.commands.setStringProperty(prop, [""], True) + + self.current_shot_status.setCurrentText("In Review") + self.current_shot_comment.setPlainText("") + + + def get_gui_image(self, filename=None): + data = rv.commands.exportCurrentFrame("c:/temp/jpg.jpg") + print(data) + print("File saved") + + + + def annotate_next(self): + all_notes = self.get_annotated_for_view() + nxt = self.get_cycle_frame(frame=rv.commands.frame(), frames_in=all_notes, do="next") + rv.commands.setFrame(int(nxt)) + rv.commands.redraw() + + + def annotate_prev(self): + all_notes = self.get_annotated_for_view() + previous = self.get_cycle_frame(frame=rv.commands.frame(), frames_in=all_notes, do="prev") + rv.commands.setFrame(int(previous)) + rv.commands.redraw() + + + def get_annotated_for_view(self): + annotated_frames = rv.extra_commands.findAnnotatedFrames() + return annotated_frames + + + def get_cycle_frame(self, frame=None, frames_in=None, do="next"): + set_start = -1 + + if len(frames_in) == 0: + pass + + elif len(frames_in) == 1: + for i,findframe in enumerate(frames_in): + if frame == findframe: + return frame + + else: + for i,findframe in enumerate(frames_in): + if frame == findframe: + frames_in.remove(frame) + set_start = i + + if set_start != -1: + if do == "next": + try: + data = [x for x in frames_in] + return data[set_start] + except: + return data[0] + else: + for y,num in enumerate(frames_in, start=set_start): + return num + else: + if do == "next": + next_frame = min([i for i in frames_in if frame < i]) + return next_frame + else: + prev_frame = max([i for i in frames_in if frame > i]) + return prev_frame + + + + def echo_change_update(self): + print("CHANGE") + + print(self.current_loaded_viewnode) + node = self.current_loaded_viewnode + #print(node) + print(rv.commands.properties(node)) + # representation + prop_representation = node + ".openpype.representation" + prop_namespace = node + ".openpype.namespace" + data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] + data_prop_representation_id = rv.commands.getStringProperty(prop_representation)[0] + print("data_prop_namespace", data_prop_namespace) + print("data_prop_representation_id", data_prop_representation_id) + from openpype.client import get_representations, get_representation_parents + project_name = os.environ["AVALON_PROJECT"] + representations = get_representations(project_name, + representation_ids=[data_prop_representation_id]) + print("REPR") + for rep in representations: + print(rep) + info = rv.extra_commands.sourceMetaInfoAtFrame(rv.commands.frame()) + print("info", info) + + + def get_task_status(self): + import ftrack_api + session = ftrack_api.Session(auto_connect_event_hub=False) + self.log.debug("Ftrack user: \"{0}\"".format(session.api_user)) + project_name = legacy_io.Session["AVALON_PROJECT"] + project_entity = session.query(( + "select project_schema from Project where full_name is \"{}\"" + ).format(project_name)).one() + project_schema = project_entity["project_schema"] + + + +def createMode(): + return ReviewMenu() \ No newline at end of file diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/PACKAGE b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/PACKAGE new file mode 100644 index 00000000000..6fec18e6ea9 --- /dev/null +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/PACKAGE @@ -0,0 +1,17 @@ +package: openpype_menus +author: Aleks Katunar +organization: Artisan software Dobro +version: 1.0 +rv: 3.12 +openrv: 1.0.0 +requires: '' +optional: true + +modes: + - file: openpype_menus + load: immediate + +description: | + +

Adds OpenPype to OpenRV

+ diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py new file mode 100644 index 00000000000..3ddd7c7ad06 --- /dev/null +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -0,0 +1,66 @@ +from rv.rvtypes import * +from rv.commands import * +from rv.extra_commands import * + +import sys + +# inject OP for now, otherwise it won work +sys.path.append("PATHTOOPENPYPE/openpype/vendor/python/common") +sys.path.append("PATHTOOPENPYPE/openpype/openpype/tools") +sys.path.append("PATHTOOPENPYPE/openpype") +sys.path.append("PATHTOOPENPYPE/openpype/.venv/Lib/site-packages") + +from openpype.tools.utils import host_tools + +from openpype.pipeline import install_host +from openpype.hosts.openrv.api import OpenRVHost + + +def install_openpype_to_host(): + host = OpenRVHost() + install_host(host) + + +class OpenPypeMenus(MinorMode): + + def __init__(self): + MinorMode.__init__(self) + self.init("py-openpype", None, None, + [ + # Menu name + # NOTE: If it already exists it will merge with existing + # and add submenus / menuitems to the existing one + ("-= OpenPype =-", + [ + # Menuitem name, actionHook (event), key, stateHook + ("Workfiles", self.workfiles, None, None), + ("Create", self.workfiles, None, None), + ("Load", self.load, None, None), + ("Publish", self.publish, None, None) + ] + ) + ] + ) + if not isConsoleVisible(): + showConsole() + + def create(self, event): + print("Launching Creator") + host_tools.show_creator() + + def load(self, event): + print("Launching Loader") + host_tools.show_loader(parent=[], use_context=True) + + def publish(self, event): + print("Launching Pyblish") + host_tools.show_publish() + + def workfiles(self, event): + print("Launching Workfiles") + host_tools.show_workfiles() + + +def createMode(): + install_openpype_to_host() + return OpenPypeMenus() \ No newline at end of file diff --git a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py new file mode 100644 index 00000000000..df5f26e2bb6 --- /dev/null +++ b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py @@ -0,0 +1,322 @@ +import os +import traceback +import json + +import ftrack_api + +from openpype.client import ( + get_asset_by_name, + get_subset_by_name, + get_version_by_name, + get_representation_by_name, get_project +) +from openpype.lib import ApplicationManager +from openpype.pipeline import ( + get_representation_path, + AvalonMongoDB, + Anatomy, +) +from openpype_modules.ftrack.lib import BaseAction, statics_icon + + +class RVActionReview(BaseAction): + """ Launch RV action """ + identifier = "openrv.review.action" + label = "Review with RV" + description = "OpenRV Launcher" + icon = statics_icon("ftrack", "action_icons", "RV.png") + + type = "Application" + + allowed_types = ["img", "mov", "exr", "mp4", "jpg", "png"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.rv_path = None + self.rv_app = "openrv/1-0" + self.application_manager = ApplicationManager() + + + def discover(self, session, entities, event): + """Return available actions based on *event*. """ + data = event['data'] + selection = data.get('selection', []) + print(selection[0]["entityType"]) + if selection[0]["entityType"] == "list": + return {'items': [{ + 'label': self.label, + 'description': self.description, + 'actionIdentifier': self.identifier + }] + } + + + def preregister(self): + return True + + + def get_components_from_list_entity(self, entity): + """Get components from list entity types. + + The components dictionary is modifid in place, so nothing is returned. + + Args: + entity (Ftrack entity) + components (dict) + """ + items_components = [] + if entity.entity_type.lower() == "assetversionlist": + + for item in entity["items"]: + print("item in assetversionlist", item) + components = dict() + + if item.entity_type.lower() == "assetversion": + for component in item["components"]: + if component["file_type"][1:] not in self.allowed_types: + continue + try: + components[item["asset"]["parent"]["name"]].append(component) + except KeyError: + components[item["asset"]["parent"]["name"]] = [component] + + items_components.append(components) + + return items_components + + + def interface(self, session, entities, event): + if event['data'].get('values', {}): + return + + user = session.query( + "User where username is '{0}'".format( + os.environ["FTRACK_API_USER"] + ) + ).one() + job = session.create( + "Job", + { + "user": user, + "status": "running", + "data": json.dumps({ + "description": "RV: Collecting components." + }) + } + ) + # Commit to feedback to user. + session.commit() + items = [] + + try: + items = self.get_interface_items(session, entities) + except Exception: + self.log.error(traceback.format_exc()) + job["status"] = "failed" + else: + job["status"] = "done" + + job["status"] = "done" + + # Commit to end job. + session.commit() + + return {"items": items} + + + + def get_interface_items(self, session, entities): + + all_item_for_ui = [] + for entity in entities: + print("ENTITY", entity) + + item_components = self.get_components_from_list_entity(entity) + for components in item_components: + print("Working on", components) + # Sort by version + for parent_name, entities in components.items(): + version_mapping = {} + for entity in entities: + try: + version_mapping[entity["version"]["version"]].append( + entity + ) + except KeyError: + version_mapping[entity["version"]["version"]] = [entity] + + # Sort same versions by date. + for version, entities in version_mapping.items(): + version_mapping[version] = sorted( + entities, key=lambda x: x["version"]["date"], reverse=True + ) + + components[parent_name] = [] + for version in reversed(sorted(version_mapping.keys())): + components[parent_name].extend(version_mapping[version]) + + # Items to present to user. + + label = "{} - v{} - {}" + loadables = ["exr"] + for parent_name, entities in components.items(): + data = [] + for entity in entities: + entity_filetype = entity["file_type"][1:] + if entity_filetype in loadables: + data.append( + { + "label": label.format( + entity["version"]["asset"]["name"], + str(entity["version"]["version"]).zfill(3), + entity["file_type"][1:] + ), + "value": entity["id"] + } + ) + + all_item_for_ui.append( + { + "label": parent_name, + "type": "enumerator", + "name": parent_name, + "data": data, + "value": data[0]["value"] + } + ) + return all_item_for_ui + + + def launch(self, session, entities, event): + """Callback method for RV action.""" + # Launching application + if "values" not in event["data"]: + return + + user = session.query( + "User where username is '{0}'".format( + os.environ["FTRACK_API_USER"] + ) + ).one() + job = session.create( + "Job", + { + "user": user, + "status": "running", + "data": json.dumps({ + "description": "RV: Collecting file paths." + }) + } + ) + # Commit to feedback to user. + session.commit() + + component_representation = [] + + try: + component_representation = self.get_representations(session, event, entities) + except Exception: + self.log.error(traceback.format_exc()) + job["status"] = "failed" + else: + job["status"] = "done" + + # Commit to end job. + session.commit() + + # launch app here + avalon_project_apps = event["data"].get("avalon_project_apps", None) + avalon_project_doc = event["data"].get("avalon_project_doc", None) + + if avalon_project_apps is None: + if avalon_project_doc is None: + ft_project = self.get_project_from_entity(entities[0]) + project_name = ft_project["full_name"] + avalon_project_doc = get_project(project_name) or False + event["data"]["avalon_project_doc"] = avalon_project_doc + + if not avalon_project_doc: + return False + + project_apps_config = avalon_project_doc["config"].get("apps", []) + avalon_project_apps = [ + app["name"] for app in project_apps_config + ] or False + event["data"]["avalon_project_apps"] = avalon_project_apps + + # set app + for a in avalon_project_apps: + if "openrv" in a: + self.rv_app = a + + # checks for what are we loading + task_name = "prepDaily" + asset_name = "DaliesPrep" + + self.application_manager.launch( + self.rv_app, + project_name=project_name, + asset_name=asset_name, + task_name=task_name, + extra=component_representation + ) + return True + + + def get_representations(self, session, event, entities): + """Get representations from selected components.""" + + ft_project = self.get_project_from_entity(entities[0]) + project_name = ft_project["full_name"] + + dbcon = AvalonMongoDB() + dbcon.Session["AVALON_PROJECT"] = project_name + + representations = [] + + for parent_name in sorted(event["data"]["values"].keys()): + componenet_check = event["data"]["values"][parent_name] + if type(componenet_check) is list: + component_data = event["data"]["values"][parent_name][0] + else: + component_data = event["data"]["values"][parent_name] + + component = session.get( + "Component", component_data + ) + subset_name = component["version"]["asset"]["name"] + version_name = component["version"]["version"] + representation_name = component["file_type"][1:] + + asset_doc = get_asset_by_name( + project_name, parent_name, fields=["_id"] + ) + subset_doc = get_subset_by_name( + project_name, + subset_name=subset_name, + asset_id=asset_doc["_id"] + ) + version_doc = get_version_by_name( + project_name, + version=version_name, + subset_id=subset_doc["_id"] + ) + repre_doc = get_representation_by_name( + project_name, + version_id=version_doc["_id"], + representation_name=representation_name + ) + if not repre_doc: + repre_doc = get_representation_by_name( + project_name, + version_id=version_doc["_id"], + representation_name="preview" + ) + representations.append(str(repre_doc["_id"])) + + return representations + + +def register(session): + """Register hooks.""" + RVActionReview(session).register() diff --git a/openpype/modules/ftrack/event_handlers_user/action_rv.py b/openpype/modules/ftrack/event_handlers_user/action_rv.py index 39cf33d6056..cf36b3221d1 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_rv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_rv.py @@ -1,5 +1,7 @@ +import getpass import os import subprocess +import sys import traceback import json @@ -19,10 +21,10 @@ from openpype_modules.ftrack.lib import BaseAction, statics_icon -class RVAction(BaseAction): +class RVActionView(BaseAction): """ Launch RV action """ - identifier = "rv.launch.action" - label = "rv" + identifier = "openrv.launch.action" + label = "Open with RV" description = "rv Launcher" icon = statics_icon("ftrack", "action_icons", "RV.png") @@ -36,15 +38,20 @@ def __init__(self, *args, **kwargs): # QUESTION load RV application data from AppplicationManager? rv_path = None + rv_path = "PATH_TO_BIN/bin/rv.exe" + self.rv_home = "PATH_TO+RV_HOME" + os.environ["RV_HOME"] = os.path.normpath(self.rv_home) + sys.path.append(os.path.join(self.rv_home, "lib")) + # RV_HOME should be set if properly installed - if os.environ.get('RV_HOME'): - rv_path = os.path.join( - os.environ.get('RV_HOME'), - 'bin', - 'rv' - ) - if not os.path.exists(rv_path): - rv_path = None + # if os.environ.get('RV_HOME'): + # rv_path = os.path.join( + # os.environ.get('RV_HOME'), + # 'bin', + # 'rv' + # ) + # if not os.path.exists(rv_path): + # rv_path = None if not rv_path: self.log.info("RV path was not found.") @@ -54,7 +61,16 @@ def __init__(self, *args, **kwargs): def discover(self, session, entities, event): """Return available actions based on *event*. """ - return True + data = event['data'] + selection = data.get('selection', []) + print(selection[0]["entityType"]) + if selection[0]["entityType"] != "list": + return {'items': [{ + 'label': self.label, + 'description': self.description, + 'actionIdentifier': self.identifier + }] + } def preregister(self): if self.rv_path is None: @@ -66,7 +82,7 @@ def preregister(self): def get_components_from_entity(self, session, entity, components): """Get components from various entity types. - The components dictionary is modified in place, so nothing is returned. + The components dictionary is modifid in place, so nothing is returned. Args: entity (Ftrack entity) @@ -248,10 +264,17 @@ def launch(self, session, entities, event): args.extend(["-fps", str(fps)]) args.extend(paths) - + # CORE EDIT SET UP THE PATHS + self.log.info("setting up env vars") + os.environ["RV_HOME"] = os.path.normpath(self.rv_home) + sys.path.append(os.path.join(self.rv_home, "lib")) + sys.path.append(self.rv_home) self.log.info("Running rv: {}".format(args)) - - subprocess.Popen(args) + self.home = os.path.normpath(os.path.join("c:/", "Users", getpass.getuser())) + os.environ["HOME"] = self.home + env = os.environ.copy() + env['PYTHONPATH'] = '' + subprocess.Popen(args, env=env) return True @@ -328,4 +351,4 @@ def get_file_paths(self, session, event): def register(session): """Register hooks.""" - RVAction(session).register() + RVActionView(session).register() diff --git a/openpype/resources/app_icons/openrv.png b/openpype/resources/app_icons/openrv.png new file mode 100644 index 0000000000000000000000000000000000000000..741e7a9772bc76815556d92323542be25f069d63 GIT binary patch literal 10126 zcmeHtS6CBI`0i{HlF%d4i$p=1^d_Cqq&JZ!MWpv8O;89(5u_=C2!bFey$VPtAc&we z1qGx8kt$tKIw!yXxji@M=3Je9_IYM@cBky@H{X2wzHhvdfhIKtD+K_6T3bu~IshEy0ypxcwv0xkR>}5Cjt|tgtl&?3D?M~lo8e! zpr1ETVFIuY4YGwBs5W)7234|GvT!{{C{`H8iU2N`Am@wFB_!~<4Et@4ju(N2^1~#k zAv0EJ`Z_9z59YxQTQNhwxC;Mfh$@sr2vb517oju>_=rAIgB~K8*aKnMGbwnBI@x6u zaJvL?B7q4DRH1|zH9$71lKn77?OCDA6cM%;plW5rvMISL9fW{DmnK=NIBfPhs#p;r zLkq20pojF3+6>TJ9HL2$Oc4W-Z1{~l;*ltf5e^3RkmH7^UM=L2EyboKdd>v(SqC|P z1C=EW-?@oC2Ln_Pz+ZvizXI#iMzSNoOIi4V4aK1?1quRxttgCGAP4}`hu*iQxXlUq zUH)%v;s)MB5-GJ)`CK5Uq=LPPIbPk1<#3Z6k+iYs2*$h@b{n3iLF|Fuel~U+b zZno^Jdfee%Z}LMa!QM_*Y@FbBWt6=xdMEqM-UZ_dMAuXLTuGOr#>FSkT6n)HU?TOH zgM2tG^EMCF*Wo?fcYc1PCH{rc4Ie$E+CEsLV1!OW_K`ri-q{fVEYMb0xe+qCo)dhJ zp@*?|+tRo|az!7JF3@(AN|&{3M5iiHn2J$5RJcK3gHO;pA3dy2uJhQIE&;0!Lu(nL z`4OVrIJzge9rCIB&^bE)&6%8u=Q$H?OaESmH{Nj`I&dCZdn&Pj`S`|^#u@-T0GR)L z1P%Z+01&BvfcU>Cd?++a{3~p*-rq4^-CZgyIT2f>GwPYIQSY9)HHnJlRlUQ6w^@wblF3*q{Eto6Rx}eJVpgz z0Hd!WOyhN*yW-c^Z=ILX0JA94ahEzcLfhk@<-6+)91trz3*=1GLP~BH89#tPa>SwV zUY2(2Sb4540uWSe4X8`C?Tc^1c|`!uWa7IU9bbGQfhTYc0u`r!t^U%ccbmALW!M66 zXF_WIf^dB4)`tE%02!r{!66QCA3Iuf(WDkT zxjeKFuU9<&3xH7l1rELy!^i?FIH3Rat*)rhe9v=|x>qb4Den2 zvycNfm%CooIe-tO&L(oPm@1u*g}_b%^MU}2Wo5x709d5Fcvx%(++#_vaQIVrB5LXg z0Oz8UW}Svtm7RbvzdG{KvX(>k>M8`DUSFqq;#H#N1&EK=!gL#oX8r<#GKHJ0ajk~F zCkqTrW&e#ZsWoj|fk5NkL#>7@oQ7A~P;zIo{nMi3;T`kB*2QJxJ(n3qL| zuqZCwmH?yCBQIr5%iP$2Iu+i+Y8a1Z1B4m@#N?||Io1n+$sGMQ(b|TJ2Jga$ur$lh zy@tdtdp)c+ZR2pX0PH8&R*GLsu14Ty=dYJtZk=Yah&*qZXtVsT->kIw)Ip3nRUnNR zGdOpGEw)%?y3Ge->tj;wS0`>GVR=6=XugKn00}BE)Ji$WJ=`+HjKEKBs))w#+Ip!0 z(J7RwHs8xRB*=?WMm(`NWeA|f$v=E0Q2$jH+{UP;Q+$;In20@rDhI%W+vjtQ@`mvH zzwvl~OvDc!JO>cL5qEJoS?05ezwf{ma7}f%*u8KNEK^=AC~t3u5y*ogzM2)MC4*vd zBTnITWt~(pU z6B{a_8UU`TrDnR<5y8_qjF{Qod?=VkIu82CZxHn~p?6(cqnjneDj1ddjryeYLh7?H!bAvIK!3xpD!w(Q-tXuUuP zrL|D;%ohZZmm%ja{V?eU;dy0lTHJLfIpI+l#{4{d91i|L3FV@TfIvyGtHS$l!K&BI zak3%M_}?pe?Dt^ff8)RfaL$PKMu1eJ9XVh}GTy2ZM-bkz?3;&-?Kq*p(+49DY#mTI z)2&LH8|f@St+T0l*vJe7pk`N9r&*va&SG>aZOmyLHP|E6%NZ;300d5judP@YP=doK zhroh%L9hkySiKfb1zgFz#~$AtK>>pYE82S{a8V_r$cf{!j$Q-HR?(FRHvfXk+=H~q z`Cn%_e*AMgr}O-rxk_ypuXuczt~fHNN@&gs&o2FXdh_u1uY{I~BLkP*6S4Krvlbm~ zCu);(nQm;gE?zR(MvfTrL*$kbJVzxM9M9ts3pCXp* z!8(#M^X^YggU|Ylw?h77G0`16f@cCg?l(u1%jItkh#b71=%x82?z?bb`+u_z3CfKx^A^0BuEKh%Y(Bg>)TFrR zWs|p2x}A<;6tNh6V$WcRAQ+%0!I3 za2jtSR^=of?u1!d8S7D1NND2Ua_EC{cJ$dOcd~bGaxYs)L{IsZ-cM9%KGkAR9g`1( zU17txZ*jf)sw>|}@mDQNkmDzZ2AbMhdbh<+RwD#KH;B%zPe0ABSxMjTQe%HDXPWMh z$)V3iJLe=!AlqG#T7~wVzv5CL%IO_U<9YY!8zBl{G1z!s`DyjuWnDG4ekChcQ6Vtd zcev%D0*MY-xl5gH_az=}&2d?oXSVaxrfyL16-mQ?4V-5HUuV+40gDgKl%I6VrK{`J zv*7eVbmrDz9666){r5?EqWR)mx)jfz>Uw(fzbiA$hA6yTty1DSX7IH_Z3lST&pgni z%(t3g?dLhBF#@>rw&(fjcYANua*D3WV@`&d=T~+XoO7$l89>y%#^eH1_k^jZ!?bJb zx58aLYhrJ`pryZ{4#x}Z_>BCqhucl|da!PBu1C18$yuo>XNM64B*Aj25747@{2R%P z#t$rBsJ=o5R9SUG4WOP|AHNI!J$aJBPXtzK9a&+7*o&*svcrPzYUMJ>na*npuQ&3AjVcP7HWTo|AT1Wli|~bcMQFt?a6#FRXctcxf}} zt-qHj?B5!?EM7=@^hmy$YtOs$UV6CBvk9i! z^%2T7XHUJs=R?vM@$G|{T?w~OKQnR?+AqzQF29d2`djF7Pc`8B8ewGj#bo2trMXF~4o_RoQquN6003Sr>ZSp7u_^h_|N(8jM; z|GPrlGIEqv^O|V6cFOPavilM@_tTA<_n*LrZSSvh9MeW`I?lSee4~SjlD(Zj;_q<$ z+z#`V7-;f9Ty!<00Wlu0{62lUy_Iev;t!_yfiHTK#r#U~iFrUG0`GCA@3V}80xia84g6fhl=LI?}jIM%mugp6?v>d>}W3p zR2i=5O`|na4KHtA88^1L={r;XO!9uz_Y%jBZN8wXc*qFxBzNKw(??O(LAR#b*Ryd~ zVFSzY*4gdcx1gtO%8#!R;_2~B#XvNsk8{NN{2!{Mh>#E#+dm;kZdd6viDcJ-z;y_C z95^KZbkp<%2J^v+aqCztrAMe_jtq=J8!FZy1LgV!*$h|1R3BU=Fns>1@Tba7Dn&}8 zHdX;vkA~1s>QKbwpU!vuAnd`znK(BGT7!A3>MR43b5U4v&_4oUGpK=!FVu(|4xSQv z(BjA1D`yg@s*k>z1{p=tfZz#$wdtlBbV1zjh-%_{C}k=Ucfw7Cn_}Sv6;I&H8(owZ z5YDxzD*a)M*#f2Ee9PoK3?&X$A5nwFTB6*mK@mcspw8W|@mYGX>F+-rLE?Ef@R`dk zJ6`T(+;dlFejnD~2{G)Z7nwDY__A?eSBWZovUlN*@G|W}800&AIG<38X{3WhZRtq$ zItDoVLObXrP<*!4omq~DW=>Qe(!elja=wVgxkMKf|Hy2CiKk1m$A5K?>Sx})2oX7I zAi@iDmfqz3&o$1GUxH?PCoUIBBR5llsE#KPrRCnhu7_XB&1s{Um9)4_@vkJQlaCJA z(zD_@#qRu}Eu8L%ZQ^Nt*-5GAg-o^OhKP3`Lyd|i1ypz&l;K>d&|rQlywqV!fgH@M zBe9f!$f^@t&os9Mg$mDXjdP%1JTO7HXa)-OKCO=rnTE3=|Q;pACRfJ@9!qBaUYr5MGm%}gn-n-LX9<#elro{KcH-RNY=rI z^KFVMrgaNq{!E6?{`Mm~A)+`ay%aT`?6Q^-6_A!k8tV^c&vW>EYanWQWF^$!t?VBz zY&d;huT!Fk7(H(7C4uLr?m-bRD}j$9Vfuy_i<@$epZD_;1mnty?EX(au;4<;Iq~KP z0K<{C&PAldrowMc#da+d!+VKsq*b%v@uG0Cm$Nje|gNKen zx(lIY`}ehTZ83uBVUgt`NDD-svLSF?`Tipy_clCt;?qvW+|>0{{Uzvd9zJY#2CS{G z{bmuR>_5FUJ1I9Hfgfe$1j=o2qsB|NuXtKl;!+%c9O@lWVGriuqUy7N{xnP2n0}G0 z$;9oxiBUiJg1HA;_oxU-YgF;2qO=L0%o30FNsr&1hl?6_fESONc#O@IZ1uD1#q}$e zQlY~5eIWCj9zQk_#*OT1IX-Fryyn0jM}?K0hhr)=f!VB07O&X3#TaGrs*NgHNAR5E zA|sRioHB>(}Oijardy+^)7-Z=!($R}Cm;L6i7)Pccj-P|Z+0%@= zrB54UR(`tOGg<_IS-ys1aq*WJ0{ls^ew6-ROh9e=GYV`;`h1@VaYn3lttPxq@Jkb& z9Or|;z62hXayo#-&xg!%F@EroS%?&Y1NF(Hy^ zTRD$6pPV>0P=DcYO;Q410ojQU482YUGk5D=nl3&5bPw7ZIr86PV&a42gSCe5^)y-- zun)m0#8nWS|R|Q`nLXu)Sv2}C-_Ool1e#L!ao3_nWVt9H+tN3`i@`6MxUKeuCXpdFL?tP$qB-Q623&GbU~ov$=C>}Rno-6ms+#_RCxei3-;D4MmO z7Qf(kIcWd%dH8|rugWv@6KL5M_2tai?^ookl$2HIM#two4&uutlF*Nm^FOz zyE#T(W+aF)IY_diO^Ktr<-@;!npwPYG`|ROUcg>{=F4mS!eLdea?D= z(#TiC-B_>_i_g`4Dvwm2ZA@uZ5P9;*}1T=guycaw6+5Y`iK>|#)MOu5)C{~pZuv>$M>${8M z?C&`h_eaQcS@3I%!?zWHQRp_$V*O)*s#_uz9cg}oJ{@P->(&x@NxiG@;z;4}9~#s| zt<88>^V`Lj34Sh3&Y6+Xu`yBuN!qdqKVWcd#XK5( z6S~ehB~SDVlm1S%CCvh8F2V6()MTb{r-=@775gY%Qd51ARCtO@@alwFtkIJoVrIL! zm~gfSGF2cI8oucWM3oWmt_QjzlUKqR=ARnccspPsN~BnT3OkT_Q#aPpRDqhmKQ{Yv zI@#3+CyGie;Fxw6oc|><{NM-TKfimX9~XO6Iq+&SAJPt-!E>QbGq6>0 z?Y+V`=}D9aZx}VpE(=51n-eh z(Y6@KE!y~&6xs)_hxvnhEyWGVcW1L!GQRX+S`}4zOOApfZN+g+xBS5{c5JaGc#~f! zr2$hl!9E#2(4xhG=i>oXssdoSeDavp?8jBHSwV?n`M+$F+Z|Fl&@sw7oQ8-)*liO{&{C;%!$!aY;beyS0WC#~t%ueRosNURKSI4TxrcPf#9f z{5z?w=!nl+1!%KInTFwR3lvX!L#E2>vGb)H^p=$msIX*Za8Pe7DHcB6ZGBZj=edGf z(b#!+o`=b+Sa_X^0K}X}&}v3*Uw!^$=SI1h<*1SrWlmVn5P*-uj5H!2|KZVp+V`_c zMAjyeoge_eVN8j`cYG_j{pl9!a@onk7kASE`rEIzSP05EAy8a27Blis3YIr|1{7KwjmJ z^sD+u5q(iFS9Wje8_msG*GS;Z)gf?&Gs;B@LHt(mL-G$rNqy*zn7C)38Rg(N@*!O6 zvEollR+ydKpN|_n?n&25{kF{nY-B$3oX)980-U~M(!Gky;PSP|no(BMdIy=D582Dk z=h#3NRsf7XmJ+wr1qNc7Xd_va+PqHTjcY7O^J$>&vqY?v@^-O$QOB^3{(=D2Yz$L>1;+yXBX^ZN)!X<*TCc$A zV2~t~s(4Y7-;xdV3r}{JaYoSFzMA)`%~C=AeBK5^FP4IjjLD}qY5Nhko$K0f>|NKB z7K%#~q^xZ37CgN!+YIuSekE3nU{7P5mA?lUd+xE4Kp9CHYg_ql8YR)X12e`}`-mWlYD0g8# z!-Y+Pyu%$vkxfUI)8FD&&f+w5(#==X|FVBRyv4xw@}+;w?c>*n59L5bd8S~0eW>dG z;hUGwBC>Ltxp+4APt#?O{%Q6s82kthJ~(*(4f&_WWTD8u<5GOY8VfQx<+N@zkuWnR zpZ4U5v+yyohT;7k`ATiQu>?}gK9n`mdQ!7!5I?B=?qDO<)YxrB9OdV8mdcpU)>L6v z^ZfPLqbubp>HjNvt42#pvZrk-PcAkRZVA5#EyU08Ulg;13RDN z0|dHn0WZ zX(9N17zVPt%E&HM{2$N`(yzQ$5!_|`g1}!;BaEs34<4VYC`j|OC^+>Rzy|v^;QeVw zsPIZeFK7(qhQ!z3vB%Psrbr_43TS1lMGpc->A(%Rf^eogRv$v|AfHjb=6z5{2HFrx zOgbVLlOWs!!V1g{^MxJX=|EWTa{X+#4&j6dn_b0ZKLJRD$2<{{kL16LakXxW$eF{|j7jbJ*?rE|^%gC65kTTt?xq#qzdn1&= zj|_`&jy`ZVLf-;LPWyDkLoy>4KP2e?;G2H_Ck^nVoi9Gr0wA_i`=TwYydDA=-6_WQ zYvvC;#sDnpAsLe_Rt5NmF-CQ&C{C07Wngp`H0fcl$xQ`{DZ16}P;0^o2jVAp^ENNK)yWoe&P-=22@brmu<&Kss7l zx&-%}qn81;iUz~uyh@CTMv=y+7*)ROs96XH!zR8r@FapuwV9-IiCF=RJ1hwSFi;93 zD_r)_V<=<;spr?|izAB0qm}4mUg9X0GtSJyx0x) zb@sY&FxdWy0KFp@{-3)HL0p@1p=yxOmw zDtrJfV5RYRP`JaAOa(yc$%W`ATAO3nNKC3C9Cb}=cSGVM7UX&R&MCCV2B?vES85MR zsAu%;`_xE)Bk_ubBwjI+iVRS!P^t77Enyr*0j89V#?Q2PE6-^vi6qV`{!a_goTZ_m z28_zgv2_W%I#(REKxzRe!aVYr{jmy!Q}O+p7ud!TO+^V*bE|Jz+vev^BGo-WD&-5d zw6roxaT)NjMj(2EM2cQ|5653Z;_`%Cafn)o)R!f-z1OudcYl&KfYjbQm<{pJLHGm} z__(2g-ghW-_ALljpw9OMUyGTswC^k9neJO_Tw z(R7Mrw4i#KeaS#m4B6zpefayS)BzaYD9lPkOu15V7HlCw)7{CP8i>RSJI(|86oluy z#%#w)5h!aalXxinC)g9p#Rq+?Tnm0!XiMX`B&#y+K9JrE}}to@k8SeGm@)v z0;+;>kB~t1Jr8rR`>;!w=*X1N1%OUsw)^}W`GJc0V(wcF1i08c;=?R_?2{NoKQd+y z!>7h|VnQ71;xdyN8SwUqP&VFki7P_x`(#Lv?+s59W8Y;N*SF?-PGgYXdHIO5G^BQC?ZxH}6{vLn~LFwiS@b|KEgnq}?q< zRJ1l@=FPF@ q-cF!~{?Edf{|o&KNj{T$dpAP2i}K^K6G|J94s8ts^-5K{sQ(4I#c_)O literal 0 HcmV?d00001 diff --git a/openpype/settings/defaults/project_settings/openrv.json b/openpype/settings/defaults/project_settings/openrv.json new file mode 100644 index 00000000000..29b02a1cc3a --- /dev/null +++ b/openpype/settings/defaults/project_settings/openrv.json @@ -0,0 +1,39 @@ +{ + "imageio": { + "workfile": { + "OCIO_config": "aces_1.2", + "customOCIOConfigPath": { + "windows": [ + "PATHTOOCIO\\ocio\\1.2\\aces_1.2\\config.ocio" + ], + "darwin": [], + "linux": [] + } + } + }, + "create": { + "CreateDebugLogs": { + "enabled": true, + "defaults": [] + } + }, + "publish": { + "ValidateWorkfilePaths": { + "enabled": true, + "optional": true, + "node_types": [], + "prohibited_vars": [] + }, + "ValidateContainers": { + "enabled": true, + "optional": true, + "active": true + } + }, + "workfile_build": { + "profiles": [] + }, + "templated_workfile_build": { + "profiles": [] + } +} \ No newline at end of file diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index eb3a88ce665..ede5f2762dc 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -1494,5 +1494,38 @@ } } }, + "openrv": { + "enabled": true, + "label": "OpenRV", + "icon": "{}/app_icons/openrv.png", + "host_name": "openrv", + "environment": { + "HOME": "C:/_pipeline/apps/openrv/home", + "RV_SUPPORT_PATH": "PTH_TO_SUPPORT_FOLDER", + "PYTHONPATH": "", + "RV_PREFS_OVERRIDE_PATH": "PREFS_OVERRIDE_PATH" + }, + "variants": { + "1-0": { + "use_python_2": false, + "executables": { + "windows": [ + "PATH_TO_OPEN_RV_BIN/bin/rv.exe" + ], + "darwin": [], + "linux": [] + }, + "arguments": { + "windows": [], + "darwin": [], + "linux": [] + }, + "environment": {} + }, + "__dynamic_keys_labels__": { + "1-0": "1.0" + } + } + }, "additional_apps": {} -} +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json new file mode 100644 index 00000000000..f412cea1faa --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json @@ -0,0 +1,66 @@ +{ + "type": "dict", + "collapsible": true, + "key": "openrv", + "label": "OpenRV", + "is_file": true, + "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "collapsible": true, + "children": [ + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "OCIO_config", + "label": "OpenColorIO Config", + "enum_items": [ + { + "srgb": "srgb" + }, + { + "aces_1.2": "aces_1.2" + } + ] + }, + { + "type": "path", + "key": "customOCIOConfigPath", + "label": "Custom OCIO config path", + "multiplatform": true, + "multipath": true + } + ] + } + ] + } + ] + }, + { + "type": "schema", + "name": "schema_openrv_create" + }, + { + "type": "schema", + "name": "schema_openrv_publish" + }, + { + "type": "schema", + "name": "schema_workfile_build" + }, + { + "type": "schema", + "name": "schema_templated_workfile_build" + } + ] +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json new file mode 100644 index 00000000000..2b4b2cdcf04 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json @@ -0,0 +1,18 @@ +{ + "type": "dict", + "collapsible": true, + "key": "create", + "label": "Creator plugins", + "children": [ + { + "type": "schema_template", + "name": "template_create_plugin", + "template_data": [ + { + "key": "CreateDebugLogs", + "label": "Create Asset debug" + } + ] + } + ] +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json new file mode 100644 index 00000000000..aa6eaf51648 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json @@ -0,0 +1,50 @@ +{ + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateWorkfilePaths", + "label": "Validate Workfile Paths", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "key": "node_types", + "label": "Node types", + "type": "list", + "object_type": "text" + }, + { + "key": "prohibited_vars", + "label": "Prohibited variables", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateContainers", + "label": "ValidateContainers" + } + ] + } + ] +} \ No newline at end of file From 4ec6643eef0e2a4e899113544f7dc3b26399d6b1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:02:07 +0200 Subject: [PATCH 002/109] Code cosmetics --- openpype/hosts/openrv/addon.py | 6 ++++- openpype/hosts/openrv/api/commands.py | 2 -- openpype/hosts/openrv/api/lib.py | 1 - openpype/hosts/openrv/api/pipeline.py | 15 ++++++------ openpype/hosts/openrv/api/review.py | 20 ++++++++-------- .../hosts/openrv/hooks/pre_config_setup.py | 1 - openpype/hosts/openrv/hooks/pre_debughooks.py | 2 -- openpype/hosts/openrv/hooks/pre_ftrackdata.py | 1 - .../openrv/plugins/create/create_debug.py | 6 +---- .../hosts/openrv/plugins/load/load_frames.py | 9 +++----- .../hosts/openrv/plugins/load/load_mov.py | 10 ++------ .../plugins/publish/collect_annotations.py | 13 ++--------- .../publish/collect_loaded_containers.py | 6 ++--- .../plugins/publish/collect_workfile.py | 4 ++-- .../plugins/publish/extract_annotations.py | 23 ++++++++++--------- 15 files changed, 47 insertions(+), 72 deletions(-) diff --git a/openpype/hosts/openrv/addon.py b/openpype/hosts/openrv/addon.py index eb2b449004b..13241fa51f3 100644 --- a/openpype/hosts/openrv/addon.py +++ b/openpype/hosts/openrv/addon.py @@ -15,7 +15,11 @@ def initialize(self, module_settings): def add_implementation_envs(self, env, app): """Modify environments to contain all required for implementation.""" - sys.path.insert(0, os.path.join(os.getenv("OPENPYPE_ROOT"), "vendor", "python")) + vendor_python = os.path.join(os.getenv("OPENPYPE_ROOT"), + "vendor", + "python") + sys.path.insert(0, vendor_python) + # Set default environments if are not set via settings defaults = { "OPENPYPE_LOG_NO_COLORS": "True" diff --git a/openpype/hosts/openrv/api/commands.py b/openpype/hosts/openrv/api/commands.py index 8ed87c6d731..528bae76801 100644 --- a/openpype/hosts/openrv/api/commands.py +++ b/openpype/hosts/openrv/api/commands.py @@ -1,5 +1,4 @@ import logging -import os import rv from openpype.pipeline.context_tools import get_current_project_asset @@ -37,7 +36,6 @@ def set_session_fps(): rv.commands.setFPS(fps) - def create_support_ticket(): import webbrowser webbrowser.open("http://localhost:5400/tickets/create_tickets") diff --git a/openpype/hosts/openrv/api/lib.py b/openpype/hosts/openrv/api/lib.py index 15e4797f5e4..02ff25b9bcf 100644 --- a/openpype/hosts/openrv/api/lib.py +++ b/openpype/hosts/openrv/api/lib.py @@ -21,4 +21,3 @@ def group_member_of_type(group_node, member_type): for node in rv.commands.nodesInGroup(group_node): if rv.commands.nodeType(node) == member_type: return node - diff --git a/openpype/hosts/openrv/api/pipeline.py b/openpype/hosts/openrv/api/pipeline.py index 45c6090a707..c02379f358a 100644 --- a/openpype/hosts/openrv/api/pipeline.py +++ b/openpype/hosts/openrv/api/pipeline.py @@ -55,18 +55,17 @@ def work_root(self, session): return work_dir def get_current_workfile(self): - print("filename", rv.commands.sessionFileName()) return rv.commands.sessionFileName() def workfile_has_unsaved_changes(self): - # dont ask to save if we are on the startup scene without a name ¬ set to untitled project and return False + # dont ask to save if we are on the startup scene without a name + # set to untitled project and return False print("filename", rv.commands.sessionFileName()) return False def get_workfile_extensions(self): return [".rv"] - def get_containers(self): """Get containers. """ @@ -95,8 +94,8 @@ def imprint(node, data): """ for attr, value in data.items(): # Create and set the attribute - prop = node + "." + OPENPYPE_ATTR_PREFIX + "." + attr - if (not rv.commands.propertyExists(prop)): + prop = "{}.{}.{}".format(node, OPENPYPE_ATTR_PREFIX, attr) + if not rv.commands.propertyExists(prop): rv.commands.newProperty(prop, rv.commands.StringType, 1) rv.commands.setStringProperty(prop, [str(value)], True) @@ -170,7 +169,6 @@ def gather_containers(): for node in all_nodes: prop = node + "." + OPENPYPE_ATTR_PREFIX + ".schema" if rv.commands.propertyExists(prop): - #print("FOUND PROPERTY ON {}".format(node)) all_files.append(node) return set(all_files) @@ -185,11 +183,12 @@ def get_containers(): @contextlib.contextmanager -def openrv_project_file_lock_and_undo_chunk(openrv_project_file, undo_queue_name="Script CMD"): +def openrv_project_file_lock_and_undo_chunk(openrv_project_file, + undo_queue_name="Script CMD"): """Lock rv session and open an undo chunk during the context""" pass def get_path_workfile(): print("filename", rv.commands.sessionFileName()) - return rv.commands.sessionFileName() \ No newline at end of file + return rv.commands.sessionFileName() diff --git a/openpype/hosts/openrv/api/review.py b/openpype/hosts/openrv/api/review.py index 9b37c7ebd1f..522a2b55a8f 100644 --- a/openpype/hosts/openrv/api/review.py +++ b/openpype/hosts/openrv/api/review.py @@ -1,20 +1,23 @@ -# review code +"""review code""" import os -import sys import rv + def get_path_annotated_frame(frame=None, asset=None, asset_folder=None): """Get path for annotations """ - filename = os.path.normpath("{}/pyblish/exports/annotated_frames/annotate_{}_{}.jpg".format( - str(asset_folder), - str(asset), - str(frame) - ) - ) + # TODO: This should be less hardcoded + filename = os.path.normpath( + "{}/pyblish/exports/annotated_frames/annotate_{}_{}.jpg".format( + str(asset_folder), + str(asset), + str(frame) + ) + ) return filename + def extract_annotated_frame(filepath=None): """Export frame to file """ @@ -42,4 +45,3 @@ def write_review_attribute(node=None, attribute=None, att_value=None): def export_current_view_frame(frame=None, export_path=None): rv.commands.setFrame(int(frame)) rv.commands.exportCurrentFrame(export_path) - diff --git a/openpype/hosts/openrv/hooks/pre_config_setup.py b/openpype/hosts/openrv/hooks/pre_config_setup.py index 45cd69023f9..ff990aa67fb 100644 --- a/openpype/hosts/openrv/hooks/pre_config_setup.py +++ b/openpype/hosts/openrv/hooks/pre_config_setup.py @@ -48,4 +48,3 @@ def execute(self): os.environ["OCIO"] = str(ocio_config_path) self.launch_context.env["OCIO"] = str(ocio_config_path) print(os.environ["OCIO"]) - diff --git a/openpype/hosts/openrv/hooks/pre_debughooks.py b/openpype/hosts/openrv/hooks/pre_debughooks.py index 9e4a1c5cae5..25e1d40fb49 100644 --- a/openpype/hosts/openrv/hooks/pre_debughooks.py +++ b/openpype/hosts/openrv/hooks/pre_debughooks.py @@ -1,5 +1,3 @@ -import os - from openpype.lib import PreLaunchHook diff --git a/openpype/hosts/openrv/hooks/pre_ftrackdata.py b/openpype/hosts/openrv/hooks/pre_ftrackdata.py index f2e7faa1a39..0e637063561 100644 --- a/openpype/hosts/openrv/hooks/pre_ftrackdata.py +++ b/openpype/hosts/openrv/hooks/pre_ftrackdata.py @@ -23,4 +23,3 @@ def execute(self): repr_file.flush() self.launch_context.env["OPENPYPE_LOADER_REPRESENTATIONS"] = str(repr_file.name) - diff --git a/openpype/hosts/openrv/plugins/create/create_debug.py b/openpype/hosts/openrv/plugins/create/create_debug.py index 63d4e929804..87f28a5dc31 100644 --- a/openpype/hosts/openrv/plugins/create/create_debug.py +++ b/openpype/hosts/openrv/plugins/create/create_debug.py @@ -3,6 +3,7 @@ LegacyCreator ) + class CreateDebugLogs(LegacyCreator): """Creates a quick debuger for avalon asset""" @@ -17,7 +18,6 @@ def __init__(self, *args, **kwargs): # Remove the active, we are checking the bypass flag of the nodes self.data.pop("active", None) - def process(self): """Creator main entry point. @@ -37,10 +37,6 @@ def process(self): print(d, asset_data[d]) print(self.name) - - print("-----done ") - - diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index f8c37ce6a74..e0dec1ac7c3 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -8,6 +8,7 @@ import rv + class FramesLoader(load.LoaderPlugin): """Load frames into OpenRV""" @@ -40,8 +41,6 @@ def load(self, context, name=None, namespace=None, data=None): loader=self.__class__.__name__ ) - - def update(self, container, representation): node = container["node"] filepath = get_representation_path(representation) @@ -50,14 +49,12 @@ def update(self, container, representation): reset_frame_range() # change path rv.commands.setSourceMedia(node, [filepath]) + # update name rv.commands.setStringProperty(node + ".media.name", ["newname"], True) rv.commands.setStringProperty(node + ".media.repName", ["repname"], True) rv.commands.setStringProperty(node + ".openpype.representation", [str(representation["_id"])], True) - - def remove(self, container): + # todo: implement remove node = container["node"] - # not implemented return - diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index 3a30e5350dd..03d113e5636 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -7,6 +7,7 @@ import rv + class MovLoader(load.LoaderPlugin): """Load mov into OpenRV""" @@ -31,9 +32,6 @@ def load(self, context, name=None, namespace=None, data=None): reset_frame_range() loaded_node = rv.commands.addSourceVerbose([filepath]) - print("PROPS") - for prop in rv.commands.properties(loaded_node): - print(prop) imprint_container( loaded_node, name=name, @@ -42,8 +40,6 @@ def load(self, context, name=None, namespace=None, data=None): loader=self.__class__.__name__ ) - - def update(self, container, representation): node = container["node"] filepath = get_representation_path(representation) @@ -57,9 +53,7 @@ def update(self, container, representation): rv.commands.setStringProperty(node + ".media.repName", ["repname"], True) rv.commands.setStringProperty(node + ".openpype.representation", [str(representation["_id"])], True) - def remove(self, container): + # todo: implement remove node = container["node"] - # not implemented return - diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py index 4f1b4a51797..a42ea0ca2e9 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/collect_annotations.py @@ -1,17 +1,13 @@ import os -import time import pyblish.api -from openpype.client import get_representation_parents, get_representations +from openpype.client import get_representations from openpype.hosts.openrv.api.pipeline import gather_containers from openpype.pipeline import ( legacy_io ) - - - class CollectSessionAnnotations(pyblish.api.ContextPlugin): """Collect session Annotations """ @@ -22,8 +18,8 @@ class CollectSessionAnnotations(pyblish.api.ContextPlugin): family = "annotation" def process(self, context): - import rv """Inject collection of annotated frames""" + import rv project_name = legacy_io.Session["AVALON_PROJECT"] source_groups = [] @@ -105,11 +101,6 @@ def process(self, context): # # instance.data["representations"].append(annotation_representation) - view_node = rv.commands.viewNode() intent = context.data.get("intent") - print("------- intent", intent) - - print("VIEWNODE:", view_node) - #print(context.data) diff --git a/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py b/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py index 726638e6060..4e5ad9a98af 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py +++ b/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py @@ -6,8 +6,6 @@ ) - - class CollectSessionContainers(pyblish.api.ContextPlugin): """Collect session containers """ @@ -25,7 +23,7 @@ def process(self, context): # create instances for item in item_collection: - print(item) + self.log.debug(item) item_name = item["namespace"] instance = context.create_instance(name=str(item_name)) subset = 'container' + task.capitalize() @@ -44,4 +42,4 @@ def process(self, context): "handleEnd": context.data['handleEnd'], }) - instance.data.update(data) \ No newline at end of file + instance.data.update(data) diff --git a/openpype/hosts/openrv/plugins/publish/collect_workfile.py b/openpype/hosts/openrv/plugins/publish/collect_workfile.py index e58dfd63785..0f5f5e4bf8c 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_workfile.py +++ b/openpype/hosts/openrv/plugins/publish/collect_workfile.py @@ -2,7 +2,7 @@ import pyblish.api from openpype.hosts.openrv.api.pipeline import get_path_workfile from openpype.pipeline import ( - legacy_io, + legacy_io, registered_host ) @@ -54,4 +54,4 @@ def process(self, context): "stagingDir": folder, }] - instance.data.update(data) \ No newline at end of file + instance.data.update(data) diff --git a/openpype/hosts/openrv/plugins/publish/extract_annotations.py b/openpype/hosts/openrv/plugins/publish/extract_annotations.py index f8212ed2fb5..a623e1a56b2 100644 --- a/openpype/hosts/openrv/plugins/publish/extract_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/extract_annotations.py @@ -1,11 +1,12 @@ import os -from pprint import pformat import pyblish.api from openpype.pipeline import publish -from openpype.hosts.openrv.api.review import get_path_annotated_frame, extract_annotated_frame +from openpype.hosts.openrv.api.review import ( + get_path_annotated_frame, + extract_annotated_frame +) -import rv class ExtractOpenRVAnnotatedFrames(publish.Extractor): @@ -16,18 +17,18 @@ class ExtractOpenRVAnnotatedFrames(publish.Extractor): def process(self, instance): - self.log.info("instance.data: `{}`".format( - pformat(instance.data))) - #self.log.info("workfile", str(instance.data['asset_folder'])) asset_folder = instance.data['asset_folder_path'] asset = instance.data['asset'] annotated_frame = instance.data['annotated_frame'] - annotated_frame_path = get_path_annotated_frame(frame=annotated_frame, - asset_folder=asset_folder) - print(annotated_frame_path) - annotated_frame_folder, file = os.path.split(annotated_frame_path) + annotated_frame_path = get_path_annotated_frame( + frame=annotated_frame, + asset=asset, + asset_folder=asset_folder + ) + self.log.info("Annotated frame path: {}".format(annotated_frame_path)) + annotated_frame_folder, file = os.path.split(annotated_frame_path) if not os.path.isdir(annotated_frame_folder): os.makedirs(annotated_frame_folder) # @@ -50,4 +51,4 @@ def process(self, instance): # if "representations" not in instance.data: # instance.data["representations"] = [] # - # instance.data["representations"].append(representation) \ No newline at end of file + # instance.data["representations"].append(representation) From 25669f8e0ddb4d4ef82ff07912b209ab099bace6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:04:52 +0200 Subject: [PATCH 003/109] Code cosmetics + cleanup --- .../event_handlers_user/action_review_openrv.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py index df5f26e2bb6..4c32e910526 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py @@ -2,8 +2,6 @@ import traceback import json -import ftrack_api - from openpype.client import ( get_asset_by_name, get_subset_by_name, @@ -12,9 +10,7 @@ ) from openpype.lib import ApplicationManager from openpype.pipeline import ( - get_representation_path, AvalonMongoDB, - Anatomy, ) from openpype_modules.ftrack.lib import BaseAction, statics_icon @@ -36,7 +32,6 @@ def __init__(self, *args, **kwargs): self.rv_app = "openrv/1-0" self.application_manager = ApplicationManager() - def discover(self, session, entities, event): """Return available actions based on *event*. """ data = event['data'] @@ -50,11 +45,9 @@ def discover(self, session, entities, event): }] } - def preregister(self): return True - def get_components_from_list_entity(self, entity): """Get components from list entity types. @@ -84,7 +77,6 @@ def get_components_from_list_entity(self, entity): return items_components - def interface(self, session, entities, event): if event['data'].get('values', {}): return @@ -123,8 +115,6 @@ def interface(self, session, entities, event): return {"items": items} - - def get_interface_items(self, session, entities): all_item_for_ui = [] @@ -186,7 +176,6 @@ def get_interface_items(self, session, entities): ) return all_item_for_ui - def launch(self, session, entities, event): """Callback method for RV action.""" # Launching application @@ -262,7 +251,6 @@ def launch(self, session, entities, event): ) return True - def get_representations(self, session, event, entities): """Get representations from selected components.""" From 7bae5de32c1b37533831aeb591fb06a0c97716ee Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:06:51 +0200 Subject: [PATCH 004/109] Re-use method from host --- openpype/hosts/openrv/api/pipeline.py | 5 ----- openpype/hosts/openrv/plugins/publish/collect_workfile.py | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/openpype/hosts/openrv/api/pipeline.py b/openpype/hosts/openrv/api/pipeline.py index c02379f358a..afb0f90ec80 100644 --- a/openpype/hosts/openrv/api/pipeline.py +++ b/openpype/hosts/openrv/api/pipeline.py @@ -187,8 +187,3 @@ def openrv_project_file_lock_and_undo_chunk(openrv_project_file, undo_queue_name="Script CMD"): """Lock rv session and open an undo chunk during the context""" pass - - -def get_path_workfile(): - print("filename", rv.commands.sessionFileName()) - return rv.commands.sessionFileName() diff --git a/openpype/hosts/openrv/plugins/publish/collect_workfile.py b/openpype/hosts/openrv/plugins/publish/collect_workfile.py index 0f5f5e4bf8c..3505a877535 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_workfile.py +++ b/openpype/hosts/openrv/plugins/publish/collect_workfile.py @@ -1,6 +1,5 @@ import os import pyblish.api -from openpype.hosts.openrv.api.pipeline import get_path_workfile from openpype.pipeline import ( legacy_io, registered_host @@ -17,7 +16,7 @@ class CollectWorkfile(pyblish.api.ContextPlugin): def process(self, context): """Inject the current working file""" host = registered_host() - current_file = get_path_workfile() + current_file = host.get_current_workfile() if not current_file: self.log.error("No current filepath detected") From ee5944d1518a62c8a9c03b454026cb9d605f05de Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:10:01 +0200 Subject: [PATCH 005/109] Remove unused function which likely came from Fusion or Clarisse integration --- openpype/hosts/openrv/api/pipeline.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/openpype/hosts/openrv/api/pipeline.py b/openpype/hosts/openrv/api/pipeline.py index afb0f90ec80..c042b27a620 100644 --- a/openpype/hosts/openrv/api/pipeline.py +++ b/openpype/hosts/openrv/api/pipeline.py @@ -180,10 +180,3 @@ def get_containers(): for rvnode in all_items: parsed = parse_container(rvnode) yield parsed - - -@contextlib.contextmanager -def openrv_project_file_lock_and_undo_chunk(openrv_project_file, - undo_queue_name="Script CMD"): - """Lock rv session and open an undo chunk during the context""" - pass From a37ddadbdb22d8273402f6082e3c6e9a3bbb929f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:13:14 +0200 Subject: [PATCH 006/109] Cleanup comments --- openpype/hosts/openrv/hooks/pre_debughooks.py | 10 +++++----- .../openrv/plugins/publish/collect_annotations.py | 8 ++++---- .../openrv/plugins/publish/extract_annotations.py | 2 +- .../openrv/startup/pkgs_source/comments/comments.py | 7 +++---- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/openrv/hooks/pre_debughooks.py b/openpype/hosts/openrv/hooks/pre_debughooks.py index 25e1d40fb49..25cede0f37e 100644 --- a/openpype/hosts/openrv/hooks/pre_debughooks.py +++ b/openpype/hosts/openrv/hooks/pre_debughooks.py @@ -20,7 +20,7 @@ def execute(self): print("------- OPENRV PREHOOK DEBUG ") print("RV_SUPPORT_PATH", self.launch_context.env.get("RV_SUPPORT_PATH")) print("HOME", self.launch_context.env.get("HOME")) - #print("PYTHONPATH", self.launch_context.env.get("PYTHONPATH")) + # print("PYTHONPATH", self.launch_context.env.get("PYTHONPATH")) print("RV_PREFS_OVERRIDE_PATH", self.launch_context.env.get("RV_PREFS_OVERRIDE_PATH")) print("----- ---- ---- ---- ---") project_name = self.launch_context.env.get("AVALON_PROJECT") @@ -28,9 +28,9 @@ def execute(self): if not workdir: self.log.warning("BUG: Workdir is not filled.") return - #self.log.info("OpenPype: Setting up config files") - #self.log.info("Workdir {}".format(workdir)) - #self.log.info("Project name {}".format(project_name)) - #print(self.data) + # self.log.info("OpenPype: Setting up config files") + # self.log.info("Workdir {}".format(workdir)) + # self.log.info("Project name {}".format(project_name)) + # print(self.data) # print("self.launch_context", self.launch_context.env) # print("self.launch_context.data", self.launch_context.data) diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py index a42ea0ca2e9..1ea261c7bb7 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/collect_annotations.py @@ -71,15 +71,15 @@ def process(self, context): # "ext": "jpg", # "files": "frames_1001.jpg", # "stagingDir": "X:\\projects\\Sync\\DaliesPrep\\work\\prepDaily", - # #"thumbnail": True, - # #"comment": "NEW COMMENT FROM UI" + # # "thumbnail": True, + # # "comment": "NEW COMMENT FROM UI" # "frameStart": noted_frame, # "frameEnd": noted_frame, # "fps": "25", # } data.update({ - #"subset": source_representation_subset + "_review_{}".format(noted_frame), + # "subset": source_representation_subset + "_review_{}".format(noted_frame), "subset": "annotation_{}".format(str(noted_frame)), "tags": ["review", "ftrackreview"], "asset": source_representation_asset, @@ -88,7 +88,7 @@ def process(self, context): "publish": True, "review": True, "family": "annotation", - #"setMembers": [""], + # "setMembers": [""], "asset_folder_path": str(asset_folder), "annotated_frame": str(noted_frame), "comment": "NEW COMMENT FROM UI {}".format(noted_frame), diff --git a/openpype/hosts/openrv/plugins/publish/extract_annotations.py b/openpype/hosts/openrv/plugins/publish/extract_annotations.py index a623e1a56b2..2a865f71c67 100644 --- a/openpype/hosts/openrv/plugins/publish/extract_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/extract_annotations.py @@ -34,7 +34,7 @@ def process(self, instance): # # # save the frame # - # #extract_annotated_frame(filepath=annotated_frame) + # # extract_annotated_frame(filepath=annotated_frame) # # assert os.path.isfile(annotated_frame) # diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 25b94a450e1..1bce2f7da14 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -26,7 +26,7 @@ def __init__(self): QtWidgets.QSizePolicy.Minimum) self.customDockWidget = QtWidgets.QWidget() - # data + # data self.current_loaded_viewnode = None self.review_main_layout = QtWidgets.QVBoxLayout() self.rev_head_label = QtWidgets.QLabel("Shot Review") @@ -132,7 +132,6 @@ def update_ui_attribs(self): data_prop_representation_id = rv.commands.getStringProperty(prop_representation)[0] print("data_prop_namespace", data_prop_namespace) self.current_loaded_shot.setText(data_prop_namespace) - #print(self.echo_change_update()) self.setup_properties() self.get_comment() @@ -153,7 +152,7 @@ def setup_properties(self): node = self.current_loaded_viewnode print(rv.commands.properties(node)) att_prop = node + ".openpype_review.task_status" - + if not rv.commands.propertyExists(att_prop): status = "In Review" rv.commands.newProperty(att_prop, rv.commands.StringType, 1) @@ -265,7 +264,7 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): def echo_change_update(self): print("CHANGE") - + print(self.current_loaded_viewnode) node = self.current_loaded_viewnode #print(node) From 751005d9231212e48b67a8cd039a7fb798e5e893 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:16:44 +0200 Subject: [PATCH 007/109] Cosmetics --- .../openrv/plugins/publish/collect_annotations.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py index 1ea261c7bb7..f1054535e46 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/collect_annotations.py @@ -34,12 +34,12 @@ def process(self, context): representations = get_representations(project_name, representation_ids=[data_prop_representation_id]) - list_representation = [x for x in representations] - - source_representation_project = list_representation[0]["context"]["project"]["name"] - source_representation_asset = list_representation[0]["context"]["asset"] - source_representation_task = list_representation[0]["context"]["task"]["name"] - source_representation_subset = list_representation[0]["context"]["subset"] + first_repre = next(iter(representations)) # first representation + repre_context = first_repre["context"] + source_representation_project = repre_context["project"]["name"] + source_representation_asset = repre_context["asset"] + source_representation_task = repre_context["task"]["name"] + source_representation_subset = repre_context["subset"] source_group = rv.commands.nodeGroup(container) print("SOURCE GROUP ", source_group) source_groups.append(source_group) From 8250f5e0f2fde5d1c4baaa52b68833fe94b6e708 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:17:48 +0200 Subject: [PATCH 008/109] Fix missing import, not sure if there was a reason it had no top import --- openpype/hosts/openrv/api/lib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/openrv/api/lib.py b/openpype/hosts/openrv/api/lib.py index 02ff25b9bcf..10f4272a341 100644 --- a/openpype/hosts/openrv/api/lib.py +++ b/openpype/hosts/openrv/api/lib.py @@ -18,6 +18,9 @@ def group_member_of_type(group_node, member_type): :param member_type: :return: """ + # todo: move import to top of file? + import rv + for node in rv.commands.nodesInGroup(group_node): if rv.commands.nodeType(node) == member_type: return node From 18be31179f1897aacc2a6650ebca510c37cd2a18 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:18:27 +0200 Subject: [PATCH 009/109] Fix typo --- openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE b/openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE index 7b8e56e20a2..c3cb0e9bbda 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/PACKAGE @@ -7,11 +7,11 @@ openrv: 1.0.0 requires: '' optional: true -modes: +modes: - file: comments load: immediate description: | -

Adds COmments to OpenRV

+

Adds Comments to OpenRV

From 26d6f6025c7df86b95c2d95e21044be182e8ee2d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:19:21 +0200 Subject: [PATCH 010/109] Shush hound --- openpype/hosts/openrv/plugins/create/create_debug.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/openrv/plugins/create/create_debug.py b/openpype/hosts/openrv/plugins/create/create_debug.py index 87f28a5dc31..cd63382852e 100644 --- a/openpype/hosts/openrv/plugins/create/create_debug.py +++ b/openpype/hosts/openrv/plugins/create/create_debug.py @@ -38,5 +38,3 @@ def process(self): print(self.name) print("-----done ") - - From 8d563b0c3c01d588c7d40f8479b8c8e2b798994d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 11:22:25 +0200 Subject: [PATCH 011/109] Remove unused `show_env` --- openpype/hosts/openrv/hooks/pre_config_setup.py | 9 --------- openpype/hosts/openrv/hooks/pre_debughooks.py | 9 --------- 2 files changed, 18 deletions(-) diff --git a/openpype/hosts/openrv/hooks/pre_config_setup.py b/openpype/hosts/openrv/hooks/pre_config_setup.py index ff990aa67fb..06484fca5d2 100644 --- a/openpype/hosts/openrv/hooks/pre_config_setup.py +++ b/openpype/hosts/openrv/hooks/pre_config_setup.py @@ -14,15 +14,6 @@ SET_STARTUP_PROJECT = None -def show_env(launch_env=None): - project_name = launch_env.env.get("AVALON_PROJECT") - workdir = launch_env.env.get("AVALON_WORKDIR") - print("OVERRIDES {}".format(project_name)) - - for k in launch_env.env.keys(): - print(k, launch_env.env[k]) - - class PreConfigSetups(PreLaunchHook): app_groups = ["openrv"] diff --git a/openpype/hosts/openrv/hooks/pre_debughooks.py b/openpype/hosts/openrv/hooks/pre_debughooks.py index 25cede0f37e..89741b025bf 100644 --- a/openpype/hosts/openrv/hooks/pre_debughooks.py +++ b/openpype/hosts/openrv/hooks/pre_debughooks.py @@ -6,15 +6,6 @@ class PreDebugHook(PreLaunchHook): """ app_groups = ["openrv"] - def show_env(launch_env=None): - project_name = launch_env.env.get("AVALON_PROJECT") - workdir = launch_env.env.get("AVALON_WORKDIR") - print("------- project_name {}".format(project_name)) - print("------- LAUNCHDATA", launch_env.data) - - for k in launch_env.env.keys(): - print(k, launch_env.env[k]) - def execute(self): print("----- ---- ---- ---- ---") print("------- OPENRV PREHOOK DEBUG ") From 289fea4b928436420d0497afa2aac53806b1b2d0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 13:18:53 +0200 Subject: [PATCH 012/109] Cleanup code --- openpype/hosts/openrv/hooks/pre_ftrackdata.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/openrv/hooks/pre_ftrackdata.py b/openpype/hosts/openrv/hooks/pre_ftrackdata.py index 0e637063561..4a1e6f0e37e 100644 --- a/openpype/hosts/openrv/hooks/pre_ftrackdata.py +++ b/openpype/hosts/openrv/hooks/pre_ftrackdata.py @@ -5,21 +5,15 @@ class PreFtrackData(PreLaunchHook): - """Pre-hook for openrv/ftrack - """ + """Pre-hook for openrv/ftrack.""" app_groups = ["openrv"] def execute(self): - print("----- ---- ---- ---- ---") - print("------- OPENRV PreFtrackData ") - print("----- ---- ---- ---- ---") - representations = self.data.get("extra", None) if representations: payload = {"representations": representations} - repr_file = tempfile.NamedTemporaryFile(mode="w+", delete=False) - json.dump(payload, repr_file) - repr_file.flush() + with tempfile.NamedTemporaryFile(mode="w+", delete=False) as file: + json.dump(payload, file) - self.launch_context.env["OPENPYPE_LOADER_REPRESENTATIONS"] = str(repr_file.name) + self.launch_context.env["OPENPYPE_LOADER_REPRESENTATIONS"] = str(file.name) # noqa From b376efb82a61323fecc034839418c48a5d3bc00c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 13:19:18 +0200 Subject: [PATCH 013/109] Remove debug files --- openpype/hosts/openrv/hooks/pre_debughooks.py | 27 ------------- .../openrv/plugins/create/create_debug.py | 40 ------------------- 2 files changed, 67 deletions(-) delete mode 100644 openpype/hosts/openrv/hooks/pre_debughooks.py delete mode 100644 openpype/hosts/openrv/plugins/create/create_debug.py diff --git a/openpype/hosts/openrv/hooks/pre_debughooks.py b/openpype/hosts/openrv/hooks/pre_debughooks.py deleted file mode 100644 index 89741b025bf..00000000000 --- a/openpype/hosts/openrv/hooks/pre_debughooks.py +++ /dev/null @@ -1,27 +0,0 @@ -from openpype.lib import PreLaunchHook - - -class PreDebugHook(PreLaunchHook): - """DEBUG hook for openrv - """ - app_groups = ["openrv"] - - def execute(self): - print("----- ---- ---- ---- ---") - print("------- OPENRV PREHOOK DEBUG ") - print("RV_SUPPORT_PATH", self.launch_context.env.get("RV_SUPPORT_PATH")) - print("HOME", self.launch_context.env.get("HOME")) - # print("PYTHONPATH", self.launch_context.env.get("PYTHONPATH")) - print("RV_PREFS_OVERRIDE_PATH", self.launch_context.env.get("RV_PREFS_OVERRIDE_PATH")) - print("----- ---- ---- ---- ---") - project_name = self.launch_context.env.get("AVALON_PROJECT") - workdir = self.launch_context.env.get("AVALON_WORKDIR") - if not workdir: - self.log.warning("BUG: Workdir is not filled.") - return - # self.log.info("OpenPype: Setting up config files") - # self.log.info("Workdir {}".format(workdir)) - # self.log.info("Project name {}".format(project_name)) - # print(self.data) - # print("self.launch_context", self.launch_context.env) - # print("self.launch_context.data", self.launch_context.data) diff --git a/openpype/hosts/openrv/plugins/create/create_debug.py b/openpype/hosts/openrv/plugins/create/create_debug.py deleted file mode 100644 index cd63382852e..00000000000 --- a/openpype/hosts/openrv/plugins/create/create_debug.py +++ /dev/null @@ -1,40 +0,0 @@ -from openpype.pipeline.context_tools import get_current_project_asset -from openpype.pipeline.create import ( - LegacyCreator -) - - -class CreateDebugLogs(LegacyCreator): - """Creates a quick debuger for avalon asset""" - - label = "Show Debug info in the Log" - family = "*" - icon = "gears" - defaults = ["Main"] - - def __init__(self, *args, **kwargs): - super(CreateDebugLogs, self).__init__(*args, **kwargs) - - # Remove the active, we are checking the bypass flag of the nodes - self.data.pop("active", None) - - def process(self): - """Creator main entry point. - - Args: - instance - - """ - asset_doc = get_current_project_asset() - asset_data = asset_doc["data"] - - print("Asset doc") - print(asset_doc) - print(type(asset_doc)) - print("Asset_data") - print(type(asset_data)) - for d in asset_data: - print(d, asset_data[d]) - - print(self.name) - print("-----done ") From d10b2de3189b67458982a7c6aed7b7bf44124057 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 13:51:51 +0200 Subject: [PATCH 014/109] More cleanup --- .../plugins/publish/collect_annotations.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py index f1054535e46..82b8d092f48 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/collect_annotations.py @@ -25,8 +25,10 @@ def process(self, context): source_groups = [] all_nodes = gather_containers() for container in all_nodes: - print("container-------- ", container) - print(rv.commands.properties(container)) + + self.log.debug("Container {} with properties: {}".format( + container, rv.commands.properties(container) + )) prop_namespace = container + ".openpype.namespace" prop_representation = container + ".openpype.representation" data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] @@ -54,16 +56,15 @@ def process(self, context): for marked in marked_frames: print("MARKED ------------ ", container, marked, source_group) - for noted_frame in annotated_frames: print("NOTED ------- ", container, noted_frame, source_group) rv.commands.setFrame(int(noted_frame)) rv.commands.redraw() - print(os.getenv("AVALON_ASSET", None)) instance = context.create_instance(name=str(container)) - item_name = "note_" + str(data_prop_namespace) + "_" + str(noted_frame) - data = {} + item_name = "note_{}_{}".format( + data_prop_namespace, noted_frame + ) # annotation_representation = { # "tags": ["review", "ftrackreview"], @@ -78,7 +79,7 @@ def process(self, context): # "fps": "25", # } - data.update({ + data = { # "subset": source_representation_subset + "_review_{}".format(noted_frame), "subset": "annotation_{}".format(str(noted_frame)), "tags": ["review", "ftrackreview"], @@ -92,7 +93,7 @@ def process(self, context): "asset_folder_path": str(asset_folder), "annotated_frame": str(noted_frame), "comment": "NEW COMMENT FROM UI {}".format(noted_frame), - }) + } instance.data.update(data) # @@ -103,4 +104,3 @@ def process(self, context): view_node = rv.commands.viewNode() intent = context.data.get("intent") - From fed5549d83e9b5e7336354e75b57a03964c28a10 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 13:52:32 +0200 Subject: [PATCH 015/109] Return if there's no current workfile --- openpype/hosts/openrv/plugins/publish/collect_workfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/openrv/plugins/publish/collect_workfile.py b/openpype/hosts/openrv/plugins/publish/collect_workfile.py index 3505a877535..97fb1e08060 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_workfile.py +++ b/openpype/hosts/openrv/plugins/publish/collect_workfile.py @@ -15,10 +15,12 @@ class CollectWorkfile(pyblish.api.ContextPlugin): def process(self, context): """Inject the current working file""" + host = registered_host() current_file = host.get_current_workfile() if not current_file: self.log.error("No current filepath detected") + return folder, file = os.path.split(current_file) filename, ext = os.path.splitext(file) From aa9766cc845278c2c03a2607fbfdad2f3de08d49 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 14:01:08 +0200 Subject: [PATCH 016/109] Cosmetics --- .../action_review_openrv.py | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py index 4c32e910526..2717d7a07e8 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py @@ -1,6 +1,7 @@ import os import traceback import json +from collections import defaultdict from openpype.client import ( get_asset_by_name, @@ -38,12 +39,13 @@ def discover(self, session, entities, event): selection = data.get('selection', []) print(selection[0]["entityType"]) if selection[0]["entityType"] == "list": - return {'items': [{ - 'label': self.label, - 'description': self.description, - 'actionIdentifier': self.identifier - }] - } + return { + 'items': [{ + 'label': self.label, + 'description': self.description, + 'actionIdentifier': self.identifier + }] + } def preregister(self): return True @@ -66,12 +68,15 @@ def get_components_from_list_entity(self, entity): if item.entity_type.lower() == "assetversion": for component in item["components"]: - if component["file_type"][1:] not in self.allowed_types: + if component["file_type"][ + 1:] not in self.allowed_types: continue try: - components[item["asset"]["parent"]["name"]].append(component) + components[item["asset"]["parent"]["name"]].append( + component) except KeyError: - components[item["asset"]["parent"]["name"]] = [component] + components[item["asset"]["parent"]["name"]] = [ + component] items_components.append(components) @@ -126,24 +131,24 @@ def get_interface_items(self, session, entities): print("Working on", components) # Sort by version for parent_name, entities in components.items(): - version_mapping = {} + version_mapping = defaultdict(list) for entity in entities: - try: - version_mapping[entity["version"]["version"]].append( - entity - ) - except KeyError: - version_mapping[entity["version"]["version"]] = [entity] + entity_version = entity["version"]["version"] + version_mapping[entity_version].append(entity) # Sort same versions by date. for version, entities in version_mapping.items(): version_mapping[version] = sorted( - entities, key=lambda x: x["version"]["date"], reverse=True + entities, + key=lambda x: x["version"]["date"], + reverse=True ) components[parent_name] = [] for version in reversed(sorted(version_mapping.keys())): - components[parent_name].extend(version_mapping[version]) + components[parent_name].extend( + version_mapping[version] + ) # Items to present to user. @@ -155,25 +160,26 @@ def get_interface_items(self, session, entities): entity_filetype = entity["file_type"][1:] if entity_filetype in loadables: data.append( - { - "label": label.format( - entity["version"]["asset"]["name"], - str(entity["version"]["version"]).zfill(3), - entity["file_type"][1:] - ), - "value": entity["id"] - } - ) + { + "label": label.format( + entity["version"]["asset"]["name"], + str(entity["version"][ + "version"]).zfill(3), + entity["file_type"][1:] + ), + "value": entity["id"] + } + ) all_item_for_ui.append( - { - "label": parent_name, - "type": "enumerator", - "name": parent_name, - "data": data, - "value": data[0]["value"] - } - ) + { + "label": parent_name, + "type": "enumerator", + "name": parent_name, + "data": data, + "value": data[0]["value"] + } + ) return all_item_for_ui def launch(self, session, entities, event): @@ -203,7 +209,9 @@ def launch(self, session, entities, event): component_representation = [] try: - component_representation = self.get_representations(session, event, entities) + component_representation = self.get_representations( + session, event, entities + ) except Exception: self.log.error(traceback.format_exc()) job["status"] = "failed" @@ -229,8 +237,9 @@ def launch(self, session, entities, event): project_apps_config = avalon_project_doc["config"].get("apps", []) avalon_project_apps = [ - app["name"] for app in project_apps_config - ] or False + app["name"] for app in + project_apps_config + ] or False event["data"]["avalon_project_apps"] = avalon_project_apps # set app From 3c636d6d5c09c499443f30fbb6f3a299b5132e24 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 14:03:56 +0200 Subject: [PATCH 017/109] Cosmetics + Shush hound --- .../ftrack/event_handlers_user/action_rv.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_rv.py b/openpype/modules/ftrack/event_handlers_user/action_rv.py index cf36b3221d1..7ab9dd95b52 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_rv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_rv.py @@ -4,6 +4,7 @@ import sys import traceback import json +from collections import defaultdict import ftrack_api @@ -35,6 +36,7 @@ class RVActionView(BaseAction): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + # TODO (Critical) This should not be as hardcoded as it is now # QUESTION load RV application data from AppplicationManager? rv_path = None @@ -173,14 +175,10 @@ def get_interface_items(self, session, entities): # Sort by version for parent_name, entities in components.items(): - version_mapping = {} + version_mapping = defaultdict(list) for entity in entities: - try: - version_mapping[entity["version"]["version"]].append( - entity - ) - except KeyError: - version_mapping[entity["version"]["version"]] = [entity] + entity_version = entity["version"]["version"] + version_mapping[entity_version].append(entity) # Sort same versions by date. for version, entities in version_mapping.items(): @@ -265,12 +263,15 @@ def launch(self, session, entities, event): args.extend(paths) # CORE EDIT SET UP THE PATHS + # TODO (Critical) This should not be as hardcoded as it is now self.log.info("setting up env vars") os.environ["RV_HOME"] = os.path.normpath(self.rv_home) sys.path.append(os.path.join(self.rv_home, "lib")) sys.path.append(self.rv_home) self.log.info("Running rv: {}".format(args)) - self.home = os.path.normpath(os.path.join("c:/", "Users", getpass.getuser())) + self.home = os.path.normpath( + os.path.join("c:/Users", getpass.getuser()) + ) os.environ["HOME"] = self.home env = os.environ.copy() env['PYTHONPATH'] = '' From 8d0d2bfb946adcacd40545be6946c6bf77aa336e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 14:05:33 +0200 Subject: [PATCH 018/109] Remove unused function --- openpype/hosts/openrv/api/commands.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openpype/hosts/openrv/api/commands.py b/openpype/hosts/openrv/api/commands.py index 528bae76801..03cccd63db1 100644 --- a/openpype/hosts/openrv/api/commands.py +++ b/openpype/hosts/openrv/api/commands.py @@ -34,8 +34,3 @@ def set_session_fps(): asset_data = asset_doc["data"] fps = float(asset_data.get("fps", 25)) rv.commands.setFPS(fps) - - -def create_support_ticket(): - import webbrowser - webbrowser.open("http://localhost:5400/tickets/create_tickets") From de715e722bf5b8f1ab8d481b0da36b99ac5ea9fb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 15:03:37 +0200 Subject: [PATCH 019/109] Cosmetics --- .../event_handlers_user/action_review_openrv.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py index 2717d7a07e8..7d4c0e9bc70 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py @@ -10,9 +10,8 @@ get_representation_by_name, get_project ) from openpype.lib import ApplicationManager -from openpype.pipeline import ( - AvalonMongoDB, -) +from openpype.pipeline import AvalonMongoDB + from openpype_modules.ftrack.lib import BaseAction, statics_icon @@ -236,10 +235,9 @@ def launch(self, session, entities, event): return False project_apps_config = avalon_project_doc["config"].get("apps", []) - avalon_project_apps = [ - app["name"] for app in - project_apps_config - ] or False + avalon_project_apps = ( + [app["name"] for app in project_apps_config] or False + ) event["data"]["avalon_project_apps"] = avalon_project_apps # set app @@ -278,9 +276,7 @@ def get_representations(self, session, event, entities): else: component_data = event["data"]["values"][parent_name] - component = session.get( - "Component", component_data - ) + component = session.get("Component", component_data) subset_name = component["version"]["asset"]["name"] version_name = component["version"]["version"] representation_name = component["file_type"][1:] From 97b9c9984fd656dd5e8bd2ce1743f09234719b16 Mon Sep 17 00:00:00 2001 From: Aleks Katunar Date: Thu, 13 Apr 2023 15:13:10 +0200 Subject: [PATCH 020/109] added missing schema --- .../host_settings/schema_openrv.json | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 openpype/settings/entities/schemas/system_schema/host_settings/schema_openrv.json diff --git a/openpype/settings/entities/schemas/system_schema/host_settings/schema_openrv.json b/openpype/settings/entities/schemas/system_schema/host_settings/schema_openrv.json new file mode 100644 index 00000000000..f2677c84362 --- /dev/null +++ b/openpype/settings/entities/schemas/system_schema/host_settings/schema_openrv.json @@ -0,0 +1,39 @@ +{ + "type": "dict", + "key": "openrv", + "label": "OpenRV", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "schema_template", + "name": "template_host_unchangables" + }, + { + "key": "environment", + "label": "Environment", + "type": "raw-json" + }, + { + "type": "dict-modifiable", + "key": "variants", + "collapsible_key": true, + "use_label_wrap": false, + "object_type": { + "type": "dict", + "collapsible": true, + "children": [ + { + "type": "schema_template", + "name": "template_host_variant_items" + } + ] + } + } + ] +} From 7a69fa5fce668fca3b5d3cc81251d4f375a0d0f3 Mon Sep 17 00:00:00 2001 From: Aleks Katunar Date: Thu, 13 Apr 2023 15:14:27 +0200 Subject: [PATCH 021/109] added missing schema --- .../entities/schemas/system_schema/schema_applications.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/settings/entities/schemas/system_schema/schema_applications.json b/openpype/settings/entities/schemas/system_schema/schema_applications.json index b17687cf71f..886f6a5c372 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_applications.json +++ b/openpype/settings/entities/schemas/system_schema/schema_applications.json @@ -100,6 +100,10 @@ { "type": "schema", "name": "schema_djv" + }, + { + "type": "schema", + "name": "schema_openrv" }, { "type": "dict-modifiable", From 6f504882db691b2d37d4c895e76a36859a7192dd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 15:15:19 +0200 Subject: [PATCH 022/109] Cosmetics --- .../entities/schemas/system_schema/schema_applications.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/system_schema/schema_applications.json b/openpype/settings/entities/schemas/system_schema/schema_applications.json index 886f6a5c372..dee52b51b0b 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_applications.json +++ b/openpype/settings/entities/schemas/system_schema/schema_applications.json @@ -101,7 +101,7 @@ "type": "schema", "name": "schema_djv" }, - { + { "type": "schema", "name": "schema_openrv" }, From 3e79ff10e440698ad005f76fb4ee8f5bd1b57e26 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 15:22:12 +0200 Subject: [PATCH 023/109] Add `openrv` to hosts enum since it registers a host --- openpype/settings/entities/enum_entity.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/enum_entity.py b/openpype/settings/entities/enum_entity.py index c0c103ea10e..f9afe7f8c71 100644 --- a/openpype/settings/entities/enum_entity.py +++ b/openpype/settings/entities/enum_entity.py @@ -169,7 +169,8 @@ class HostsEnumEntity(BaseEnumEntity): "unreal", "standalonepublisher", "traypublisher", - "webpublisher" + "webpublisher", + "openrv" ] def _item_initialization(self): From 2310252d2d2a07b41c7996f1b29cfa635386a8cb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 15:31:45 +0200 Subject: [PATCH 024/109] Use FusionPreLaunchOCIO hook as global hook for re-use with `openrv` --- .../pre_fusion_ocio_hook.py => hooks/pre_ocio_hook.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename openpype/{hosts/fusion/hooks/pre_fusion_ocio_hook.py => hooks/pre_ocio_hook.py} (88%) diff --git a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py b/openpype/hooks/pre_ocio_hook.py similarity index 88% rename from openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py rename to openpype/hooks/pre_ocio_hook.py index 6bf0f55081b..11ea27097b8 100644 --- a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py +++ b/openpype/hooks/pre_ocio_hook.py @@ -4,9 +4,9 @@ from openpype.pipeline.template_data import get_template_data_with_names -class FusionPreLaunchOCIO(PreLaunchHook): - """Set OCIO environment variable for Fusion""" - app_groups = ["fusion"] +class PreLaunchOCIO(PreLaunchHook): + """Set OCIO environment variable for application""" + app_groups = ["fusion", "openrv"] def execute(self): """Hook entry method.""" From 45234161252dfab9e58ce7659c6a390e0bf5cc4b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 15:32:24 +0200 Subject: [PATCH 025/109] Remove pre config hook in favor of global OCIO hook --- .../hosts/openrv/hooks/pre_config_setup.py | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 openpype/hosts/openrv/hooks/pre_config_setup.py diff --git a/openpype/hosts/openrv/hooks/pre_config_setup.py b/openpype/hosts/openrv/hooks/pre_config_setup.py deleted file mode 100644 index 06484fca5d2..00000000000 --- a/openpype/hosts/openrv/hooks/pre_config_setup.py +++ /dev/null @@ -1,41 +0,0 @@ -import os -import platform - -from openpype.lib import PreLaunchHook -from openpype.settings import ( - get_project_settings -) - - -SET_CONFIG_FILE_PATH = None -SET_OCIO_ENV_VAR = None -SET_USERS_CONFIG = None -SET_AOV_CONFIGS = None -SET_STARTUP_PROJECT = None - - -class PreConfigSetups(PreLaunchHook): - app_groups = ["openrv"] - - def execute(self): - self.log.info("PRE-SETUP OPENRV CONFIGS HOOK") - - self.project_name = self.launch_context.env.get("AVALON_PROJECT") - if not self.project_name: - self.project_name = os.environ.get("AVALON_PROJECT") - - print("Loaded project", self.project_name) - - self.load_config = get_project_settings(self.project_name)["openrv"]["imageio"]["workfile"]["OCIO_config"] - print(self.load_config) - - if "srgb" in self.load_config.lower(): - print("OCIO Disabled.") - os.environ["OCIO"] = "" - self.launch_context.env["OCIO"] = "" - else: - print("OCIO Enabled.") - ocio_config_path = get_project_settings(self.project_name)["openrv"]["imageio"]["workfile"]["customOCIOConfigPath"][str(platform.system().lower())][0] - os.environ["OCIO"] = str(ocio_config_path) - self.launch_context.env["OCIO"] = str(ocio_config_path) - print(os.environ["OCIO"]) From f4d69f895c50636ed435552166f7becc38870176 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 16:28:25 +0200 Subject: [PATCH 026/109] Cleanup code --- .../startup/pkgs_source/comments/comments.py | 84 ++++++++----------- .../openpype_menus-1.0/openpype_menus.py | 30 +++---- 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 1bce2f7da14..ec70541e19f 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -1,9 +1,6 @@ # review code import os -import sys from PySide2 import QtWidgets, QtGui -from PySide2.QtCore import QRect -from PySide2.QtWidgets import QApplication from rv.qtutils import * from rv.rvtypes import * @@ -15,15 +12,25 @@ class ReviewMenu(MinorMode): def __init__(self): MinorMode.__init__(self) - self.init("py-ReviewMenu-mode", None, None, [("-= OpenPype =-", [("Review", self.runme, None, None)])]) + self.init("py-ReviewMenu-mode", None, None, + [("-= OpenPype =-", [("Review", self.runme, None, None)])]) # spacers - self.verticalSpacer = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, - QtWidgets.QSizePolicy.Expanding) - self.verticalSpacerMin = QtWidgets.QSpacerItem(2, 2, QtWidgets.QSizePolicy.Minimum, - QtWidgets.QSizePolicy.Minimum) - self.horizontalSpacer = QtWidgets.QSpacerItem(40, 10, QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Minimum) + self.verticalSpacer = QtWidgets.QSpacerItem( + 20, 40, + QtWidgets.QSizePolicy.Minimum, + QtWidgets.QSizePolicy.Expanding + ) + self.verticalSpacerMin = QtWidgets.QSpacerItem( + 2, 2, + QtWidgets.QSizePolicy.Minimum, + QtWidgets.QSizePolicy.Minimum + ) + self.horizontalSpacer = QtWidgets.QSpacerItem( + 40, 10, + QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Minimum + ) self.customDockWidget = QtWidgets.QWidget() # data @@ -34,9 +41,13 @@ def __init__(self): self.rev_head_name = QtWidgets.QLabel("Shot Name") self.current_loaded_shot = QtWidgets.QLabel("") self.current_shot_status = QtWidgets.QComboBox() - self.current_shot_status.addItems(["In Review", "Ready For Review", "Reviewed", "Approved", "Deliver"]) + self.current_shot_status.addItems([ + "In Review", "Ready For Review", "Reviewed", "Approved", "Deliver" + ]) self.current_shot_comment = QtWidgets.QPlainTextEdit() - self.current_shot_comment.setStyleSheet("color: white; background-color: black") + self.current_shot_comment.setStyleSheet( + "color: white; background-color: black" + ) self.review_main_layout_head = QtWidgets.QVBoxLayout() self.review_main_layout_head.addWidget(self.rev_head_label) @@ -48,7 +59,7 @@ def __init__(self): self.get_view_image = QtWidgets.QPushButton("Get image") self.review_main_layout_head.addWidget(self.get_view_image) - self.remove_cmnt_status_btn = QtWidgets.QPushButton("Remove comment and status") + self.remove_cmnt_status_btn = QtWidgets.QPushButton("Remove comment and status") # noqa self.review_main_layout_head.addWidget(self.remove_cmnt_status_btn) # annotations controls @@ -67,7 +78,7 @@ def __init__(self): self.customDockWidget.setLayout(self.review_main_layout) # signals - self.current_shot_status.currentTextChanged.connect(self.setup_combo_status) + self.current_shot_status.currentTextChanged.connect(self.setup_combo_status) # noqa self.current_shot_comment.textChanged.connect(self.comment_update) self.get_view_image.clicked.connect(self.get_gui_image) self.remove_cmnt_status_btn.clicked.connect(self.clean_cmnt_status) @@ -76,7 +87,6 @@ def __init__(self): self.runme() - def runme(self, arg1=None, arg2=None): self.rvWindow = rv.qtutils.sessionWindow() @@ -88,8 +98,6 @@ def runme(self, arg1=None, arg2=None): self.rvWindow.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget) self.setup_listeners() - - def set_item_font(self, item, size=14, noweight=False, bold=True): font = QtGui.QFont() if bold: @@ -102,27 +110,26 @@ def set_item_font(self, item, size=14, noweight=False, bold=True): font.setWeight(75) item.setFont(font) - def setup_listeners(self): - # new-source, graph-state-change, after-progressive-loading, media-relocated - rv.commands.bind("default", "global", "source-media-set", self.graph_change, "Doc string") - rv.commands.bind("default", "global", "after-graph-view-change", self.graph_change, "Doc string") - + # Some other supported signals: + # new-source + # graph-state-change, + # after-progressive-loading, + # media-relocated + rv.commands.bind("default", "global", "source-media-set", + self.graph_change, "Doc string") + rv.commands.bind("default", "global", "after-graph-view-change", + self.graph_change, "Doc string") def graph_change(self, event): - print("image structure change") - print(event.contents()) - print(rv.commands.sourcesAtFrame(rv.commands.frame())) - print(rv.commands.frame()) # update the view self.get_view_source() - def get_view_source(self): - self.current_loaded_viewnode = rv.commands.sourcesAtFrame(rv.commands.frame())[0] + sources = rv.commands.sourcesAtFrame(rv.commands.frame()) + self.current_loaded_viewnode = sources[0] if sources else None self.update_ui_attribs() - def update_ui_attribs(self): node = self.current_loaded_viewnode # representation @@ -135,8 +142,6 @@ def update_ui_attribs(self): self.setup_properties() self.get_comment() - - def setup_combo_status(self): # setup properties node = self.current_loaded_viewnode @@ -146,7 +151,6 @@ def setup_combo_status(self): rv.commands.setStringProperty(att_prop, [str(status)], True) self.current_shot_status.setCurrentText(status) - def setup_properties(self): # setup properties node = self.current_loaded_viewnode @@ -162,7 +166,6 @@ def setup_properties(self): status = rv.commands.getStringProperty(att_prop)[0] self.current_shot_status.setCurrentText(status) - def comment_update(self): node = self.current_loaded_viewnode comment = self.current_shot_comment.toPlainText() @@ -170,7 +173,6 @@ def comment_update(self): rv.commands.newProperty(att_prop, rv.commands.StringType, 1) rv.commands.setStringProperty(att_prop, [str(comment)], True) - def get_comment(self): node = self.current_loaded_viewnode att_prop = node + ".openpype_review.task_comment" @@ -181,7 +183,6 @@ def get_comment(self): status = rv.commands.getStringProperty(att_prop)[0] self.current_shot_comment.setPlainText(status) - def clean_cmnt_status(self): attribs = [] node = self.current_loaded_viewnode @@ -198,33 +199,27 @@ def clean_cmnt_status(self): self.current_shot_status.setCurrentText("In Review") self.current_shot_comment.setPlainText("") - def get_gui_image(self, filename=None): data = rv.commands.exportCurrentFrame("c:/temp/jpg.jpg") print(data) print("File saved") - - def annotate_next(self): all_notes = self.get_annotated_for_view() nxt = self.get_cycle_frame(frame=rv.commands.frame(), frames_in=all_notes, do="next") rv.commands.setFrame(int(nxt)) rv.commands.redraw() - def annotate_prev(self): all_notes = self.get_annotated_for_view() previous = self.get_cycle_frame(frame=rv.commands.frame(), frames_in=all_notes, do="prev") rv.commands.setFrame(int(previous)) rv.commands.redraw() - def get_annotated_for_view(self): annotated_frames = rv.extra_commands.findAnnotatedFrames() return annotated_frames - def get_cycle_frame(self, frame=None, frames_in=None, do="next"): set_start = -1 @@ -260,14 +255,11 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): prev_frame = max([i for i in frames_in if frame > i]) return prev_frame - - def echo_change_update(self): print("CHANGE") print(self.current_loaded_viewnode) node = self.current_loaded_viewnode - #print(node) print(rv.commands.properties(node)) # representation prop_representation = node + ".openpype.representation" @@ -286,7 +278,6 @@ def echo_change_update(self): info = rv.extra_commands.sourceMetaInfoAtFrame(rv.commands.frame()) print("info", info) - def get_task_status(self): import ftrack_api session = ftrack_api.Session(auto_connect_event_hub=False) @@ -298,6 +289,5 @@ def get_task_status(self): project_schema = project_entity["project_schema"] - def createMode(): - return ReviewMenu() \ No newline at end of file + return ReviewMenu() diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 3ddd7c7ad06..d95b33e8e35 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -25,22 +25,18 @@ class OpenPypeMenus(MinorMode): def __init__(self): MinorMode.__init__(self) - self.init("py-openpype", None, None, - [ - # Menu name - # NOTE: If it already exists it will merge with existing - # and add submenus / menuitems to the existing one - ("-= OpenPype =-", - [ - # Menuitem name, actionHook (event), key, stateHook - ("Workfiles", self.workfiles, None, None), - ("Create", self.workfiles, None, None), - ("Load", self.load, None, None), - ("Publish", self.publish, None, None) - ] - ) - ] - ) + self.init("py-openpype", None, None, [ + # Menu name + # NOTE: If it already exists it will merge with existing + # and add submenus / menuitems to the existing one + ("-= OpenPype =-", [ + # Menuitem name, actionHook (event), key, stateHook + ("Workfiles", self.workfiles, None, None), + ("Create", self.create, None, None), + ("Load", self.load, None, None), + ("Publish", self.publish, None, None) + ]) + ]) if not isConsoleVisible(): showConsole() @@ -63,4 +59,4 @@ def workfiles(self, event): def createMode(): install_openpype_to_host() - return OpenPypeMenus() \ No newline at end of file + return OpenPypeMenus() From dbec91de7846cf146dea67a5905f275dfa61db41 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 16:29:08 +0200 Subject: [PATCH 027/109] Don't add to sys.path, don't clear PYTHONPATH --- .../pkgs_source/openpype_menus-1.0/openpype_menus.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index d95b33e8e35..26594f0c25f 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -2,14 +2,6 @@ from rv.commands import * from rv.extra_commands import * -import sys - -# inject OP for now, otherwise it won work -sys.path.append("PATHTOOPENPYPE/openpype/vendor/python/common") -sys.path.append("PATHTOOPENPYPE/openpype/openpype/tools") -sys.path.append("PATHTOOPENPYPE/openpype") -sys.path.append("PATHTOOPENPYPE/openpype/.venv/Lib/site-packages") - from openpype.tools.utils import host_tools from openpype.pipeline import install_host From 028e85f53c570c3193e3fb91724fec8dcf419e2d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 16:31:40 +0200 Subject: [PATCH 028/109] More specific imports --- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 26594f0c25f..86006b8303c 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -1,6 +1,5 @@ -from rv.rvtypes import * -from rv.commands import * -from rv.extra_commands import * +from rv.rvtypes import MinorMode +from rv.commands import isConsoleVisible, showConsole from openpype.tools.utils import host_tools From d716d04b7809e1e5b2363624cb9c50ee9c87d4eb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 16:40:48 +0200 Subject: [PATCH 029/109] Clean up imports --- .../startup/pkgs_source/comments/comments.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index ec70541e19f..28100bc3d8a 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -1,12 +1,10 @@ # review code import os -from PySide2 import QtWidgets, QtGui +from PySide2 import QtCore, QtWidgets, QtGui -from rv.qtutils import * -from rv.rvtypes import * - -from rv.commands import * -from rv.extra_commands import * +from rv.rvtypes import MinorMode +import rv.qtutils +import rv.commands class ReviewMenu(MinorMode): @@ -91,11 +89,13 @@ def runme(self, arg1=None, arg2=None): self.rvWindow = rv.qtutils.sessionWindow() # Create DockWidget and add the Custom Widget to it - self.dockWidget = QDockWidget("OpenPype Review", self.rvWindow) + self.dockWidget = QtWidgets.QDockWidget("OpenPype Review", + self.rvWindow) self.dockWidget.setWidget(self.customDockWidget) # Dock widget to the RV MainWindow - self.rvWindow.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget) + self.rvWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, + self.dockWidget) self.setup_listeners() def set_item_font(self, item, size=14, noweight=False, bold=True): From 86cc3b67c85da25894baedb2b60cd21f6b0cbb50 Mon Sep 17 00:00:00 2001 From: Aleks Katunar Date: Thu, 13 Apr 2023 16:47:41 +0200 Subject: [PATCH 030/109] proper menu file --- .../openpype_menus-1.0/openpype_menus.py | 88 ++++++++++++++++--- 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 86006b8303c..8fd7afb9183 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -1,5 +1,28 @@ -from rv.rvtypes import MinorMode -from rv.commands import isConsoleVisible, showConsole +import os +import sys +import json + +from rv.rvtypes import * +from rv.commands import * +from rv.extra_commands import * + + +# openrv strips out the pythonpath and we need to rebuild it here +# this is also documented at the RV docs page +print(os.getenv("OPENPYPE_ROOT")) + +sys.path.append(os.getenv("OPENPYPE_ROOT")) +sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "openpype")) +sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), ".venv", "Lib", "site-packages")) +sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "dependencies")) +sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "openpype","vendor", "python", "common")) +sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "openpype", "tools")) + +for b in sys.path: + if os.path.isdir(b): + print("=OK=", b) + else: + print("NOT / ", b) from openpype.tools.utils import host_tools @@ -16,18 +39,23 @@ class OpenPypeMenus(MinorMode): def __init__(self): MinorMode.__init__(self) - self.init("py-openpype", None, None, [ - # Menu name - # NOTE: If it already exists it will merge with existing - # and add submenus / menuitems to the existing one - ("-= OpenPype =-", [ - # Menuitem name, actionHook (event), key, stateHook - ("Workfiles", self.workfiles, None, None), - ("Create", self.create, None, None), - ("Load", self.load, None, None), - ("Publish", self.publish, None, None) - ]) - ]) + self.init("py-openpype", None, None, + [ + # Menu name + # NOTE: If it already exists it will merge with existing + # and add submenus / menuitems to the existing one + ("-= OpenPype =-", + [ + # Menuitem name, actionHook (event), key, stateHook + ("Workfiles", self.workfiles, None, None), + ("Create", self.create, None, None), + ("Load", self.load, None, None), + ("Publish", self.publish, None, None), + ("Inventory", self.scene_inventory, None, None), + ] + ) + ] + ) if not isConsoleVisible(): showConsole() @@ -47,7 +75,39 @@ def workfiles(self, event): print("Launching Workfiles") host_tools.show_workfiles() + def scene_inventory(self, event): + print("Launching inventory") + host_tools.show_scene_inventory() + + +def data_loader(): + incoming_data_file = os.environ.get("OPENPYPE_LOADER_REPRESENTATIONS", None) + if incoming_data_file: + with open(incoming_data_file, 'rb') as pypefile: + decoded_data = json.load(pypefile) + os.remove(incoming_data_file) + load_data(dataset=decoded_data["representations"]) + else: + print("No data for auto-loader") + + +def load_data(dataset=None): + from openpype.pipeline.load import discover_loader_plugins + from openpype.pipeline import load_container + from openpype.client import get_representations + + project_name = os.environ["AVALON_PROJECT"] + available_loaders = discover_loader_plugins(project_name) + Loader = next(loader for loader in available_loaders if loader.__name__ == "FramesLoader") + + representations = get_representations(project_name, + representation_ids=dataset) + + for representation in representations: + container = load_container(Loader, representation) + def createMode(): install_openpype_to_host() + data_loader() return OpenPypeMenus() From 12c21e1701f2321749712b3648e96472afa04eb6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 16:51:20 +0200 Subject: [PATCH 031/109] Cleanup file again. --- .../openpype_menus-1.0/openpype_menus.py | 59 ++++++------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 8fd7afb9183..bf5f1fafa00 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -1,28 +1,8 @@ import os -import sys import json -from rv.rvtypes import * -from rv.commands import * -from rv.extra_commands import * - - -# openrv strips out the pythonpath and we need to rebuild it here -# this is also documented at the RV docs page -print(os.getenv("OPENPYPE_ROOT")) - -sys.path.append(os.getenv("OPENPYPE_ROOT")) -sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "openpype")) -sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), ".venv", "Lib", "site-packages")) -sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "dependencies")) -sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "openpype","vendor", "python", "common")) -sys.path.append(os.path.join(os.getenv("OPENPYPE_ROOT"), "openpype", "tools")) - -for b in sys.path: - if os.path.isdir(b): - print("=OK=", b) - else: - print("NOT / ", b) +from rv.rvtypes import MinorMode +from rv.commands import isConsoleVisible, showConsole from openpype.tools.utils import host_tools @@ -39,23 +19,19 @@ class OpenPypeMenus(MinorMode): def __init__(self): MinorMode.__init__(self) - self.init("py-openpype", None, None, - [ - # Menu name - # NOTE: If it already exists it will merge with existing - # and add submenus / menuitems to the existing one - ("-= OpenPype =-", - [ - # Menuitem name, actionHook (event), key, stateHook - ("Workfiles", self.workfiles, None, None), - ("Create", self.create, None, None), - ("Load", self.load, None, None), - ("Publish", self.publish, None, None), - ("Inventory", self.scene_inventory, None, None), - ] - ) - ] - ) + self.init("py-openpype", None, None, [ + # Menu name + # NOTE: If it already exists it will merge with existing + # and add submenus / menuitems to the existing one + ("-= OpenPype =-", [ + # Menuitem name, actionHook (event), key, stateHook + ("Workfiles", self.workfiles, None, None), + ("Create", self.create, None, None), + ("Load", self.load, None, None), + ("Publish", self.publish, None, None), + ("Inventory", self.scene_inventory, None, None), + ]) + ]) if not isConsoleVisible(): showConsole() @@ -93,12 +69,13 @@ def data_loader(): def load_data(dataset=None): from openpype.pipeline.load import discover_loader_plugins - from openpype.pipeline import load_container + from openpype.pipeline import load_container from openpype.client import get_representations project_name = os.environ["AVALON_PROJECT"] available_loaders = discover_loader_plugins(project_name) - Loader = next(loader for loader in available_loaders if loader.__name__ == "FramesLoader") + Loader = next(loader for loader in available_loaders + if loader.__name__ == "FramesLoader") representations = get_representations(project_name, representation_ids=dataset) From 265d12ce7d772800965534ac03f305c7e455dae0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 16:56:22 +0200 Subject: [PATCH 032/109] Hound --- .../plugins/publish/collect_annotations.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py index 82b8d092f48..4066a3d3bd7 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/collect_annotations.py @@ -31,11 +31,16 @@ def process(self, context): )) prop_namespace = container + ".openpype.namespace" prop_representation = container + ".openpype.representation" - data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] - data_prop_representation_id = rv.commands.getStringProperty(prop_representation)[0] - - representations = get_representations(project_name, - representation_ids=[data_prop_representation_id]) + data_prop_namespace = rv.commands.getStringProperty( + prop_namespace + )[0] + data_prop_representation_id = rv.commands.getStringProperty( + prop_representation + )[0] + + representations = get_representations( + project_name, representation_ids=[data_prop_representation_id] + ) first_repre = next(iter(representations)) # first representation repre_context = first_repre["context"] source_representation_project = repre_context["project"]["name"] @@ -71,7 +76,7 @@ def process(self, context): # "name": "thumbnail", # "ext": "jpg", # "files": "frames_1001.jpg", - # "stagingDir": "X:\\projects\\Sync\\DaliesPrep\\work\\prepDaily", + # "stagingDir": "path\\to\\prepDaily", # # "thumbnail": True, # # "comment": "NEW COMMENT FROM UI" # "frameStart": noted_frame, @@ -80,7 +85,6 @@ def process(self, context): # } data = { - # "subset": source_representation_subset + "_review_{}".format(noted_frame), "subset": "annotation_{}".format(str(noted_frame)), "tags": ["review", "ftrackreview"], "asset": source_representation_asset, @@ -100,7 +104,9 @@ def process(self, context): # if "representations" not in instance.data: # instance.data["representations"] = [] # - # instance.data["representations"].append(annotation_representation) + # instance.data["representations"].append( + # annotation_representation + # ) view_node = rv.commands.viewNode() intent = context.data.get("intent") From 552c2f6e84d5f73923861146a2c8e64e62860fc1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:00:06 +0200 Subject: [PATCH 033/109] Match menu labels of other integrations + clarify arguments to `init` --- .../openpype_menus-1.0/openpype_menus.py | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index bf5f1fafa00..76f9d93ae63 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -19,19 +19,25 @@ class OpenPypeMenus(MinorMode): def __init__(self): MinorMode.__init__(self) - self.init("py-openpype", None, None, [ - # Menu name - # NOTE: If it already exists it will merge with existing - # and add submenus / menuitems to the existing one - ("-= OpenPype =-", [ - # Menuitem name, actionHook (event), key, stateHook - ("Workfiles", self.workfiles, None, None), - ("Create", self.create, None, None), - ("Load", self.load, None, None), - ("Publish", self.publish, None, None), - ("Inventory", self.scene_inventory, None, None), - ]) - ]) + self.init( + name="py-openpype", + globalBindings=None, + overrideBindings=None, + menu=[ + # Menu name + # NOTE: If it already exists it will merge with existing + # and add submenus / menuitems to the existing one + ("OpenPype", [ + # Menuitem name, actionHook (event), key, stateHook + ("Create...", self.create, None, None), + ("Load...", self.load, None, None), + ("Publish...", self.publish, None, None), + ("Manage...", self.scene_inventory, None, None), + # TODO: add separator if possible + ("Work Files...", self.workfiles, None, None), + ]) + ] + ) if not isConsoleVisible(): showConsole() From 8303faab41d3a864eb57f8032342ef1991e5b024 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:00:39 +0200 Subject: [PATCH 034/109] Shush hound --- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 76f9d93ae63..59cb50e83d6 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -63,7 +63,8 @@ def scene_inventory(self, event): def data_loader(): - incoming_data_file = os.environ.get("OPENPYPE_LOADER_REPRESENTATIONS", None) + incoming_data_file = os.environ.get("OPENPYPE_LOADER_REPRESENTATIONS", + None) if incoming_data_file: with open(incoming_data_file, 'rb') as pypefile: decoded_data = json.load(pypefile) From a336e1b7b2a412232e8d29451e5c0ce5d0263db2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:01:02 +0200 Subject: [PATCH 035/109] Remove unused variable --- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 59cb50e83d6..8f4de437071 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -88,7 +88,7 @@ def load_data(dataset=None): representation_ids=dataset) for representation in representations: - container = load_container(Loader, representation) + load_container(Loader, representation) def createMode(): From c8df2ee9bab7682cbfa86184c6562cdbbfd3bf11 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:02:19 +0200 Subject: [PATCH 036/109] Hound --- .../openrv/plugins/publish/collect_annotations.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py index 4066a3d3bd7..039a5185dd4 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/collect_annotations.py @@ -43,10 +43,10 @@ def process(self, context): ) first_repre = next(iter(representations)) # first representation repre_context = first_repre["context"] - source_representation_project = repre_context["project"]["name"] + # source_representation_project = repre_context["project"]["name"] source_representation_asset = repre_context["asset"] source_representation_task = repre_context["task"]["name"] - source_representation_subset = repre_context["subset"] + # source_representation_subset = repre_context["subset"] source_group = rv.commands.nodeGroup(container) print("SOURCE GROUP ", source_group) source_groups.append(source_group) @@ -59,7 +59,7 @@ def process(self, context): asset_folder, file = os.path.split(rv.commands.sessionFileName()) for marked in marked_frames: - print("MARKED ------------ ", container, marked, source_group) + print("MARKED ------------ ", container, marked, source_group) for noted_frame in annotated_frames: print("NOTED ------- ", container, noted_frame, source_group) @@ -108,5 +108,5 @@ def process(self, context): # annotation_representation # ) - view_node = rv.commands.viewNode() - intent = context.data.get("intent") + # view_node = rv.commands.viewNode() + # intent = context.data.get("intent") From bf2188f4ae55b17be920d86904e995e4891dfa5e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:24:38 +0200 Subject: [PATCH 037/109] Update menu name --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 28100bc3d8a..fdf85197b2e 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -11,7 +11,7 @@ class ReviewMenu(MinorMode): def __init__(self): MinorMode.__init__(self) self.init("py-ReviewMenu-mode", None, None, - [("-= OpenPype =-", [("Review", self.runme, None, None)])]) + [("OpenPype", [("Review", self.runme, None, None)])]) # spacers self.verticalSpacer = QtWidgets.QSpacerItem( From 850cb211c81e958f177c34355234fa3ae1e69c55 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:34:46 +0200 Subject: [PATCH 038/109] Fix workfile saving with correct extension --- openpype/hosts/openrv/api/pipeline.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/api/pipeline.py b/openpype/hosts/openrv/api/pipeline.py index c042b27a620..9a7fb3ab0aa 100644 --- a/openpype/hosts/openrv/api/pipeline.py +++ b/openpype/hosts/openrv/api/pipeline.py @@ -55,7 +55,11 @@ def work_root(self, session): return work_dir def get_current_workfile(self): - return rv.commands.sessionFileName() + filename = rv.commands.sessionFileName() + if filename == "Untitled": + return + else: + return filename def workfile_has_unsaved_changes(self): # dont ask to save if we are on the startup scene without a name From aa303b9190c09b63cd3d0704b4ea11e835fca1b9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:37:51 +0200 Subject: [PATCH 039/109] Hack: For now "auto-deploy" and load packages --- .../hosts/openrv/hooks/pre_setup_openrv.py | 39 +++++++++++++++++++ openpype/hosts/openrv/startup/Mu/rvload | 1 + openpype/hosts/openrv/startup/Mu/rvload2 | 3 ++ .../hosts/openrv/startup/Packages/rvinstall | 2 + 4 files changed, 45 insertions(+) create mode 100644 openpype/hosts/openrv/hooks/pre_setup_openrv.py create mode 100644 openpype/hosts/openrv/startup/Mu/rvload create mode 100644 openpype/hosts/openrv/startup/Mu/rvload2 create mode 100644 openpype/hosts/openrv/startup/Packages/rvinstall diff --git a/openpype/hosts/openrv/hooks/pre_setup_openrv.py b/openpype/hosts/openrv/hooks/pre_setup_openrv.py new file mode 100644 index 00000000000..3c7ce7cc047 --- /dev/null +++ b/openpype/hosts/openrv/hooks/pre_setup_openrv.py @@ -0,0 +1,39 @@ +from pathlib import Path + +from openpype.lib import PreLaunchHook +from openpype.hosts.openrv import OPENRV_ROOT_DIR + + +class PreSetupOpenRV(PreLaunchHook): + """Pre-hook for openrv""" + app_groups = ["openrv"] + + def execute(self): + root = Path(OPENRV_ROOT_DIR) + startup = root / "startup" + packages_dir = startup / "Packages" + + # TODO: Auto deployment should not be this hacky + # Redeploy the source packages to zips to auto-update + # during development of OpenRV + import zipfile + for package_name in ["comments", "openpype_menus-1.0"]: + package_src = startup / "pkgs_source" / package_name + package_dest = packages_dir / "{}.zip".format(package_name) + print(f"Writing: {package_dest}") + with zipfile.ZipFile(package_dest, mode="w") as zip: + for filepath in package_src.iterdir(): + if not filepath.is_file(): + continue + + zip.write(filepath, + arcname=filepath.name) + + # TODO: Make sure we don't override a full studios RV_SUPPORT_PATH + print("Setting RV_SUPPORT_PATH", startup) + self.launch_context.env["RV_SUPPORT_PATH"] = str(startup) + + # TODO: OpenRV does write files into RV_SUPPORT_PATH during runtime + # so we should actually not deploy that as a path inside OP deploy + # to avoid openpype checksum validation issues, etc. but for now + # it helps running from code to run the integration for development diff --git a/openpype/hosts/openrv/startup/Mu/rvload b/openpype/hosts/openrv/startup/Mu/rvload new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/openpype/hosts/openrv/startup/Mu/rvload @@ -0,0 +1 @@ +1 diff --git a/openpype/hosts/openrv/startup/Mu/rvload2 b/openpype/hosts/openrv/startup/Mu/rvload2 new file mode 100644 index 00000000000..ca2f24baab2 --- /dev/null +++ b/openpype/hosts/openrv/startup/Mu/rvload2 @@ -0,0 +1,3 @@ +4 +openpype_menus,openpype_menus-1.0.zip,nil,nil,nil,true,true,3.12,true,1.0.0 +comments,comments.zip,nil,nil,nil,true,true,3.12,true,1.0.0 diff --git a/openpype/hosts/openrv/startup/Packages/rvinstall b/openpype/hosts/openrv/startup/Packages/rvinstall new file mode 100644 index 00000000000..4b4cf5c9943 --- /dev/null +++ b/openpype/hosts/openrv/startup/Packages/rvinstall @@ -0,0 +1,2 @@ +*comments.zip +*openpype_menus-1.0.zip From f3e357be19ad5a7a40b6b0348e8fdb60f8d7649b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:45:53 +0200 Subject: [PATCH 040/109] Update python files on deployment --- .../hosts/openrv/hooks/pre_setup_openrv.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/openrv/hooks/pre_setup_openrv.py b/openpype/hosts/openrv/hooks/pre_setup_openrv.py index 3c7ce7cc047..ed2a10b5d30 100644 --- a/openpype/hosts/openrv/hooks/pre_setup_openrv.py +++ b/openpype/hosts/openrv/hooks/pre_setup_openrv.py @@ -1,3 +1,4 @@ +import shutil from pathlib import Path from openpype.lib import PreLaunchHook @@ -11,7 +12,11 @@ class PreSetupOpenRV(PreLaunchHook): def execute(self): root = Path(OPENRV_ROOT_DIR) startup = root / "startup" - packages_dir = startup / "Packages" + startup_packages = startup / "Packages" + startup_python = startup / "Python" + + # Ensure folder exists + startup_python.mkdir(exist_ok=True) # TODO: Auto deployment should not be this hacky # Redeploy the source packages to zips to auto-update @@ -19,8 +24,8 @@ def execute(self): import zipfile for package_name in ["comments", "openpype_menus-1.0"]: package_src = startup / "pkgs_source" / package_name - package_dest = packages_dir / "{}.zip".format(package_name) - print(f"Writing: {package_dest}") + package_dest = startup_packages / "{}.zip".format(package_name) + self.log.info(f"Writing: {package_dest}") with zipfile.ZipFile(package_dest, mode="w") as zip: for filepath in package_src.iterdir(): if not filepath.is_file(): @@ -29,6 +34,15 @@ def execute(self): zip.write(filepath, arcname=filepath.name) + if filepath.suffix == ".py": + # Include it in Python subfolder where OpenRV deploys + # the files after first install (and does not update + # after) + self.log.info( + f"Copying {filepath} to folder {startup_python}" + ) + shutil.copy(filepath, startup_python) + # TODO: Make sure we don't override a full studios RV_SUPPORT_PATH print("Setting RV_SUPPORT_PATH", startup) self.launch_context.env["RV_SUPPORT_PATH"] = str(startup) From 492ed6aa69d886a6e3b30cffb052a6e10e83aa80 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:55:29 +0200 Subject: [PATCH 041/109] Avoid errors when no currently loaded content --- .../startup/pkgs_source/comments/comments.py | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index fdf85197b2e..e5f709d1f7d 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -132,13 +132,15 @@ def get_view_source(self): def update_ui_attribs(self): node = self.current_loaded_viewnode - # representation - prop_representation = node + ".openpype.representation" - prop_namespace = node + ".openpype.namespace" - data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] - data_prop_representation_id = rv.commands.getStringProperty(prop_representation)[0] - print("data_prop_namespace", data_prop_namespace) - self.current_loaded_shot.setText(data_prop_namespace) + + # Use namespace as loaded shot label + namespace = "" + if node is not None: + namespace = rv.commands.getStringProperty( + "{}.openpype.namespace".format(node) + )[0] + self.current_loaded_shot.setText(namespace) + self.setup_properties() self.get_comment() @@ -154,9 +156,11 @@ def setup_combo_status(self): def setup_properties(self): # setup properties node = self.current_loaded_viewnode - print(rv.commands.properties(node)) - att_prop = node + ".openpype_review.task_status" + if node is None: + self.current_shot_status.setCurrentIndex(0) + return + att_prop = node + ".openpype_review.task_status" if not rv.commands.propertyExists(att_prop): status = "In Review" rv.commands.newProperty(att_prop, rv.commands.StringType, 1) @@ -168,6 +172,9 @@ def setup_properties(self): def comment_update(self): node = self.current_loaded_viewnode + if node is None: + return + comment = self.current_shot_comment.toPlainText() att_prop = node + ".openpype_review.task_comment" rv.commands.newProperty(att_prop, rv.commands.StringType, 1) @@ -175,6 +182,10 @@ def comment_update(self): def get_comment(self): node = self.current_loaded_viewnode + if node is None: + self.current_shot_comment.setPlainText("") + return + att_prop = node + ".openpype_review.task_comment" if not rv.commands.propertyExists(att_prop): rv.commands.newProperty(att_prop, rv.commands.StringType, 1) From 97a83f196570a45e008d9e7cb2dda282dd73b719 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 17:57:11 +0200 Subject: [PATCH 042/109] No need for default environment overrides --- .../settings/defaults/system_settings/applications.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 4766fb24cc8..ab1227704d3 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -1561,12 +1561,7 @@ "label": "OpenRV", "icon": "{}/app_icons/openrv.png", "host_name": "openrv", - "environment": { - "HOME": "C:/_pipeline/apps/openrv/home", - "RV_SUPPORT_PATH": "PTH_TO_SUPPORT_FOLDER", - "PYTHONPATH": "", - "RV_PREFS_OVERRIDE_PATH": "PREFS_OVERRIDE_PATH" - }, + "environment": {}, "variants": { "1-0": { "use_python_2": false, From cd34df7e497d11042419bbe2ba1c8996868e6cb8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:04:44 +0200 Subject: [PATCH 043/109] Shush hound --- .../startup/pkgs_source/comments/comments.py | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index e5f709d1f7d..c9d821723a1 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -217,13 +217,17 @@ def get_gui_image(self, filename=None): def annotate_next(self): all_notes = self.get_annotated_for_view() - nxt = self.get_cycle_frame(frame=rv.commands.frame(), frames_in=all_notes, do="next") + nxt = self.get_cycle_frame(frame=rv.commands.frame(), + frames_in=all_notes, + do="next") rv.commands.setFrame(int(nxt)) rv.commands.redraw() def annotate_prev(self): all_notes = self.get_annotated_for_view() - previous = self.get_cycle_frame(frame=rv.commands.frame(), frames_in=all_notes, do="prev") + previous = self.get_cycle_frame(frame=rv.commands.frame(), + frames_in=all_notes, + do="prev") rv.commands.setFrame(int(previous)) rv.commands.redraw() @@ -269,20 +273,25 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): def echo_change_update(self): print("CHANGE") - print(self.current_loaded_viewnode) + print("node", self.current_loaded_viewnode) node = self.current_loaded_viewnode - print(rv.commands.properties(node)) + if node is None: + return # representation + print("node properties", rv.commands.properties(node)) prop_representation = node + ".openpype.representation" prop_namespace = node + ".openpype.namespace" data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] - data_prop_representation_id = rv.commands.getStringProperty(prop_representation)[0] + data_prop_representation_id = rv.commands.getStringProperty( + prop_representation + )[0] print("data_prop_namespace", data_prop_namespace) print("data_prop_representation_id", data_prop_representation_id) - from openpype.client import get_representations, get_representation_parents + from openpype.client import get_representations project_name = os.environ["AVALON_PROJECT"] - representations = get_representations(project_name, - representation_ids=[data_prop_representation_id]) + representations = get_representations( + project_name, representation_ids=[data_prop_representation_id] + ) print("REPR") for rep in representations: print(rep) From 9bf9667013f1ef0bb0b7a3f8344a5922928bb1d0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:05:55 +0200 Subject: [PATCH 044/109] Fix line length --- openpype/hosts/openrv/plugins/load/load_frames.py | 9 ++++++--- openpype/hosts/openrv/plugins/load/load_mov.py | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index e0dec1ac7c3..9ba30b51544 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -50,9 +50,12 @@ def update(self, container, representation): # change path rv.commands.setSourceMedia(node, [filepath]) # update name - rv.commands.setStringProperty(node + ".media.name", ["newname"], True) - rv.commands.setStringProperty(node + ".media.repName", ["repname"], True) - rv.commands.setStringProperty(node + ".openpype.representation", [str(representation["_id"])], True) + rv.commands.setStringProperty(node + ".media.name", + ["newname"], True) + rv.commands.setStringProperty(node + ".media.repName", + ["repname"], True) + rv.commands.setStringProperty(node + ".openpype.representation", + [str(representation["_id"])], True) def remove(self, container): # todo: implement remove diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index 03d113e5636..55009c7a619 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -49,9 +49,12 @@ def update(self, container, representation): # change path update_node = rv.commands.setSourceMedia(node, [filepath]) # update name - rv.commands.setStringProperty(node + ".media.name", ["newname"], True) - rv.commands.setStringProperty(node + ".media.repName", ["repname"], True) - rv.commands.setStringProperty(node + ".openpype.representation", [str(representation["_id"])], True) + rv.commands.setStringProperty(node + ".media.name", + ["newname"], True) + rv.commands.setStringProperty(node + ".media.repName", + ["repname"], True) + rv.commands.setStringProperty(node + ".openpype.representation", + [str(representation["_id"])], True) def remove(self, container): # todo: implement remove From f1ba2be634681d1c3b2d3c102e78980898852c18 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:06:52 +0200 Subject: [PATCH 045/109] Shush hound --- openpype/hosts/openrv/plugins/load/load_frames.py | 4 ++-- openpype/hosts/openrv/plugins/load/load_mov.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index 9ba30b51544..26a2c9920bf 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -26,7 +26,7 @@ def load(self, context, name=None, namespace=None, data=None): # Command fails on unicode so we must force it to be strings filepath = str(filepath) - node_name = "{}_{}".format(namespace, name) if namespace else name + # node_name = "{}_{}".format(namespace, name) if namespace else name namespace = namespace if namespace else context["asset"]["name"] set_session_fps() @@ -59,5 +59,5 @@ def update(self, container, representation): def remove(self, container): # todo: implement remove - node = container["node"] + # node = container["node"] return diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index 55009c7a619..954503d2e46 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -25,7 +25,7 @@ def load(self, context, name=None, namespace=None, data=None): # Command fails on unicode so we must force it to be strings filepath = str(filepath) - node_name = "{}_{}".format(namespace, name) if namespace else name + # node_name = "{}_{}".format(namespace, name) if namespace else name namespace = namespace if namespace else context["asset"]["name"] set_session_fps() @@ -47,7 +47,7 @@ def update(self, container, representation): set_session_fps() reset_frame_range() # change path - update_node = rv.commands.setSourceMedia(node, [filepath]) + _update_node = rv.commands.setSourceMedia(node, [filepath]) # update name rv.commands.setStringProperty(node + ".media.name", ["newname"], True) @@ -58,5 +58,5 @@ def update(self, container, representation): def remove(self, container): # todo: implement remove - node = container["node"] + # node = container["node"] return From 0ec4afa2c3df682991764509025eefdbcd58a286 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:07:29 +0200 Subject: [PATCH 046/109] Shush hound --- openpype/hosts/openrv/api/review.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/openrv/api/review.py b/openpype/hosts/openrv/api/review.py index 522a2b55a8f..bc10dca89a9 100644 --- a/openpype/hosts/openrv/api/review.py +++ b/openpype/hosts/openrv/api/review.py @@ -26,8 +26,10 @@ def extract_annotated_frame(filepath=None): def review_attributes(node=None): - prop_status = node + ".openpype" + ".review_status" - prop_comment = node + ".openpype" + ".review_comment" + # TODO: Implement + # prop_status = node + ".openpype" + ".review_status" + # prop_comment = node + ".openpype" + ".review_comment" + pass def get_review_attribute(node=None, attribute=None): From a85ffd3b8ab849b37adf4fb64037d09559d796b2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:07:53 +0200 Subject: [PATCH 047/109] Shush hound --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index c9d821723a1..01f7a40a20d 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -69,7 +69,6 @@ def __init__(self): self.notes_layout.addWidget(self.btn_note_prev) self.notes_layout.addWidget(self.btn_note_next) - self.review_main_layout.addLayout(self.review_main_layout_head) self.review_main_layout.addLayout(self.notes_layout) self.review_main_layout.addStretch(1) From 48bf981b55d71b8dc34c863656237aa8b795ade6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:09:10 +0200 Subject: [PATCH 048/109] Shush hound --- .../modules/ftrack/event_handlers_user/action_review_openrv.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py index 7d4c0e9bc70..4ea17983ae6 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py @@ -162,8 +162,7 @@ def get_interface_items(self, session, entities): { "label": label.format( entity["version"]["asset"]["name"], - str(entity["version"][ - "version"]).zfill(3), + str(entity["version"]["version"]).zfill(3), # noqa entity["file_type"][1:] ), "value": entity["id"] From 763d4f783b02b5f1bd1d462848ca253337282927 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:10:10 +0200 Subject: [PATCH 049/109] Cleanup --- .../modules/ftrack/event_handlers_user/action_review_openrv.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py index 4ea17983ae6..1635db021a4 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_review_openrv.py @@ -150,7 +150,6 @@ def get_interface_items(self, session, entities): ) # Items to present to user. - label = "{} - v{} - {}" loadables = ["exr"] for parent_name, entities in components.items(): From 3377f323f4451183bd1d900b5dd997e0cee5c360 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:23:55 +0200 Subject: [PATCH 050/109] Fix line length --- openpype/hosts/openrv/plugins/load/load_mov.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index 954503d2e46..5063115c2f9 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -1,4 +1,7 @@ -from openpype.hosts.openrv.api.commands import set_session_fps, reset_frame_range +from openpype.hosts.openrv.api.commands import ( + set_session_fps, + reset_frame_range +) from openpype.pipeline import ( load, get_representation_path From 6503152d102af8ec54c726b38bd1bcc1f1617a75 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 18:31:26 +0200 Subject: [PATCH 051/109] Hound --- .../hosts/openrv/startup/pkgs_source/comments/comments.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 01f7a40a20d..b44a8dd97b8 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -241,12 +241,12 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): pass elif len(frames_in) == 1: - for i,findframe in enumerate(frames_in): + for i, findframe in enumerate(frames_in): if frame == findframe: return frame else: - for i,findframe in enumerate(frames_in): + for i, findframe in enumerate(frames_in): if frame == findframe: frames_in.remove(frame) set_start = i @@ -256,7 +256,7 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): try: data = [x for x in frames_in] return data[set_start] - except: + except KeyError: return data[0] else: for y,num in enumerate(frames_in, start=set_start): From 0e1e23a2f34340a2eb22c1384dd40f8698f32736 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 20:28:29 +0200 Subject: [PATCH 052/109] Cleanup --- .../hosts/openrv/startup/pkgs_source/comments/comments.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index b44a8dd97b8..32ffe871203 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -241,7 +241,8 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): pass elif len(frames_in) == 1: - for i, findframe in enumerate(frames_in): + # todo: this doesn't really need the loop if it's length of one? + for findframe in frames_in: if frame == findframe: return frame @@ -253,13 +254,13 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): if set_start != -1: if do == "next": + data = [x for x in frames_in] try: - data = [x for x in frames_in] return data[set_start] except KeyError: return data[0] else: - for y,num in enumerate(frames_in, start=set_start): + for y, num in enumerate(frames_in, start=set_start): return num else: if do == "next": From fbea68a049a9155507d79ba38abc8f55b65114aa Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 20:33:16 +0200 Subject: [PATCH 053/109] Comment out unused lines which end up doing nothing --- .../openrv/startup/pkgs_source/comments/comments.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 32ffe871203..e6c2836db8a 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -302,11 +302,11 @@ def get_task_status(self): import ftrack_api session = ftrack_api.Session(auto_connect_event_hub=False) self.log.debug("Ftrack user: \"{0}\"".format(session.api_user)) - project_name = legacy_io.Session["AVALON_PROJECT"] - project_entity = session.query(( - "select project_schema from Project where full_name is \"{}\"" - ).format(project_name)).one() - project_schema = project_entity["project_schema"] + # project_name = legacy_io.Session["AVALON_PROJECT"] + # project_entity = session.query(( + # "select project_schema from Project where full_name is \"{}\"" + # ).format(project_name)).one() + # project_schema = project_entity["project_schema"] def createMode(): From 759152b199ea60743748504d2fa11a0f64f788d0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 20:33:29 +0200 Subject: [PATCH 054/109] Returned value was unused --- openpype/hosts/openrv/plugins/load/load_mov.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index 5063115c2f9..e398a80ab4c 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -50,7 +50,7 @@ def update(self, container, representation): set_session_fps() reset_frame_range() # change path - _update_node = rv.commands.setSourceMedia(node, [filepath]) + rv.commands.setSourceMedia(node, [filepath]) # update name rv.commands.setStringProperty(node + ".media.name", ["newname"], True) From 69a94366b2df6f3eb8f9550beb122639ca08e7be Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 20:34:45 +0200 Subject: [PATCH 055/109] Comment import + add todo --- openpype/hosts/openrv/plugins/publish/extract_annotations.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/plugins/publish/extract_annotations.py b/openpype/hosts/openrv/plugins/publish/extract_annotations.py index 2a865f71c67..cefe0f80f8e 100644 --- a/openpype/hosts/openrv/plugins/publish/extract_annotations.py +++ b/openpype/hosts/openrv/plugins/publish/extract_annotations.py @@ -4,7 +4,7 @@ from openpype.pipeline import publish from openpype.hosts.openrv.api.review import ( get_path_annotated_frame, - extract_annotated_frame + # extract_annotated_frame ) @@ -31,6 +31,8 @@ def process(self, instance): annotated_frame_folder, file = os.path.split(annotated_frame_path) if not os.path.isdir(annotated_frame_folder): os.makedirs(annotated_frame_folder) + + # TODO: finish this extractor # # # save the frame # From ab3568d8c545640e76bd3c1f7c684e8ef3e3fa5b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 20:53:05 +0200 Subject: [PATCH 056/109] Hound --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index e6c2836db8a..a01d19f89f5 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -260,7 +260,8 @@ def get_cycle_frame(self, frame=None, frames_in=None, do="next"): except KeyError: return data[0] else: - for y, num in enumerate(frames_in, start=set_start): + # todo: there seems to be no reason for the enumerate here + for _, num in enumerate(frames_in, start=set_start): return num else: if do == "next": From c2d156fd2737ac37baf4858f42daf816694e3a8c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 21:46:53 +0200 Subject: [PATCH 057/109] Cleanup cycle annotate frames logic --- .../startup/pkgs_source/comments/comments.py | 94 ++++++++++--------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index a01d19f89f5..e8995166003 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -7,6 +7,48 @@ import rv.commands +def get_cycle_frame(frame=None, frames_lookup=None, direction="next"): + """Return nearest frame in direction in frames lookup. + + If the nearest frame in that direction does not exist then cycle + over to the frames taking the first entry at the other end. + + Note: + This function can return None if there are no frames to lookup in. + + Args: + frame (int): frame to search from + frames_lookup (list): frames to search in. + direction (str, optional): search direction, either "next" or "prev" + Defaults to "next". + + Returns: + int or None: The nearest frame number in that direction or None + if no lookup frames were passed. + + """ + if direction not in {"prev", "next"}: + raise ValueError("Direction must be either 'next' or 'prev'. " + "Got: {}".format(direction)) + + if not frames_lookup: + return + + elif len(frames_lookup) == 1: + return frames_lookup[0] + + # TODO: We could skip this sorting if we knew the input list was sorted + frames_lookup = list(sorted(frames_lookup)) + if direction == "next": + # Return next nearest number or cycle to the lowest number + return next((i for i in frames_lookup if i > frame), + frames_lookup[0]) + elif direction == "prev": + # Return previous nearest number or cycle to the highest number + return next((i for i in reversed(frames_lookup) if i < frame), + frames_lookup[-1]) + + class ReviewMenu(MinorMode): def __init__(self): MinorMode.__init__(self) @@ -215,62 +257,28 @@ def get_gui_image(self, filename=None): print("File saved") def annotate_next(self): + """Set frame to next annotated frame""" all_notes = self.get_annotated_for_view() - nxt = self.get_cycle_frame(frame=rv.commands.frame(), - frames_in=all_notes, - do="next") + nxt = get_cycle_frame(frame=rv.commands.frame(), + frames_lookup=all_notes, + direction="next") rv.commands.setFrame(int(nxt)) rv.commands.redraw() def annotate_prev(self): + """Set frame to previous annotated frame""" all_notes = self.get_annotated_for_view() - previous = self.get_cycle_frame(frame=rv.commands.frame(), - frames_in=all_notes, - do="prev") + previous = get_cycle_frame(frame=rv.commands.frame(), + frames_lookup=all_notes, + direction="prev") rv.commands.setFrame(int(previous)) rv.commands.redraw() def get_annotated_for_view(self): + """Return the frame numbers for all annotated frames""" annotated_frames = rv.extra_commands.findAnnotatedFrames() return annotated_frames - def get_cycle_frame(self, frame=None, frames_in=None, do="next"): - set_start = -1 - - if len(frames_in) == 0: - pass - - elif len(frames_in) == 1: - # todo: this doesn't really need the loop if it's length of one? - for findframe in frames_in: - if frame == findframe: - return frame - - else: - for i, findframe in enumerate(frames_in): - if frame == findframe: - frames_in.remove(frame) - set_start = i - - if set_start != -1: - if do == "next": - data = [x for x in frames_in] - try: - return data[set_start] - except KeyError: - return data[0] - else: - # todo: there seems to be no reason for the enumerate here - for _, num in enumerate(frames_in, start=set_start): - return num - else: - if do == "next": - next_frame = min([i for i in frames_in if frame < i]) - return next_frame - else: - prev_frame = max([i for i in frames_in if frame > i]) - return prev_frame - def echo_change_update(self): print("CHANGE") From 1134aa25ab23b033678077bd3177e9e6d94e40ef Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 21:47:23 +0200 Subject: [PATCH 058/109] Comment import + add todo --- .../startup/pkgs_source/comments/comments.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index e8995166003..e112839c318 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -252,9 +252,21 @@ def clean_cmnt_status(self): self.current_shot_comment.setPlainText("") def get_gui_image(self, filename=None): - data = rv.commands.exportCurrentFrame("c:/temp/jpg.jpg") - print(data) - print("File saved") + + if not filename: + # Allow user to pick filename + filename, _ = QtWidgets.QFileDialog.getSaveFileName( + self.customDockWidget, + "Save image", + "image.png", + "Images (*.png *.jpg *.jpeg *.exr)" + ) + if not filename: + # User cancelled + return + + rv.commands.exportCurrentFrame(filename) + print("Current frame exported to: {}".format(filename)) def annotate_next(self): """Set frame to next annotated frame""" From 189bf5e3dba2a3af6f823f0e944b49c8136e55aa Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 21:54:02 +0200 Subject: [PATCH 059/109] Comment to explain we require the sorting --- .../hosts/openrv/startup/pkgs_source/comments/comments.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index e112839c318..7ac54d78c7f 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -37,7 +37,10 @@ def get_cycle_frame(frame=None, frames_lookup=None, direction="next"): elif len(frames_lookup) == 1: return frames_lookup[0] - # TODO: We could skip this sorting if we knew the input list was sorted + # We require the sorting of the lookup frames because we pass e.g. the + # result of `rv.extra_commands.findAnnotatedFrames()` as lookup frames + # which according to its documentations states: + # The array is not sorted and some frames may appear more than once. frames_lookup = list(sorted(frames_lookup)) if direction == "next": # Return next nearest number or cycle to the lowest number From 54ea549381cbc099925fc3783e8bbe025ec65850 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:02:09 +0200 Subject: [PATCH 060/109] Don't pass emtpy list as parent --- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 8f4de437071..b9e9df8af93 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -47,7 +47,7 @@ def create(self, event): def load(self, event): print("Launching Loader") - host_tools.show_loader(parent=[], use_context=True) + host_tools.show_loader(use_context=True) def publish(self, event): print("Launching Pyblish") From fa8a964fad2c65233e7ba7438d058911004fb85f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:07:58 +0200 Subject: [PATCH 061/109] Create dock widget only on first 'review' click and toggle view otherwise - Fixes issue where multiple empty dock widgets would be added to the RV ui --- .../startup/pkgs_source/comments/comments.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 7ac54d78c7f..15a146224e8 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -105,6 +105,9 @@ def __init__(self): self.remove_cmnt_status_btn = QtWidgets.QPushButton("Remove comment and status") # noqa self.review_main_layout_head.addWidget(self.remove_cmnt_status_btn) + self.rvWindow = None + self.dockWidget = None + # annotations controls self.notes_layout = QtWidgets.QVBoxLayout() self.notes_layout_label = QtWidgets.QLabel("Annotations") @@ -132,15 +135,20 @@ def __init__(self): def runme(self, arg1=None, arg2=None): self.rvWindow = rv.qtutils.sessionWindow() - # Create DockWidget and add the Custom Widget to it - self.dockWidget = QtWidgets.QDockWidget("OpenPype Review", - self.rvWindow) - self.dockWidget.setWidget(self.customDockWidget) + if self.dockWidget is None: + # Create DockWidget and add the Custom Widget on first run + self.dockWidget = QtWidgets.QDockWidget("OpenPype Review", + self.rvWindow) + self.dockWidget.setWidget(self.customDockWidget) + + # Dock widget to the RV MainWindow + self.rvWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, + self.dockWidget) - # Dock widget to the RV MainWindow - self.rvWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, - self.dockWidget) - self.setup_listeners() + self.setup_listeners() + else: + # Toggle visibility state + self.dockWidget.toggleViewAction().trigger() def set_item_font(self, item, size=14, noweight=False, bold=True): font = QtGui.QFont() From d734b90b3ade43d0bebb57d93a04e100c4d442fd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:22:38 +0200 Subject: [PATCH 062/109] Parent the OpenPype tools to RV window --- .../openpype_menus-1.0/openpype_menus.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index b9e9df8af93..0a4578835f6 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -1,6 +1,7 @@ import os import json +import rv.qtutils from rv.rvtypes import MinorMode from rv.commands import isConsoleVisible, showConsole @@ -41,25 +42,27 @@ def __init__(self): if not isConsoleVisible(): showConsole() + self._parent = rv.qtutils.sessionWindow() + def create(self, event): print("Launching Creator") - host_tools.show_creator() + host_tools.show_creator(parent=self._parent) def load(self, event): print("Launching Loader") - host_tools.show_loader(use_context=True) + host_tools.show_loader(parent=self._parent, use_context=True) def publish(self, event): print("Launching Pyblish") - host_tools.show_publish() + host_tools.show_publish(parent=self._parent) def workfiles(self, event): print("Launching Workfiles") - host_tools.show_workfiles() + host_tools.show_workfiles(parent=self._parent) def scene_inventory(self, event): print("Launching inventory") - host_tools.show_scene_inventory() + host_tools.show_scene_inventory(parent=self._parent) def data_loader(): From b079095d70ea1e4937f9253a0f785f12c070e536 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:23:55 +0200 Subject: [PATCH 063/109] Cosmetics --- .../pkgs_source/openpype_menus-1.0/openpype_menus.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 0a4578835f6..c1a870d34da 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -66,11 +66,12 @@ def scene_inventory(self, event): def data_loader(): - incoming_data_file = os.environ.get("OPENPYPE_LOADER_REPRESENTATIONS", - None) + incoming_data_file = os.environ.get( + "OPENPYPE_LOADER_REPRESENTATIONS", None + ) if incoming_data_file: - with open(incoming_data_file, 'rb') as pypefile: - decoded_data = json.load(pypefile) + with open(incoming_data_file, 'rb') as file: + decoded_data = json.load(file) os.remove(incoming_data_file) load_data(dataset=decoded_data["representations"]) else: From 09fdae3250668cbe93d324be557b113c2f76ac67 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:25:22 +0200 Subject: [PATCH 064/109] Cleanup imports --- .../pkgs_source/openpype_menus-1.0/openpype_menus.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index c1a870d34da..2b9470cfc44 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -6,8 +6,12 @@ from rv.commands import isConsoleVisible, showConsole from openpype.tools.utils import host_tools - -from openpype.pipeline import install_host +from openpype.client import get_representations +from openpype.pipeline import ( + install_host, + discover_loader_plugins, + load_container +) from openpype.hosts.openrv.api import OpenRVHost @@ -79,9 +83,6 @@ def data_loader(): def load_data(dataset=None): - from openpype.pipeline.load import discover_loader_plugins - from openpype.pipeline import load_container - from openpype.client import get_representations project_name = os.environ["AVALON_PROJECT"] available_loaders = discover_loader_plugins(project_name) From eb74f7e49ef18d95189ae01d4ab2e68f26f14b46 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:30:03 +0200 Subject: [PATCH 065/109] Do nothing if there are no annotations --- .../hosts/openrv/startup/pkgs_source/comments/comments.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 15a146224e8..556815f820f 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -282,15 +282,20 @@ def get_gui_image(self, filename=None): def annotate_next(self): """Set frame to next annotated frame""" all_notes = self.get_annotated_for_view() + if not all_notes: + return nxt = get_cycle_frame(frame=rv.commands.frame(), frames_lookup=all_notes, direction="next") + rv.commands.setFrame(int(nxt)) rv.commands.redraw() def annotate_prev(self): """Set frame to previous annotated frame""" all_notes = self.get_annotated_for_view() + if not all_notes: + return previous = get_cycle_frame(frame=rv.commands.frame(), frames_lookup=all_notes, direction="prev") From dbcc9cc47e5cdadb43394778bc068722418b21bb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:31:36 +0200 Subject: [PATCH 066/109] Remove debugging method --- .../startup/pkgs_source/comments/comments.py | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 556815f820f..a5cd0630006 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -307,34 +307,6 @@ def get_annotated_for_view(self): annotated_frames = rv.extra_commands.findAnnotatedFrames() return annotated_frames - def echo_change_update(self): - print("CHANGE") - - print("node", self.current_loaded_viewnode) - node = self.current_loaded_viewnode - if node is None: - return - # representation - print("node properties", rv.commands.properties(node)) - prop_representation = node + ".openpype.representation" - prop_namespace = node + ".openpype.namespace" - data_prop_namespace = rv.commands.getStringProperty(prop_namespace)[0] - data_prop_representation_id = rv.commands.getStringProperty( - prop_representation - )[0] - print("data_prop_namespace", data_prop_namespace) - print("data_prop_representation_id", data_prop_representation_id) - from openpype.client import get_representations - project_name = os.environ["AVALON_PROJECT"] - representations = get_representations( - project_name, representation_ids=[data_prop_representation_id] - ) - print("REPR") - for rep in representations: - print(rep) - info = rv.extra_commands.sourceMetaInfoAtFrame(rv.commands.frame()) - print("info", info) - def get_task_status(self): import ftrack_api session = ftrack_api.Session(auto_connect_event_hub=False) From ee86ab6f7eced7b6fac9f278d3d67c1903f01bb9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:50:01 +0200 Subject: [PATCH 067/109] Always return the session window live because there can be multiple session windows --- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 2b9470cfc44..5a5a2370f94 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -46,7 +46,9 @@ def __init__(self): if not isConsoleVisible(): showConsole() - self._parent = rv.qtutils.sessionWindow() + @property + def _parent(self): + return rv.qtutils.sessionWindow() def create(self, event): print("Launching Creator") From dff3997b3f45807f1a9734f213cdf63bbee189ae Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:51:37 +0200 Subject: [PATCH 068/109] Add todo --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index a5cd0630006..1e2fe635efd 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -134,7 +134,8 @@ def __init__(self): def runme(self, arg1=None, arg2=None): self.rvWindow = rv.qtutils.sessionWindow() - + # TODO: There can actually be multiple session windows and as such we + # should actually be able to have a review widget per session window if self.dockWidget is None: # Create DockWidget and add the Custom Widget on first run self.dockWidget = QtWidgets.QDockWidget("OpenPype Review", From abbcca4061c0712c54ae0849e6cd5e9da63ec47f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 22:56:41 +0200 Subject: [PATCH 069/109] Only install once, not per session window --- .../pkgs_source/openpype_menus-1.0/openpype_menus.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 5a5a2370f94..3a25f5fd362 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -8,6 +8,7 @@ from openpype.tools.utils import host_tools from openpype.client import get_representations from openpype.pipeline import ( + registered_host, install_host, discover_loader_plugins, load_container @@ -99,6 +100,11 @@ def load_data(dataset=None): def createMode(): - install_openpype_to_host() - data_loader() + # This function triggers for each RV session window being opened, for + # example when using File > New Session this will trigger again. As such + # we only want to trigger the startup install when the host is not + # registered yet. + if not registered_host(): + install_openpype_to_host() + data_loader() return OpenPypeMenus() From ab8b0a1cd62c8f0a1329f1dac08feef3d3567d7f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 23:02:32 +0200 Subject: [PATCH 070/109] Remove unused import --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 1e2fe635efd..01deb72844c 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -1,5 +1,4 @@ # review code -import os from PySide2 import QtCore, QtWidgets, QtGui from rv.rvtypes import MinorMode From 28d352dac5f45f6098372ff89964185d911c3749 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 23:04:20 +0200 Subject: [PATCH 071/109] Works fine for multiple session windows since it initializes a class when the session windows gets opened --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 01deb72844c..f08b1b2a6e2 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -133,8 +133,6 @@ def __init__(self): def runme(self, arg1=None, arg2=None): self.rvWindow = rv.qtutils.sessionWindow() - # TODO: There can actually be multiple session windows and as such we - # should actually be able to have a review widget per session window if self.dockWidget is None: # Create DockWidget and add the Custom Widget on first run self.dockWidget = QtWidgets.QDockWidget("OpenPype Review", From 12b17b20a52ef1da66e47495fd5dff93ddc1dfb4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Apr 2023 23:14:28 +0200 Subject: [PATCH 072/109] Do not force show the RV console --- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 3a25f5fd362..26edad78f37 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -3,7 +3,6 @@ import rv.qtutils from rv.rvtypes import MinorMode -from rv.commands import isConsoleVisible, showConsole from openpype.tools.utils import host_tools from openpype.client import get_representations @@ -44,8 +43,6 @@ def __init__(self): ]) ] ) - if not isConsoleVisible(): - showConsole() @property def _parent(self): From 5c699258c345ed5fa1dc161f8f62acda86d71b60 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 00:39:09 +0200 Subject: [PATCH 073/109] Support loading image sequences --- .../hosts/openrv/plugins/load/load_frames.py | 132 +++++++++++++++++- 1 file changed, 127 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index 26a2c9920bf..626ef43f4e1 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -1,10 +1,19 @@ +import copy + +import clique + from openpype.pipeline import ( load, - get_representation_path + get_representation_context ) +from openpype.pipeline.load import get_representation_path_from_context + from openpype.hosts.openrv.api.pipeline import imprint_container -from openpype.hosts.openrv.api.commands import set_session_fps -from openpype.hosts.openrv.api.commands import reset_frame_range +from openpype.hosts.openrv.api.commands import ( + set_session_fps, + reset_frame_range +) +from openpype.lib.transcoding import IMAGE_EXTENSIONS import rv @@ -22,7 +31,7 @@ class FramesLoader(load.LoaderPlugin): def load(self, context, name=None, namespace=None, data=None): - filepath = self.fname + filepath = self._format_path(context) # Command fails on unicode so we must force it to be strings filepath = str(filepath) @@ -43,8 +52,11 @@ def load(self, context, name=None, namespace=None, data=None): def update(self, container, representation): node = container["node"] - filepath = get_representation_path(representation) + + context = get_representation_context(representation) + filepath = self._format_path(context) filepath = str(filepath) + set_session_fps() reset_frame_range() # change path @@ -61,3 +73,113 @@ def remove(self, container): # todo: implement remove # node = container["node"] return + + def _get_sequence_range(self, context): + """Return frame range for image sequences. + + The start and end frame is based on the start frame and end frame of + the representation or version documents. A single frame is never + considered to be a sequence. + + Warning: + If there are published sequences that do *not* have start and + end frame data in the database then this will FAIL to detect + it as a sequence. + + Args: + context (dict): Representation context. + + Returns: + tuple or None: (start, end) tuple if it is an image sequence + otherwise it returns None. + + """ + version = context.get("version", {}) + representation = context.get("representation", {}) + + # Only images may be sequences, not videos + ext = representation.get("ext", representation.get("name")) + if f".{ext}" not in IMAGE_EXTENSIONS: + return + + for doc in [representation, version]: + # Frame range can be set on version or representation. + # When set on representation it overrides version data. + data = doc.get("data", {}) + start = data.get("frameStartHandle", data.get("frameStart", None)) + end = data.get("frameEndHandle", data.get("frameEnd", None)) + + if start is None or end is None: + continue + + if start != end: + return start, end + else: + # Single frame + return + + # Fallback for image sequence that does not have frame start and frame + # end stored in the database. + if "frame" in representation.get("context", {}): + # Guess the frame range from the files + files = representation.get("files", []) + if len(files) > 1: + paths = [f["path"] for f in representation["files"]] + collections, _remainder = clique.assemble(paths) + if collections: + collection = collections[0] + frames = list(collection.indexes) + return frames[0], frames[-1] + + return + + def _format_path(self, context): + """Format the path with correct frame range. + + The openRV load command requires image sequences to be provided + with `{start}-{end}#` for its frame numbers, for example: + /path/to/sequence.1001-1010#.exr + + """ + + sequence_range = self._get_sequence_range(context) + if not sequence_range: + return get_representation_path_from_context(context) + + context = copy.deepcopy(context) + representation = context["representation"] + template = representation.get("data", {}).get("template") + if not template: + # No template to find token locations for + return get_representation_path_from_context(context) + + def _placeholder(key): + # Substitute with a long placeholder value so that potential + # custom formatting with padding doesn't find its way into + # our formatting, so that wouldn't be padded as 0 + return "___{}___".format(key) + + # We format UDIM and Frame numbers with their specific tokens. To do so + # we in-place change the representation context data to format the path + # with our own data + start, end = sequence_range + tokens = { + "frame": f"{start}-{end}#", + } + has_tokens = False + repre_context = representation["context"] + for key, _token in tokens.items(): + if key in repre_context: + repre_context[key] = _placeholder(key) + has_tokens = True + + # Replace with our custom template that has the tokens set + representation["data"]["template"] = template + path = get_representation_path_from_context(context) + + if has_tokens: + for key, token in tokens.items(): + if key in repre_context: + path = path.replace(_placeholder(key), token) + + return path From b31201942a7a58831d9bc72117c5bb4e253de9d4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 00:45:25 +0200 Subject: [PATCH 074/109] Allow any mov or mp4 extension --- openpype/hosts/openrv/plugins/load/load_mov.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index e398a80ab4c..2d3661ee749 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -16,7 +16,8 @@ class MovLoader(load.LoaderPlugin): label = "Load MOV" families = ["*"] - representations = ["mov"] + representations = ["*"] + extensions = ["mov", "mp4"] order = 0 icon = "code-fork" From 95aedf991b1aa4c6333d58101c1924c22b950e41 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 00:52:19 +0200 Subject: [PATCH 075/109] Allow loading more image extensions --- openpype/hosts/openrv/plugins/load/load_frames.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index 626ef43f4e1..4a5b8e3c450 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -23,7 +23,8 @@ class FramesLoader(load.LoaderPlugin): label = "Load Frames" families = ["*"] - representations = ["exr"] + representations = ["*"] + extensions = [ext.lstrip(".") for ext in IMAGE_EXTENSIONS] order = 0 icon = "code-fork" From 98874047002752e09f13ce71719647f48b1ea1e2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 00:58:38 +0200 Subject: [PATCH 076/109] Add temporary hack to avoid clash with `PyOpenColorIO` in OpenPype --- .../openpype_menus-1.0/openpype_menus.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 26edad78f37..ef8a3b4f30b 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -1,5 +1,7 @@ import os import json +import contextlib +import sys import rv.qtutils from rv.rvtypes import MinorMode @@ -15,6 +17,31 @@ from openpype.hosts.openrv.api import OpenRVHost +# TODO (Critical) Remove this temporary hack to avoid clash with PyOpenColorIO +# that is contained within OpenPype's venv +@contextlib.contextmanager +def no_openpype_env(): + paths = sys.path.copy() + venv_part = os.path.normpath("OpenPype/.venv") + minified_paths = [p for p in paths if venv_part not in os.path.normpath(p)] + try: + sys.path[:] = minified_paths + yield + finally: + sys.path[:] = paths + + +with no_openpype_env(): + # Ensure PyOpenColorIO is loaded from RV instead of from OpenPype lib + # Somehow this only works if we completely remove the OpenPype paths + # from sys.path. It fails if we just push them to the end of the list + # to try and force the search order to prioritize the RV packages + # Note: This will only work if PyOpenColorIO is not reloaded elsewhere + import importlib + import PyOpenColorIO + importlib.reload(PyOpenColorIO) + + def install_openpype_to_host(): host = OpenRVHost() install_host(host) From 37662411c78468abd1e6bbefd65ccffc64e24729 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 01:07:32 +0200 Subject: [PATCH 077/109] Add todo --- openpype/hosts/openrv/plugins/load/load_frames.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index 4a5b8e3c450..4af6b2253f5 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -121,6 +121,7 @@ def _get_sequence_range(self, context): # Fallback for image sequence that does not have frame start and frame # end stored in the database. + # TODO: Maybe rely on rv.commands.sequenceOfFile instead? if "frame" in representation.get("context", {}): # Guess the frame range from the files files = representation.get("files", []) From de683b38c94b877aa66abed07a10dbcff07e8822 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 01:09:46 +0200 Subject: [PATCH 078/109] Do not force show review tool on launch --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index f08b1b2a6e2..c45a169fe0f 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -129,8 +129,6 @@ def __init__(self): self.btn_note_prev.clicked.connect(self.annotate_prev) self.btn_note_next.clicked.connect(self.annotate_next) - self.runme() - def runme(self, arg1=None, arg2=None): self.rvWindow = rv.qtutils.sessionWindow() if self.dockWidget is None: From cead75f8497393387541408889ec3950b1e65458 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 01:47:03 +0200 Subject: [PATCH 079/109] Simplify lib clash workaround - Can now also support later reloads of the PyOpenColorIO - This might also help avoid other clashes of libs --- .../openpype_menus-1.0/openpype_menus.py | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index ef8a3b4f30b..7f22af8169c 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -1,7 +1,7 @@ import os import json -import contextlib import sys +import importlib import rv.qtutils from rv.rvtypes import MinorMode @@ -16,30 +16,23 @@ ) from openpype.hosts.openrv.api import OpenRVHost - # TODO (Critical) Remove this temporary hack to avoid clash with PyOpenColorIO # that is contained within OpenPype's venv -@contextlib.contextmanager -def no_openpype_env(): - paths = sys.path.copy() - venv_part = os.path.normpath("OpenPype/.venv") - minified_paths = [p for p in paths if venv_part not in os.path.normpath(p)] - try: - sys.path[:] = minified_paths - yield - finally: - sys.path[:] = paths - - -with no_openpype_env(): - # Ensure PyOpenColorIO is loaded from RV instead of from OpenPype lib - # Somehow this only works if we completely remove the OpenPype paths - # from sys.path. It fails if we just push them to the end of the list - # to try and force the search order to prioritize the RV packages - # Note: This will only work if PyOpenColorIO is not reloaded elsewhere - import importlib - import PyOpenColorIO - importlib.reload(PyOpenColorIO) +# Ensure PyOpenColorIO is loaded from RV instead of from OpenPype lib by +# moving all rv related paths to start of sys.path so RV libs are imported +# We consider the `/openrv` folder the root to `/openrv/bin/rv` executable +rv_root = os.path.normpath(os.path.dirname(os.path.dirname(sys.executable))) +rv_paths = [] +non_rv_paths = [] +for path in sys.path: + if os.path.normpath(path).startswith(rv_root): + rv_paths.append(path) + else: + non_rv_paths.append(path) +sys.path[:] = rv_paths + non_rv_paths + +import PyOpenColorIO # noqa +importlib.reload(PyOpenColorIO) def install_openpype_to_host(): From bdde431e2c3470ac38155076b497ff78ca72db68 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 02:12:47 +0200 Subject: [PATCH 080/109] Add init sorted order to make "Review" the last entry in the menu. --- .../hosts/openrv/startup/pkgs_source/comments/comments.py | 5 ++++- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index c45a169fe0f..f1b9d70a10c 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -55,7 +55,10 @@ class ReviewMenu(MinorMode): def __init__(self): MinorMode.__init__(self) self.init("py-ReviewMenu-mode", None, None, - [("OpenPype", [("Review", self.runme, None, None)])]) + [("OpenPype", [("Review", self.runme, None, None)])], + # initialization order + sortKey="source_setup", + ordering=20) # spacers self.verticalSpacer = QtWidgets.QSpacerItem( diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 7f22af8169c..399d7a16ea5 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -61,7 +61,10 @@ def __init__(self): # TODO: add separator if possible ("Work Files...", self.workfiles, None, None), ]) - ] + ], + # initialization order + sortKey="source_setup", + ordering=15 ) @property From da2d988ade4b90bd2ef573a7c885069541e86f45 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 12:33:11 +0200 Subject: [PATCH 081/109] Add OpenPype script editor (python interpreter widget) package to help debugging --- .../hosts/openrv/hooks/pre_setup_openrv.py | 4 +- openpype/hosts/openrv/startup/Mu/rvload2 | 1 + .../hosts/openrv/startup/Packages/rvinstall | 1 + .../pkgs_source/openpype_scripteditor/PACKAGE | 16 ++++++ .../openpype_scripteditor.py | 55 +++++++++++++++++++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE create mode 100644 openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py diff --git a/openpype/hosts/openrv/hooks/pre_setup_openrv.py b/openpype/hosts/openrv/hooks/pre_setup_openrv.py index ed2a10b5d30..1a4c2670fdb 100644 --- a/openpype/hosts/openrv/hooks/pre_setup_openrv.py +++ b/openpype/hosts/openrv/hooks/pre_setup_openrv.py @@ -22,7 +22,9 @@ def execute(self): # Redeploy the source packages to zips to auto-update # during development of OpenRV import zipfile - for package_name in ["comments", "openpype_menus-1.0"]: + for package_name in ["comments", + "openpype_menus-1.0", + "openpype_scripteditor"]: package_src = startup / "pkgs_source" / package_name package_dest = startup_packages / "{}.zip".format(package_name) self.log.info(f"Writing: {package_dest}") diff --git a/openpype/hosts/openrv/startup/Mu/rvload2 b/openpype/hosts/openrv/startup/Mu/rvload2 index ca2f24baab2..2899850a30c 100644 --- a/openpype/hosts/openrv/startup/Mu/rvload2 +++ b/openpype/hosts/openrv/startup/Mu/rvload2 @@ -1,3 +1,4 @@ 4 openpype_menus,openpype_menus-1.0.zip,nil,nil,nil,true,true,3.12,true,1.0.0 comments,comments.zip,nil,nil,nil,true,true,3.12,true,1.0.0 +openpype_scripteditor,openpype_scripteditor.zip,nil,nil,nil,true,true,3.12,false,1.0.0 diff --git a/openpype/hosts/openrv/startup/Packages/rvinstall b/openpype/hosts/openrv/startup/Packages/rvinstall index 4b4cf5c9943..06e233b2f81 100644 --- a/openpype/hosts/openrv/startup/Packages/rvinstall +++ b/openpype/hosts/openrv/startup/Packages/rvinstall @@ -1,2 +1,3 @@ *comments.zip *openpype_menus-1.0.zip +*openpype_scripteditor.zip diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE new file mode 100644 index 00000000000..e0d596d3821 --- /dev/null +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE @@ -0,0 +1,16 @@ +package: openpype_scripteditor +author: Roy Nieterau +organization: Artisan software Dobro +version: 1.0 +rv: 3.12 +openrv: 1.0.0 +requires: '' +optional: false + +modes: + - file: openpype_scripteditor + load: immediate + +description: | + +

Adds OpenPype Script Editor to OpenRV

diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py new file mode 100644 index 00000000000..da329773c14 --- /dev/null +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py @@ -0,0 +1,55 @@ +import rv.qtutils +from rv.rvtypes import MinorMode + +from qtpy import QtCore +from openpype_modules.python_console_interpreter.window import PythonInterpreterWidget # noqa + + +class OpenPypeMenus(MinorMode): + + def __init__(self): + MinorMode.__init__(self) + self.init( + name="py-openpype-scripteditor", + globalBindings=None, + overrideBindings=None, + menu=[ + # Menu name + # NOTE: If it already exists it will merge with existing + # and add submenus / menuitems to the existing one + ("Tools", [ + # Menuitem name, actionHook (event), key, stateHook + ("Script Editor", self.show_scripteditor, None, None), + ]) + ], + # initialization order + sortKey="source_setup", + ordering=25 + ) + + self._widget = None + + @property + def _parent(self): + return rv.qtutils.sessionWindow() + + def show_scripteditor(self, event): + """Show the console - create if not exists""" + if self._widget is not None: + self._widget.show() + self._widget.raise_() + return + + widget = PythonInterpreterWidget(parent=self._parent) + widget.setWindowTitle("Python Script Editor - OpenRV") + widget.setWindowFlags(widget.windowFlags() | + QtCore.Qt.Dialog | + QtCore.Qt.WindowMinimizeButtonHint) + widget.show() + widget.raise_() + + self._widget = widget + + +def createMode(): + return OpenPypeMenus() From bb8932ca40cbaf02b16bbc554f335ea0fc1c1a93 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 12:33:55 +0200 Subject: [PATCH 082/109] Tweak organization --- .../openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE index e0d596d3821..113df287b12 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE @@ -1,6 +1,6 @@ package: openpype_scripteditor author: Roy Nieterau -organization: Artisan software Dobro +organization: Colorbleed version: 1.0 rv: 3.12 openrv: 1.0.0 From 0fd51b2374351baffa535655f848b6477a294e63 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 16:00:37 +0200 Subject: [PATCH 083/109] Remove forcing of asset fps/frames + support removal of containers --- .../hosts/openrv/plugins/load/load_frames.py | 15 +++------------ openpype/hosts/openrv/plugins/load/load_mov.py | 16 ++++------------ 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index 4af6b2253f5..ce400b06f55 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -9,10 +9,6 @@ from openpype.pipeline.load import get_representation_path_from_context from openpype.hosts.openrv.api.pipeline import imprint_container -from openpype.hosts.openrv.api.commands import ( - set_session_fps, - reset_frame_range -) from openpype.lib.transcoding import IMAGE_EXTENSIONS import rv @@ -39,10 +35,7 @@ def load(self, context, name=None, namespace=None, data=None): # node_name = "{}_{}".format(namespace, name) if namespace else name namespace = namespace if namespace else context["asset"]["name"] - set_session_fps() - reset_frame_range() loaded_node = rv.commands.addSourceVerbose([filepath]) - print("loaded_node", loaded_node) imprint_container( loaded_node, name=name, @@ -58,8 +51,6 @@ def update(self, container, representation): filepath = self._format_path(context) filepath = str(filepath) - set_session_fps() - reset_frame_range() # change path rv.commands.setSourceMedia(node, [filepath]) # update name @@ -71,9 +62,9 @@ def update(self, container, representation): [str(representation["_id"])], True) def remove(self, container): - # todo: implement remove - # node = container["node"] - return + node = container["node"] + group = rv.commands.nodeGroup(node) + rv.commands.deleteNode(group) def _get_sequence_range(self, context): """Return frame range for image sequences. diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index 2d3661ee749..e9a593a73ad 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -1,7 +1,3 @@ -from openpype.hosts.openrv.api.commands import ( - set_session_fps, - reset_frame_range -) from openpype.pipeline import ( load, get_representation_path @@ -32,9 +28,6 @@ def load(self, context, name=None, namespace=None, data=None): # node_name = "{}_{}".format(namespace, name) if namespace else name namespace = namespace if namespace else context["asset"]["name"] - set_session_fps() - reset_frame_range() - loaded_node = rv.commands.addSourceVerbose([filepath]) imprint_container( loaded_node, @@ -48,8 +41,7 @@ def update(self, container, representation): node = container["node"] filepath = get_representation_path(representation) filepath = str(filepath) - set_session_fps() - reset_frame_range() + # change path rv.commands.setSourceMedia(node, [filepath]) # update name @@ -61,6 +53,6 @@ def update(self, container, representation): [str(representation["_id"])], True) def remove(self, container): - # todo: implement remove - # node = container["node"] - return + node = container["node"] + group = rv.commands.nodeGroup(node) + rv.commands.deleteNode(group) From 28e27fdd3897080b0e3dab9dcf4ffd2af999432c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 16:17:05 +0200 Subject: [PATCH 084/109] Fix RV Project Settings + remove unused redundant settings --- .../defaults/project_settings/openrv.json | 25 ---------- .../schemas/projects_schema/schema_main.json | 4 ++ .../schema_project_openrv.json | 16 ------ .../schemas/schema_openrv_create.json | 18 ------- .../schemas/schema_openrv_publish.json | 50 ------------------- 5 files changed, 4 insertions(+), 109 deletions(-) delete mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json delete mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json diff --git a/openpype/settings/defaults/project_settings/openrv.json b/openpype/settings/defaults/project_settings/openrv.json index 29b02a1cc3a..d506d8f2d77 100644 --- a/openpype/settings/defaults/project_settings/openrv.json +++ b/openpype/settings/defaults/project_settings/openrv.json @@ -10,30 +10,5 @@ "linux": [] } } - }, - "create": { - "CreateDebugLogs": { - "enabled": true, - "defaults": [] - } - }, - "publish": { - "ValidateWorkfilePaths": { - "enabled": true, - "optional": true, - "node_types": [], - "prohibited_vars": [] - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - } - }, - "workfile_build": { - "profiles": [] - }, - "templated_workfile_build": { - "profiles": [] } } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_main.json b/openpype/settings/entities/schemas/projects_schema/schema_main.json index 8c1d8ccbdd1..49c72d79696 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_main.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_main.json @@ -146,6 +146,10 @@ "type": "schema", "name": "schema_project_standalonepublisher" }, + { + "type": "schema", + "name": "schema_project_openrv" + }, { "type": "schema", "name": "schema_project_traypublisher" diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json index f412cea1faa..44437c1b3e4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json @@ -45,22 +45,6 @@ ] } ] - }, - { - "type": "schema", - "name": "schema_openrv_create" - }, - { - "type": "schema", - "name": "schema_openrv_publish" - }, - { - "type": "schema", - "name": "schema_workfile_build" - }, - { - "type": "schema", - "name": "schema_templated_workfile_build" } ] } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json deleted file mode 100644 index 2b4b2cdcf04..00000000000 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_create.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "type": "dict", - "collapsible": true, - "key": "create", - "label": "Creator plugins", - "children": [ - { - "type": "schema_template", - "name": "template_create_plugin", - "template_data": [ - { - "key": "CreateDebugLogs", - "label": "Create Asset debug" - } - ] - } - ] -} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json deleted file mode 100644 index aa6eaf51648..00000000000 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_openrv_publish.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "type": "dict", - "collapsible": true, - "key": "publish", - "label": "Publish plugins", - "children": [ - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ValidateWorkfilePaths", - "label": "Validate Workfile Paths", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "key": "node_types", - "label": "Node types", - "type": "list", - "object_type": "text" - }, - { - "key": "prohibited_vars", - "label": "Prohibited variables", - "type": "list", - "object_type": "text" - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateContainers", - "label": "ValidateContainers" - } - ] - } - ] -} \ No newline at end of file From 267f5d519f650d729398ec0793591edfab807fac Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Apr 2023 17:05:52 +0200 Subject: [PATCH 085/109] Use OCIO schemas like other hosts --- .../defaults/project_settings/openrv.json | 18 ++++----- .../schema_project_openrv.json | 38 ++++--------------- 2 files changed, 15 insertions(+), 41 deletions(-) diff --git a/openpype/settings/defaults/project_settings/openrv.json b/openpype/settings/defaults/project_settings/openrv.json index d506d8f2d77..bb7bc74a2de 100644 --- a/openpype/settings/defaults/project_settings/openrv.json +++ b/openpype/settings/defaults/project_settings/openrv.json @@ -1,14 +1,12 @@ { "imageio": { - "workfile": { - "OCIO_config": "aces_1.2", - "customOCIOConfigPath": { - "windows": [ - "PATHTOOCIO\\ocio\\1.2\\aces_1.2\\config.ocio" - ], - "darwin": [], - "linux": [] - } + "ocio_config": { + "enabled": true, + "filepath": [] + }, + "file_rules": { + "enabled": true, + "rules": {} } } -} \ No newline at end of file +} diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json index 44437c1b3e4..0fbdf80833f 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json @@ -10,39 +10,15 @@ "type": "dict", "label": "Color Management (ImageIO)", "collapsible": true, + "is_group": true, "children": [ { - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsible": false, - "children": [ - { - "type": "form", - "children": [ - { - "type": "enum", - "key": "OCIO_config", - "label": "OpenColorIO Config", - "enum_items": [ - { - "srgb": "srgb" - }, - { - "aces_1.2": "aces_1.2" - } - ] - }, - { - "type": "path", - "key": "customOCIOConfigPath", - "label": "Custom OCIO config path", - "multiplatform": true, - "multipath": true - } - ] - } - ] + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" } ] } From e3cf566aabb4748122a3359eb01d1bf3fe90b91d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 10:42:17 +0200 Subject: [PATCH 086/109] Allow import even when OpenPype hasn't initialized/installed --- .../openpype_scripteditor/openpype_scripteditor.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py index da329773c14..42b419b4fd7 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py @@ -2,7 +2,16 @@ from rv.rvtypes import MinorMode from qtpy import QtCore -from openpype_modules.python_console_interpreter.window import PythonInterpreterWidget # noqa + +# On OpenPype installation it moves `openpype.modules` entries into +# `openype_modules`. However, if OpenPype installation has not triggered yet. +# For example when the openpype_menus RV package hasn't loaded then the move +# of that package hasn't happened. So we'll allow both ways to import to ensure +# it is found +try: + from openpype_modules.python_console_interpreter.window import PythonInterpreterWidget # noqa +except ModuleNotFoundError: + from openpype.modules.python_console_interpreter.window import PythonInterpreterWidget # noqa class OpenPypeMenus(MinorMode): From f38e24e27638d2946f4724ded2a8a0db878e6b9f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 10:42:35 +0200 Subject: [PATCH 087/109] Fix typo --- .../pkgs_source/openpype_scripteditor/openpype_scripteditor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py index 42b419b4fd7..16abd2a5169 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py @@ -4,7 +4,7 @@ from qtpy import QtCore # On OpenPype installation it moves `openpype.modules` entries into -# `openype_modules`. However, if OpenPype installation has not triggered yet. +# `openpype_modules`. However, if OpenPype installation has not triggered yet. # For example when the openpype_menus RV package hasn't loaded then the move # of that package hasn't happened. So we'll allow both ways to import to ensure # it is found From 859217febc089cdf367d17d948a6af96cd2f9cb0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 12:32:40 +0200 Subject: [PATCH 088/109] Add checkbox active state to "review" menu entry --- .../startup/pkgs_source/comments/comments.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index f1b9d70a10c..478b8aa5a66 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -55,7 +55,12 @@ class ReviewMenu(MinorMode): def __init__(self): MinorMode.__init__(self) self.init("py-ReviewMenu-mode", None, None, - [("OpenPype", [("Review", self.runme, None, None)])], + [("OpenPype", [( + "Review", + self.runme, + None, + self._is_active)] + )], # initialization order sortKey="source_setup", ordering=20) @@ -149,6 +154,15 @@ def runme(self, arg1=None, arg2=None): # Toggle visibility state self.dockWidget.toggleViewAction().trigger() + def _is_active(self): + if self.dockWidget is None: + return rv.commands.UncheckedMenuState + + if self.dockWidget is not None and self.dockWidget.isVisible(): + return rv.commands.CheckedMenuState + else: + return rv.commands.UncheckedMenuState + def set_item_font(self, item, size=14, noweight=False, bold=True): font = QtGui.QFont() if bold: From 86ee0de3bf30d90f4ffc4c516bf024bfd76886f9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 16:03:17 +0200 Subject: [PATCH 089/109] Add separators to menu --- .../openrv/startup/pkgs_source/comments/comments.py | 10 ++++------ .../pkgs_source/openpype_menus-1.0/openpype_menus.py | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 478b8aa5a66..40f6f9e90bc 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -55,12 +55,10 @@ class ReviewMenu(MinorMode): def __init__(self): MinorMode.__init__(self) self.init("py-ReviewMenu-mode", None, None, - [("OpenPype", [( - "Review", - self.runme, - None, - self._is_active)] - )], + [("OpenPype", [ + ("_", None), # separator + ("Review", self.runme, None, self._is_active) + ])], # initialization order sortKey="source_setup", ordering=20) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 399d7a16ea5..60485dfded8 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -58,7 +58,7 @@ def __init__(self): ("Load...", self.load, None, None), ("Publish...", self.publish, None, None), ("Manage...", self.scene_inventory, None, None), - # TODO: add separator if possible + ("_", None), # separator ("Work Files...", self.workfiles, None, None), ]) ], From 41c72e8dc03f362f2a3434b6ee096938bef48e66 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 17:41:50 +0200 Subject: [PATCH 090/109] Ignore namespace attribute if it does not exist --- .../hosts/openrv/startup/pkgs_source/comments/comments.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 40f6f9e90bc..01e0c264f7a 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -199,9 +199,10 @@ def update_ui_attribs(self): # Use namespace as loaded shot label namespace = "" if node is not None: - namespace = rv.commands.getStringProperty( - "{}.openpype.namespace".format(node) - )[0] + property_name = "{}.openpype.namespace".format(node) + if rv.commands.propertyExists(property_name): + namespace = rv.commands.getStringProperty(property_name)[0] + self.current_loaded_shot.setText(namespace) self.setup_properties() @@ -210,7 +211,6 @@ def update_ui_attribs(self): def setup_combo_status(self): # setup properties node = self.current_loaded_viewnode - print(rv.commands.properties(node)) att_prop = node + ".openpype_review.task_status" status = self.current_shot_status.currentText() rv.commands.setStringProperty(att_prop, [str(status)], True) From 371ec048f0dddeda2a60a93eeb06341ddfd2e271 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 20:43:22 +0200 Subject: [PATCH 091/109] Cleanup, move import to top + improve docstring --- openpype/hosts/openrv/api/lib.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/openrv/api/lib.py b/openpype/hosts/openrv/api/lib.py index 10f4272a341..61ce022366b 100644 --- a/openpype/hosts/openrv/api/lib.py +++ b/openpype/hosts/openrv/api/lib.py @@ -1,5 +1,7 @@ import contextlib +import rv + @contextlib.contextmanager def maintained_selection(): @@ -12,15 +14,18 @@ def command_batch(name): def group_member_of_type(group_node, member_type): - """ - usage layout_stack = group_member_of_type(sequence_layout, "RVStack") - :param group_node: - :param member_type: - :return: - """ - # todo: move import to top of file? - import rv + """Return first member of group that is of the given node type. + This is similar to `rv.extra_commands.nodesInGroupOfType` but only + returns the first entry directly if it has any match. + + Args: + group_node (str): The group node to search in. + member_type (str): The node type to search for. + + Returns: + str or None: The first member found of given type or None + """ for node in rv.commands.nodesInGroup(group_node): if rv.commands.nodeType(node) == member_type: return node From fe1e776f592b1b39149ca91c1f6589dadebb6373 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 20:47:25 +0200 Subject: [PATCH 092/109] Implement draft functionality to set OCIO colorspace on file load and update. - Enabling the OCIO on the file nodes is quite hacky, see todo in the code. --- openpype/hosts/openrv/api/lib.py | 11 ++ openpype/hosts/openrv/api/ocio.py | 115 ++++++++++++++++++ .../hosts/openrv/plugins/load/load_frames.py | 29 ++++- .../hosts/openrv/plugins/load/load_mov.py | 33 ++++- 4 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 openpype/hosts/openrv/api/ocio.py diff --git a/openpype/hosts/openrv/api/lib.py b/openpype/hosts/openrv/api/lib.py index 61ce022366b..bcfaf987f6c 100644 --- a/openpype/hosts/openrv/api/lib.py +++ b/openpype/hosts/openrv/api/lib.py @@ -13,6 +13,17 @@ def command_batch(name): return +@contextlib.contextmanager +def active_view(node): + """Set active view during contet""" + original = rv.commands.viewNode() + try: + rv.commands.setViewNode(node) + yield + finally: + rv.commands.setViewNode(original) + + def group_member_of_type(group_node, member_type): """Return first member of group that is of the given node type. diff --git a/openpype/hosts/openrv/api/ocio.py b/openpype/hosts/openrv/api/ocio.py new file mode 100644 index 00000000000..6b848b1bc27 --- /dev/null +++ b/openpype/hosts/openrv/api/ocio.py @@ -0,0 +1,115 @@ +"""Helper functions to apply OCIO colorspace settings on groups. + +This tries to set the relevant OCIO settings on the group's look and render +pipeline similar to what the OpenColorIO Basic Color Management package does in +OpenRV through its `ocio_source_setup` python file. + +This assumes that the OpenColorIO Basic Color Management package of RV is both +installed and loaded. + +""" +import rv.commands +import rv.qtutils + +from .lib import ( + group_member_of_type, + active_view +) + + +class OCIONotActiveForGroup(RuntimeError): + """Error raised when OCIO is not enabled on the group node.""" + + +def get_group_ocio_look_node(group): + """Return OCIOLook node from source group""" + pipeline = group_member_of_type(group, "RVLookPipelineGroup") + if pipeline: + return group_member_of_type(pipeline, "OCIOLook") + + +def get_group_ocio_file_node(group): + """Return OCIOFile node from source group""" + pipeline = group_member_of_type(group, "RVLinearizePipelineGroup") + if pipeline: + return group_member_of_type(pipeline, "OCIOFile") + + +def set_group_ocio_colorspace(group, colorspace): + """Set the group's OCIOFile node ocio.inColorSpace property. + + This only works if OCIO is already 'active' for the group. T + + """ + import ocio_source_setup # noqa, RV OCIO package + node = get_group_ocio_file_node(group) + + if not node: + raise OCIONotActiveForGroup( + "Unable to find OCIOFile node for {}".format(group) + ) + + rv.commands.setStringProperty( + f"{node}.ocio.inColorSpace", [colorspace], True + ) + + +def set_current_ocio_active_state(state): + """Set the OCIO state for the currently active source. + + This is a hacky workaround to enable/disable the OCIO active state for + a source since it appears to be that there's no way to explicitly trigger + this callback from the `ocio_source_setup.OCIOSourceSetupMode` instance + which does these changes. + + """ + # TODO: Make this logic less hacky + # See: https://community.shotgridsoftware.com/t/how-to-enable-disable-ocio-and-set-ocio-colorspace-for-group-using-python/17178 # noqa + + group = rv.commands.viewNode() + ocio_node = get_group_ocio_file_node(group) + if state == bool(ocio_node): + # Already in correct state + return + + window = rv.qtutils.sessionWindow() + menu_bar = window.menuBar() + for action in menu_bar.actions(): + if action.text() != "OCIO" or action.toolTip() != "OCIO": + continue + + ocio_menu = action.menu() + + for ocio_action in ocio_menu.actions(): + if ocio_action.toolTip() == "File Color Space": + # The first entry is for "current source" instead + # of all sources so we need to break the for loop + # The first action of the file color space menu + # is the "Active" action. So lets take that one + active_action = ocio_action.menu().actions()[0] + + active_action.trigger() + return + + raise RuntimeError( + "Unable to set active state for current source. Make " + "sure the OCIO package is installed and loaded." + ) + + +def set_group_ocio_active_state(group, state): + """Set the OCIO state for the 'currently active source'. + + This is a hacky workaround to enable/disable the OCIO active state for + a source since it appears to be that there's no way to explicitly trigger + this callback from the `ocio_source_setup.OCIOSourceSetupMode` instance + which does these changes. + + """ + ocio_node = get_group_ocio_file_node(group) + if state == bool(ocio_node): + # Already in correct state + return + + with active_view(group): + set_current_ocio_active_state(state) diff --git a/openpype/hosts/openrv/plugins/load/load_frames.py b/openpype/hosts/openrv/plugins/load/load_frames.py index ce400b06f55..3b0bc2fafa2 100644 --- a/openpype/hosts/openrv/plugins/load/load_frames.py +++ b/openpype/hosts/openrv/plugins/load/load_frames.py @@ -7,9 +7,13 @@ get_representation_context ) from openpype.pipeline.load import get_representation_path_from_context +from openpype.lib.transcoding import IMAGE_EXTENSIONS from openpype.hosts.openrv.api.pipeline import imprint_container -from openpype.lib.transcoding import IMAGE_EXTENSIONS +from openpype.hosts.openrv.api.ocio import ( + set_group_ocio_active_state, + set_group_ocio_colorspace +) import rv @@ -36,6 +40,11 @@ def load(self, context, name=None, namespace=None, data=None): namespace = namespace if namespace else context["asset"]["name"] loaded_node = rv.commands.addSourceVerbose([filepath]) + + # update colorspace + self.set_representation_colorspace(loaded_node, + context["representation"]) + imprint_container( loaded_node, name=name, @@ -53,6 +62,10 @@ def update(self, container, representation): # change path rv.commands.setSourceMedia(node, [filepath]) + + # update colorspace + self.set_representation_colorspace(node, context["representation"]) + # update name rv.commands.setStringProperty(node + ".media.name", ["newname"], True) @@ -176,3 +189,17 @@ def _placeholder(key): path = path.replace(_placeholder(key), token) return path + + def set_representation_colorspace(self, node, representation): + colorspace_data = representation.get("data", {}).get("colorspaceData") + if colorspace_data: + colorspace = colorspace_data["colorspace"] + # TODO: Confirm colorspace is valid in current OCIO config + # otherwise errors will be spammed from OpenRV for invalid space + + self.log.info(f"Setting colorspace: {colorspace}") + group = rv.commands.nodeGroup(node) + + # Enable OCIO for the node and set the colorspace + set_group_ocio_active_state(group, state=True) + set_group_ocio_colorspace(group, colorspace) diff --git a/openpype/hosts/openrv/plugins/load/load_mov.py b/openpype/hosts/openrv/plugins/load/load_mov.py index e9a593a73ad..0d36e6c7bb7 100644 --- a/openpype/hosts/openrv/plugins/load/load_mov.py +++ b/openpype/hosts/openrv/plugins/load/load_mov.py @@ -1,8 +1,12 @@ from openpype.pipeline import ( load, - get_representation_path + get_representation_context ) from openpype.hosts.openrv.api.pipeline import imprint_container +from openpype.hosts.openrv.api.ocio import ( + set_group_ocio_active_state, + set_group_ocio_colorspace +) import rv @@ -29,6 +33,11 @@ def load(self, context, name=None, namespace=None, data=None): namespace = namespace if namespace else context["asset"]["name"] loaded_node = rv.commands.addSourceVerbose([filepath]) + + # update colorspace + self.set_representation_colorspace(loaded_node, + context["representation"]) + imprint_container( loaded_node, name=name, @@ -39,11 +48,17 @@ def load(self, context, name=None, namespace=None, data=None): def update(self, container, representation): node = container["node"] - filepath = get_representation_path(representation) + + context = get_representation_context(representation) + filepath = load.get_representation_path_from_context(context) filepath = str(filepath) # change path rv.commands.setSourceMedia(node, [filepath]) + + # update colorspace + self.set_representation_colorspace(node, context["representation"]) + # update name rv.commands.setStringProperty(node + ".media.name", ["newname"], True) @@ -56,3 +71,17 @@ def remove(self, container): node = container["node"] group = rv.commands.nodeGroup(node) rv.commands.deleteNode(group) + + def set_representation_colorspace(self, node, representation): + colorspace_data = representation.get("data", {}).get("colorspaceData") + if colorspace_data: + colorspace = colorspace_data["colorspace"] + # TODO: Confirm colorspace is valid in current OCIO config + # otherwise errors will be spammed from OpenRV for invalid space + + self.log.info(f"Setting colorspace: {colorspace}") + group = rv.commands.nodeGroup(node) + + # Enable OCIO for the node and set the colorspace + set_group_ocio_active_state(group, state=True) + set_group_ocio_colorspace(group, colorspace) From d1ceeff7964bd3312a65df1ddbfcad5b7a385323 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 21:53:02 +0200 Subject: [PATCH 093/109] Add active state to the menu --- .../openpype_scripteditor.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py index 16abd2a5169..2333fff3adc 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py @@ -1,3 +1,4 @@ +import rv.commands import rv.qtutils from rv.rvtypes import MinorMode @@ -28,7 +29,12 @@ def __init__(self): # and add submenus / menuitems to the existing one ("Tools", [ # Menuitem name, actionHook (event), key, stateHook - ("Script Editor", self.show_scripteditor, None, None), + ( + "Script Editor", + self.show_scripteditor, + None, + self.is_active + ), ]) ], # initialization order @@ -45,9 +51,15 @@ def _parent(self): def show_scripteditor(self, event): """Show the console - create if not exists""" if self._widget is not None: - self._widget.show() - self._widget.raise_() - return + if self._widget.isVisible(): + # Closing also saves the scripts directly. + # Thus we prefer to close instead of hide here + self._widget.close() + return + else: + self._widget.show() + self._widget.raise_() + return widget = PythonInterpreterWidget(parent=self._parent) widget.setWindowTitle("Python Script Editor - OpenRV") @@ -59,6 +71,12 @@ def show_scripteditor(self, event): self._widget = widget + def is_active(self): + if self._widget is not None and self._widget.isVisible(): + return rv.commands.CheckedMenuState + else: + return rv.commands.UncheckedMenuState + def createMode(): return OpenPypeMenus() From b0970445571cfe0d21009ad2d7863c426e9ef4f5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 21:53:26 +0200 Subject: [PATCH 094/109] Remove redundant check, it's already checked in other if statement --- openpype/hosts/openrv/startup/pkgs_source/comments/comments.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py index 01e0c264f7a..0b364cc8269 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py +++ b/openpype/hosts/openrv/startup/pkgs_source/comments/comments.py @@ -153,9 +153,6 @@ def runme(self, arg1=None, arg2=None): self.dockWidget.toggleViewAction().trigger() def _is_active(self): - if self.dockWidget is None: - return rv.commands.UncheckedMenuState - if self.dockWidget is not None and self.dockWidget.isVisible(): return rv.commands.CheckedMenuState else: From 1b2683512574738e204a5a072c5d13ba3869132d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Apr 2023 21:54:04 +0200 Subject: [PATCH 095/109] Hound --- .../pkgs_source/openpype_scripteditor/openpype_scripteditor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py index 2333fff3adc..2cb26e438f2 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/openpype_scripteditor.py @@ -34,7 +34,7 @@ def __init__(self): self.show_scripteditor, None, self.is_active - ), + ), ]) ], # initialization order From 262147d4aaa5ad6f2aa2db38246f130aa90080b3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 12:10:36 +0200 Subject: [PATCH 096/109] Add support for open last workfile --- openpype/hooks/pre_add_last_workfile_arg.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hooks/pre_add_last_workfile_arg.py b/openpype/hooks/pre_add_last_workfile_arg.py index 2558daef303..57916d2fb50 100644 --- a/openpype/hooks/pre_add_last_workfile_arg.py +++ b/openpype/hooks/pre_add_last_workfile_arg.py @@ -25,7 +25,8 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): "blender", "photoshop", "tvpaint", - "aftereffects" + "aftereffects", + "openrv" ] def execute(self): From 85483c51e3890174bd459e7a0448247de2189ab1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 13:56:03 +0200 Subject: [PATCH 097/109] Refactor to using new publisher --- openpype/hosts/openrv/api/lib.py | 21 +-- openpype/hosts/openrv/api/pipeline.py | 157 ++++++++++++------ .../plugins/create/create_annotations.py | 113 +++++++++++++ .../openrv/plugins/create/create_workfile.py | 97 +++++++++++ .../plugins/publish/collect_annotations.py | 112 ------------- .../publish/collect_loaded_containers.py | 45 ----- .../plugins/publish/collect_workfile.py | 46 ++--- .../openpype_menus-1.0/openpype_menus.py | 11 +- 8 files changed, 342 insertions(+), 260 deletions(-) create mode 100644 openpype/hosts/openrv/plugins/create/create_annotations.py create mode 100644 openpype/hosts/openrv/plugins/create/create_workfile.py delete mode 100644 openpype/hosts/openrv/plugins/publish/collect_annotations.py delete mode 100644 openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py diff --git a/openpype/hosts/openrv/api/lib.py b/openpype/hosts/openrv/api/lib.py index bcfaf987f6c..4d3fcd43243 100644 --- a/openpype/hosts/openrv/api/lib.py +++ b/openpype/hosts/openrv/api/lib.py @@ -4,24 +4,21 @@ @contextlib.contextmanager -def maintained_selection(): - return - - -@contextlib.contextmanager -def command_batch(name): - return +def maintained_view(): + """Reset to original view node after context""" + original = rv.commands.viewNode() + try: + yield + finally: + rv.commands.setViewNode(original) @contextlib.contextmanager def active_view(node): - """Set active view during contet""" - original = rv.commands.viewNode() - try: + """Set active view during context""" + with maintained_view(): rv.commands.setViewNode(node) yield - finally: - rv.commands.setViewNode(original) def group_member_of_type(group_node, member_type): diff --git a/openpype/hosts/openrv/api/pipeline.py b/openpype/hosts/openrv/api/pipeline.py index 9a7fb3ab0aa..79beba16b29 100644 --- a/openpype/hosts/openrv/api/pipeline.py +++ b/openpype/hosts/openrv/api/pipeline.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -import contextlib import os +import json from collections import OrderedDict import pyblish import rv -from openpype.host import HostBase, ILoadHost, IWorkfileHost +from openpype.host import HostBase, ILoadHost, IWorkfileHost, IPublishHost from openpype.hosts.openrv import OPENRV_ROOT_DIR from openpype.pipeline import ( register_loader_plugin_path, @@ -14,7 +14,6 @@ register_creator_plugin_path, AVALON_CONTAINER_ID, ) -from . import lib PLUGINS_DIR = os.path.join(OPENRV_ROOT_DIR, "plugins") PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") @@ -22,10 +21,11 @@ CREATE_PATH = os.path.join(PLUGINS_DIR, "create") INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") -OPENPYPE_ATTR_PREFIX = "openpype" +OPENPYPE_ATTR_PREFIX = "openpype." +JSON_PREFIX = "JSON:::" -class OpenRVHost(HostBase, IWorkfileHost, ILoadHost): +class OpenRVHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): name = "openrv" def __init__(self): @@ -62,48 +62,110 @@ def get_current_workfile(self): return filename def workfile_has_unsaved_changes(self): - # dont ask to save if we are on the startup scene without a name - # set to untitled project and return False - print("filename", rv.commands.sessionFileName()) + # RV has `State.unsavedChanges` attribute however that appears to + # always return false and is never set to be true. As such, for now + # we always return False. return False def get_workfile_extensions(self): return [".rv"] def get_containers(self): - """Get containers. - """ - all_items = gather_containers() - for rvnode in all_items: - parsed = parse_container(rvnode) - yield parsed + for container in get_containers(): + yield container - @contextlib.contextmanager - def maintained_selection(self): - with lib.maintained_selection(): - yield + def update_context_data(self, data, changes): + imprint("root", data, prefix=OPENPYPE_ATTR_PREFIX) + def get_context_data(self): + return read("root", prefix=OPENPYPE_ATTR_PREFIX) -def imprint(node, data): - """Store string attributes with value on a node + +def imprint(node, data, prefix=None): + """Store attributes with value on a node. Args: node (object): The node to imprint data on. data (dict): Key value pairs of attributes to create. - group (str): The Group to add the attributes to. + prefix (str): A prefix to add to all keys in the data. Returns: None """ + node_prefix = f"{node}.{prefix}" if prefix else f"{node}." for attr, value in data.items(): # Create and set the attribute - prop = "{}.{}.{}".format(node, OPENPYPE_ATTR_PREFIX, attr) + prop = f"{node_prefix}.{attr}" + + if isinstance(value, (dict, list, tuple)): + value = f"{JSON_PREFIX}{json.dumps(value)}" + + if isinstance(value, (bool, int)): + type_name = "Int" + elif isinstance(value, float): + type_name = "Float" + elif isinstance(value, str): + type_name = "String" + else: + raise TypeError("Unsupport data type to imprint: " + "{} (type: {})".format(value, type(value))) + if not rv.commands.propertyExists(prop): - rv.commands.newProperty(prop, rv.commands.StringType, 1) - rv.commands.setStringProperty(prop, [str(value)], True) + type_ = getattr(rv.commands, f"{type_name}Type") + rv.commands.newProperty(prop, type_, 1) + set_property = getattr(rv.commands, f"set{type_name}Property") + set_property(prop, [value], True) + + +def read(node, prefix=None): + """Read properties from the given node with the values + + This function assumes all read values are of a single width and will + return only the first entry. As such, arrays or multidimensional properties + will not be returned correctly. + + Args: + node (str): Name of node. + prefix (str, optional): A prefix for the attributes to consider. + This prefix will be stripped from the output key. + + Returns: + dict: The key, value of the properties. - return + """ + properties = rv.commands.properties(node) + node_prefix = f"{node}.{prefix}" if prefix else f"{node}." + type_getters = { + 1: rv.commands.getFloatProperty, + 2: rv.commands.getIntProperty, + # Not sure why 3, 4 and 5 don't seem to be types + 5: rv.commands.getHalfProperty, + 6: rv.commands.getByteProperty, + 8: rv.commands.getStringProperty + } + + data = {} + for prop in properties: + if prefix is not None and not prop.startswith(node_prefix): + continue + + info = rv.commands.propertyInfo(prop) + type_num = info["type"] + value = type_getters[type_num](prop) + if value: + value = value[0] + else: + value = None + + if type_num == 8 and value and value.strip().startswith(JSON_PREFIX): + # String + value = json.loads(value.strip()[len(JSON_PREFIX):]) + + key = prop[len(node_prefix):] + data[key] = value + + return data def imprint_container(node, name, namespace, context, loader): @@ -123,18 +185,18 @@ def imprint_container(node, name, namespace, context, loader): data = [ ("schema", "openpype:container-2.0"), - ("id", AVALON_CONTAINER_ID), - ("name", name), - ("namespace", namespace), - ("loader", loader), - ("representation", context["representation"]["_id"]) + ("id", str(AVALON_CONTAINER_ID)), + ("name", str(name)), + ("namespace", str(namespace)), + ("loader", str(loader)), + ("representation", str(context["representation"]["_id"])) ] # We use an OrderedDict to make sure the attributes # are always created in the same order. This is solely # to make debugging easier when reading the values in # the attribute editor. - imprint(node, OrderedDict(data)) + imprint(node, OrderedDict(data), prefix=OPENPYPE_ATTR_PREFIX) def parse_container(node): @@ -149,11 +211,11 @@ def parse_container(node): data = {} for key in required: - attr = node + "." + OPENPYPE_ATTR_PREFIX + "." + key - if not rv.commands.propertyExists(attr): + prop = f"{node}.{OPENPYPE_ATTR_PREFIX}{key}" + if not rv.commands.propertyExists(prop): return - value = rv.commands.getStringProperty(attr)[0] + value = rv.commands.getStringProperty(prop)[0] data[key] = value # Store the node's name @@ -165,22 +227,19 @@ def parse_container(node): return data -def gather_containers(): - """gathers all rv nodes list - """ - all_files = [] - all_nodes = rv.commands.nodes() - for node in all_nodes: - prop = node + "." + OPENPYPE_ATTR_PREFIX + ".schema" +def get_container_nodes(): + """Return a list of node names that are marked as loaded container.""" + container_nodes = [] + for node in rv.commands.nodes(): + prop = f"{node}.{OPENPYPE_ATTR_PREFIX}schema" if rv.commands.propertyExists(prop): - all_files.append(node) - return set(all_files) + container_nodes.append(node) + return container_nodes def get_containers(): - """Get containers. - """ - all_items = gather_containers() - for rvnode in all_items: - parsed = parse_container(rvnode) - yield parsed + """Yield container data for each container found in current workfile.""" + for node in get_container_nodes(): + container = parse_container(node) + if container: + yield container diff --git a/openpype/hosts/openrv/plugins/create/create_annotations.py b/openpype/hosts/openrv/plugins/create/create_annotations.py new file mode 100644 index 00000000000..2731310978b --- /dev/null +++ b/openpype/hosts/openrv/plugins/create/create_annotations.py @@ -0,0 +1,113 @@ +import qtawesome +import rv + +from openpype.client import get_representations +from openpype.hosts.openrv.api.pipeline import get_containers +from openpype.hosts.openrv.api import lib +from openpype.pipeline import get_current_project_name + +from openpype.pipeline import ( + AutoCreator, + CreatedInstance, +) + + +class AnnotationCreator(AutoCreator): + identifier = "annotation" + family = "annotation" + label = "annotation" + + default_variant = "Main" + + create_allow_context_change = False + + def create(self, options=None): + # We never create an instance since it's collected from user + # drawn annotations + pass + + def collect_instances(self): + + project_name = get_current_project_name() + + # Query the representations in one go (optimization) + # TODO: We could optimize more by first checking annotated frames + # and then only query the representations for those containers + # that have any annotated frames. + containers = list(get_containers()) + representation_ids = set(c["representation"] for c in containers) + representations = get_representations( + project_name, representation_ids=representation_ids + ) + representations_by_id = { + str(repre["_id"]): repre for repre in representations + } + + with lib.maintained_view(): + for container in containers: + self._collect_container(container, representations_by_id) + + def _collect_container(self, container, representations_by_id): + + node = container["node"] + self.log.debug(f"Processing container node: {node}") + + # View this particular group to get its marked and annotated frames + source_group = rv.commands.nodeGroup(node) + rv.commands.setViewNode(source_group) + annotated_frames = rv.extra_commands.findAnnotatedFrames() + if not annotated_frames: + return + + namespace = container["namespace"] + repre_id = container["representation"] + repre_doc = representations_by_id.get(repre_id) + if not repre_doc: + # This could happen if for example a representation was loaded + # through the library loader + self.log.warning(f"No representation found in database for " + f"container: {container}") + return + + repre_context = repre_doc["context"] + source_representation_asset = repre_context["asset"] + source_representation_task = repre_context["task"]["name"] + + # QUESTION Do we want to do anything with marked frames? + # for marked in marked_frames: + # print("MARKED ------------ ", container, marked, source_group) + + for noted_frame in annotated_frames: + print(f"Found annotation for {source_group} frame {noted_frame}") + + subset_name = f"note_{namespace}_{noted_frame}" + data = { + "tags": ["review", "ftrackreview"], + "task": source_representation_task, + "asset": source_representation_asset, + "subset": subset_name, + "label": subset_name, + "publish": True, + "review": True, + "annotated_frame": noted_frame, + + # TODO: Retrieve actual review comment for annotated frame + "comment": "NEW COMMENT FROM UI {}".format(noted_frame), + } + + instance = CreatedInstance( + family=self.family, + subset_name=data["subset"], + data=data, + creator=self + ) + + self._add_instance_to_context(instance) + + def update_instances(self, update_list): + # TODO: Implement storage of annotation instance settings + # Need to define where to store the annotation instance data. + pass + + def get_icon(self): + return qtawesome.icon("fa.comments", color="white") diff --git a/openpype/hosts/openrv/plugins/create/create_workfile.py b/openpype/hosts/openrv/plugins/create/create_workfile.py new file mode 100644 index 00000000000..d55056cae36 --- /dev/null +++ b/openpype/hosts/openrv/plugins/create/create_workfile.py @@ -0,0 +1,97 @@ +import qtawesome + +from openpype.hosts.openrv.api.pipeline import ( + read, imprint +) +from openpype.client import get_asset_by_name +from openpype.pipeline import ( + AutoCreator, + CreatedInstance, + legacy_io +) + + +class OpenRVWorkfileCreator(AutoCreator): + identifier = "workfile" + family = "workfile" + label = "Workfile" + + default_variant = "Main" + + create_allow_context_change = False + + data_store_node = "root" + data_store_prefix = "openpype_workfile." + + def collect_instances(self): + + data = read(node=self.data_store_node, + prefix=self.data_store_prefix) + if not data: + return + + instance = CreatedInstance( + family=self.family, + subset_name=data["subset"], + data=data, + creator=self + ) + + self._add_instance_to_context(instance) + + def update_instances(self, update_list): + for created_inst, _changes in update_list: + data = created_inst.data_to_store() + imprint(node=self.data_store_node, + data=data, + prefix=self.data_store_prefix) + + def create(self, options=None): + + existing_instance = None + for instance in self.create_context.instances: + if instance.family == self.family: + existing_instance = instance + break + + project_name = legacy_io.Session["AVALON_PROJECT"] + asset_name = legacy_io.Session["AVALON_ASSET"] + task_name = legacy_io.Session["AVALON_TASK"] + host_name = legacy_io.Session["AVALON_APP"] + + if existing_instance is None: + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, task_name, asset_doc, + project_name, host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant + } + data.update(self.get_dynamic_data( + self.default_variant, task_name, asset_doc, + project_name, host_name, None + )) + + new_instance = CreatedInstance( + self.family, subset_name, data, self + ) + self._add_instance_to_context(new_instance) + + elif ( + existing_instance["asset"] != asset_name + or existing_instance["task"] != task_name + ): + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, task_name, asset_doc, + project_name, host_name + ) + existing_instance["asset"] = asset_name + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name + + def get_icon(self): + return qtawesome.icon("fa.file-o", color="white") diff --git a/openpype/hosts/openrv/plugins/publish/collect_annotations.py b/openpype/hosts/openrv/plugins/publish/collect_annotations.py deleted file mode 100644 index 039a5185dd4..00000000000 --- a/openpype/hosts/openrv/plugins/publish/collect_annotations.py +++ /dev/null @@ -1,112 +0,0 @@ -import os -import pyblish.api - -from openpype.client import get_representations -from openpype.hosts.openrv.api.pipeline import gather_containers -from openpype.pipeline import ( - legacy_io -) - - -class CollectSessionAnnotations(pyblish.api.ContextPlugin): - """Collect session Annotations - """ - - order = pyblish.api.CollectorOrder - 0.02 - label = "Collect Session Annotations" - hosts = ["openrv"] - family = "annotation" - - def process(self, context): - """Inject collection of annotated frames""" - import rv - - project_name = legacy_io.Session["AVALON_PROJECT"] - source_groups = [] - all_nodes = gather_containers() - for container in all_nodes: - - self.log.debug("Container {} with properties: {}".format( - container, rv.commands.properties(container) - )) - prop_namespace = container + ".openpype.namespace" - prop_representation = container + ".openpype.representation" - data_prop_namespace = rv.commands.getStringProperty( - prop_namespace - )[0] - data_prop_representation_id = rv.commands.getStringProperty( - prop_representation - )[0] - - representations = get_representations( - project_name, representation_ids=[data_prop_representation_id] - ) - first_repre = next(iter(representations)) # first representation - repre_context = first_repre["context"] - # source_representation_project = repre_context["project"]["name"] - source_representation_asset = repre_context["asset"] - source_representation_task = repre_context["task"]["name"] - # source_representation_subset = repre_context["subset"] - source_group = rv.commands.nodeGroup(container) - print("SOURCE GROUP ", source_group) - source_groups.append(source_group) - rv.commands.setViewNode(source_group) - rv.commands.redraw() - - marked_frames = rv.commands.markedFrames() - annotated_frames = rv.extra_commands.findAnnotatedFrames() - - asset_folder, file = os.path.split(rv.commands.sessionFileName()) - - for marked in marked_frames: - print("MARKED ------------ ", container, marked, source_group) - - for noted_frame in annotated_frames: - print("NOTED ------- ", container, noted_frame, source_group) - rv.commands.setFrame(int(noted_frame)) - rv.commands.redraw() - - instance = context.create_instance(name=str(container)) - item_name = "note_{}_{}".format( - data_prop_namespace, noted_frame - ) - - # annotation_representation = { - # "tags": ["review", "ftrackreview"], - # "name": "thumbnail", - # "ext": "jpg", - # "files": "frames_1001.jpg", - # "stagingDir": "path\\to\\prepDaily", - # # "thumbnail": True, - # # "comment": "NEW COMMENT FROM UI" - # "frameStart": noted_frame, - # "frameEnd": noted_frame, - # "fps": "25", - # } - - data = { - "subset": "annotation_{}".format(str(noted_frame)), - "tags": ["review", "ftrackreview"], - "asset": source_representation_asset, - "task": source_representation_task, - "label": str(item_name), - "publish": True, - "review": True, - "family": "annotation", - # "setMembers": [""], - "asset_folder_path": str(asset_folder), - "annotated_frame": str(noted_frame), - "comment": "NEW COMMENT FROM UI {}".format(noted_frame), - } - - instance.data.update(data) - # - # if "representations" not in instance.data: - # instance.data["representations"] = [] - # - # instance.data["representations"].append( - # annotation_representation - # ) - - # view_node = rv.commands.viewNode() - # intent = context.data.get("intent") diff --git a/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py b/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py deleted file mode 100644 index 4e5ad9a98af..00000000000 --- a/openpype/hosts/openrv/plugins/publish/collect_loaded_containers.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import pyblish.api -from openpype.hosts.openrv.api.pipeline import get_containers -from openpype.pipeline import ( - legacy_io -) - - -class CollectSessionContainers(pyblish.api.ContextPlugin): - """Collect session containers - """ - - order = pyblish.api.CollectorOrder - 0.01 - label = "Collect Session Containers" - hosts = ["openrv"] - family = "containers" - - def process(self, context): - """Inject the current camera output and file""" - - task = legacy_io.Session["AVALON_TASK"] - item_collection = get_containers() - - # create instances - for item in item_collection: - self.log.debug(item) - item_name = item["namespace"] - instance = context.create_instance(name=str(item_name)) - subset = 'container' + task.capitalize() - - data = {} - data.update({ - "subset": subset, - "asset": os.getenv("AVALON_ASSET", None), - "label": str(item_name), - "publish": False, - "family": 'containers', - "setMembers": [""], - "frameStart": context.data['frameStart'], - "frameEnd": context.data['frameEnd'], - "handleStart": context.data['handleStart'], - "handleEnd": context.data['handleEnd'], - }) - - instance.data.update(data) diff --git a/openpype/hosts/openrv/plugins/publish/collect_workfile.py b/openpype/hosts/openrv/plugins/publish/collect_workfile.py index 97fb1e08060..b4eb5f72b5d 100644 --- a/openpype/hosts/openrv/plugins/publish/collect_workfile.py +++ b/openpype/hosts/openrv/plugins/publish/collect_workfile.py @@ -1,58 +1,34 @@ import os import pyblish.api -from openpype.pipeline import ( - legacy_io, - registered_host -) +from openpype.pipeline import registered_host -class CollectWorkfile(pyblish.api.ContextPlugin): +class CollectWorkfile(pyblish.api.InstancePlugin): """Inject the current working file into context""" order = pyblish.api.CollectorOrder - 0.01 label = "OpenRV Session Workfile" - hosts = ['openrv'] + hosts = ["openrv"] + families = ["workfile"] - def process(self, context): + def process(self, instance): """Inject the current working file""" host = registered_host() current_file = host.get_current_workfile() if not current_file: - self.log.error("No current filepath detected") - return + self.log.error("No current filepath detected. " + "Make sure to save your OpenRV session") + current_file = "" folder, file = os.path.split(current_file) filename, ext = os.path.splitext(file) - task = legacy_io.Session["AVALON_TASK"] - - # create instance - instance = context.create_instance(name=filename) - subset = 'workfile' + task.capitalize() - - context.data['currentFile'] = current_file - - data = {} - data.update({ - "subset": subset, - "asset": os.getenv("AVALON_ASSET", None), - "label": subset, - "publish": True, - "family": 'workfile', - "families": ['workfile'], - "setMembers": [current_file], - "frameStart": context.data['frameStart'], - "frameEnd": context.data['frameEnd'], - "handleStart": context.data['handleStart'], - "handleEnd": context.data['handleEnd'] - }) - - data['representations'] = [{ + instance.context.data["currentFile"] = current_file + + instance.data['representations'] = [{ 'name': ext.lstrip("."), 'ext': ext.lstrip("."), 'files': file, "stagingDir": folder, }] - - instance.data.update(data) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 60485dfded8..1eafaff4664 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -72,23 +72,20 @@ def _parent(self): return rv.qtutils.sessionWindow() def create(self, event): - print("Launching Creator") - host_tools.show_creator(parent=self._parent) + host_tools.show_publisher(parent=self._parent, + tab="create") def load(self, event): - print("Launching Loader") host_tools.show_loader(parent=self._parent, use_context=True) def publish(self, event): - print("Launching Pyblish") - host_tools.show_publish(parent=self._parent) + host_tools.show_publisher(parent=self._parent, + tab="publish") def workfiles(self, event): - print("Launching Workfiles") host_tools.show_workfiles(parent=self._parent) def scene_inventory(self, event): - print("Launching inventory") host_tools.show_scene_inventory(parent=self._parent) From 367884e7d00d4974ff0510d60ec5556c5ebd5bae Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 14:01:25 +0200 Subject: [PATCH 098/109] Expose library loader --- .../startup/pkgs_source/openpype_menus-1.0/openpype_menus.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py index 1eafaff4664..9e06a711a99 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py @@ -58,6 +58,7 @@ def __init__(self): ("Load...", self.load, None, None), ("Publish...", self.publish, None, None), ("Manage...", self.scene_inventory, None, None), + ("Library...", self.library, None, None), ("_", None), # separator ("Work Files...", self.workfiles, None, None), ]) @@ -88,6 +89,9 @@ def workfiles(self, event): def scene_inventory(self, event): host_tools.show_scene_inventory(parent=self._parent) + def library(self, event): + host_tools.show_library_loader(parent=self._parent) + def data_loader(): incoming_data_file = os.environ.get( From db24d5bed985c42d3cfba5b139bde2b82e84acd1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 14:14:36 +0200 Subject: [PATCH 099/109] Add todo --- openpype/hosts/openrv/plugins/create/create_annotations.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/openrv/plugins/create/create_annotations.py b/openpype/hosts/openrv/plugins/create/create_annotations.py index 2731310978b..89a10c88d28 100644 --- a/openpype/hosts/openrv/plugins/create/create_annotations.py +++ b/openpype/hosts/openrv/plugins/create/create_annotations.py @@ -53,6 +53,8 @@ def _collect_container(self, container, representations_by_id): self.log.debug(f"Processing container node: {node}") # View this particular group to get its marked and annotated frames + # TODO: This will only find annotations on the actual source group + # and not for e.g. the source in the `defaultSequence`. source_group = rv.commands.nodeGroup(node) rv.commands.setViewNode(source_group) annotated_frames = rv.extra_commands.findAnnotatedFrames() From d7c46307af4624e58e10c09148f508be0e2457e3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 14:46:13 +0200 Subject: [PATCH 100/109] Define subset name correctly using templates --- .../plugins/create/create_annotations.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/openrv/plugins/create/create_annotations.py b/openpype/hosts/openrv/plugins/create/create_annotations.py index 89a10c88d28..144aca87e5c 100644 --- a/openpype/hosts/openrv/plugins/create/create_annotations.py +++ b/openpype/hosts/openrv/plugins/create/create_annotations.py @@ -1,7 +1,7 @@ import qtawesome import rv -from openpype.client import get_representations +from openpype.client import get_representations, get_asset_by_name from openpype.hosts.openrv.api.pipeline import get_containers from openpype.hosts.openrv.api import lib from openpype.pipeline import get_current_project_name @@ -45,9 +45,14 @@ def collect_instances(self): with lib.maintained_view(): for container in containers: - self._collect_container(container, representations_by_id) + self._collect_container(container, + project_name, + representations_by_id) - def _collect_container(self, container, representations_by_id): + def _collect_container(self, + container, + project_name, + representations_by_id): node = container["node"] self.log.debug(f"Processing container node: {node}") @@ -79,10 +84,21 @@ def _collect_container(self, container, representations_by_id): # for marked in marked_frames: # print("MARKED ------------ ", container, marked, source_group) + source_representation_asset_doc = get_asset_by_name( + project_name=project_name, + asset_name=source_representation_asset + ) + for noted_frame in annotated_frames: print(f"Found annotation for {source_group} frame {noted_frame}") - subset_name = f"note_{namespace}_{noted_frame}" + variant = f"{namespace}_{noted_frame}" + subset_name = self.get_subset_name( + variant=variant, + task_name=source_representation_task, + asset_doc=source_representation_asset_doc, + project_name=project_name, + ) data = { "tags": ["review", "ftrackreview"], "task": source_representation_task, From dd664c8b85f1701fa285c73637894ea9b2d94b1e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 14:46:35 +0200 Subject: [PATCH 101/109] Nicer label + docstring --- openpype/hosts/openrv/plugins/create/create_annotations.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/openrv/plugins/create/create_annotations.py b/openpype/hosts/openrv/plugins/create/create_annotations.py index 144aca87e5c..1d27ed5ec8b 100644 --- a/openpype/hosts/openrv/plugins/create/create_annotations.py +++ b/openpype/hosts/openrv/plugins/create/create_annotations.py @@ -13,9 +13,11 @@ class AnnotationCreator(AutoCreator): + """Collect each drawn annotation over a loaded container as an annotation. + """ identifier = "annotation" family = "annotation" - label = "annotation" + label = "Annotation" default_variant = "Main" From 81d5d161b750e38f0b6c1e92906f8a20b6eb8f09 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 15:05:34 +0200 Subject: [PATCH 102/109] Add comment about a workaround --- openpype/hosts/openrv/plugins/create/create_annotations.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/openrv/plugins/create/create_annotations.py b/openpype/hosts/openrv/plugins/create/create_annotations.py index 1d27ed5ec8b..0ce7bfffa20 100644 --- a/openpype/hosts/openrv/plugins/create/create_annotations.py +++ b/openpype/hosts/openrv/plugins/create/create_annotations.py @@ -62,6 +62,8 @@ def _collect_container(self, # View this particular group to get its marked and annotated frames # TODO: This will only find annotations on the actual source group # and not for e.g. the source in the `defaultSequence`. + # For now it's easiest to enable 'Annotation > Configure > Draw On + # Source If Possible' so that most annotations end up on source source_group = rv.commands.nodeGroup(node) rv.commands.setViewNode(source_group) annotated_frames = rv.extra_commands.findAnnotatedFrames() From fce4e7d05aff02079f75a4ae412c06c61fa2b0f9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 20:20:01 +0200 Subject: [PATCH 103/109] Cleanup `reset_frame_range` logic. Remove old `edit_in` and `edit_out` --- openpype/hosts/openrv/api/commands.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/openrv/api/commands.py b/openpype/hosts/openrv/api/commands.py index 03cccd63db1..911cc4b12ef 100644 --- a/openpype/hosts/openrv/api/commands.py +++ b/openpype/hosts/openrv/api/commands.py @@ -10,22 +10,20 @@ def reset_frame_range(): """ Set timeline frame range. """ asset_doc = get_current_project_asset() + asset_name = asset_doc["name"] asset_data = asset_doc["data"] - frame_start = int(asset_data.get( - "frameStart", - asset_data.get("edit_in"))) + frame_start = asset_data.get("frameStart") + frame_end = asset_data.get("frameEnd") - frame_end = int(asset_data.get( - "frameEnd", - asset_data.get("edit_out"))) + if frame_start is None or frame_end is None: + log.warning("No edit information found for {}".format(asset_name)) + return rv.commands.setFrameStart(frame_start) rv.commands.setFrameEnd(frame_end) rv.commands.setFrame(frame_start) - log.info("Project frame range set") - def set_session_fps(): """ Set session fps. From 1da142c86fc67c9b977643e21e5d947783263c44 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Apr 2023 20:22:58 +0200 Subject: [PATCH 104/109] Remove redundant adding of vendor/python --- openpype/hosts/openrv/addon.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/hosts/openrv/addon.py b/openpype/hosts/openrv/addon.py index 13241fa51f3..3ae3992308e 100644 --- a/openpype/hosts/openrv/addon.py +++ b/openpype/hosts/openrv/addon.py @@ -1,5 +1,4 @@ import os -import sys from openpype.modules import OpenPypeModule from openpype.modules.interfaces import IHostAddon @@ -15,11 +14,6 @@ def initialize(self, module_settings): def add_implementation_envs(self, env, app): """Modify environments to contain all required for implementation.""" - vendor_python = os.path.join(os.getenv("OPENPYPE_ROOT"), - "vendor", - "python") - sys.path.insert(0, vendor_python) - # Set default environments if are not set via settings defaults = { "OPENPYPE_LOG_NO_COLORS": "True" From a2b37e99bb32f400e41a1ae157355f88589c5c4a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Apr 2023 13:50:29 +0200 Subject: [PATCH 105/109] Preserve predefined RV_SUPPORT_PATH paths --- openpype/hosts/openrv/hooks/pre_setup_openrv.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/openrv/hooks/pre_setup_openrv.py b/openpype/hosts/openrv/hooks/pre_setup_openrv.py index 1a4c2670fdb..76ef8527e65 100644 --- a/openpype/hosts/openrv/hooks/pre_setup_openrv.py +++ b/openpype/hosts/openrv/hooks/pre_setup_openrv.py @@ -1,3 +1,4 @@ +import os import shutil from pathlib import Path @@ -45,9 +46,14 @@ def execute(self): ) shutil.copy(filepath, startup_python) - # TODO: Make sure we don't override a full studios RV_SUPPORT_PATH - print("Setting RV_SUPPORT_PATH", startup) - self.launch_context.env["RV_SUPPORT_PATH"] = str(startup) + self.log.debug(f"Adding RV_SUPPORT_PATH: {startup}") + support_path = self.launch_context.env.get("RV_SUPPORT_PATH") + if support_path: + support_path = os.pathsep.join([support_path, str(startup)]) + else: + support_path = str(startup) + self.log.debug(f"Setting RV_SUPPORT_PATH: {support_path}") + self.launch_context.env["RV_SUPPORT_PATH"] = support_path # TODO: OpenRV does write files into RV_SUPPORT_PATH during runtime # so we should actually not deploy that as a path inside OP deploy From aa746636b1d8cbd7507315118198b8983f49a29d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Apr 2023 14:35:27 +0200 Subject: [PATCH 106/109] Install OpenPype packages to temp folder on launch + preserve predefined RV_SUPPORT_PATH paths --- .../hosts/openrv/hooks/pre_setup_openrv.py | 90 ++++++++++--------- openpype/hosts/openrv/startup/Mu/rvload | 1 - openpype/hosts/openrv/startup/Mu/rvload2 | 4 - .../hosts/openrv/startup/Packages/rvinstall | 3 - .../PACKAGE | 0 .../openpype_menus.py | 0 .../pkgs_source/openpype_scripteditor/PACKAGE | 2 +- 7 files changed, 48 insertions(+), 52 deletions(-) delete mode 100644 openpype/hosts/openrv/startup/Mu/rvload delete mode 100644 openpype/hosts/openrv/startup/Mu/rvload2 delete mode 100644 openpype/hosts/openrv/startup/Packages/rvinstall rename openpype/hosts/openrv/startup/pkgs_source/{openpype_menus-1.0 => openpype_menus}/PACKAGE (100%) rename openpype/hosts/openrv/startup/pkgs_source/{openpype_menus-1.0 => openpype_menus}/openpype_menus.py (100%) diff --git a/openpype/hosts/openrv/hooks/pre_setup_openrv.py b/openpype/hosts/openrv/hooks/pre_setup_openrv.py index 76ef8527e65..fd818e45241 100644 --- a/openpype/hosts/openrv/hooks/pre_setup_openrv.py +++ b/openpype/hosts/openrv/hooks/pre_setup_openrv.py @@ -1,9 +1,12 @@ import os import shutil +import zipfile +import tempfile from pathlib import Path from openpype.lib import PreLaunchHook from openpype.hosts.openrv import OPENRV_ROOT_DIR +from openpype.lib.execute import run_subprocess class PreSetupOpenRV(PreLaunchHook): @@ -11,51 +14,52 @@ class PreSetupOpenRV(PreLaunchHook): app_groups = ["openrv"] def execute(self): - root = Path(OPENRV_ROOT_DIR) - startup = root / "startup" - startup_packages = startup / "Packages" - startup_python = startup / "Python" - - # Ensure folder exists - startup_python.mkdir(exist_ok=True) - - # TODO: Auto deployment should not be this hacky - # Redeploy the source packages to zips to auto-update - # during development of OpenRV - import zipfile - for package_name in ["comments", - "openpype_menus-1.0", - "openpype_scripteditor"]: - package_src = startup / "pkgs_source" / package_name - package_dest = startup_packages / "{}.zip".format(package_name) - self.log.info(f"Writing: {package_dest}") - with zipfile.ZipFile(package_dest, mode="w") as zip: - for filepath in package_src.iterdir(): - if not filepath.is_file(): - continue - - zip.write(filepath, - arcname=filepath.name) - - if filepath.suffix == ".py": - # Include it in Python subfolder where OpenRV deploys - # the files after first install (and does not update - # after) - self.log.info( - f"Copying {filepath} to folder {startup_python}" - ) - shutil.copy(filepath, startup_python) - - self.log.debug(f"Adding RV_SUPPORT_PATH: {startup}") + + executable = self.application.find_executable() + if not executable: + self.log.error("Unable to find executable for RV.") + return + + # We use the `rvpkg` executable next to the `rv` executable to + # install and opt-in to the OpenPype plug-in packages + rvpkg = Path(os.path.dirname(str(executable))) / "rvpkg" + packages_src_folder = Path(OPENRV_ROOT_DIR) / "startup" / "pkgs_source" + + # TODO: Are we sure we want to deploy the addons into a temporary + # RV_SUPPORT_PATH on each launch. This would create redundant temp + # files that remain on disk but it does allow us to ensure RV is + # now running with the correct version of the RV packages of this + # current running OpenPype version + op_support_path = Path(tempfile.mkdtemp( + prefix="openpype_rv_support_path_" + )) + + # Write the OpenPype RV package zips directly to the support path + # Packages/ folder then we don't need to `rvpkg -add` them afterwards + packages_dest_folder = op_support_path / "Packages" + packages_dest_folder.mkdir(exist_ok=True) + packages = ["comments", "openpype_menus", "openpype_scripteditor"] + for package_name in packages: + package_src = packages_src_folder / package_name + package_dest = packages_dest_folder / "{}.zip".format(package_name) + + self.log.debug(f"Writing: {package_dest}") + shutil.make_archive(str(package_dest), "zip", str(package_src)) + + # Install and opt-in the OpenPype RV packages + install_args = [rvpkg, "-only", op_support_path, "-install"] + install_args.extend(packages) + optin_args = [rvpkg, "-only", op_support_path, "-optin"] + optin_args.extend(packages) + run_subprocess(install_args, logger=self.log) + run_subprocess(optin_args, logger=self.log) + + self.log.debug(f"Adding RV_SUPPORT_PATH: {op_support_path}") support_path = self.launch_context.env.get("RV_SUPPORT_PATH") if support_path: - support_path = os.pathsep.join([support_path, str(startup)]) + support_path = os.pathsep.join([support_path, + str(op_support_path)]) else: - support_path = str(startup) + support_path = str(op_support_path) self.log.debug(f"Setting RV_SUPPORT_PATH: {support_path}") self.launch_context.env["RV_SUPPORT_PATH"] = support_path - - # TODO: OpenRV does write files into RV_SUPPORT_PATH during runtime - # so we should actually not deploy that as a path inside OP deploy - # to avoid openpype checksum validation issues, etc. but for now - # it helps running from code to run the integration for development diff --git a/openpype/hosts/openrv/startup/Mu/rvload b/openpype/hosts/openrv/startup/Mu/rvload deleted file mode 100644 index d00491fd7e5..00000000000 --- a/openpype/hosts/openrv/startup/Mu/rvload +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/openpype/hosts/openrv/startup/Mu/rvload2 b/openpype/hosts/openrv/startup/Mu/rvload2 deleted file mode 100644 index 2899850a30c..00000000000 --- a/openpype/hosts/openrv/startup/Mu/rvload2 +++ /dev/null @@ -1,4 +0,0 @@ -4 -openpype_menus,openpype_menus-1.0.zip,nil,nil,nil,true,true,3.12,true,1.0.0 -comments,comments.zip,nil,nil,nil,true,true,3.12,true,1.0.0 -openpype_scripteditor,openpype_scripteditor.zip,nil,nil,nil,true,true,3.12,false,1.0.0 diff --git a/openpype/hosts/openrv/startup/Packages/rvinstall b/openpype/hosts/openrv/startup/Packages/rvinstall deleted file mode 100644 index 06e233b2f81..00000000000 --- a/openpype/hosts/openrv/startup/Packages/rvinstall +++ /dev/null @@ -1,3 +0,0 @@ -*comments.zip -*openpype_menus-1.0.zip -*openpype_scripteditor.zip diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/PACKAGE b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus/PACKAGE similarity index 100% rename from openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/PACKAGE rename to openpype/hosts/openrv/startup/pkgs_source/openpype_menus/PACKAGE diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py b/openpype/hosts/openrv/startup/pkgs_source/openpype_menus/openpype_menus.py similarity index 100% rename from openpype/hosts/openrv/startup/pkgs_source/openpype_menus-1.0/openpype_menus.py rename to openpype/hosts/openrv/startup/pkgs_source/openpype_menus/openpype_menus.py diff --git a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE index 113df287b12..7f865d2bd40 100644 --- a/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE +++ b/openpype/hosts/openrv/startup/pkgs_source/openpype_scripteditor/PACKAGE @@ -5,7 +5,7 @@ version: 1.0 rv: 3.12 openrv: 1.0.0 requires: '' -optional: false +optional: true modes: - file: openpype_scripteditor From 4e41b4807cc15515265a5f702db960e82c05e82d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Apr 2023 14:36:03 +0200 Subject: [PATCH 107/109] Remove unused import --- openpype/hosts/openrv/hooks/pre_setup_openrv.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/openrv/hooks/pre_setup_openrv.py b/openpype/hosts/openrv/hooks/pre_setup_openrv.py index fd818e45241..d5a585378ae 100644 --- a/openpype/hosts/openrv/hooks/pre_setup_openrv.py +++ b/openpype/hosts/openrv/hooks/pre_setup_openrv.py @@ -1,6 +1,5 @@ import os import shutil -import zipfile import tempfile from pathlib import Path From 0dda77b42e9b3d0488c0d2671e295da9fa2126bd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Apr 2023 14:39:18 +0200 Subject: [PATCH 108/109] Force `rvpkg` just to be sure it's not trying to be used interactively --- openpype/hosts/openrv/hooks/pre_setup_openrv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/openrv/hooks/pre_setup_openrv.py b/openpype/hosts/openrv/hooks/pre_setup_openrv.py index d5a585378ae..0f1921d4f2e 100644 --- a/openpype/hosts/openrv/hooks/pre_setup_openrv.py +++ b/openpype/hosts/openrv/hooks/pre_setup_openrv.py @@ -46,9 +46,9 @@ def execute(self): shutil.make_archive(str(package_dest), "zip", str(package_src)) # Install and opt-in the OpenPype RV packages - install_args = [rvpkg, "-only", op_support_path, "-install"] + install_args = [rvpkg, "-only", op_support_path, "-install", "-force"] install_args.extend(packages) - optin_args = [rvpkg, "-only", op_support_path, "-optin"] + optin_args = [rvpkg, "-only", op_support_path, "-optin", "-force"] optin_args.extend(packages) run_subprocess(install_args, logger=self.log) run_subprocess(optin_args, logger=self.log) From 305efbb41b23cd18f8d82dce2fecda8cef16e146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 13 Dec 2023 11:46:12 +0100 Subject: [PATCH 109/109] :recycle: update color management settings --- .../settings/defaults/project_settings/openrv.json | 5 +++-- .../projects_schema/schema_project_openrv.json | 12 ++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/settings/defaults/project_settings/openrv.json b/openpype/settings/defaults/project_settings/openrv.json index bb7bc74a2de..af98c5e2b30 100644 --- a/openpype/settings/defaults/project_settings/openrv.json +++ b/openpype/settings/defaults/project_settings/openrv.json @@ -1,11 +1,12 @@ { "imageio": { + "activate_host_color_management": true, "ocio_config": { - "enabled": true, + "override_global_config": false, "filepath": [] }, "file_rules": { - "enabled": true, + "activate_host_rules": false, "rules": {} } } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json index 0fbdf80833f..09d03e8c4e5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_openrv.json @@ -8,19 +8,15 @@ { "key": "imageio", "type": "dict", - "label": "Color Management (ImageIO)", + "label": "Color Management (OCIO managed)", "collapsible": true, "is_group": true, "children": [ { - "type": "schema", - "name": "schema_imageio_config" - }, - { - "type": "schema", - "name": "schema_imageio_file_rules" + "type": "template", + "name": "template_host_color_management_ocio" } ] } ] -} \ No newline at end of file +}