feat(resolve): wip integration

workio, pipeline and basic avalon methods, menu,
This commit is contained in:
Jakub Jezek 2020-05-27 18:20:52 +02:00
parent 1dc624cdc6
commit 341379e546
No known key found for this signature in database
GPG key ID: C4B96E101D2A47F3
22 changed files with 1014 additions and 354 deletions

View file

@ -2,7 +2,7 @@ import os
import traceback
from pype.lib import PypeHook
from pypeapp import Logger
from pype.resolve import lib as rlib
from pype.resolve import utils
class ResolvePrelaunch(PypeHook):
@ -27,14 +27,15 @@ class ResolvePrelaunch(PypeHook):
env = os.environ
# making sure pyton 3.6 is installed at provided path
py36_dir = os.path.normpath(env.get("PYTHON36_RES", ""))
py36_dir = os.path.normpath(env.get("PYTHON36_RESOLVE", ""))
assert os.path.isdir(py36_dir), (
"Python 3.6 is not installed at the provided folder path. Either "
"make sure the `environments\resolve.json` is having correctly set "
"`PYTHON36_RES` or make sure Python 3.6 is installed in given path."
f"\nPYTHON36_RES: `{py36_dir}`"
"`PYTHON36_RESOLVE` or make sure Python 3.6 is installed in given path."
f"\nPYTHON36_RESOLVE: `{py36_dir}`"
)
env["PYTHON36_RES"] = py36_dir
self.log.info(f"Path to Resolve Python folder: `{py36_dir}`...")
env["PYTHON36_RESOLVE"] = py36_dir
# setting utility scripts dir for scripts syncing
us_dir = os.path.normpath(env.get("RESOLVE_UTILITY_SCRIPTS_DIR", ""))
@ -59,6 +60,6 @@ class ResolvePrelaunch(PypeHook):
else:
# Resolve Setup integration
rlib.setup(env)
utils.setup(env)
return True

View file

@ -0,0 +1,17 @@
import pyblish.api
from python_get_resolve import GetResolve
class CollectProject(pyblish.api.ContextPlugin):
"""Collect Project object"""
order = pyblish.api.CollectorOrder - 0.1
label = "Collect Project"
hosts = ["resolve"]
def process(self, context):
resolve = GetResolve()
PM = resolve.GetProjectManager()
P = PM.GetCurrentProject()
self.log.info(P.GetName())

View file

