mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into enhancement/AY-5538_workfile-increment-in-one-go
This commit is contained in:
commit
acd39c4598
327 changed files with 786 additions and 528 deletions
|
|
@ -50,10 +50,13 @@ IGNORED_MODULES_IN_AYON = set()
|
|||
# - this is used to log the missing addon
|
||||
MOVED_ADDON_MILESTONE_VERSIONS = {
|
||||
"applications": VersionInfo(0, 2, 0),
|
||||
"blender": VersionInfo(0, 2, 0),
|
||||
"celaction": VersionInfo(0, 2, 0),
|
||||
"clockify": VersionInfo(0, 2, 0),
|
||||
"flame": VersionInfo(0, 2, 0),
|
||||
"fusion": VersionInfo(0, 2, 0),
|
||||
"harmony": VersionInfo(0, 2, 0),
|
||||
"hiero": VersionInfo(0, 2, 0),
|
||||
"max": VersionInfo(0, 2, 0),
|
||||
"photoshop": VersionInfo(0, 2, 0),
|
||||
"traypublisher": VersionInfo(0, 2, 0),
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
from .addon import BlenderAddon
|
||||
|
||||
|
||||
__all__ = (
|
||||
"BlenderAddon",
|
||||
)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
from .addon import (
|
||||
HIERO_ROOT_DIR,
|
||||
HieroAddon,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"HIERO_ROOT_DIR",
|
||||
"HieroAddon",
|
||||
)
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
from .version import __version__
|
||||
from .structures import HostMsgAction
|
||||
from .webserver_module import (
|
||||
WebServerAddon
|
||||
)
|
||||
|
|
@ -7,5 +8,6 @@ from .webserver_module import (
|
|||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"HostMsgAction",
|
||||
"WebServerAddon",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,22 +9,18 @@ from qtpy import QtWidgets
|
|||
from ayon_core.addon import ITrayService
|
||||
from ayon_core.tools.stdout_broker.window import ConsoleDialog
|
||||
|
||||
from .structures import HostMsgAction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Host listener icon type
|
||||
class IconType:
|
||||
IDLE = "idle"
|
||||
RUNNING = "running"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class MsgAction:
|
||||
CONNECTING = "connecting"
|
||||
INITIALIZED = "initialized"
|
||||
ADD = "add"
|
||||
CLOSE = "close"
|
||||
|
||||
|
||||
class HostListener:
|
||||
def __init__(self, webserver, module):
|
||||
self._window_per_id = {}
|
||||
|
|
@ -96,22 +92,22 @@ class HostListener:
|
|||
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||
host_name, action, text = self._parse_message(msg)
|
||||
|
||||
if action == MsgAction.CONNECTING:
|
||||
if action == HostMsgAction.CONNECTING:
|
||||
self._action_per_id[host_name] = None
|
||||
# must be sent to main thread, or action wont trigger
|
||||
self.module.execute_in_main_thread(
|
||||
lambda: self._host_is_connecting(host_name, text))
|
||||
elif action == MsgAction.CLOSE:
|
||||
elif action == HostMsgAction.CLOSE:
|
||||
# clean close
|
||||
self._close(host_name)
|
||||
await ws.close()
|
||||
elif action == MsgAction.INITIALIZED:
|
||||
elif action == HostMsgAction.INITIALIZED:
|
||||
self.module.execute_in_main_thread(
|
||||
# must be queued as _host_is_connecting might not
|
||||
# be triggered/finished yet
|
||||
lambda: self._set_host_icon(host_name,
|
||||
IconType.RUNNING))
|
||||
elif action == MsgAction.ADD:
|
||||
elif action == HostMsgAction.ADD:
|
||||
self.module.execute_in_main_thread(
|
||||
lambda: self._add_text(host_name, text))
|
||||
elif msg.type == aiohttp.WSMsgType.ERROR:
|
||||
|
|
|
|||
6
client/ayon_core/modules/webserver/structures.py
Normal file
6
client/ayon_core/modules/webserver/structures.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Host listener message actions
|
||||
class HostMsgAction:
|
||||
CONNECTING = "connecting"
|
||||
INITIALIZED = "initialized"
|
||||
ADD = "add"
|
||||
CLOSE = "close"
|
||||
|
|
@ -172,12 +172,30 @@ class VersionItem:
|
|||
def __gt__(self, other):
|
||||
if not isinstance(other, VersionItem):
|
||||
return False
|
||||
if (
|
||||
other.version == self.version
|
||||
and self.is_hero
|
||||
):
|
||||
# Make sure hero versions are positive
|
||||
version = abs(self.version)
|
||||
other_version = abs(other.version)
|
||||
# Hero version is greater than non-hero
|
||||
if version == other_version:
|
||||
return self.is_hero
|
||||
return version > other_version
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, VersionItem):
|
||||
return True
|
||||
return other.version < self.version
|
||||
# Make sure hero versions are positive
|
||||
version = abs(self.version)
|
||||
other_version = abs(other.version)
|
||||
# Non-hero version is lesser than hero
|
||||
if version == other_version:
|
||||
return not self.is_hero
|
||||
return version < other_version
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.__eq__(other) or self.__gt__(other)
|
||||
|
||||
def __le__(self, other):
|
||||
return self.__eq__(other) or self.__lt__(other)
|
||||
|
||||
def to_data(self):
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -199,7 +199,9 @@ class ProductsModel(QtGui.QStandardItemModel):
|
|||
product_item = self._product_items_by_id.get(product_id)
|
||||
if product_item is None:
|
||||
return None
|
||||
return list(product_item.version_items.values())
|
||||
product_items = list(product_item.version_items.values())
|
||||
product_items.sort(reverse=True)
|
||||
return product_items
|
||||
|
||||
if role == QtCore.Qt.EditRole:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
from .broker import StdOutBroker
|
||||
|
||||
__all__ = (
|
||||
"StdOutBroker",
|
||||
)
|
||||
|
|
@ -1,173 +1,12 @@
|
|||
import os
|
||||
import sys
|
||||
import threading
|
||||
import collections
|
||||
import websocket
|
||||
import json
|
||||
from datetime import datetime
|
||||
import warnings
|
||||
from .broker import StdOutBroker
|
||||
|
||||
from ayon_core.lib import Logger
|
||||
from openpype_modules.webserver.host_console_listener import MsgAction
|
||||
warnings.warn(
|
||||
(
|
||||
"Import of 'StdOutBroker' from 'ayon_core.tools.stdout_broker.app'"
|
||||
" is deprecated. Please use 'ayon_core.tools.stdout_broker' instead."
|
||||
),
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
|
||||
class StdOutBroker:
|
||||
"""
|
||||
Application showing console in Services tray for non python hosts
|
||||
instead of cmd window.
|
||||
"""
|
||||
MAX_LINES = 10000
|
||||
TIMER_TIMEOUT = 0.200
|
||||
|
||||
def __init__(self, host_name):
|
||||
self.host_name = host_name
|
||||
self.webserver_client = None
|
||||
|
||||
self.original_stdout_write = None
|
||||
self.original_stderr_write = None
|
||||
self.log_queue = collections.deque()
|
||||
|
||||
date_str = datetime.now().strftime("%d%m%Y%H%M%S")
|
||||
self.host_id = "{}_{}".format(self.host_name, date_str)
|
||||
|
||||
self._std_available = False
|
||||
self._is_running = False
|
||||
self._catch_std_outputs()
|
||||
|
||||
self._timer = None
|
||||
|
||||
@property
|
||||
def send_to_tray(self):
|
||||
"""Checks if connected to tray and have access to logs."""
|
||||
return self.webserver_client and self._std_available
|
||||
|
||||
def start(self):
|
||||
"""Start app, create and start timer"""
|
||||
if not self._std_available or self._is_running:
|
||||
return
|
||||
self._is_running = True
|
||||
self._create_timer()
|
||||
self._connect_to_tray()
|
||||
|
||||
def stop(self):
|
||||
"""Disconnect from Tray, process last logs"""
|
||||
if not self._is_running:
|
||||
return
|
||||
self._is_running = False
|
||||
self._process_queue()
|
||||
self._disconnect_from_tray()
|
||||
|
||||
def host_connected(self):
|
||||
"""Send to Tray console that host is ready - icon change. """
|
||||
log.info("Host {} connected".format(self.host_id))
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.INITIALIZED,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _create_timer(self):
|
||||
timer = threading.Timer(self.TIMER_TIMEOUT, self._timer_callback)
|
||||
timer.start()
|
||||
self._timer = timer
|
||||
|
||||
def _timer_callback(self):
|
||||
if not self._is_running:
|
||||
return
|
||||
self._process_queue()
|
||||
self._create_timer()
|
||||
|
||||
def _connect_to_tray(self):
|
||||
"""Connect to Tray webserver to pass console output. """
|
||||
if not self._std_available: # not content to log
|
||||
return
|
||||
ws = websocket.WebSocket()
|
||||
webserver_url = os.environ.get("AYON_WEBSERVER_URL")
|
||||
|
||||
if not webserver_url:
|
||||
print("Unknown webserver url, cannot connect to pass log")
|
||||
return
|
||||
|
||||
webserver_url = webserver_url.replace("http", "ws")
|
||||
ws.connect("{}/ws/host_listener".format(webserver_url))
|
||||
self.webserver_client = ws
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.CONNECTING,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _disconnect_from_tray(self):
|
||||
"""Send to Tray that host is closing - remove from Services. """
|
||||
print("Host {} closing".format(self.host_name))
|
||||
if not self.webserver_client:
|
||||
return
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.CLOSE,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
self.webserver_client.close()
|
||||
|
||||
def _catch_std_outputs(self):
|
||||
"""Redirects standard out and error to own functions"""
|
||||
if sys.stdout:
|
||||
self.original_stdout_write = sys.stdout.write
|
||||
sys.stdout.write = self._my_stdout_write
|
||||
self._std_available = True
|
||||
|
||||
if sys.stderr:
|
||||
self.original_stderr_write = sys.stderr.write
|
||||
sys.stderr.write = self._my_stderr_write
|
||||
self._std_available = True
|
||||
|
||||
def _my_stdout_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stdout"""
|
||||
if self.original_stdout_write is not None:
|
||||
self.original_stdout_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _my_stderr_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stderr"""
|
||||
if self.original_stderr_write is not None:
|
||||
self.original_stderr_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _process_queue(self):
|
||||
"""Sends lines and purges queue"""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
lines = tuple(self.log_queue)
|
||||
self.log_queue.clear()
|
||||
if lines:
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.ADD,
|
||||
"text": "\n".join(lines)
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
|
||||
def _send(self, payload):
|
||||
"""Worker method to send to existing websocket connection."""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
try:
|
||||
self.webserver_client.send(json.dumps(payload))
|
||||
except ConnectionResetError: # Tray closed
|
||||
self._connect_to_tray()
|
||||
__all__ = ("StdOutBroker", )
|
||||
|
|
|
|||
174
client/ayon_core/tools/stdout_broker/broker.py
Normal file
174
client/ayon_core/tools/stdout_broker/broker.py
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import os
|
||||
import sys
|
||||
import threading
|
||||
import collections
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
import websocket
|
||||
|
||||
from ayon_core.lib import Logger
|
||||
from ayon_core.modules.webserver import HostMsgAction
|
||||
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
|
||||
class StdOutBroker:
|
||||
"""
|
||||
Application showing console in Services tray for non python hosts
|
||||
instead of cmd window.
|
||||
"""
|
||||
MAX_LINES = 10000
|
||||
TIMER_TIMEOUT = 0.200
|
||||
|
||||
def __init__(self, host_name):
|
||||
self.host_name = host_name
|
||||
self.webserver_client = None
|
||||
|
||||
self.original_stdout_write = None
|
||||
self.original_stderr_write = None
|
||||
self.log_queue = collections.deque()
|
||||
|
||||
date_str = datetime.now().strftime("%d%m%Y%H%M%S")
|
||||
self.host_id = "{}_{}".format(self.host_name, date_str)
|
||||
|
||||
self._std_available = False
|
||||
self._is_running = False
|
||||
self._catch_std_outputs()
|
||||
|
||||
self._timer = None
|
||||
|
||||
@property
|
||||
def send_to_tray(self):
|
||||
"""Checks if connected to tray and have access to logs."""
|
||||
return self.webserver_client and self._std_available
|
||||
|
||||
def start(self):
|
||||
"""Start app, create and start timer"""
|
||||
if not self._std_available or self._is_running:
|
||||
return
|
||||
self._is_running = True
|
||||
self._create_timer()
|
||||
self._connect_to_tray()
|
||||
|
||||
def stop(self):
|
||||
"""Disconnect from Tray, process last logs"""
|
||||
if not self._is_running:
|
||||
return
|
||||
self._is_running = False
|
||||
self._process_queue()
|
||||
self._disconnect_from_tray()
|
||||
|
||||
def host_connected(self):
|
||||
"""Send to Tray console that host is ready - icon change. """
|
||||
log.info("Host {} connected".format(self.host_id))
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.INITIALIZED,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _create_timer(self):
|
||||
timer = threading.Timer(self.TIMER_TIMEOUT, self._timer_callback)
|
||||
timer.start()
|
||||
self._timer = timer
|
||||
|
||||
def _timer_callback(self):
|
||||
if not self._is_running:
|
||||
return
|
||||
self._process_queue()
|
||||
self._create_timer()
|
||||
|
||||
def _connect_to_tray(self):
|
||||
"""Connect to Tray webserver to pass console output. """
|
||||
if not self._std_available: # not content to log
|
||||
return
|
||||
ws = websocket.WebSocket()
|
||||
webserver_url = os.environ.get("AYON_WEBSERVER_URL")
|
||||
|
||||
if not webserver_url:
|
||||
print("Unknown webserver url, cannot connect to pass log")
|
||||
return
|
||||
|
||||
webserver_url = webserver_url.replace("http", "ws")
|
||||
ws.connect("{}/ws/host_listener".format(webserver_url))
|
||||
self.webserver_client = ws
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.CONNECTING,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _disconnect_from_tray(self):
|
||||
"""Send to Tray that host is closing - remove from Services. """
|
||||
print("Host {} closing".format(self.host_name))
|
||||
if not self.webserver_client:
|
||||
return
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.CLOSE,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
self.webserver_client.close()
|
||||
|
||||
def _catch_std_outputs(self):
|
||||
"""Redirects standard out and error to own functions"""
|
||||
if sys.stdout:
|
||||
self.original_stdout_write = sys.stdout.write
|
||||
sys.stdout.write = self._my_stdout_write
|
||||
self._std_available = True
|
||||
|
||||
if sys.stderr:
|
||||
self.original_stderr_write = sys.stderr.write
|
||||
sys.stderr.write = self._my_stderr_write
|
||||
self._std_available = True
|
||||
|
||||
def _my_stdout_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stdout"""
|
||||
if self.original_stdout_write is not None:
|
||||
self.original_stdout_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _my_stderr_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stderr"""
|
||||
if self.original_stderr_write is not None:
|
||||
self.original_stderr_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _process_queue(self):
|
||||
"""Sends lines and purges queue"""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
lines = tuple(self.log_queue)
|
||||
self.log_queue.clear()
|
||||
if lines:
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.ADD,
|
||||
"text": "\n".join(lines)
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
|
||||
def _send(self, payload):
|
||||
"""Worker method to send to existing websocket connection."""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
try:
|
||||
self.webserver_client.send(json.dumps(payload))
|
||||
except ConnectionResetError: # Tray closed
|
||||
self._connect_to_tray()
|
||||
|
|
@ -80,11 +80,11 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|||
exclude = [
|
||||
"client/ayon_core/hosts/unreal/integration/*",
|
||||
"client/ayon_core/hosts/aftereffects/api/extension/js/libs/*",
|
||||
"client/ayon_core/hosts/hiero/api/startup/*",
|
||||
"client/ayon_core/modules/deadline/repository/custom/plugins/CelAction/*",
|
||||
"client/ayon_core/modules/deadline/repository/custom/plugins/HarmonyAYON/*",
|
||||
"client/ayon_core/modules/click_wrap.py",
|
||||
"client/ayon_core/scripts/slates/__init__.py"
|
||||
"client/ayon_core/scripts/slates/__init__.py",
|
||||
"server_addon/hiero/client/ayon_hiero/api/startup/*"
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
|
|
|
|||
13
server_addon/blender/client/ayon_blender/__init__.py
Normal file
13
server_addon/blender/client/ayon_blender/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from .version import __version__
|
||||
from .addon import (
|
||||
BlenderAddon,
|
||||
BLENDER_ADDON_ROOT,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"BlenderAddon",
|
||||
"BLENDER_ADDON_ROOT",
|
||||
)
|
||||
|
|
@ -1,18 +1,21 @@
|
|||
import os
|
||||
from ayon_core.addon import AYONAddon, IHostAddon
|
||||
|
||||
BLENDER_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
from .version import __version__
|
||||
|
||||
BLENDER_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class BlenderAddon(AYONAddon, IHostAddon):
|
||||
name = "blender"
|
||||
version = __version__
|
||||
host_name = "blender"
|
||||
|
||||
def add_implementation_envs(self, env, _app):
|
||||
"""Modify environments to contain all required for implementation."""
|
||||
# Prepare path to implementation script
|
||||
implementation_user_script_path = os.path.join(
|
||||
BLENDER_ROOT_DIR,
|
||||
BLENDER_ADDON_ROOT,
|
||||
"blender_addon"
|
||||
)
|
||||
|
||||
|
|
@ -61,7 +64,7 @@ class BlenderAddon(AYONAddon, IHostAddon):
|
|||
if app.host_name != self.host_name:
|
||||
return []
|
||||
return [
|
||||
os.path.join(BLENDER_ROOT_DIR, "hooks")
|
||||
os.path.join(BLENDER_ADDON_ROOT, "hooks")
|
||||
]
|
||||
|
||||
def get_workfile_extensions(self):
|
||||
|
|
@ -15,7 +15,6 @@ from .pipeline import (
|
|||
|
||||
from .plugin import (
|
||||
Creator,
|
||||
Loader,
|
||||
)
|
||||
|
||||
from .workio import (
|
||||
|
|
@ -51,7 +50,6 @@ __all__ = [
|
|||
"BlenderHost",
|
||||
|
||||
"Creator",
|
||||
"Loader",
|
||||
|
||||
# Workfiles API
|
||||
"open_file",
|
||||
|
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 632 B |
|
|
@ -305,7 +305,7 @@ class LaunchCreator(LaunchQtApp):
|
|||
|
||||
|
||||
class LaunchLoader(LaunchQtApp):
|
||||
"""Launch Avalon Loader."""
|
||||
"""Launch AYON Loader."""
|
||||
|
||||
bl_idname = "wm.avalon_loader"
|
||||
bl_label = "Load..."
|
||||
|
|
@ -5,9 +5,6 @@ from typing import Callable, Dict, Iterator, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from . import lib
|
||||
from . import ops
|
||||
|
||||
import pyblish.api
|
||||
import ayon_api
|
||||
|
||||
|
|
@ -33,8 +30,12 @@ from ayon_core.lib import (
|
|||
register_event_callback,
|
||||
emit_event
|
||||
)
|
||||
import ayon_core.hosts.blender
|
||||
from ayon_core.settings import get_project_settings
|
||||
from ayon_blender import BLENDER_ADDON_ROOT
|
||||
|
||||
from . import lib
|
||||
from . import ops
|
||||
|
||||
from .workio import (
|
||||
open_file,
|
||||
save_file,
|
||||
|
|
@ -44,9 +45,7 @@ from .workio import (
|
|||
work_root,
|
||||
)
|
||||
|
||||
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(ayon_core.hosts.blender.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PLUGINS_DIR = os.path.join(BLENDER_ADDON_ROOT, "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")
|
||||
|
|
@ -4,6 +4,7 @@ import itertools
|
|||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import pyblish.api
|
||||
import bpy
|
||||
|
||||
from ayon_core.pipeline import (
|
||||
|
|
@ -13,6 +14,7 @@ from ayon_core.pipeline import (
|
|||
AVALON_INSTANCE_ID,
|
||||
AYON_INSTANCE_ID,
|
||||
)
|
||||
from ayon_core.pipeline.publish import Extractor
|
||||
from ayon_core.lib import BoolDef
|
||||
|
||||
from .pipeline import (
|
||||
|
|
@ -161,10 +163,23 @@ def deselect_all():
|
|||
bpy.context.view_layer.objects.active = active
|
||||
|
||||
|
||||
class BaseCreator(Creator):
|
||||
class BlenderInstancePlugin(pyblish.api.InstancePlugin):
|
||||
settings_category = "blender"
|
||||
|
||||
|
||||
class BlenderContextPlugin(pyblish.api.ContextPlugin):
|
||||
settings_category = "blender"
|
||||
|
||||
|
||||
class BlenderExtractor(Extractor):
|
||||
settings_category = "blender"
|
||||
|
||||
|
||||
class BlenderCreator(Creator):
|
||||
"""Base class for Blender Creator plug-ins."""
|
||||
defaults = ['Main']
|
||||
|
||||
settings_category = "blender"
|
||||
create_as_asset_group = False
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -265,7 +280,7 @@ class BaseCreator(Creator):
|
|||
return instance_node
|
||||
|
||||
def collect_instances(self):
|
||||
"""Override abstract method from BaseCreator.
|
||||
"""Override abstract method from BlenderCreator.
|
||||
Collect existing instances related to this creator plugin."""
|
||||
|
||||
# Cache instances in shared data
|
||||
|
|
@ -292,7 +307,7 @@ class BaseCreator(Creator):
|
|||
self._add_instance_to_context(instance)
|
||||
|
||||
def update_instances(self, update_list):
|
||||
"""Override abstract method from BaseCreator.
|
||||
"""Override abstract method from BlenderCreator.
|
||||
Store changes of existing instances so they can be recollected.
|
||||
|
||||
Args:
|
||||
|
|
@ -376,13 +391,7 @@ class BaseCreator(Creator):
|
|||
]
|
||||
|
||||
|
||||
class Loader(LoaderPlugin):
|
||||
"""Base class for Loader plug-ins."""
|
||||
|
||||
hosts = ["blender"]
|
||||
|
||||
|
||||
class AssetLoader(LoaderPlugin):
|
||||
class BlenderLoader(LoaderPlugin):
|
||||
"""A basic AssetLoader for Blender
|
||||
|
||||
This will implement the basic logic for linking/appending assets
|
||||
|
|
@ -392,6 +401,7 @@ class AssetLoader(LoaderPlugin):
|
|||
it's different for different types (e.g. model, rig, animation,
|
||||
etc.).
|
||||
"""
|
||||
settings_category = "blender"
|
||||
|
||||
@staticmethod
|
||||
def _get_instance_empty(instance_name: str, nodes: List) -> Optional[bpy.types.Object]:
|
||||
|
|
@ -496,7 +506,7 @@ class AssetLoader(LoaderPlugin):
|
|||
# Only containerise if it's not already a collection from a .blend file.
|
||||
# representation = context["representation"]["name"]
|
||||
# if representation != "blend":
|
||||
# from ayon_core.hosts.blender.api.pipeline import containerise
|
||||
# from ayon_blender.api.pipeline import containerise
|
||||
# return containerise(
|
||||
# name=name,
|
||||
# namespace=namespace,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from ayon_core.pipeline import install_host
|
||||
from ayon_core.hosts.blender.api import BlenderHost
|
||||
from ayon_blender.api import BlenderHost
|
||||
|
||||
|
||||
def register():
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Converter for legacy Houdini products."""
|
||||
from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin
|
||||
from ayon_core.hosts.blender.api.lib import imprint
|
||||
from ayon_blender.api.lib import imprint
|
||||
|
||||
|
||||
class BlenderLegacyConvertor(ProductConvertorPlugin):
|
||||
|
|
@ -42,7 +42,7 @@ class BlenderLegacyConvertor(ProductConvertorPlugin):
|
|||
parameter on them.
|
||||
|
||||
This is using cached entries done in
|
||||
:py:meth:`~BaseCreator.cache_instance_data()`
|
||||
:py:meth:`~BlenderCreator.cache_instance_data()`
|
||||
|
||||
"""
|
||||
self.legacy_instances = self.collection_shared_data.get(
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import lib, plugin
|
||||
from ayon_blender.api import lib, plugin
|
||||
|
||||
|
||||
class CreateAction(plugin.BaseCreator):
|
||||
class CreateAction(plugin.BlenderCreator):
|
||||
"""Action output for character rigs."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.action"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create an animation asset."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateAnimation(plugin.BaseCreator):
|
||||
class CreateAnimation(plugin.BlenderCreator):
|
||||
"""Animation output for character rigs."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.animation"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateBlendScene(plugin.BaseCreator):
|
||||
class CreateBlendScene(plugin.BlenderCreator):
|
||||
"""Generic group of assets."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.blendscene"
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_INSTANCES
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import AVALON_INSTANCES
|
||||
|
||||
|
||||
class CreateCamera(plugin.BaseCreator):
|
||||
class CreateCamera(plugin.BlenderCreator):
|
||||
"""Polygonal static geometry."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.camera"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateLayout(plugin.BaseCreator):
|
||||
class CreateLayout(plugin.BlenderCreator):
|
||||
"""Layout output for character rigs."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.layout"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateModel(plugin.BaseCreator):
|
||||
class CreateModel(plugin.BlenderCreator):
|
||||
"""Polygonal static geometry."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.model"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create a pointcache asset."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreatePointcache(plugin.BaseCreator):
|
||||
class CreatePointcache(plugin.BlenderCreator):
|
||||
"""Polygonal static geometry."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.pointcache"
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
import bpy
|
||||
|
||||
from ayon_core.lib import version_up
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.render_lib import prepare_rendering
|
||||
from ayon_core.hosts.blender.api.workio import save_file
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.render_lib import prepare_rendering
|
||||
from ayon_blender.api.workio import save_file
|
||||
|
||||
|
||||
class CreateRenderlayer(plugin.BaseCreator):
|
||||
class CreateRenderlayer(plugin.BlenderCreator):
|
||||
"""Single baked camera."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.render"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create review."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateReview(plugin.BaseCreator):
|
||||
class CreateReview(plugin.BlenderCreator):
|
||||
"""Single baked camera."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.review"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateRig(plugin.BaseCreator):
|
||||
class CreateRig(plugin.BlenderCreator):
|
||||
"""Artist-friendly rig with controls to direct motion."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.rig"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create a USD Export."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateUSD(plugin.BaseCreator):
|
||||
class CreateUSD(plugin.BlenderCreator):
|
||||
"""Create USD Export"""
|
||||
|
||||
identifier = "io.openpype.creators.blender.usd"
|
||||
|
|
@ -2,14 +2,14 @@ import bpy
|
|||
import ayon_api
|
||||
|
||||
from ayon_core.pipeline import CreatedInstance, AutoCreator
|
||||
from ayon_core.hosts.blender.api.plugin import BaseCreator
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api.plugin import BlenderCreator
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_PROPERTY,
|
||||
AVALON_CONTAINERS
|
||||
)
|
||||
|
||||
|
||||
class CreateWorkfile(BaseCreator, AutoCreator):
|
||||
class CreateWorkfile(BlenderCreator, AutoCreator):
|
||||
"""Workfile auto-creator.
|
||||
|
||||
The workfile instance stores its data on the `AVALON_CONTAINERS` collection
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
def append_workfile(context, fname, do_import):
|
||||
|
|
@ -34,7 +34,7 @@ def append_workfile(context, fname, do_import):
|
|||
collection.children.link(coll)
|
||||
|
||||
|
||||
class AppendBlendLoader(plugin.AssetLoader):
|
||||
class AppendBlendLoader(plugin.BlenderLoader):
|
||||
"""Append workfile in Blender (unmanaged)
|
||||
|
||||
Warning:
|
||||
|
|
@ -59,7 +59,7 @@ class AppendBlendLoader(plugin.AssetLoader):
|
|||
return
|
||||
|
||||
|
||||
class ImportBlendLoader(plugin.AssetLoader):
|
||||
class ImportBlendLoader(plugin.BlenderLoader):
|
||||
"""Import workfile in the current Blender scene (unmanaged)
|
||||
|
||||
Warning:
|
||||
|
|
@ -7,8 +7,8 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
from ayon_core.pipeline import get_representation_path
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import (
|
||||
containerise_existing,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
|
@ -16,7 +16,7 @@ from ayon_core.hosts.blender.api.pipeline import (
|
|||
logger = logging.getLogger("ayon").getChild("blender").getChild("load_action")
|
||||
|
||||
|
||||
class BlendActionLoader(plugin.AssetLoader):
|
||||
class BlendActionLoader(plugin.BlenderLoader):
|
||||
"""Load action from a .blend file.
|
||||
|
||||
Warning:
|
||||
|
|
@ -4,11 +4,11 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
class BlendAnimationLoader(plugin.AssetLoader):
|
||||
class BlendAnimationLoader(plugin.BlenderLoader):
|
||||
"""Load animations from a .blend file.
|
||||
|
||||
Warning:
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class AudioLoader(plugin.AssetLoader):
|
||||
class AudioLoader(plugin.BlenderLoader):
|
||||
"""Load audio in Blender."""
|
||||
|
||||
product_types = {"audio"}
|
||||
|
|
@ -9,15 +9,15 @@ from ayon_core.pipeline import (
|
|||
registered_host
|
||||
)
|
||||
from ayon_core.pipeline.create import CreateContext
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.lib import imprint
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.lib import imprint
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class BlendLoader(plugin.AssetLoader):
|
||||
class BlendLoader(plugin.BlenderLoader):
|
||||
"""Load assets from a .blend file."""
|
||||
|
||||
product_types = {"model", "rig", "layout", "camera"}
|
||||
|
|
@ -7,15 +7,15 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.lib import imprint
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.lib import imprint
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class BlendSceneLoader(plugin.AssetLoader):
|
||||
class BlendSceneLoader(plugin.BlenderLoader):
|
||||
"""Load assets from a .blend file."""
|
||||
|
||||
product_types = {"blendScene"}
|
||||
|
|
@ -11,14 +11,14 @@ from ayon_core.pipeline import (
|
|||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CacheModelLoader(plugin.AssetLoader):
|
||||
class CacheModelLoader(plugin.BlenderLoader):
|
||||
"""Load cache models.
|
||||
|
||||
Stores the imported asset in a collection named after the asset.
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class AbcCameraLoader(plugin.AssetLoader):
|
||||
class AbcCameraLoader(plugin.BlenderLoader):
|
||||
"""Load a camera from Alembic file.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class FbxCameraLoader(plugin.AssetLoader):
|
||||
class FbxCameraLoader(plugin.BlenderLoader):
|
||||
"""Load a camera from FBX.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class FbxModelLoader(plugin.AssetLoader):
|
||||
class FbxModelLoader(plugin.BlenderLoader):
|
||||
"""Load FBX models.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
|
|
@ -15,15 +15,15 @@ from ayon_core.pipeline import (
|
|||
loaders_from_representation,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_INSTANCES,
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class JsonLayoutLoader(plugin.AssetLoader):
|
||||
class JsonLayoutLoader(plugin.BlenderLoader):
|
||||
"""Load layout published from Unreal."""
|
||||
|
||||
product_types = {"layout"}
|
||||
|
|
@ -9,14 +9,14 @@ import json
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import get_representation_path
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import (
|
||||
containerise_existing,
|
||||
AVALON_PROPERTY
|
||||
)
|
||||
|
||||
|
||||
class BlendLookLoader(plugin.AssetLoader):
|
||||
class BlendLookLoader(plugin.BlenderLoader):
|
||||
"""Load models from a .blend file.
|
||||
|
||||
Because they come from a .blend file we can simply link the collection that
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import pyblish.api
|
||||
from ayon_core.hosts.blender.api import workio
|
||||
from ayon_blender.api import workio, plugin
|
||||
|
||||
|
||||
class CollectBlenderCurrentFile(pyblish.api.ContextPlugin):
|
||||
class CollectBlenderCurrentFile(plugin.BlenderContextPlugin):
|
||||
"""Inject the current working file into context"""
|
||||
|
||||
order = pyblish.api.CollectorOrder - 0.5
|
||||
|
|
@ -3,10 +3,11 @@ import bpy
|
|||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.publish import KnownPublishError
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
class CollectBlenderInstanceData(pyblish.api.InstancePlugin):
|
||||
class CollectBlenderInstanceData(plugin.BlenderInstancePlugin):
|
||||
"""Validator to verify that the instance is not empty"""
|
||||
|
||||
order = pyblish.api.CollectorOrder
|
||||
|
|
@ -5,12 +5,12 @@ import os
|
|||
import re
|
||||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import colorspace
|
||||
import pyblish.api
|
||||
|
||||
from ayon_blender.api import colorspace, plugin
|
||||
|
||||
class CollectBlenderRender(pyblish.api.InstancePlugin):
|
||||
|
||||
class CollectBlenderRender(plugin.BlenderInstancePlugin):
|
||||
"""Gather all publishable render instances."""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.01
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class CollectReview(pyblish.api.InstancePlugin):
|
||||
class CollectReview(plugin.BlenderInstancePlugin):
|
||||
"""Collect Review data
|
||||
|
||||
"""
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pyblish.api import InstancePlugin, CollectorOrder
|
||||
from pyblish.api import CollectorOrder
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class CollectWorkfile(InstancePlugin):
|
||||
class CollectWorkfile(plugin.BlenderInstancePlugin):
|
||||
"""Inject workfile data into its instance."""
|
||||
|
||||
order = CollectorOrder
|
||||
|
|
@ -4,10 +4,10 @@ import bpy
|
|||
|
||||
from ayon_core.lib import BoolDef
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractABC(plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin):
|
||||
"""Extract as ABC."""
|
||||
|
||||
label = "Extract ABC"
|
||||
|
|
@ -3,12 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractAnimationABC(
|
||||
publish.Extractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
plugin.BlenderExtractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Extract as ABC."""
|
||||
|
||||
|
|
@ -3,9 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractBlend(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract a blend file."""
|
||||
|
||||
label = "Extract Blend"
|
||||
|
|
@ -3,11 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractBlendAnimation(
|
||||
publish.Extractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
plugin.BlenderExtractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Extract a blend file."""
|
||||
|
||||
|
|
@ -3,10 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractCameraABC(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract camera as ABC."""
|
||||
|
||||
label = "Extract Camera (ABC)"
|
||||
|
|
@ -3,10 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractCamera(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract as the camera as FBX."""
|
||||
|
||||
label = "Extract Camera (FBX)"
|
||||
|
|
@ -3,10 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractFBX(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract as FBX."""
|
||||
|
||||
label = "Extract FBX"
|
||||
|
|
@ -6,8 +6,8 @@ import bpy_extras
|
|||
import bpy_extras.anim_utils
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
def get_all_parents(obj):
|
||||
|
|
@ -42,8 +42,8 @@ def get_highest_root(objects):
|
|||
|
||||
|
||||
class ExtractAnimationFBX(
|
||||
publish.Extractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
plugin.BlenderExtractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Extract as animation."""
|
||||
|
||||
|
|
@ -8,11 +8,13 @@ import bpy_extras.anim_utils
|
|||
from ayon_api import get_representations
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractLayout(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract a layout."""
|
||||
|
||||
label = "Extract Layout (JSON)"
|
||||
|
|
@ -7,11 +7,13 @@ import pyblish.api
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import capture
|
||||
from ayon_core.hosts.blender.api.lib import maintained_time
|
||||
from ayon_blender.api import capture, plugin
|
||||
from ayon_blender.api.lib import maintained_time
|
||||
|
||||
|
||||
class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractPlayblast(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""
|
||||
Extract viewport playblast.
|
||||
|
||||
|
|
@ -3,14 +3,13 @@ import glob
|
|||
import json
|
||||
|
||||
import pyblish.api
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import capture
|
||||
from ayon_core.hosts.blender.api.lib import maintained_time
|
||||
from ayon_blender.api import capture, plugin
|
||||
from ayon_blender.api.lib import maintained_time
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class ExtractThumbnail(publish.Extractor):
|
||||
class ExtractThumbnail(plugin.BlenderExtractor):
|
||||
"""Extract viewport thumbnail.
|
||||
|
||||
Takes review camera and creates a thumbnail based on viewport
|
||||
|
|
@ -2,11 +2,11 @@ import os
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.pipeline import KnownPublishError
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class ExtractUSD(publish.Extractor):
|
||||
class ExtractUSD(plugin.BlenderExtractor):
|
||||
"""Extract as USD."""
|
||||
|
||||
label = "Extract USD"
|
||||
|
|
@ -40,7 +40,7 @@ class ExtractUSD(publish.Extractor):
|
|||
root = lib.get_highest_root(objects=instance[:])
|
||||
if not root:
|
||||
instance_node = instance.data["transientData"]["instance_node"]
|
||||
raise publish.KnownPublishError(
|
||||
raise KnownPublishError(
|
||||
f"No root object found in instance: {instance_node.name}"
|
||||
)
|
||||
self.log.debug(f"Exporting using active root: {root.name}")
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import pyblish.api
|
||||
from ayon_core.pipeline.publish import OptionalPyblishPluginMixin
|
||||
from ayon_core.hosts.blender.api.workio import save_file
|
||||
from ayon_blender.api.workio import save_file
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class IncrementWorkfileVersion(
|
||||
pyblish.api.ContextPlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
plugin.BlenderContextPlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Increment current workfile version."""
|
||||
|
||||
|
|
@ -2,11 +2,12 @@ import json
|
|||
|
||||
import pyblish.api
|
||||
from ayon_core.pipeline.publish import OptionalPyblishPluginMixin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class IntegrateAnimation(
|
||||
pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Generate a JSON file for animation."""
|
||||
|
||||
|
|
@ -2,9 +2,8 @@ from typing import List
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
import ayon_core.hosts.blender.api.action
|
||||
import ayon_blender.api.action
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
PublishValidationError,
|
||||
|
|
@ -12,8 +11,10 @@ from ayon_core.pipeline.publish import (
|
|||
)
|
||||
|
||||
|
||||
class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateCameraZeroKeyframe(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Camera must have a keyframe at frame 0.
|
||||
|
||||
Unreal shifts the first keyframe to frame 0. Forcing the camera to have
|
||||
|
|
@ -25,7 +26,7 @@ class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin,
|
|||
hosts = ["blender"]
|
||||
families = ["camera"]
|
||||
label = "Zero Keyframe"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
|
|
@ -2,18 +2,20 @@ import os
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
from ayon_core.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
PublishValidationError,
|
||||
OptionalPyblishPluginMixin
|
||||
)
|
||||
from ayon_core.hosts.blender.api.render_lib import prepare_rendering
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.render_lib import prepare_rendering
|
||||
|
||||
|
||||
class ValidateDeadlinePublish(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateDeadlinePublish(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Validates Render File Directory is
|
||||
not the same in every submission
|
||||
"""
|
||||
|
|
@ -6,6 +6,7 @@ from ayon_core.pipeline.publish import (
|
|||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
)
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class SaveWorkfileAction(pyblish.api.Action):
|
||||
|
|
@ -18,8 +19,10 @@ class SaveWorkfileAction(pyblish.api.Action):
|
|||
bpy.ops.wm.avalon_workfiles()
|
||||
|
||||
|
||||
class ValidateFileSaved(pyblish.api.ContextPlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateFileSaved(
|
||||
plugin.BlenderContextPlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Validate that the workfile has been saved."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder - 0.01
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import pyblish.api
|
||||
from ayon_core.pipeline.publish import PublishValidationError
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ValidateInstanceEmpty(pyblish.api.InstancePlugin):
|
||||
class ValidateInstanceEmpty(plugin.BlenderInstancePlugin):
|
||||
"""Validator to verify that the instance is not empty"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder - 0.01
|
||||
|
|
@ -2,19 +2,18 @@ from typing import List
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
)
|
||||
import ayon_core.hosts.blender.api.action
|
||||
import ayon_blender.api.action
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ValidateMeshHasUvs(
|
||||
pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Validate that the current mesh has UV's."""
|
||||
|
||||
|
|
@ -22,7 +21,7 @@ class ValidateMeshHasUvs(
|
|||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
label = "Mesh Has UVs"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction]
|
||||
optional = True
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -2,25 +2,26 @@ from typing import List
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
)
|
||||
import ayon_core.hosts.blender.api.action
|
||||
import ayon_blender.api.action
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ValidateMeshNoNegativeScale(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateMeshNoNegativeScale(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Ensure that meshes don't have a negative scale."""
|
||||
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
label = "Mesh No Negative Scale"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
|
|
@ -3,20 +3,19 @@ from typing import List
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError,
|
||||
RepairAction
|
||||
)
|
||||
import ayon_core.hosts.blender.api.action
|
||||
import ayon_blender.api.action
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ValidateModelMeshUvMap1(
|
||||
pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Validate model mesh uvs are named `map1`.
|
||||
|
||||
|
|
@ -27,7 +26,7 @@ class ValidateModelMeshUvMap1(
|
|||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
label = "Mesh UVs named map1"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction,
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction,
|
||||
RepairAction]
|
||||
optional = True
|
||||
enabled = False
|
||||
|
|
@ -2,9 +2,8 @@ from typing import List
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
import ayon_core.hosts.blender.api.action
|
||||
import ayon_blender.api.action
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
OptionalPyblishPluginMixin,
|
||||
|
|
@ -12,8 +11,10 @@ from ayon_core.pipeline.publish import (
|
|||
)
|
||||
|
||||
|
||||
class ValidateNoColonsInName(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateNoColonsInName(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""There cannot be colons in names
|
||||
|
||||
Object or bone names cannot include colons. Other software do not
|
||||
|
|
@ -25,7 +26,7 @@ class ValidateNoColonsInName(pyblish.api.InstancePlugin,
|
|||
hosts = ["blender"]
|
||||
families = ["model", "rig"]
|
||||
label = "No Colons in names"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
|
|
@ -7,12 +7,13 @@ from ayon_core.pipeline.publish import (
|
|||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
)
|
||||
import ayon_core.hosts.blender.api.action
|
||||
import ayon_blender.api.action
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ValidateObjectIsInObjectMode(
|
||||
pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Validate that the objects in the instance are in Object Mode."""
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ class ValidateObjectIsInObjectMode(
|
|||
hosts = ["blender"]
|
||||
families = ["model", "rig", "layout"]
|
||||
label = "Validate Object Mode"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction]
|
||||
optional = False
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -6,10 +6,13 @@ from ayon_core.pipeline.publish import (
|
|||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
)
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ValidateRenderCameraIsSet(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateRenderCameraIsSet(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Validate that there is a camera set as active for rendering."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
|
|
@ -4,10 +4,8 @@ from typing import List
|
|||
import mathutils
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
import ayon_core.hosts.blender.api.action
|
||||
from ayon_blender.api import plugin, lib
|
||||
import ayon_blender.api.action
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
OptionalPyblishPluginMixin,
|
||||
|
|
@ -16,15 +14,17 @@ from ayon_core.pipeline.publish import (
|
|||
)
|
||||
|
||||
|
||||
class ValidateTransformZero(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateTransformZero(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Transforms can't have any values"""
|
||||
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
label = "Transform Zero"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction,
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction,
|
||||
RepairAction]
|
||||
|
||||
_identity = mathutils.Matrix()
|
||||
3
server_addon/blender/client/ayon_blender/version.py
Normal file
3
server_addon/blender/client/ayon_blender/version.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring AYON addon 'blender' version."""
|
||||
__version__ = "0.2.0"
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
name = "blender"
|
||||
title = "Blender"
|
||||
version = "0.1.9"
|
||||
version = "0.2.0"
|
||||
|
||||
client_dir = "ayon_blender"
|
||||
|
||||
ayon_required_addons = {
|
||||
"core": ">0.3.2",
|
||||
}
|
||||
ayon_compatible_addons = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from .version import __version__
|
||||
from .addon import (
|
||||
HARMONY_ADDON_ROOT,
|
||||
HarmonyAddon,
|
||||
|
|
@ -6,6 +7,8 @@ from .addon import (
|
|||
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"HARMONY_ADDON_ROOT",
|
||||
"HarmonyAddon",
|
||||
"get_launch_script_path",
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import os
|
||||
from ayon_core.addon import AYONAddon, IHostAddon
|
||||
|
||||
from .version import __version__
|
||||
|
||||
HARMONY_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class HarmonyAddon(AYONAddon, IHostAddon):
|
||||
name = "harmony"
|
||||
version = __version__
|
||||
host_name = "harmony"
|
||||
|
||||
def add_implementation_envs(self, env, _app):
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
The easiest way to setup for using Toon Boom Harmony is to use the built-in launch:
|
||||
|
||||
```
|
||||
python -c "import ayon_core.hosts.harmony.api as harmony;harmony.launch("path/to/harmony/executable")"
|
||||
python -c "import ayon_harmony.api as harmony;harmony.launch("path/to/harmony/executable")"
|
||||
```
|
||||
|
||||
Communication with Harmony happens with a server/client relationship where the server is in the Python process and the client is in the Harmony process. Messages between Python and Harmony are required to be dictionaries, which are serialized to strings:
|
||||
|
|
@ -59,7 +59,7 @@ You can show the Workfiles app when Harmony launches by setting environment vari
|
|||
### Low level messaging
|
||||
To send from Python to Harmony you can use the exposed method:
|
||||
```python
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ print(harmony.send({"function": func, "args": ["Python"]})["result"])
|
|||
|
||||
To send a function with multiple arguments its best to declare the arguments within the function:
|
||||
```python
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
from uuid import uuid4
|
||||
|
||||
signature = str(uuid4()).replace("-", "_")
|
||||
|
|
@ -114,7 +114,7 @@ PypeHarmony.myAwesomeFunction = function() {
|
|||
Then you can call that javascript code from your Python like:
|
||||
|
||||
```Python
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
harmony.send({"function": "PypeHarmony.myAwesomeFunction"});
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ Now in python, just read all those files and send them to Harmony.
|
|||
|
||||
```python
|
||||
from pathlib import Path
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
path_to_js = Path('/path/to/my/js')
|
||||
script_to_send = ""
|
||||
|
|
@ -178,7 +178,7 @@ harmony.send({"function": "Master.Boo.B"})
|
|||
### Scene Save
|
||||
Instead of sending a request to Harmony with `scene.saveAll` please use:
|
||||
```python
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
harmony.save_scene()
|
||||
```
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ These plugins were made with the [polly config](https://github.com/mindbender-st
|
|||
|
||||
#### Creator Plugin
|
||||
```python
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ class CreateComposite(harmony.Creator):
|
|||
The creator plugin can be configured to use other node types. For example here is a write node creator:
|
||||
```python
|
||||
from uuid import uuid4
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
|
||||
class CreateRender(harmony.Creator):
|
||||
|
|
@ -244,7 +244,7 @@ class CreateRender(harmony.Creator):
|
|||
```python
|
||||
import pyblish.api
|
||||
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
|
||||
class CollectInstances(pyblish.api.ContextPlugin):
|
||||
|
|
@ -292,7 +292,7 @@ import os
|
|||
from uuid import uuid4
|
||||
|
||||
import pyblish.api
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
import clique
|
||||
|
||||
|
|
@ -423,7 +423,7 @@ class ExtractImage(pyblish.api.InstancePlugin):
|
|||
import os
|
||||
from uuid import uuid4
|
||||
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
signature = str(uuid4()).replace("-", "_")
|
||||
copy_files = """function copyFile(srcFilename, dstFilename)
|
||||
|
|
@ -387,7 +387,7 @@ function start() {
|
|||
*/
|
||||
self.onCreator = function() {
|
||||
app.avalonClient.send({
|
||||
'module': 'ayon_core.hosts.harmony.api.lib',
|
||||
'module': 'ayon_harmony.api.lib',
|
||||
'method': 'show',
|
||||
'args': ['creator']
|
||||
}, false);
|
||||
|
|
@ -402,7 +402,7 @@ function start() {
|
|||
*/
|
||||
self.onWorkfiles = function() {
|
||||
app.avalonClient.send({
|
||||
'module': 'ayon_core.hosts.harmony.api.lib',
|
||||
'module': 'ayon_harmony.api.lib',
|
||||
'method': 'show',
|
||||
'args': ['workfiles']
|
||||
}, false);
|
||||
|
|
@ -417,7 +417,7 @@ function start() {
|
|||
*/
|
||||
self.onLoad = function() {
|
||||
app.avalonClient.send({
|
||||
'module': 'ayon_core.hosts.harmony.api.lib',
|
||||
'module': 'ayon_harmony.api.lib',
|
||||
'method': 'show',
|
||||
'args': ['loader']
|
||||
}, false);
|
||||
|
|
@ -433,7 +433,7 @@ function start() {
|
|||
*/
|
||||
self.onPublish = function() {
|
||||
app.avalonClient.send({
|
||||
'module': 'ayon_core.hosts.harmony.api.lib',
|
||||
'module': 'ayon_harmony.api.lib',
|
||||
'method': 'show',
|
||||
'args': ['publish']
|
||||
}, false);
|
||||
|
|
@ -449,7 +449,7 @@ function start() {
|
|||
*/
|
||||
self.onManage = function() {
|
||||
app.avalonClient.send({
|
||||
'module': 'ayon_core.hosts.harmony.api.lib',
|
||||
'module': 'ayon_harmony.api.lib',
|
||||
'method': 'show',
|
||||
'args': ['sceneinventory']
|
||||
}, false);
|
||||
|
|
@ -465,7 +465,7 @@ function start() {
|
|||
*/
|
||||
self.onSubsetManage = function() {
|
||||
app.avalonClient.send({
|
||||
'module': 'ayon_core.hosts.harmony.api.lib',
|
||||
'module': 'ayon_harmony.api.lib',
|
||||
'method': 'show',
|
||||
'args': ['subsetmanager']
|
||||
}, false);
|
||||
|
|
@ -482,7 +482,7 @@ function start() {
|
|||
self.onSetSceneSettings = function() {
|
||||
app.avalonClient.send(
|
||||
{
|
||||
"module": "ayon_core.hosts.harmony.api",
|
||||
"module": "ayon_harmony.api",
|
||||
"method": "ensure_scene_settings",
|
||||
"args": []
|
||||
},
|
||||
|
|
@ -500,7 +500,7 @@ function start() {
|
|||
*/
|
||||
self.onExperimentalTools = function() {
|
||||
app.avalonClient.send({
|
||||
'module': 'ayon_core.hosts.harmony.api.lib',
|
||||
'module': 'ayon_harmony.api.lib',
|
||||
'method': 'show',
|
||||
'args': ['experimental_tools']
|
||||
}, false);
|
||||
|
|
@ -550,7 +550,7 @@ function ensureSceneSettings() {
|
|||
var app = QCoreApplication.instance();
|
||||
app.avalonClient.send(
|
||||
{
|
||||
"module": "ayon_core.hosts.harmony.api",
|
||||
"module": "ayon_harmony.api",
|
||||
"method": "ensure_scene_settings",
|
||||
"args": []
|
||||
},
|
||||
|
|
@ -8,7 +8,7 @@ workfile or others.
|
|||
import os
|
||||
import sys
|
||||
|
||||
from ayon_core.hosts.harmony.api.lib import main as host_main
|
||||
from ayon_harmony.api.lib import main as host_main
|
||||
|
||||
# Get current file to locate start point of sys.argv
|
||||
CURRENT_FILE = os.path.abspath(__file__)
|
||||
|
|
@ -20,7 +20,7 @@ import collections
|
|||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
|
||||
from ayon_core.lib import is_using_ayon_console
|
||||
from ayon_core.tools.stdout_broker.app import StdOutBroker
|
||||
from ayon_core.tools.stdout_broker import StdOutBroker
|
||||
from ayon_core.tools.utils import host_tools
|
||||
from ayon_core import style
|
||||
|
||||
|
|
@ -186,7 +186,7 @@ def launch(application_path, *args):
|
|||
|
||||
"""
|
||||
from ayon_core.pipeline import install_host
|
||||
from ayon_core.hosts.harmony import api as harmony
|
||||
from ayon_harmony import api as harmony
|
||||
|
||||
install_host(harmony)
|
||||
|
||||
|
|
@ -486,7 +486,7 @@ def imprint(node_id, data, remove=False):
|
|||
remove (bool): Removes the data from the scene.
|
||||
|
||||
Example:
|
||||
>>> from ayon_core.hosts.harmony.api import lib
|
||||
>>> from ayon_harmony.api import lib
|
||||
>>> node = "Top/Display"
|
||||
>>> data = {"str": "something", "int": 1, "float": 0.32, "bool": True}
|
||||
>>> lib.imprint(layer, data)
|
||||
|
|
@ -15,11 +15,11 @@ from ayon_core.pipeline import (
|
|||
from ayon_core.pipeline.load import get_outdated_containers
|
||||
from ayon_core.pipeline.context_tools import get_current_folder_entity
|
||||
|
||||
from ayon_core.hosts.harmony import HARMONY_ADDON_ROOT
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
from ayon_harmony import HARMONY_ADDON_ROOT
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
|
||||
log = logging.getLogger("ayon_core.hosts.harmony")
|
||||
log = logging.getLogger("ayon_harmony")
|
||||
|
||||
PLUGINS_DIR = os.path.join(HARMONY_ADDON_ROOT, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from ayon_core.pipeline import LegacyCreator
|
||||
import ayon_core.hosts.harmony.api as harmony
|
||||
import ayon_harmony.api as harmony
|
||||
|
||||
|
||||
class Creator(LegacyCreator):
|
||||
|
|
@ -69,6 +69,8 @@ class Server(threading.Thread):
|
|||
self.log.debug(
|
||||
f"[{self.timestamp()}] Processing request:\n{pretty}")
|
||||
|
||||
# TODO javascript should not define which module is imported and
|
||||
# which function is called. It should send predefined requests.
|
||||
try:
|
||||
module = importlib.import_module(request["module"])
|
||||
method = getattr(module, request["method"])
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue