From 2701e152de05fc51b1aee1e6b40dbeb9a2d5a0f2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 19 Aug 2020 13:40:39 +0200 Subject: [PATCH] feat(fusion): menu script --- pype/hosts/fusion/__init__.py | 63 ------- pype/hosts/fusion/menu.py | 154 ++++++++++++++++ pype/hosts/fusion/pipeline.py | 171 ++++++++++++++++++ .../hosts/fusion/utility_scripts/Pype_menu.py | 26 +++ pype/hosts/fusion/utility_scripts/creator.py | 7 - pype/hosts/fusion/utility_scripts/loader.py | 7 - pype/hosts/fusion/utility_scripts/manager.py | 7 - pype/hosts/fusion/utility_scripts/publish.py | 62 ------- .../hosts/fusion/utility_scripts/workfiles.py | 7 - 9 files changed, 351 insertions(+), 153 deletions(-) create mode 100644 pype/hosts/fusion/menu.py create mode 100644 pype/hosts/fusion/pipeline.py create mode 100644 pype/hosts/fusion/utility_scripts/Pype_menu.py delete mode 100644 pype/hosts/fusion/utility_scripts/creator.py delete mode 100644 pype/hosts/fusion/utility_scripts/loader.py delete mode 100644 pype/hosts/fusion/utility_scripts/manager.py delete mode 100644 pype/hosts/fusion/utility_scripts/publish.py delete mode 100644 pype/hosts/fusion/utility_scripts/workfiles.py diff --git a/pype/hosts/fusion/__init__.py b/pype/hosts/fusion/__init__.py index 7af75cebc8..e69de29bb2 100644 --- a/pype/hosts/fusion/__init__.py +++ b/pype/hosts/fusion/__init__.py @@ -1,63 +0,0 @@ -import os - -from avalon import api as avalon -from pyblish import api as pyblish -from pype import PLUGINS_DIR - -PUBLISH_PATH = os.path.join(PLUGINS_DIR, "fusion", "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "fusion", "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "fusion", "create") -INVENTORY_PATH = os.path.join(PLUGINS_DIR, "fusion", "inventory") - - -def install(): - print("Registering Fusion plug-ins..") - pyblish.register_plugin_path(PUBLISH_PATH) - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(avalon.Creator, CREATE_PATH) - avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) - - pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled) - - # Disable all families except for the ones we explicitly want to see - family_states = ["imagesequence", - "camera", - "pointcache"] - - avalon.data["familiesStateDefault"] = False - avalon.data["familiesStateToggled"] = family_states - - -def uninstall(): - print("Deregistering Fusion plug-ins..") - pyblish.deregister_plugin_path(PUBLISH_PATH) - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) - - pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled) - - -def on_pyblish_instance_toggled(instance, new_value, old_value): - """Toggle saver tool passthrough states on instance toggles.""" - - from avalon.fusion import comp_lock_and_undo_chunk - - comp = instance.context.data.get("currentComp") - if not comp: - return - - savers = [tool for tool in instance if - getattr(tool, "ID", None) == "Saver"] - if not savers: - return - - # Whether instances should be passthrough based on new value - passthrough = not new_value - with comp_lock_and_undo_chunk(comp, - undo_queue_name="Change instance " - "active state"): - for tool in savers: - attrs = tool.GetAttrs() - current = attrs["TOOLB_PassThrough"] - if current != passthrough: - tool.SetAttrs({"TOOLB_PassThrough": passthrough}) diff --git a/pype/hosts/fusion/menu.py b/pype/hosts/fusion/menu.py new file mode 100644 index 0000000000..73ea937513 --- /dev/null +++ b/pype/hosts/fusion/menu.py @@ -0,0 +1,154 @@ +import os +import sys + +from Qt import QtWidgets, QtCore + +from .pipeline import ( + publish, + launch_workfiles_app +) + +from avalon.tools import ( + creator, + loader, + sceneinventory, + libraryloader +) + + +def load_stylesheet(): + path = os.path.join(os.path.dirname(__file__), "menu_style.qss") + if not os.path.exists(path): + print("Unable to load stylesheet, file not found in resources") + return "" + + with open(path, "r") as file_stream: + stylesheet = file_stream.read() + return stylesheet + + +class Spacer(QtWidgets.QWidget): + def __init__(self, height, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + + self.setFixedHeight(height) + + real_spacer = QtWidgets.QWidget(self) + real_spacer.setObjectName("Spacer") + real_spacer.setFixedHeight(height) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(real_spacer) + + self.setLayout(layout) + + +class PypeMenu(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + + self.setObjectName("PypeMenu") + + self.setWindowFlags( + QtCore.Qt.Window + | QtCore.Qt.CustomizeWindowHint + | QtCore.Qt.WindowTitleHint + | QtCore.Qt.WindowCloseButtonHint + | QtCore.Qt.WindowStaysOnTopHint + ) + + self.setWindowTitle("Pype") + workfiles_btn = QtWidgets.QPushButton("Workfiles", self) + create_btn = QtWidgets.QPushButton("Create", self) + publish_btn = QtWidgets.QPushButton("Publish", self) + load_btn = QtWidgets.QPushButton("Load", self) + inventory_btn = QtWidgets.QPushButton("Inventory", self) + libload_btn = QtWidgets.QPushButton("Library", self) + rename_btn = QtWidgets.QPushButton("Rename", self) + set_colorspace_btn = QtWidgets.QPushButton( + "Set colorspace from presets", self + ) + reset_resolution_btn = QtWidgets.QPushButton( + "Reset Resolution from peresets", self + ) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(10, 20, 10, 20) + + layout.addWidget(workfiles_btn) + layout.addWidget(create_btn) + layout.addWidget(publish_btn) + layout.addWidget(load_btn) + layout.addWidget(inventory_btn) + + layout.addWidget(Spacer(15, self)) + + layout.addWidget(libload_btn) + + layout.addWidget(Spacer(15, self)) + + layout.addWidget(rename_btn) + + layout.addWidget(Spacer(15, self)) + + layout.addWidget(set_colorspace_btn) + layout.addWidget(reset_resolution_btn) + + self.setLayout(layout) + + workfiles_btn.clicked.connect(self.on_workfile_clicked) + create_btn.clicked.connect(self.on_create_clicked) + publish_btn.clicked.connect(self.on_publish_clicked) + load_btn.clicked.connect(self.on_load_clicked) + inventory_btn.clicked.connect(self.on_inventory_clicked) + libload_btn.clicked.connect(self.on_libload_clicked) + rename_btn.clicked.connect(self.on_rename_clicked) + set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) + reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked) + + def on_workfile_clicked(self): + print("Clicked Workfile") + launch_workfiles_app() + + def on_create_clicked(self): + print("Clicked Create") + creator.show() + + def on_publish_clicked(self): + print("Clicked Publish") + publish(None) + + def on_load_clicked(self): + print("Clicked Load") + loader.show(use_context=True) + + def on_inventory_clicked(self): + print("Clicked Inventory") + sceneinventory.show() + + def on_libload_clicked(self): + print("Clicked Library") + libraryloader.show() + + def on_rename_clicked(self): + print("Clicked Rename") + + def on_set_colorspace_clicked(self): + print("Clicked Set Colorspace") + + def on_reset_resolution_clicked(self): + print("Clicked Reset Resolution") + + +def launch_pype_menu(): + app = QtWidgets.QApplication(sys.argv) + + pype_menu = PypeMenu() + + stylesheet = load_stylesheet() + pype_menu.setStyleSheet(stylesheet) + + pype_menu.show() + + sys.exit(app.exec_()) diff --git a/pype/hosts/fusion/pipeline.py b/pype/hosts/fusion/pipeline.py new file mode 100644 index 0000000000..6ade71767f --- /dev/null +++ b/pype/hosts/fusion/pipeline.py @@ -0,0 +1,171 @@ +""" +Basic avalon integration +""" +import os +# import sys +from avalon.tools import workfiles +from avalon import api as avalon +from pyblish import api as pyblish +from pypeapp import Logger + +log = Logger().get_logger(__name__, "fusion") + +# self = sys.modules[__name__] + +AVALON_CONFIG = os.environ["AVALON_CONFIG"] +PARENT_DIR = os.path.dirname(__file__) +PACKAGE_DIR = os.path.dirname(PARENT_DIR) +PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins") + +LOAD_PATH = os.path.join(PLUGINS_DIR, "fusion", "load") +CREATE_PATH = os.path.join(PLUGINS_DIR, "fusion", "create") +INVENTORY_PATH = os.path.join(PLUGINS_DIR, "fusion", "inventory") + +PUBLISH_PATH = os.path.join( + PLUGINS_DIR, "fusion", "publish" +).replace("\\", "/") + +AVALON_CONTAINERS = ":AVALON_CONTAINERS" +# IS_HEADLESS = not hasattr(cmds, "about") or cmds.about(batch=True) + + +def install(): + """Install fusion-specific functionality of avalon-core. + + This is where you install menus and register families, data + and loaders into fusion. + + It is called automatically when installing via `api.install(avalon.fusion)` + + See the Maya equivalent for inspiration on how to implement this. + + """ + + # Disable all families except for the ones we explicitly want to see + family_states = ["imagesequence", + "camera", + "pointcache"] + avalon.data["familiesStateDefault"] = False + avalon.data["familiesStateToggled"] = family_states + + log.info("pype.hosts.fusion installed") + + pyblish.register_host("fusion") + pyblish.register_plugin_path(PUBLISH_PATH) + log.info("Registering DaVinci Resovle plug-ins..") + + avalon.register_plugin_path(avalon.Loader, LOAD_PATH) + avalon.register_plugin_path(avalon.Creator, CREATE_PATH) + avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) + + pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled) + + +def uninstall(): + """Uninstall all tha was installed + + This is where you undo everything that was done in `install()`. + That means, removing menus, deregistering families and data + and everything. It should be as though `install()` was never run, + because odds are calling this function means the user is interested + in re-installing shortly afterwards. If, for example, he has been + modifying the menu or registered families. + + """ + pyblish.deregister_host("fusion") + pyblish.deregister_plugin_path(PUBLISH_PATH) + log.info("Deregistering DaVinci Resovle plug-ins..") + + avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) + avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) + avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) + + pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled) + + +def on_pyblish_instance_toggled(instance, new_value, old_value): + """Toggle saver tool passthrough states on instance toggles.""" + + from avalon.fusion import comp_lock_and_undo_chunk + + comp = instance.context.data.get("currentComp") + if not comp: + return + + savers = [tool for tool in instance if + getattr(tool, "ID", None) == "Saver"] + if not savers: + return + + # Whether instances should be passthrough based on new value + passthrough = not new_value + with comp_lock_and_undo_chunk(comp, + undo_queue_name="Change instance " + "active state"): + for tool in savers: + attrs = tool.GetAttrs() + current = attrs["TOOLB_PassThrough"] + if current != passthrough: + tool.SetAttrs({"TOOLB_PassThrough": passthrough}) + + +def containerise(obj, + name, + namespace, + context, + loader=None, + data=None): + """Bundle Fusion's object into an assembly and imprint it with metadata + + Containerisation enables a tracking of version, author and origin + for loaded assets. + + Arguments: + obj (obj): Resolve's object to imprint as container + name (str): Name of resulting assembly + namespace (str): Namespace under which to host container + context (dict): Asset information + loader (str, optional): Name of node used to produce this container. + + Returns: + obj (obj): containerised object + + """ + pass + + +def ls(): + """List available containers. + + This function is used by the Container Manager in Nuke. You'll + need to implement a for-loop that then *yields* one Container at + a time. + + See the `container.json` schema for details on how it should look, + and the Maya equivalent, which is in `avalon.maya.pipeline` + """ + pass + + +def parse_container(container): + """Return the container node's full container data. + + Args: + container (str): A container node name. + + Returns: + dict: The container schema data for this container node. + + """ + pass + + +def launch_workfiles_app(*args): + workdir = os.environ["AVALON_WORKDIR"] + workfiles.show(workdir) + + +def publish(parent): + """Shorthand to publish from within host""" + from avalon.tools import publish + return publish.show(parent) diff --git a/pype/hosts/fusion/utility_scripts/Pype_menu.py b/pype/hosts/fusion/utility_scripts/Pype_menu.py new file mode 100644 index 0000000000..bf42c75cde --- /dev/null +++ b/pype/hosts/fusion/utility_scripts/Pype_menu.py @@ -0,0 +1,26 @@ +import os +import sys +import avalon.api as avalon +import pype + +from pypeapp import Logger + +log = Logger().get_logger(__name__) + + +def main(env): + from pype.hosts import fusion + # Registers pype's Global pyblish plugins + pype.install() + + # activate resolve from pype + avalon.install(bmdvr) + + log.info(f"Avalon registred hosts: {avalon.registered_host()}") + + bmdvr.launch_pype_menu() + + +if __name__ == "__main__": + result = main(os.environ) + sys.exit(not bool(result)) diff --git a/pype/hosts/fusion/utility_scripts/creator.py b/pype/hosts/fusion/utility_scripts/creator.py deleted file mode 100644 index 25050e6f27..0000000000 --- a/pype/hosts/fusion/utility_scripts/creator.py +++ /dev/null @@ -1,7 +0,0 @@ -import avalon.api -import avalon.fusion -import avalon.tools.creator as tool - - -avalon.api.install(avalon.fusion) -tool.show() diff --git a/pype/hosts/fusion/utility_scripts/loader.py b/pype/hosts/fusion/utility_scripts/loader.py deleted file mode 100644 index 6a7af1566f..0000000000 --- a/pype/hosts/fusion/utility_scripts/loader.py +++ /dev/null @@ -1,7 +0,0 @@ -import avalon.api -import avalon.fusion -import avalon.tools.loader as tool - - -avalon.api.install(avalon.fusion) -tool.show(use_context=True) diff --git a/pype/hosts/fusion/utility_scripts/manager.py b/pype/hosts/fusion/utility_scripts/manager.py deleted file mode 100644 index 0bbe0a3213..0000000000 --- a/pype/hosts/fusion/utility_scripts/manager.py +++ /dev/null @@ -1,7 +0,0 @@ -import avalon.api -import avalon.fusion -import avalon.tools.sceneinventory as tool - - -avalon.api.install(avalon.fusion) -tool.show() diff --git a/pype/hosts/fusion/utility_scripts/publish.py b/pype/hosts/fusion/utility_scripts/publish.py deleted file mode 100644 index 38f6079711..0000000000 --- a/pype/hosts/fusion/utility_scripts/publish.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -import sys - -import avalon.api -import avalon.fusion - -import pyblish_qml - - -def _install_fusion(): - - from pyblish_qml import settings - - sys.stdout.write("Setting up Pyblish QML in Fusion\n") - - if settings.ContextLabel == settings.ContextLabelDefault: - settings.ContextLabel = "Fusion" - if settings.WindowTitle == settings.WindowTitleDefault: - settings.WindowTitle = "Pyblish (Fusion)" - - -def _set_current_working_dir(): - # Set current working directory next to comp - - try: - # Provided by Fusion - comp - except NameError: - comp = None - - if comp is None: - raise RuntimeError("Fusion 'comp' variable not set. " - "Are you running this as Comp script?") - - filename = comp.MapPath(comp.GetAttrs()["COMPS_FileName"]) - if filename and os.path.exists(filename): - cwd = os.path.dirname(filename) - else: - # Fallback to Avalon projects root - # for unsaved files. - cwd = os.environ["AVALON_PROJECTS"] - - os.chdir(cwd) - - -print("Starting Pyblish setup..") - -# Install avalon -avalon.api.install(avalon.fusion) - -# force current working directory to NON FUSION path -# os.getcwd will return the binary folder of Fusion in this case -_set_current_working_dir() - -# install fusion title -_install_fusion() - -# Run QML in modal mode so it keeps listening to the -# server in the main thread and keeps this process -# open until QML finishes. -print("Running publish_qml.show(modal=True)..") -pyblish_qml.show(modal=True) diff --git a/pype/hosts/fusion/utility_scripts/workfiles.py b/pype/hosts/fusion/utility_scripts/workfiles.py deleted file mode 100644 index d93eeadc9c..0000000000 --- a/pype/hosts/fusion/utility_scripts/workfiles.py +++ /dev/null @@ -1,7 +0,0 @@ -import avalon.api -import avalon.fusion -import avalon.tools.workfiles as tool - - -avalon.api.install(avalon.fusion) -tool.show()