@ -1,71 +1,47 @@
import os
from avalon import api as avalon
from pyblish import api as pyblish
from pypeapp import Logger
from .lib import (
setup,
reload_pipeline,
from .pipeline import (
install,
uninstall,
ls,
# LOAD_PATH,
# CREATE_PATH,
PUBLISH_PATH
containerise,
reload_pipeline,
publish,
launch_workfiles_app
)
from .utils import (
setup,
get_resolve_module
)
from .workio import (
open_file,
save_file,
current_file,
has_unsaved_changes,
file_extensions,
work_root
)
# from .lib import (
#
# )
__all__ = [
"setup",
"install",
"uninstall",
"ls",
"containerise",
"reload_pipeline",
"ls"
"publish",
"launch_workfiles_app",
"setup",
"get_resolve_module",
"open_file",
"save_file",
"current_file",
"has_unsaved_changes",
"file_extensions",
"work_root"
]
log = Logger().get_logger(__name__, "resolve")
def install():
"""Install resolve-specific functionality of avalon-core.
This is where you install menus and register families, data
and loaders into resolve.
It is called automatically when installing via `api.install(resolve)`.
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",
"mov"
]
avalon.data["familiesStateDefault"] = False
avalon.data["familiesStateToggled"] = family_states
log.info("pype.resolve installed")
pyblish.register_host("resolve")
pyblish.register_plugin_path(PUBLISH_PATH)
log.info("Registering Premiera plug-ins..")
# avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
# avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
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("resolve")
pyblish.deregister_plugin_path(PUBLISH_PATH)
log.info("Deregistering Premiera plug-ins..")
# avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
# avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)

53
pype/resolve/action.py Normal file
View file

@ -0,0 +1,53 @@
# absolute_import is needed to counter the `module has no cmds error` in Maya
from __future__ import absolute_import
import pyblish.api
from ..action import get_errored_instances_from_context
class SelectInvalidAction(pyblish.api.Action):
"""Select invalid clips in Resolve timeline when plug-in failed.
To retrieve the invalid nodes this assumes a static `get_invalid()`
method is available on the plugin.
"""
label = "Select invalid"
on = "failed" # This action is only available on a failed plug-in
icon = "search" # Icon from Awesome Icon
def process(self, context, plugin):
try:
from pype.resolve.utils import get_resolve_module
resolve = get_resolve_module()
except ImportError:
raise ImportError("Current host is not Resolve")
errored_instances = get_errored_instances_from_context(context)
# Apply pyblish.logic to get the instances for the plug-in
instances = pyblish.api.instances_by_plugin(errored_instances, plugin)
# Get the invalid nodes for the plug-ins
self.log.info("Finding invalid clips..")
invalid = list()
for instance in instances:
invalid_nodes = plugin.get_invalid(instance)
if invalid_nodes:
if isinstance(invalid_nodes, (list, tuple)):
invalid.extend(invalid_nodes)
else:
self.log.warning("Plug-in returned to be invalid, "
"but has no selectable nodes.")
# Ensure unique (process each node only once)
invalid = list(set(invalid))
if invalid:
self.log.info("Selecting invalid nodes: %s" % ", ".join(invalid))
# TODO: select resolve timeline track items in current timeline
else:
self.log.info("No invalid nodes found.")

View file

@ -1,109 +0,0 @@
import os
import sys
import shutil
from avalon import api
from pype.widgets.message_window import message
from pypeapp import Logger
log = Logger().get_logger(__name__, "resolve")
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")
self.UTILITY_SCRIPTS = os.path.join(PARENT_DIR, "resolve_utility_scripts")
self.PUBLISH_PATH = os.path.join(
PLUGINS_DIR, "resolve", "publish"
).replace("\\", "/")
if os.getenv("PUBLISH_PATH", None):
if self.PUBLISH_PATH not in os.environ["PUBLISH_PATH"]:
os.environ["PUBLISH_PATH"] = os.pathsep.join(
os.environ["PUBLISH_PATH"].split(os.pathsep) +
[self.PUBLISH_PATH]
)
else:
os.environ["PUBLISH_PATH"] = self.PUBLISH_PATH
def ls():
pass
def sync_utility_scripts(env=None):
""" Synchronizing basic utlility scripts for resolve.
To be able to run scripts from inside `Resolve/Workspace/Scripts` menu
all scripts has to be accessible from defined folder.
"""
if not env:
env = os.environ
us_dir = env.get("RESOLVE_UTILITY_SCRIPTS_DIR", "")
scripts = os.listdir(self.UTILITY_SCRIPTS)
log.info(f"Utility Scripts Dir: `{self.UTILITY_SCRIPTS}`")
log.info(f"Utility Scripts: `{scripts}`")
# make sure no script file is in folder
if next((s for s in os.listdir(us_dir)), None):
for s in os.listdir(us_dir):
path = os.path.join(us_dir, s)
log.info(f"Removing `{path}`...")
os.remove(path)
# copy scripts into Resolve's utility scripts dir
for s in scripts:
src = os.path.join(self.UTILITY_SCRIPTS, s)
dst = os.path.join(us_dir, s)
log.info(f"Copying `{src}` to `{dst}`...")
shutil.copy2(src, dst)
def reload_pipeline():
"""Attempt to reload pipeline at run-time.
CAUTION: This is primarily for development and debugging purposes.
"""
import importlib
import pype.resolve
api.uninstall()
for module in ("avalon.io",
"avalon.lib",
"avalon.pipeline",
"avalon.api",
"avalon.tools",
"{}".format(AVALON_CONFIG),
"{}.resolve".format(AVALON_CONFIG),
"{}.resolve.lib".format(AVALON_CONFIG)
):
log.info("Reloading module: {}...".format(module))
try:
module = importlib.import_module(module)
importlib.reload(module)
except Exception as e:
log.warning("Cannot reload module: {}".format(e))
api.install(pype.resolve)
def setup(env=None):
""" Running wrapper
"""
if not env:
env = os.environ
# synchronize resolve utility scripts
sync_utility_scripts(env)
log.info("Resolve Pype wrapper has been installed")

133
pype/resolve/menu.py Normal file
View file

@ -0,0 +1,133 @@
import os
import sys
from Qt import QtWidgets, QtCore
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(int(height / 3))
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)
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
)
reload_pipeline_btn = QtWidgets.QPushButton("Reload pipeline", self)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(10, 10, 10, 10)
layout.addWidget(workfiles_btn)
layout.addWidget(create_btn)
layout.addWidget(publish_btn)
layout.addWidget(load_btn)
layout.addWidget(inventory_btn)
layout.addWidget(Spacer(20, self))
layout.addWidget(rename_btn)
layout.addWidget(set_colorspace_btn)
layout.addWidget(reset_resolution_btn)
layout.addWidget(Spacer(20, self))
layout.addWidget(reload_pipeline_btn)
self.setLayout(layout)
workfiles_btn.clicked.connect(self.on_reload_pipeline_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)
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)
reload_pipeline_btn.clicked.connect(self.on_reload_pipeline_clicked)
def on_workfile_clicked(self):
print("Clicked Workfile")
def on_create_clicked(self):
print("Clicked Create")
def on_publish_clicked(self):
print("Clicked Publish")
def on_load_clicked(self):
print("Clicked Load")
def on_inventory_clicked(self):
print("Clicked Inventory")
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 on_reload_pipeline_clicked(self):
print("Clicked Reload Pipeline")
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_())

View file

@ -0,0 +1,34 @@
QWidget {
background-color: #3a3939;
border-radius: 5;
}
QPushButton {
border: 1px solid #6d6d6d;
background-color: #201f1f;
color: #6d6d6d;
padding: 5;
}
QPushButton:focus {
background-color: "#272525";
}
QPushButton:pressed {
background-color: "#686464";
color: #333333;
}
QPushButton:hover {
color: #d0d0d0;
background-color: "#343232";
}
#PypeMenu {
border: 1px solid #333333;
}
#Spacer {
padding: 10;
background-color: #464646;
}

184
pype/resolve/pipeline.py Normal file
View file

@ -0,0 +1,184 @@
"""
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__, "resolve")
# 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, "resolve", "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "resolve", "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "resolve", "inventory")
PUBLISH_PATH = os.path.join(
PLUGINS_DIR, "resolve", "publish"
).replace("\\", "/")
AVALON_CONTAINERS = ":AVALON_CONTAINERS"
# IS_HEADLESS = not hasattr(cmds, "about") or cmds.about(batch=True)
def install():
"""Install resolve-specific functionality of avalon-core.
This is where you install menus and register families, data
and loaders into resolve.
It is called automatically when installing via `api.install(resolve)`.
See the Maya equivalent for inspiration on how to implement this.
"""
from .menu import launch_pype_menu
# Disable all families except for the ones we explicitly want to see
family_states = [
"imagesequence",
"mov"
]
avalon.data["familiesStateDefault"] = False
avalon.data["familiesStateToggled"] = family_states
log.info("pype.resolve installed")
pyblish.register_host("resolve")
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)
# opening menu
launch_pype_menu()
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("resolve")
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)
def containerise(obj,
name,
namespace,
context,
loader=None,
data=None):
"""Bundle Resolve'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 reload_pipeline():
"""Attempt to reload pipeline at run-time.
CAUTION: This is primarily for development and debugging purposes.
"""
import importlib
import pype.resolve
avalon.uninstall()
# get avalon config name
config = os.getenv("AVALON_CONFIG", "pype")
for module in ("avalon.io",
"avalon.lib",
"avalon.pipeline",
"avalon.api",
"avalon.tools",
"{}".format(config),
"{}.resolve".format(config),
"{}.resolve.lib".format(config),
"{}.resolve.menu".format(config),
"{}.resolve.plugin".format(config),
"{}.resolve.pipeline".format(config)
):
log.info("Reloading module: {}...".format(module))
try:
module = importlib.import_module(module)
importlib.reload(module)
except Exception as e:
log.warning("Cannot reload module: {}".format(e))
avalon.install(pype.resolve)
def publish(parent):
"""Shorthand to publish from within host"""
from avalon.tools import publish
return publish.show(parent)

75
pype/resolve/plugin.py Normal file
View file

@ -0,0 +1,75 @@
from avalon import api
from pype.resolve import lib as drlib
from avalon.vendor import qargparse
def get_reference_node_parents(ref):
"""Return all parent reference nodes of reference node
Args:
ref (str): reference node.
Returns:
list: The upstream parent reference nodes.
"""
parents = []
return parents
class SequenceLoader(api.Loader):
"""A basic SequenceLoader for Resolve
This will implement the basic behavior for a loader to inherit from that
will containerize the reference and will implement the `remove` and
`update` logic.
"""
options = [
qargparse.Toggle(
"handles",
label="Include handles",
default=0,
help="Load with handles or without?"
),
qargparse.Choice(
"load_to",
label="Where to load clips",
items=[
"Current timeline",
"New timeline"
],
default=0,
help="Where do you want clips to be loaded?"
),
qargparse.Choice(
"load_how",
label="How to load clips",
items=[
"original timing",
"sequential in order"
],
default=0,
help="Would you like to place it at orignal timing?"
)
]
def load(
self,
context,
name=None,
namespace=None,
options=None
):
pass
def update(self, container, representation):
"""Update an existing `container`
"""
pass
def remove(self, container):
"""Remove an existing `container`
"""
pass

View file

@ -0,0 +1,29 @@
#!/usr/bin/env python
import time
from pype.resolve.utils import get_resolve_module
from pypeapp import Logger
log = Logger().get_logger(__name__, "resolve")
wait_delay = 2.5
wait = 0.00
ready = None
while True:
try:
# Create project and set parameters:
resolve = get_resolve_module()
pm = resolve.GetProjectManager()
p = pm.GetCurrentProject()
if p.GetName() == "Untitled Project":
ready = None
else:
ready = True
except AttributeError:
pass
if ready is None:
time.sleep(wait_delay)
log.info(f"Waiting {wait}s for Resolve to be open in project")
wait += wait_delay
else:
break

View file

@ -0,0 +1,34 @@
import os
import sys
import importlib
import avalon
import pype
from pypeapp import Logger
log = Logger().get_logger(__name__)
def main(env):
# Registers pype's Global pyblish plugins
pype.install()
# Register Host (and it's pyblish plugins)
host_name = env["AVALON_APP"]
host_import_str = "pype.resolve"
try:
host_module = importlib.import_module(host_import_str)
except ModuleNotFoundError:
log.error((
f"Host \"{host_name}\" can't be imported."
f" Import string \"{host_import_str}\" failed."
))
return False
avalon.api.install(host_module)
if __name__ == "__main__":
result = main(os.environ)
sys.exit(not bool(result))

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,111 @@
#! python3
# -*- coding: utf-8 -*-
# DaVinci Resolve scripting proof of concept. Resolve page external switcher.
# Local or TCP/IP control mode.
# Refer to Resolve V15 public beta 2 scripting API documentation for host setup.
# Copyright 2018 Igor Riđanović, www.hdhead.com
from Qt.QtGui import *
from Qt.QtWidgets import *
from Qt.QtCore import *
import sys
# If API module not found assume we"re working as a remote control
try:
import DaVinciResolveScript
# Instantiate Resolve object
resolve = DaVinciResolveScript.scriptapp("Resolve")
checkboxState = False
except ImportError:
print("Resolve API not found.")
checkboxState = True
try:
_encoding = QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QApplication.translate(context, text, disambig)
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(str("Resolve Page Switcher"))
Form.resize(561, 88)
Form.setStyleSheet(str((
"background-color: #282828;"
"border-color: #555555;"
"color: #929292;"
"font-size: 13px;"
)))
self.horizontalLayout = QHBoxLayout(Form)
self.horizontalLayout.setObjectName(str("horizontalLayout"))
self.mediaButton = QPushButton(Form)
self.mediaButton.setObjectName(str("mediaButton"))
self.horizontalLayout.addWidget(self.mediaButton)
self.editButton = QPushButton(Form)
self.editButton.setObjectName(str("editButton"))
self.horizontalLayout.addWidget(self.editButton)
self.fusionButton = QPushButton(Form)
self.fusionButton.setObjectName(str("fusionButton"))
self.horizontalLayout.addWidget(self.fusionButton)
self.colorButton = QPushButton(Form)
self.colorButton.setObjectName(str("colorButton"))
self.horizontalLayout.addWidget(self.colorButton)
self.fairlightButton = QPushButton(Form)
self.fairlightButton.setObjectName(str("fairlightButton"))
self.horizontalLayout.addWidget(self.fairlightButton)
self.deliverButton = QPushButton(Form)
self.deliverButton.setObjectName(str("deliverButton"))
self.horizontalLayout.addWidget(self.deliverButton)
self.mediaButton.clicked.connect(lambda: self.pageswitch("media"))
self.editButton.clicked.connect(lambda: self.pageswitch("edit"))
self.fusionButton.clicked.connect(lambda: self.pageswitch("fusion"))
self.colorButton.clicked.connect(lambda: self.pageswitch("color"))
self.fairlightButton.clicked.connect(
lambda: self.pageswitch("fairlight"))
self.deliverButton.clicked.connect(lambda: self.pageswitch("deliver"))
self.mediaButton.setStyleSheet(str("background-color: #181818;"))
self.editButton.setStyleSheet(str("background-color: #181818;"))
self.fusionButton.setStyleSheet(
str("background-color: #181818;"))
self.colorButton.setStyleSheet(str("background-color: #181818;"))
self.fairlightButton.setStyleSheet(
str("background-color: #181818;"))
self.deliverButton.setStyleSheet(
str("background-color: #181818;"))
self.retranslateUi(Form)
QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(_translate("Resolve Page Switcher",
"Resolve Page Switcher", None))
self.mediaButton.setText(_translate("Form", "Media", None))
self.editButton.setText(_translate("Form", "Edit", None))
self.fusionButton.setText(_translate("Form", "Fusion", None))
self.colorButton.setText(_translate("Form", "Color", None))
self.fairlightButton.setText(_translate("Form", "Fairlight", None))
self.deliverButton.setText(_translate("Form", "Deliver", None))
def pageswitch(self, page):
# Send page name to server to switch remote Resolve"s page
try:
resolve.OpenPage(page)
print(f"Switched to {page}")
except NameError:
print("Resolve API not found. Run in remote mode instead?")
if __name__ == "__main__":
app = QApplication(sys.argv)
Form = QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,57 @@
import os
import sys
import pype
import importlib
import pyblish.api
import pyblish.util
import avalon.api
from avalon.tools import publish
from pypeapp import Logger
log = Logger().get_logger(__name__)
def main(env):
# Registers pype's Global pyblish plugins
pype.install()
# Register Host (and it's pyblish plugins)
host_name = env["AVALON_APP"]
# TODO not sure if use "pype." or "avalon." for host import
host_import_str = f"pype.{host_name}"
try:
host_module = importlib.import_module(host_import_str)
except ModuleNotFoundError:
log.error((
f"Host \"{host_name}\" can't be imported."
f" Import string \"{host_import_str}\" failed."
))
return False
avalon.api.install(host_module)
# Register additional paths
addition_paths_str = env.get("PUBLISH_PATHS") or ""
addition_paths = addition_paths_str.split(os.pathsep)
for path in addition_paths:
path = os.path.normpath(path)
if not os.path.exists(path):
continue
pyblish.api.register_plugin_path(path)
# Register project specific plugins
project_name = os.environ["AVALON_PROJECT"]
project_plugins_paths = env.get("PYPE_PROJECT_PLUGINS") or ""
for path in project_plugins_paths.split(os.pathsep):
plugin_path = os.path.join(path, project_name, "plugins")
if os.path.exists(plugin_path):
pyblish.api.register_plugin_path(plugin_path)
return publish.show()
if __name__ == "__main__":
result = main(os.environ)
sys.exit(not bool(result))

View file

@ -0,0 +1,36 @@
#! python3
# -*- coding: utf-8 -*-
import os
import sys
from pypeapp import execute, Logger
from pype.resolve.utils import get_resolve_module
log = Logger().get_logger("Resolve")
CURRENT_DIR = os.getenv("RESOLVE_UTILITY_SCRIPTS_DIR", "")
python_dir = os.getenv("PYTHON36_RESOLVE")
python_exe = os.path.normpath(
os.path.join(python_dir, "python.exe")
)
resolve = get_resolve_module()
PM = resolve.GetProjectManager()
P = PM.GetCurrentProject()
log.info(P.GetName())
# ______________________________________________________
# testing subprocessing Scripts
testing_py = os.path.join(CURRENT_DIR, "ResolvePageSwitcher.py")
testing_py = os.path.normpath(testing_py)
log.info(f"Testing path to script: `{testing_py}`")
returncode = execute(
[python_exe, os.path.normpath(testing_py)],
env=dict(os.environ)
)
# Check if output file exists
if returncode != 0:
log.error("Executing failed!")

View file

@ -1,34 +0,0 @@
#!/usr/bin/env python
"""
This file serves to return a DaVinci Resolve object
"""
import sys
def GetResolve():
try:
# The PYTHONPATH needs to be set correctly for this import statement to work.
# An alternative is to import the DaVinciResolveScript by specifying absolute path (see ExceptionHandler logic)
import DaVinciResolveScript as bmd
except ImportError:
if sys.platform.startswith("darwin"):
expectedPath="/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting/Modules/"
elif sys.platform.startswith("win") or sys.platform.startswith("cygwin"):
import os
expectedPath=os.getenv('PROGRAMDATA') + "\\Blackmagic Design\\DaVinci Resolve\\Support\\Developer\\Scripting\\Modules\\"
elif sys.platform.startswith("linux"):
expectedPath="/opt/resolve/libs/Fusion/Modules/"
# check if the default path has it...
print("Unable to find module DaVinciResolveScript from $PYTHONPATH - trying default locations")
try:
import imp
bmd = imp.load_source('DaVinciResolveScript', expectedPath+"DaVinciResolveScript.py")
except ImportError:
# No fallbacks ... report error:
print("Unable to find module DaVinciResolveScript - please ensure that the module DaVinciResolveScript is discoverable by python")
print("For a default DaVinci Resolve installation, the module is expected to be located in: "+expectedPath)
sys.exit()
return bmd.scriptapp("Resolve")

View file

@ -1,72 +0,0 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# This script tests Resolve 15 scripting API on MacOS.
# We suspect an issue with import of fusionscript.so.
# To test launch Resolve Studio first and then run this script.
# The script will save a text report.
# igor@hdhead.com
from datetime import datetime
import os
import sys
import imp
eol = '\n'
pathLib = 'C:\\Program Files\\Blackmagic Design\\DaVinci Resolve\\fusionscript.dll'
reportDir = "C:\\Users\\jezsc"
# Create initial report file. It will overwrite existing!
reportName = 'Resolve_API_Report.txt'
reportPath = os.path.join(reportDir, reportName)
reportfile = open(reportPath, 'w')
reportfile.close()
def report(entry):
# Print to console
print entry
# Write a report entry
reportfile = open(reportPath, 'a')
reportfile.write(entry)
reportfile.write(eol)
reportfile.close()
# These are the values we'll discover and save
report('Time: ' + str(datetime.now()))
report('Python Version: ' + sys.version)
report('Interpreter Path: ' + sys.executable)
report('___________________________________' + eol)
report('If no lines follow we have likely experienced a Fatal Python Error.')
try:
# Will the API library import? Does it exist?
smodule = imp.load_dynamic('fusionscript', pathLib)
report('Imported fusionscript.so')
# It looks like the library imported. Can we create a resolve instance now?
try:
resolve = smodule.scriptapp('Resolve')
if 'None' in str(type(resolve)):
report('Resolve instance is created, but Resolve is not found.')
sys.exit()
if 'PyRemoteObject' in str(type(resolve)):
report('Resolve instance is created and Resolve is responsive.')
except Exception, e:
report(str(e))
# Let's go nuts and count how many projects are in the Project Manager
try:
projman = resolve.GetProjectManager()
projects = projman.GetProjectsInCurrentFolder()
report('Project Count: ' + str(len(projects)))
report('All is well!')
except Exception, e:
report(str(e))
except Exception, e:
report(str(e))

View file

@ -1,8 +0,0 @@
#!/usr/bin/env python3.6
from python_get_resolve import GetResolve
resolve = GetResolve()
PM = resolve.GetProjectManager()
P = PM.GetCurrentProject()
print(P.GetName())

View file

@ -1,26 +0,0 @@
#!/usr/bin/env python
import time
from python_get_resolve import GetResolve
wait_delay = 2.5
wait = 0.00
ready = None
while True:
try:
# Create project and set parameters:
resolve = GetResolve()
PM = resolve.GetProjectManager()
P = PM.GetCurrentProject()
if P.GetName() == "Untitled Project":
ready = None
else:
ready = True
except AttributeError:
pass
if ready is None:
time.sleep(wait_delay)
print(f"Waiting {wait}s for Resolve to be open inproject")
wait += wait_delay
else:
break

View file

@ -1,34 +0,0 @@
#!/usr/bin/env python
"""
This file serves to return a DaVinci Resolve object
"""
import sys
def GetResolve():
try:
# The PYTHONPATH needs to be set correctly for this import statement to work.
# An alternative is to import the DaVinciResolveScript by specifying absolute path (see ExceptionHandler logic)
import DaVinciResolveScript as bmd
except ImportError:
if sys.platform.startswith("darwin"):
expectedPath="/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting/Modules/"
elif sys.platform.startswith("win") or sys.platform.startswith("cygwin"):
import os
expectedPath=os.getenv('PROGRAMDATA') + "\\Blackmagic Design\\DaVinci Resolve\\Support\\Developer\\Scripting\\Modules\\"
elif sys.platform.startswith("linux"):
expectedPath="/opt/resolve/libs/Fusion/Modules/"
# check if the default path has it...
print("Unable to find module DaVinciResolveScript from $PYTHONPATH - trying default locations")
try:
import imp
bmd = imp.load_source('DaVinciResolveScript', expectedPath+"DaVinciResolveScript.py")
except ImportError:
# No fallbacks ... report error:
print("Unable to find module DaVinciResolveScript - please ensure that the module DaVinciResolveScript is discoverable by python")
print("For a default DaVinci Resolve installation, the module is expected to be located in: "+expectedPath)
sys.exit()
return bmd.scriptapp("Resolve")

114
pype/resolve/utils.py Normal file
View file

@ -0,0 +1,114 @@
#! python3
"""
Resolve's tools for setting environment
"""
import sys
import os
import shutil
from pypeapp import Logger
log = Logger().get_logger(__name__, "resolve")
UTILITY_SCRIPTS = os.path.join(
os.path.dirname(__file__),
"resolve_utility_scripts"
)
def get_resolve_module():
try:
"""
The PYTHONPATH needs to be set correctly for this import
statement to work. An alternative is to import the
DaVinciResolveScript by specifying absolute path
(see ExceptionHandler logic)
"""
import DaVinciResolveScript as bmd
except ImportError:
if sys.platform.startswith("darwin"):
expected_path = ("/Library/Application Support/Blackmagic Design"
"/DaVinci Resolve/Developer/Scripting/Modules")
elif sys.platform.startswith("win") \
or sys.platform.startswith("cygwin"):
expected_path = os.path.normpath(
os.getenv('PROGRAMDATA') + (
"/Blackmagic Design/DaVinci Resolve/Support/Developer"
"/Scripting/Modules"
)
)
elif sys.platform.startswith("linux"):
expected_path = "/opt/resolve/libs/Fusion/Modules"
# check if the default path has it...
print(("Unable to find module DaVinciResolveScript from "
"$PYTHONPATH - trying default locations"))
module_path = os.path.normpath(
os.path.join(
expected_path,
"DaVinciResolveScript.py"
)
)
try:
import imp
bmd = imp.load_source('DaVinciResolveScript', module_path)
except ImportError:
# No fallbacks ... report error:
log.error(
("Unable to find module DaVinciResolveScript - please "
"ensure that the module DaVinciResolveScript is "
"discoverable by python")
)
log.error(
("For a default DaVinci Resolve installation, the "
f"module is expected to be located in: {expected_path}")
)
sys.exit()
return bmd.scriptapp("Resolve")
def _sync_utility_scripts(env=None):
""" Synchronizing basic utlility scripts for resolve.
To be able to run scripts from inside `Resolve/Workspace/Scripts` menu
all scripts has to be accessible from defined folder.
"""
if not env:
env = os.environ
us_dir = env.get("RESOLVE_UTILITY_SCRIPTS_DIR", "")
scripts = os.listdir(UTILITY_SCRIPTS)
log.info(f"Utility Scripts Dir: `{UTILITY_SCRIPTS}`")
log.info(f"Utility Scripts: `{scripts}`")
# make sure no script file is in folder
if next((s for s in os.listdir(us_dir)), None):
for s in os.listdir(us_dir):
path = os.path.join(us_dir, s)
log.info(f"Removing `{path}`...")
os.remove(path)
# copy scripts into Resolve's utility scripts dir
for s in scripts:
src = os.path.join(UTILITY_SCRIPTS, s)
dst = os.path.join(us_dir, s)
log.info(f"Copying `{src}` to `{dst}`...")
shutil.copy2(src, dst)
def setup(env=None):
""" Wrapper installer started from pype.hooks.resolve.ResolvePrelaunch()
"""
if not env:
env = os.environ
# synchronize resolve utility scripts
_sync_utility_scripts(env)
log.info("Resolve Pype wrapper has been installed")

88
pype/resolve/workio.py Normal file
View file

@ -0,0 +1,88 @@
"""Host API required Work Files tool"""
import os
import sys
from pypeapp import Logger
from .utils import get_resolve_module
log = Logger().get_logger(__name__, "nukestudio")
exported_projet_ext = ".drp"
self = sys.modules[__name__]
self.pm = None
def get_project_manager():
if not self.pm:
resolve = get_resolve_module()
self.pm = resolve.GetProjectManager()
return self.pm
def file_extensions():
return [exported_projet_ext]
def has_unsaved_changes():
get_project_manager().SaveProject()
return False
def save_file(filepath):
pm = get_project_manager()
file = os.path.basename(filepath)
fname, _ = os.path.splitext(file)
project = pm.GetCurrentProject()
name = project.GetName()
if "Untitled Project" not in name:
log.info("Saving project: `{}` as '{}'".format(name, file))
pm.ExportProject(name, filepath)
else:
log.info("Creating new project...")
pm.CreateProject(fname)
pm.ExportProject(name, filepath)
def open_file(filepath):
"""
Loading project
"""
pm = get_project_manager()
file = os.path.basename(filepath)
fname, _ = os.path.splitext(file)
# deal with current project
project = pm.GetCurrentProject()
pm.SaveProject()
pm.CloseProject(project)
try:
# load project from input path
project = pm.LoadProject(fname)
log.info(f"Project {project.GetName()} opened...")
return True
except NameError as E:
log.error(f"Project with name `{fname}` does not exist!\n\nError: {E}")
return False
def current_file():
pm = get_project_manager()
current_dir = os.getenv("AVALON_WORKDIR")
project = pm.GetCurrentProject()
name = project.GetName()
fname = name + exported_projet_ext
current_file = os.path.join(current_dir, fname)
normalised = os.path.normpath(current_file)
# Unsaved current file
if normalised == "":
return None
return normalised
def work_root(session):
return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/")