mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch '2.0/develop' into develop
This commit is contained in:
commit
2460cd1fdc
236 changed files with 13976 additions and 1252 deletions
25
README.md
25
README.md
|
|
@ -1,23 +1,20 @@
|
|||
The base studio *config* for [Avalon](https://getavalon.github.io/)
|
||||
he base studio _config_ for [Avalon](https://getavalon.github.io/)
|
||||
|
||||
Currently this config is dependent on our customised avalon instalation so it won't work with vanilla avalon core. We're working on open sourcing all of the necessary code though. You can still get inspiration or take our individual validators and scripts which should work just fine in other pipelines.
|
||||
|
||||
|
||||
_This configuration acts as a starting point for all pype club clients wth avalon deployment._
|
||||
|
||||
|
||||
|
||||
### Code convention
|
||||
|
||||
Below are some of the standard practices applied to this repositories.
|
||||
|
||||
- **Etiquette: PEP8**
|
||||
- All code is written in PEP8. It is recommended you use a linter as you work, flake8 and pylinter are both good options.
|
||||
- **Etiquette: Napoleon docstrings**
|
||||
- Any docstrings are made in Google Napoleon format. See [Napoleon](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) for details.
|
||||
- **Etiquette: Semantic Versioning**
|
||||
- This project follows [semantic versioning](http://semver.org).
|
||||
- **Etiquette: Underscore means private**
|
||||
- Anything prefixed with an underscore means that it is internal to wherever it is used. For example, a variable name is only ever used in the parent function or class. A module is not for use by the end-user. In contrast, anything without an underscore is public, but not necessarily part of the API. Members of the API resides in `api.py`.
|
||||
- **API: Idempotence**
|
||||
- A public function must be able to be called twice and produce the exact same result. This means no changing of state without restoring previous state when finishing. For example, if a function requires changing the current selection in Autodesk Maya, it must restore the previous selection prior to completing.
|
||||
- **Etiquette: PEP8**
|
||||
\- All code is written in PEP8. It is recommended you use a linter as you work, flake8 and pylinter are both good options.
|
||||
- **Etiquette: Napoleon docstrings**
|
||||
\- Any docstrings are made in Google Napoleon format. See [Napoleon](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) for details.
|
||||
- **Etiquette: Semantic Versioning**
|
||||
\- This project follows [semantic versioning](http://semver.org).
|
||||
- **Etiquette: Underscore means private**
|
||||
\- Anything prefixed with an underscore means that it is internal to wherever it is used. For example, a variable name is only ever used in the parent function or class. A module is not for use by the end-user. In contrast, anything without an underscore is public, but not necessarily part of the API. Members of the API resides in `api.py`.
|
||||
- **API: Idempotence**
|
||||
\- A public function must be able to be called twice and produce the exact same result. This means no changing of state without restoring previous state when finishing. For example, if a function requires changing the current selection in Autodesk Maya, it must restore the previous selection prior to completing.
|
||||
|
|
|
|||
|
|
@ -9,10 +9,9 @@ from .lib import collect_container_metadata
|
|||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# do not delete these are mandatory
|
||||
# # do not delete these are mandatory
|
||||
Anatomy = None
|
||||
Dataflow = None
|
||||
Metadata = None
|
||||
Colorspace = None
|
||||
|
||||
PACKAGE_DIR = os.path.dirname(__file__)
|
||||
|
|
|
|||
|
|
@ -15,12 +15,11 @@ from .action import (
|
|||
RepairContextAction
|
||||
)
|
||||
|
||||
from app.api import Logger
|
||||
from pypeapp import Logger
|
||||
|
||||
from . import (
|
||||
Anatomy,
|
||||
Colorspace,
|
||||
Metadata,
|
||||
Dataflow
|
||||
)
|
||||
|
||||
|
|
@ -47,8 +46,6 @@ from .lib import (
|
|||
get_data_hierarchical_attr
|
||||
)
|
||||
|
||||
from .widgets.message_window import message
|
||||
|
||||
__all__ = [
|
||||
# plugin classes
|
||||
"Extractor",
|
||||
|
|
@ -88,10 +85,6 @@ __all__ = [
|
|||
# preloaded templates
|
||||
"Anatomy",
|
||||
"Colorspace",
|
||||
"Metadata",
|
||||
"Dataflow",
|
||||
|
||||
# QtWidgets
|
||||
"message"
|
||||
|
||||
]
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import sys
|
|||
|
||||
from avalon import api as avalon
|
||||
from pyblish import api as pyblish
|
||||
from app import api as app
|
||||
from pypeapp import execute, Logger
|
||||
|
||||
from .. import api
|
||||
|
||||
log = api.Logger.getLogger(__name__, "aport")
|
||||
log = Logger().get_logger(__name__, "aport")
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ def pico_server_launch():
|
|||
"api"
|
||||
]
|
||||
|
||||
app.forward(
|
||||
execute(
|
||||
args,
|
||||
cwd=path
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ from avalon import io
|
|||
|
||||
import pyblish.api as pyblish
|
||||
|
||||
from app.api import forward
|
||||
from pypeapp import execute
|
||||
from pype import api as pype
|
||||
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "aport")
|
||||
log = pype.Logger().get_logger(__name__, "aport")
|
||||
|
||||
|
||||
SESSION = avalon.session
|
||||
|
|
@ -56,7 +56,7 @@ def publish(json_data_path, gui):
|
|||
|
||||
log.info("avalon.session is: \n{}".format(SESSION))
|
||||
|
||||
pype_start = os.path.join(os.getenv('PYPE_SETUP_ROOT'),
|
||||
pype_start = os.path.join(os.getenv('PYPE_ROOT'),
|
||||
"app", "pype-start.py")
|
||||
|
||||
publish = "--publish-gui" if gui else "--publish"
|
||||
|
|
@ -70,7 +70,7 @@ def publish(json_data_path, gui):
|
|||
log.debug(args)
|
||||
|
||||
# start standalone pyblish qml
|
||||
forward([
|
||||
execute([
|
||||
sys.executable, "-u"
|
||||
] + args,
|
||||
cwd=cwd
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ from avalon import io
|
|||
|
||||
import pyblish.api as pyblish
|
||||
|
||||
from app.api import forward
|
||||
from pypeapp import execute
|
||||
from pype import api as pype
|
||||
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "aport")
|
||||
log = pype.Logger().get_logger(__name__, "aport")
|
||||
|
||||
|
||||
SESSION = avalon.session
|
||||
|
|
@ -56,7 +56,7 @@ def publish(json_data_path, staging_dir=None):
|
|||
return_json_path = os.path.join(staging_dir, "return_data.json")
|
||||
|
||||
log.info("avalon.session is: \n{}".format(SESSION))
|
||||
pype_start = os.path.join(os.getenv('PYPE_SETUP_ROOT'),
|
||||
pype_start = os.path.join(os.getenv('PYPE_ROOT'),
|
||||
"app", "pype-start.py")
|
||||
|
||||
args = [pype_start, "--publish",
|
||||
|
|
@ -68,7 +68,7 @@ def publish(json_data_path, staging_dir=None):
|
|||
log.debug(args)
|
||||
|
||||
# start standalone pyblish qml
|
||||
forward([
|
||||
execute([
|
||||
sys.executable, "-u"
|
||||
] + args,
|
||||
cwd=cwd
|
||||
|
|
@ -239,8 +239,8 @@ app.register_module(__name__)
|
|||
|
||||
# remove all Handlers created by pico
|
||||
for name, handler in [(handler.get_name(), handler)
|
||||
for handler in pype.Logger.logging.root.handlers[:]]:
|
||||
for handler in Logger().logging.root.handlers[:]]:
|
||||
if "pype" not in str(name).lower():
|
||||
print(name)
|
||||
print(handler)
|
||||
pype.Logger.logging.root.removeHandler(handler)
|
||||
Logger().logging.root.removeHandler(handler)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from avalon import io
|
|||
|
||||
import pyblish.api as pyblish
|
||||
|
||||
from app.api import forward
|
||||
from pypeapp import execute
|
||||
from pype import api as pype
|
||||
|
||||
# remove all Handlers created by pico
|
||||
|
|
@ -20,7 +20,7 @@ for name, handler in [(handler.get_name(), handler)
|
|||
if "pype" not in str(name).lower():
|
||||
pype.Logger.logging.root.removeHandler(handler)
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "aport")
|
||||
log = pype.Logger().get_logger(__name__, "aport")
|
||||
|
||||
|
||||
SESSION = avalon.session
|
||||
|
|
@ -55,7 +55,7 @@ def publish(json_data_path, staging_dir=None):
|
|||
return_json_path = os.path.join(staging_dir, "return_data.json")
|
||||
|
||||
log.debug("avalon.session is: \n{}".format(SESSION))
|
||||
pype_start = os.path.join(os.getenv('PYPE_SETUP_ROOT'),
|
||||
pype_start = os.path.join(os.getenv('PYPE_ROOT'),
|
||||
"app", "pype-start.py")
|
||||
|
||||
args = [pype_start, "--publish",
|
||||
|
|
@ -67,7 +67,7 @@ def publish(json_data_path, staging_dir=None):
|
|||
log.debug(args)
|
||||
|
||||
# start standalone pyblish qml
|
||||
forward([
|
||||
execute([
|
||||
sys.executable, "-u"
|
||||
] + args,
|
||||
cwd=os.getenv('AVALON_WORKDIR').replace("\\", "/")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from pype import api as pype
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "aport")
|
||||
log = pype.Logger().get_logger(__name__, "aport")
|
||||
|
||||
|
||||
def get_anatomy(**kwarg):
|
||||
|
|
|
|||
5
pype/avalon_apps/__init__.py
Normal file
5
pype/avalon_apps/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from .avalon_app import AvalonApps
|
||||
|
||||
|
||||
def tray_init(tray_widget, main_widget):
|
||||
return AvalonApps(main_widget, tray_widget)
|
||||
55
pype/avalon_apps/avalon_app.py
Normal file
55
pype/avalon_apps/avalon_app.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import os
|
||||
import argparse
|
||||
from Qt import QtGui, QtWidgets
|
||||
from avalon.tools import libraryloader
|
||||
from pypeapp import Logger
|
||||
from avalon import io
|
||||
from launcher import launcher_widget, lib as launcher_lib
|
||||
|
||||
|
||||
class AvalonApps:
|
||||
def __init__(self, main_parent=None, parent=None):
|
||||
self.log = Logger().get_logger(__name__)
|
||||
self.main_parent = main_parent
|
||||
self.parent = parent
|
||||
self.app_launcher = None
|
||||
|
||||
# Definition of Tray menu
|
||||
def tray_menu(self, parent_menu=None):
|
||||
# Actions
|
||||
if parent_menu is None:
|
||||
if self.parent is None:
|
||||
self.log.warning('Parent menu is not set')
|
||||
return
|
||||
elif self.parent.hasattr('menu'):
|
||||
parent_menu = self.parent.menu
|
||||
else:
|
||||
self.log.warning('Parent menu is not set')
|
||||
return
|
||||
|
||||
icon = QtGui.QIcon(launcher_lib.resource("icon", "main.png"))
|
||||
aShowLauncher = QtWidgets.QAction(icon, "&Launcher", parent_menu)
|
||||
aLibraryLoader = QtWidgets.QAction("Library", parent_menu)
|
||||
|
||||
aShowLauncher.triggered.connect(self.show_launcher)
|
||||
aLibraryLoader.triggered.connect(self.show_library_loader)
|
||||
|
||||
parent_menu.addAction(aShowLauncher)
|
||||
parent_menu.addAction(aLibraryLoader)
|
||||
|
||||
def show_launcher(self):
|
||||
# if app_launcher don't exist create it/otherwise only show main window
|
||||
if self.app_launcher is None:
|
||||
root = os.path.realpath(os.environ["AVALON_PROJECTS"])
|
||||
io.install()
|
||||
APP_PATH = launcher_lib.resource("qml", "main.qml")
|
||||
self.app_launcher = launcher_widget.Launcher(root, APP_PATH)
|
||||
self.app_launcher.window.show()
|
||||
|
||||
def show_library_loader(self):
|
||||
libraryloader.show(
|
||||
parent=self.main_parent,
|
||||
icon=self.parent.icon,
|
||||
show_projects=True,
|
||||
show_libraries=True
|
||||
)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import threading
|
||||
from app import style
|
||||
from app.vendor.Qt import QtWidgets
|
||||
from pypeapp import style
|
||||
from Qt import QtWidgets
|
||||
from pype.clockify import ClockifySettings, ClockifyAPI
|
||||
|
||||
|
||||
|
|
@ -35,6 +35,28 @@ class ClockifyModule:
|
|||
|
||||
self.set_menu_visibility()
|
||||
|
||||
def process_modules(self, modules):
|
||||
if 'FtrackModule' in modules:
|
||||
actions_path = os.path.sep.join([
|
||||
os.path.dirname(__file__),
|
||||
'ftrack_actions'
|
||||
])
|
||||
current = os.environ('FTRACK_ACTIONS_PATH', '')
|
||||
if current:
|
||||
current += os.pathsep
|
||||
os.environ['FTRACK_ACTIONS_PATH'] = current + actions_path
|
||||
|
||||
if 'AvalonApps' in modules:
|
||||
from launcher import lib
|
||||
actions_path = os.path.sep.join([
|
||||
os.path.dirname(__file__),
|
||||
'launcher_actions'
|
||||
])
|
||||
current = os.environ.get('AVALON_ACTIONS', '')
|
||||
if current:
|
||||
current += os.pathsep
|
||||
os.environ['AVALON_ACTIONS'] = current + actions_path
|
||||
|
||||
def start_timer_check(self):
|
||||
self.bool_thread_check_running = True
|
||||
if self.thread_timer_check is None:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pype.clockify import ClockifyAPI
|
||||
|
||||
|
|
@ -17,7 +18,9 @@ class StartClockify(BaseAction):
|
|||
#: Action description.
|
||||
description = 'Starts timer on clockify'
|
||||
#: roles that are allowed to register this action
|
||||
icon = 'https://clockify.me/assets/images/clockify-logo.png'
|
||||
icon = '{}/app_icons/clockify.png'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
#: Clockify api
|
||||
clockapi = ClockifyAPI()
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction, MissingPermision
|
||||
from pype.clockify import ClockifyAPI
|
||||
|
||||
|
|
@ -21,7 +22,9 @@ class SyncClocify(BaseAction):
|
|||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub', 'Administrator']
|
||||
#: icon
|
||||
icon = 'https://clockify.me/assets/images/clockify-logo-white.svg'
|
||||
icon = '{}/app_icons/clockify-white.png'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
#: CLockifyApi
|
||||
clockapi = ClockifyAPI()
|
||||
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
from avalon import api, io
|
||||
from pype.api import Logger
|
||||
try:
|
||||
from pype.clockify import ClockifyAPI
|
||||
except Exception:
|
||||
pass
|
||||
from pype.clockify import ClockifyAPI
|
||||
|
||||
log = Logger.getLogger(__name__, "clockify_start")
|
||||
|
||||
log = Logger().get_logger(__name__, "clockify_start")
|
||||
|
||||
|
||||
class ClockifyStart(api.Action):
|
||||
|
|
@ -14,13 +12,10 @@ class ClockifyStart(api.Action):
|
|||
label = "Clockify - Start Timer"
|
||||
icon = "clockify_icon"
|
||||
order = 500
|
||||
|
||||
exec("try: clockapi = ClockifyAPI()\nexcept: clockapi = None")
|
||||
clockapi = ClockifyAPI()
|
||||
|
||||
def is_compatible(self, session):
|
||||
"""Return whether the action is compatible with the session"""
|
||||
if self.clockapi is None:
|
||||
return False
|
||||
if "AVALON_TASK" in session:
|
||||
return True
|
||||
return False
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
from avalon import api, io
|
||||
try:
|
||||
from pype.clockify import ClockifyAPI
|
||||
except Exception:
|
||||
pass
|
||||
from pype.clockify import ClockifyAPI
|
||||
from pype.api import Logger
|
||||
log = Logger.getLogger(__name__, "clockify_sync")
|
||||
log = Logger().get_logger(__name__, "clockify_sync")
|
||||
|
||||
|
||||
class ClockifySync(api.Action):
|
||||
|
|
@ -13,16 +10,11 @@ class ClockifySync(api.Action):
|
|||
label = "Sync to Clockify"
|
||||
icon = "clockify_white_icon"
|
||||
order = 500
|
||||
exec(
|
||||
"try:\n\tclockapi = ClockifyAPI()"
|
||||
"\n\thave_permissions = clockapi.validate_workspace_perm()"
|
||||
"\nexcept:\n\tclockapi = None"
|
||||
)
|
||||
clockapi = ClockifyAPI()
|
||||
have_permissions = clockapi.validate_workspace_perm()
|
||||
|
||||
def is_compatible(self, session):
|
||||
"""Return whether the action is compatible with the session"""
|
||||
if self.clockapi is None:
|
||||
return False
|
||||
return self.have_permissions
|
||||
|
||||
def process(self, session, **kwargs):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
from app.vendor.Qt import QtCore, QtGui, QtWidgets
|
||||
from app import style
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
from pypeapp import style
|
||||
|
||||
|
||||
class ClockifySettings(QtWidgets.QWidget):
|
||||
|
|
@ -26,7 +26,7 @@ class ClockifySettings(QtWidgets.QWidget):
|
|||
elif hasattr(parent, 'parent') and hasattr(parent.parent, 'icon'):
|
||||
self.setWindowIcon(self.parent.parent.icon)
|
||||
else:
|
||||
pype_setup = os.getenv('PYPE_SETUP_ROOT')
|
||||
pype_setup = os.getenv('PYPE_ROOT')
|
||||
items = [pype_setup, "app", "resources", "icon.png"]
|
||||
fname = os.path.sep.join(items)
|
||||
icon = QtGui.QIcon(fname)
|
||||
|
|
|
|||
|
|
@ -1,2 +1,7 @@
|
|||
from .lib import *
|
||||
from .ftrack_server import *
|
||||
from .ftrack_module import FtrackModule
|
||||
|
||||
|
||||
def tray_init(tray_widget, main_widget):
|
||||
return FtrackModule(main_widget, tray_widget)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import os
|
||||
import toml
|
||||
import time
|
||||
from pype.ftrack import AppAction
|
||||
from avalon import lib
|
||||
from app.api import Logger
|
||||
from pypeapp import Logger
|
||||
from pype import lib as pypelib
|
||||
|
||||
log = Logger.getLogger(__name__)
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
def registerApp(app, session):
|
||||
|
|
@ -37,6 +38,9 @@ def registerApp(app, session):
|
|||
description = apptoml.get('description', None)
|
||||
preactions = apptoml.get('preactions', [])
|
||||
|
||||
if icon:
|
||||
icon = icon.format(os.environ.get('PYPE_STATICS_SERVER', ''))
|
||||
|
||||
# register action
|
||||
AppAction(
|
||||
session, label, name, executable, variant,
|
||||
|
|
@ -65,4 +69,4 @@ def register(session):
|
|||
time.sleep(0.1)
|
||||
app_counter += 1
|
||||
except Exception as e:
|
||||
log.warning("'{0}' - not proper App ({1})".format(app['name'], e))
|
||||
log.exception("'{0}' - not proper App ({1})".format(app['name'], e))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import sys
|
|||
import argparse
|
||||
import logging
|
||||
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import subprocess
|
||||
import os
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
@ -15,9 +15,8 @@ class ComponentOpen(BaseAction):
|
|||
# Action label
|
||||
label = 'Open File'
|
||||
# Action icon
|
||||
icon = (
|
||||
'https://cdn4.iconfinder.com/data/icons/rcons-application/32/'
|
||||
'application_go_run-256.png'
|
||||
icon = '{}/ftrack/action_icons/ComponentOpen.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import os
|
|||
import sys
|
||||
import argparse
|
||||
import json
|
||||
import ftrack_api
|
||||
import arrow
|
||||
import logging
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction, get_ca_mongoid
|
||||
from pypeapp import config
|
||||
|
||||
"""
|
||||
This action creates/updates custom attributes.
|
||||
|
|
@ -113,20 +114,13 @@ class CustomAttributes(BaseAction):
|
|||
description = 'Creates Avalon/Mongo ID for double check'
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub', 'Administrator']
|
||||
icon = (
|
||||
'https://cdn4.iconfinder.com/data/icons/'
|
||||
'ios-web-user-interface-multi-circle-flat-vol-4/512/'
|
||||
'Bullet_list_menu_lines_points_items_options-512.png'
|
||||
icon = '{}/ftrack/action_icons/CustomAttributes.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def __init__(self, session):
|
||||
super().__init__(session)
|
||||
|
||||
templates = os.environ['PYPE_STUDIO_TEMPLATES']
|
||||
path_items = [
|
||||
templates, 'presets', 'ftrack', 'ftrack_custom_attributes.json'
|
||||
]
|
||||
self.filepath = os.path.sep.join(path_items)
|
||||
self.types = {}
|
||||
self.object_type_ids = {}
|
||||
self.groups = {}
|
||||
|
|
@ -230,22 +224,12 @@ class CustomAttributes(BaseAction):
|
|||
self.process_attribute(data)
|
||||
|
||||
def custom_attributes_from_file(self, session, event):
|
||||
try:
|
||||
with open(self.filepath) as data_file:
|
||||
json_dict = json.load(data_file)
|
||||
except Exception as e:
|
||||
msg = (
|
||||
'Loading "Custom attribute file" Failed.'
|
||||
' Please check log for more information'
|
||||
)
|
||||
self.log.warning("{} - {}".format(msg, str(e)))
|
||||
self.show_message(event, msg)
|
||||
return
|
||||
presets = config.get_presets()['ftrack']['ftrack_custom_attributes']
|
||||
|
||||
for cust_attr_name in json_dict:
|
||||
for cust_attr_name in presets:
|
||||
try:
|
||||
data = {}
|
||||
cust_attr = json_dict[cust_attr_name]
|
||||
cust_attr = presets[cust_attr_name]
|
||||
# Get key, label, type
|
||||
data.update(self.get_required(cust_attr))
|
||||
# Get hierachical/ entity_type/ object_id
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@ import sys
|
|||
import logging
|
||||
import argparse
|
||||
import re
|
||||
import json
|
||||
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pype import api as pype, lib as pypelib
|
||||
from avalon import lib as avalonlib
|
||||
from avalon.tools.libraryloader.io_nonsingleton import DbConnector
|
||||
from pypeapp import config, Anatomy
|
||||
|
||||
|
||||
class CreateFolders(BaseAction):
|
||||
|
|
@ -23,10 +22,10 @@ class CreateFolders(BaseAction):
|
|||
label = 'Create Folders'
|
||||
|
||||
#: Action Icon.
|
||||
icon = (
|
||||
'https://cdn1.iconfinder.com/data/icons/hawcons/32/'
|
||||
'698620-icon-105-folder-add-512.png'
|
||||
icon = '{}/ftrack/action_icons/CreateFolders.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
db = DbConnector()
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
|
|
@ -130,12 +129,12 @@ class CreateFolders(BaseAction):
|
|||
template_publish = av_project['config']['template']['publish']
|
||||
self.db.uninstall()
|
||||
except Exception:
|
||||
anatomy = pype.Anatomy
|
||||
template_work = anatomy.avalon.work
|
||||
template_publish = anatomy.avalon.publish
|
||||
templates = Anatomy().templates
|
||||
template_work = templates["avalon"]["work"]
|
||||
template_publish = templates["avalon"]["publish"]
|
||||
|
||||
collected_paths = []
|
||||
presets = self.get_presets()
|
||||
presets = config.get_presets()['tools']['sw_folders']
|
||||
for entity in all_entities:
|
||||
if entity.entity_type.lower() == 'project':
|
||||
continue
|
||||
|
|
@ -238,17 +237,6 @@ class CreateFolders(BaseAction):
|
|||
output.extend(self.get_notask_children(child))
|
||||
return output
|
||||
|
||||
def get_presets(self):
|
||||
fpath_items = [pypelib.get_presets_path(), 'tools', 'sw_folders.json']
|
||||
filepath = os.path.normpath(os.path.sep.join(fpath_items))
|
||||
presets = dict()
|
||||
try:
|
||||
with open(filepath) as data_file:
|
||||
presets = json.load(data_file)
|
||||
except Exception as e:
|
||||
self.log.warning('Wasn\'t able to load presets')
|
||||
return dict(presets)
|
||||
|
||||
def template_format(self, template, data):
|
||||
|
||||
partial_data = PartialDict(data)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@ import sys
|
|||
import re
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
|
||||
import ftrack_api
|
||||
from pype import lib as pypelib
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class CreateProjectFolders(BaseAction):
|
||||
|
|
@ -21,9 +20,8 @@ class CreateProjectFolders(BaseAction):
|
|||
description = 'Creates folder structure'
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub', 'Administrator']
|
||||
icon = (
|
||||
'https://cdn2.iconfinder.com/data/icons/'
|
||||
'buttons-9/512/Button_Add-01.png'
|
||||
icon = '{}/ftrack/action_icons/CreateProjectFolders.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
pattern_array = re.compile('\[.*\]')
|
||||
|
|
@ -43,7 +41,7 @@ class CreateProjectFolders(BaseAction):
|
|||
else:
|
||||
project = entity['project']
|
||||
|
||||
presets = self.load_presets()
|
||||
presets = config.get_presets()['tools']['project_folder_structure']
|
||||
try:
|
||||
# Get paths based on presets
|
||||
basic_paths = self.get_path_items(presets)
|
||||
|
|
@ -143,28 +141,6 @@ class CreateProjectFolders(BaseAction):
|
|||
self.session.commit()
|
||||
return new_ent
|
||||
|
||||
def load_presets(self):
|
||||
preset_items = [
|
||||
pypelib.get_presets_path(),
|
||||
'tools',
|
||||
'project_folder_structure.json'
|
||||
]
|
||||
filepath = os.path.sep.join(preset_items)
|
||||
|
||||
# Load folder structure template from presets
|
||||
presets = dict()
|
||||
try:
|
||||
with open(filepath) as data_file:
|
||||
presets = json.load(data_file)
|
||||
except Exception as e:
|
||||
msg = 'Unable to load Folder structure preset'
|
||||
self.log.warning(msg)
|
||||
return {
|
||||
'success': False,
|
||||
'message': msg
|
||||
}
|
||||
return presets
|
||||
|
||||
def get_path_items(self, in_dict):
|
||||
output = []
|
||||
for key, value in in_dict.items():
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import os
|
||||
import sys
|
||||
import logging
|
||||
from bson.objectid import ObjectId
|
||||
import argparse
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from avalon.tools.libraryloader.io_nonsingleton import DbConnector
|
||||
|
||||
|
|
@ -16,10 +17,8 @@ class DeleteAsset(BaseAction):
|
|||
label = 'Delete Asset/Subsets'
|
||||
#: Action description.
|
||||
description = 'Removes from Avalon with all childs and asset from Ftrack'
|
||||
icon = (
|
||||
'https://cdn4.iconfinder.com/data/icons/'
|
||||
'ios-web-user-interface-multi-circle-flat-vol-5/512/'
|
||||
'Delete_dustbin_empty_recycle_recycling_remove_trash-512.png'
|
||||
icon = '{}/ftrack/action_icons/DeleteAsset.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub', 'Administrator']
|
||||
|
|
@ -86,6 +85,12 @@ class DeleteAsset(BaseAction):
|
|||
'type': 'asset',
|
||||
'name': entity['name']
|
||||
})
|
||||
|
||||
if av_entity is None:
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'Didn\'t found assets in avalon'
|
||||
}
|
||||
|
||||
asset_label = {
|
||||
'type': 'label',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from avalon.tools.libraryloader.io_nonsingleton import DbConnector
|
||||
|
||||
|
|
@ -17,10 +18,8 @@ class AssetsRemover(BaseAction):
|
|||
description = 'Removes assets from Ftrack and Avalon db with all childs'
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub', 'Administrator']
|
||||
icon = (
|
||||
'https://cdn4.iconfinder.com/data/icons/'
|
||||
'ios-web-user-interface-multi-circle-flat-vol-5/512/'
|
||||
'Clipboard_copy_delete_minus_paste_remove-512.png'
|
||||
icon = '{}/ftrack/action_icons/AssetsRemover.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
#: Db
|
||||
db = DbConnector()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
from operator import itemgetter
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from app.api import Logger
|
||||
from pype import lib as pypelib
|
||||
from pypeapp import Logger, config
|
||||
|
||||
|
||||
log = Logger.getLogger(__name__)
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
class DJVViewAction(BaseAction):
|
||||
|
|
@ -19,16 +16,17 @@ class DJVViewAction(BaseAction):
|
|||
identifier = "djvview-launch-action"
|
||||
label = "DJV View"
|
||||
description = "DJV View Launcher"
|
||||
icon = "http://a.fsdn.com/allura/p/djv/icon"
|
||||
icon = '{}/app_icons/djvView.png'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
type = 'Application'
|
||||
|
||||
def __init__(self, session):
|
||||
'''Expects a ftrack_api.Session instance'''
|
||||
super().__init__(session)
|
||||
self.djv_path = None
|
||||
self.config_data = None
|
||||
|
||||
self.load_config_data()
|
||||
self.config_data = config.get_presets()['djv_view']['config']
|
||||
self.set_djv_path()
|
||||
|
||||
if self.djv_path is None:
|
||||
|
|
@ -56,21 +54,6 @@ class DJVViewAction(BaseAction):
|
|||
return True
|
||||
return False
|
||||
|
||||
def load_config_data(self):
|
||||
path_items = [pypelib.get_presets_path(), 'djv_view', 'config.json']
|
||||
filepath = os.path.sep.join(path_items)
|
||||
|
||||
data = dict()
|
||||
try:
|
||||
with open(filepath) as data_file:
|
||||
data = json.load(data_file)
|
||||
except Exception as e:
|
||||
log.warning(
|
||||
'Failed to load data from DJV presets file ({})'.format(e)
|
||||
)
|
||||
|
||||
self.config_data = data
|
||||
|
||||
def set_djv_path(self):
|
||||
for path in self.config_data.get("djv_paths", []):
|
||||
if os.path.exists(path):
|
||||
|
|
@ -234,6 +217,7 @@ class DJVViewAction(BaseAction):
|
|||
|
||||
return True
|
||||
|
||||
|
||||
def register(session):
|
||||
"""Register hooks."""
|
||||
if not isinstance(session, ftrack_api.session.Session):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
@ -18,9 +19,8 @@ class JobKiller(BaseAction):
|
|||
description = 'Killing selected running jobs'
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub', 'Administrator']
|
||||
icon = (
|
||||
'https://cdn2.iconfinder.com/data/icons/new-year-resolutions/64/'
|
||||
'resolutions-23-512.png'
|
||||
icon = '{}/ftrack/action_icons/JobKiller.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
|
|
@ -46,10 +46,7 @@ class JobKiller(BaseAction):
|
|||
desctiption = data['description']
|
||||
except Exception:
|
||||
desctiption = '*No description*'
|
||||
try:
|
||||
user = job['user']['username']
|
||||
except Exception:
|
||||
user = '*No user'
|
||||
user = job['user']['username']
|
||||
created = job['created_at'].strftime('%d.%m.%Y %H:%M:%S')
|
||||
label = '{} - {} - {}'.format(
|
||||
desctiption, created, user
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import os
|
|||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
|
@ -17,9 +16,8 @@ class MultipleNotes(BaseAction):
|
|||
label = 'Multiple Notes'
|
||||
#: Action description.
|
||||
description = 'Add same note to multiple Asset Versions'
|
||||
icon = (
|
||||
'https://cdn2.iconfinder.com/data/icons/'
|
||||
'mixed-rounded-flat-icon/512/note_1-512.png'
|
||||
icon = '{}/ftrack/action_icons/MultipleNotes.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ import os
|
|||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
import logging
|
||||
import operator
|
||||
import re
|
||||
from pype import lib as pypelib
|
||||
from app.api import Logger
|
||||
from pypeapp import Logger, config
|
||||
|
||||
log = Logger.getLogger(__name__)
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
class RVAction(BaseAction):
|
||||
|
|
@ -18,7 +17,9 @@ class RVAction(BaseAction):
|
|||
identifier = "rv.launch.action"
|
||||
label = "rv"
|
||||
description = "rv Launcher"
|
||||
icon = "https://img.icons8.com/color/48/000000/circled-play.png"
|
||||
icon = '{}/ftrack/action_icons/RV.png'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
type = 'Application'
|
||||
|
||||
def __init__(self, session):
|
||||
|
|
@ -40,7 +41,7 @@ class RVAction(BaseAction):
|
|||
)
|
||||
else:
|
||||
# if not, fallback to config file location
|
||||
self.load_config_data()
|
||||
self.config_data = config.get_presets()['djv_view']['config']
|
||||
self.set_rv_path()
|
||||
|
||||
if self.rv_path is None:
|
||||
|
|
@ -61,21 +62,6 @@ class RVAction(BaseAction):
|
|||
return True
|
||||
return False
|
||||
|
||||
def load_config_data(self):
|
||||
path_items = [pypelib.get_presets_path(), 'rv', 'config.json']
|
||||
filepath = os.path.sep.join(path_items)
|
||||
|
||||
data = dict()
|
||||
try:
|
||||
with open(filepath) as data_file:
|
||||
data = json.load(data_file)
|
||||
except Exception as e:
|
||||
log.warning(
|
||||
'Failed to load data from RV presets file ({})'.format(e)
|
||||
)
|
||||
|
||||
self.config_data = data
|
||||
|
||||
def set_rv_path(self):
|
||||
self.rv_path = self.config_data.get("rv_path")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import argparse
|
|||
import logging
|
||||
import json
|
||||
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction, lib as ftracklib
|
||||
|
||||
|
||||
|
|
@ -50,9 +50,8 @@ class SyncToAvalon(BaseAction):
|
|||
#: Action description.
|
||||
description = 'Send data from Ftrack to Avalon'
|
||||
#: Action icon.
|
||||
icon = (
|
||||
'https://cdn1.iconfinder.com/data/icons/hawcons/32/'
|
||||
'699650-icon-92-inbox-download-512.png'
|
||||
icon = '{}/ftrack/action_icons/SyncToAvalon-local.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub']
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import collections
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from avalon import io, inventory, schema
|
||||
|
||||
|
|
@ -27,9 +27,8 @@ class TestAction(BaseAction):
|
|||
priority = 10000
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ['Pypeclub']
|
||||
icon = (
|
||||
'https://cdn4.iconfinder.com/data/icons/hospital-19/512/'
|
||||
'8_hospital-512.png'
|
||||
icon = '{}/ftrack/action_icons/TestAction.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
@ -15,9 +16,8 @@ class ThumbToChildren(BaseAction):
|
|||
# Action label
|
||||
label = 'Thumbnail to Children'
|
||||
# Action icon
|
||||
icon = (
|
||||
'https://cdn3.iconfinder.com/data/icons/transfers/100/'
|
||||
'239322-download_transfer-128.png'
|
||||
icon = '{}/ftrack/action_icons/thumbToChildren.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
@ -14,9 +15,8 @@ class ThumbToParent(BaseAction):
|
|||
# Action label
|
||||
label = 'Thumbnail to Parent'
|
||||
# Action icon
|
||||
icon = (
|
||||
"https://cdn3.iconfinder.com/data/icons/transfers/100/"
|
||||
"239419-upload_transfer-512.png"
|
||||
icon = '{}/ftrack/action_icons/thumbToParent.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
import json
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
import appdirs
|
||||
|
||||
|
||||
|
|
@ -77,7 +77,6 @@ def _check_credentials(username=None, apiKey=None):
|
|||
session = ftrack_api.Session()
|
||||
session.close()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import os
|
|||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import ftrack_api
|
||||
import json
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseAction, lib
|
||||
|
||||
|
||||
|
|
@ -49,9 +49,8 @@ class Sync_To_Avalon(BaseAction):
|
|||
#: Action description.
|
||||
description = 'Send data from Ftrack to Avalon'
|
||||
#: Action icon.
|
||||
icon = (
|
||||
'https://cdn1.iconfinder.com/data/icons/hawcons/32/'
|
||||
'699650-icon-92-inbox-download-512.png'
|
||||
icon = '{}/ftrack/action_icons/SyncToAvalon.svg'.format(
|
||||
os.environ.get('PYPE_STATICS_SERVER', '')
|
||||
)
|
||||
|
||||
def register(self):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent, get_ca_mongoid
|
||||
from pype.ftrack.events.event_sync_to_avalon import Sync_to_Avalon
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
import operator
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent, lib
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
from app import api
|
||||
|
||||
|
||||
ignore_me = True
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
|
||||
|
||||
|
|
|
|||
329
pype/ftrack/ftrack_module.py
Normal file
329
pype/ftrack/ftrack_module.py
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
import os
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
from pypeapp import style
|
||||
from pype.ftrack import FtrackServer, credentials, login_dialog as login_dialog
|
||||
|
||||
from pype import api as pype
|
||||
|
||||
|
||||
log = pype.Logger().get_logger("FtrackModule", "ftrack")
|
||||
|
||||
|
||||
class FtrackModule:
|
||||
def __init__(self, main_parent=None, parent=None):
|
||||
|
||||
self.parent = parent
|
||||
self.widget_login = login_dialog.Login_Dialog_ui(self)
|
||||
self.action_server = FtrackServer('action')
|
||||
self.thread_action_server = None
|
||||
self.thread_timer = None
|
||||
|
||||
self.bool_logged = False
|
||||
self.bool_action_server = False
|
||||
self.bool_timer_event = False
|
||||
|
||||
def show_login_widget(self):
|
||||
self.widget_login.show()
|
||||
|
||||
def validate(self):
|
||||
validation = False
|
||||
cred = credentials._get_credentials()
|
||||
try:
|
||||
if 'username' in cred and 'apiKey' in cred:
|
||||
validation = credentials._check_credentials(
|
||||
cred['username'],
|
||||
cred['apiKey']
|
||||
)
|
||||
if validation is False:
|
||||
self.show_login_widget()
|
||||
else:
|
||||
self.show_login_widget()
|
||||
|
||||
except Exception as e:
|
||||
log.error("We are unable to connect to Ftrack: {0}".format(e))
|
||||
|
||||
validation = credentials._check_credentials()
|
||||
if validation is True:
|
||||
log.info("Connected to Ftrack successfully")
|
||||
self.loginChange()
|
||||
else:
|
||||
log.warning("Please sign in to Ftrack")
|
||||
self.bool_logged = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
return validation
|
||||
|
||||
# Necessary - login_dialog works with this method after logging in
|
||||
def loginChange(self):
|
||||
self.bool_logged = True
|
||||
self.set_menu_visibility()
|
||||
self.start_action_server()
|
||||
|
||||
def logout(self):
|
||||
credentials._clear_credentials()
|
||||
self.stop_action_server()
|
||||
|
||||
log.info("Logged out of Ftrack")
|
||||
self.bool_logged = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
# Actions part
|
||||
def start_action_server(self):
|
||||
if self.thread_action_server is None:
|
||||
self.thread_action_server = threading.Thread(
|
||||
target=self.set_action_server
|
||||
)
|
||||
self.thread_action_server.daemon = True
|
||||
self.thread_action_server.start()
|
||||
|
||||
log.info("Ftrack action server launched")
|
||||
self.bool_action_server = True
|
||||
self.set_menu_visibility()
|
||||
|
||||
def set_action_server(self):
|
||||
try:
|
||||
self.action_server.run_server()
|
||||
except Exception:
|
||||
msg = 'Ftrack Action server crashed! Please try to start again.'
|
||||
log.error(msg)
|
||||
# TODO show message to user
|
||||
self.bool_action_server = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
def reset_action_server(self):
|
||||
self.stop_action_server()
|
||||
self.start_action_server()
|
||||
|
||||
def stop_action_server(self):
|
||||
try:
|
||||
self.action_server.stop_session()
|
||||
if self.thread_action_server is not None:
|
||||
self.thread_action_server.join()
|
||||
self.thread_action_server = None
|
||||
|
||||
log.info("Ftrack action server stopped")
|
||||
self.bool_action_server = False
|
||||
self.set_menu_visibility()
|
||||
except Exception as e:
|
||||
log.error("During Killing action server: {0}".format(e))
|
||||
|
||||
# Definition of Tray menu
|
||||
def tray_menu(self, parent_menu):
|
||||
# Menu for Tray App
|
||||
self.menu = QtWidgets.QMenu('Ftrack', parent_menu)
|
||||
self.menu.setProperty('submenu', 'on')
|
||||
|
||||
# Actions - server
|
||||
self.smActionS = self.menu.addMenu("Action server")
|
||||
|
||||
self.aRunActionS = QtWidgets.QAction(
|
||||
"Run action server", self.smActionS
|
||||
)
|
||||
self.aResetActionS = QtWidgets.QAction(
|
||||
"Reset action server", self.smActionS
|
||||
)
|
||||
self.aStopActionS = QtWidgets.QAction(
|
||||
"Stop action server", self.smActionS
|
||||
)
|
||||
|
||||
self.aRunActionS.triggered.connect(self.start_action_server)
|
||||
self.aResetActionS.triggered.connect(self.reset_action_server)
|
||||
self.aStopActionS.triggered.connect(self.stop_action_server)
|
||||
|
||||
self.smActionS.addAction(self.aRunActionS)
|
||||
self.smActionS.addAction(self.aResetActionS)
|
||||
self.smActionS.addAction(self.aStopActionS)
|
||||
|
||||
# Actions - basic
|
||||
self.aLogin = QtWidgets.QAction("Login", self.menu)
|
||||
self.aLogin.triggered.connect(self.validate)
|
||||
self.aLogout = QtWidgets.QAction("Logout", self.menu)
|
||||
self.aLogout.triggered.connect(self.logout)
|
||||
|
||||
self.menu.addAction(self.aLogin)
|
||||
self.menu.addAction(self.aLogout)
|
||||
|
||||
self.bool_logged = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
parent_menu.addMenu(self.menu)
|
||||
|
||||
def tray_start(self):
|
||||
self.validate()
|
||||
|
||||
# Definition of visibility of each menu actions
|
||||
def set_menu_visibility(self):
|
||||
|
||||
self.smActionS.menuAction().setVisible(self.bool_logged)
|
||||
self.aLogin.setVisible(not self.bool_logged)
|
||||
self.aLogout.setVisible(self.bool_logged)
|
||||
|
||||
if self.bool_logged is False:
|
||||
if self.bool_timer_event is True:
|
||||
self.stop_timer_thread()
|
||||
return
|
||||
|
||||
self.aRunActionS.setVisible(not self.bool_action_server)
|
||||
self.aResetActionS.setVisible(self.bool_action_server)
|
||||
self.aStopActionS.setVisible(self.bool_action_server)
|
||||
|
||||
if self.bool_timer_event is False:
|
||||
self.start_timer_thread()
|
||||
|
||||
def start_timer_thread(self):
|
||||
try:
|
||||
if self.thread_timer is None:
|
||||
self.thread_timer = FtrackEventsThread(self)
|
||||
self.bool_timer_event = True
|
||||
self.thread_timer.signal_timer_started.connect(
|
||||
self.timer_started
|
||||
)
|
||||
self.thread_timer.signal_timer_stopped.connect(
|
||||
self.timer_stopped
|
||||
)
|
||||
self.thread_timer.start()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def stop_timer_thread(self):
|
||||
try:
|
||||
if self.thread_timer is not None:
|
||||
self.thread_timer.terminate()
|
||||
self.thread_timer.wait()
|
||||
self.thread_timer = None
|
||||
|
||||
except Exception as e:
|
||||
log.error("During Killing Timer event server: {0}".format(e))
|
||||
|
||||
def process_modules(self, modules):
|
||||
if 'TimersManager' in modules:
|
||||
self.timer_manager = modules['TimersManager']
|
||||
self.timer_manager.add_module(self)
|
||||
|
||||
def start_timer_manager(self, data):
|
||||
if self.thread_timer is not None:
|
||||
self.thread_timer.ftrack_start_timer(data)
|
||||
|
||||
def stop_timer_manager(self):
|
||||
if self.thread_timer is not None:
|
||||
self.thread_timer.ftrack_stop_timer()
|
||||
|
||||
def timer_started(self, data):
|
||||
if hasattr(self, 'timer_manager'):
|
||||
self.timer_manager.start_timers(data)
|
||||
|
||||
def timer_stopped(self):
|
||||
if hasattr(self, 'timer_manager'):
|
||||
self.timer_manager.stop_timers()
|
||||
|
||||
|
||||
class FtrackEventsThread(QtCore.QThread):
|
||||
# Senders
|
||||
signal_timer_started = QtCore.Signal(object)
|
||||
signal_timer_stopped = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(FtrackEventsThread, self).__init__()
|
||||
cred = credentials._get_credentials()
|
||||
self.username = cred['username']
|
||||
self.user = None
|
||||
self.last_task = None
|
||||
|
||||
def run(self):
|
||||
self.timer_session = ftrack_api.Session(auto_connect_event_hub=True)
|
||||
self.timer_session.event_hub.subscribe(
|
||||
'topic=ftrack.update and source.user.username={}'.format(
|
||||
self.username
|
||||
),
|
||||
self.event_handler)
|
||||
|
||||
user_query = 'User where username is "{}"'.format(self.username)
|
||||
self.user = self.timer_session.query(user_query).one()
|
||||
|
||||
timer_query = 'Timer where user.username is "{}"'.format(self.username)
|
||||
timer = self.timer_session.query(timer_query).first()
|
||||
if timer is not None:
|
||||
self.last_task = timer['context']
|
||||
self.signal_timer_started.emit(
|
||||
self.get_data_from_task(self.last_task)
|
||||
)
|
||||
|
||||
self.timer_session.event_hub.wait()
|
||||
|
||||
def get_data_from_task(self, task_entity):
|
||||
data = {}
|
||||
data['task_name'] = task_entity['name']
|
||||
data['task_type'] = task_entity['type']['name']
|
||||
data['project_name'] = task_entity['project']['full_name']
|
||||
data['hierarchy'] = self.get_parents(task_entity['parent'])
|
||||
|
||||
return data
|
||||
|
||||
def get_parents(self, entity):
|
||||
output = []
|
||||
if entity.entity_type.lower() == 'project':
|
||||
return output
|
||||
output.extend(self.get_parents(entity['parent']))
|
||||
output.append(entity['name'])
|
||||
|
||||
return output
|
||||
|
||||
def event_handler(self, event):
|
||||
try:
|
||||
if event['data']['entities'][0]['objectTypeId'] != 'timer':
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
|
||||
new = event['data']['entities'][0]['changes']['start']['new']
|
||||
old = event['data']['entities'][0]['changes']['start']['old']
|
||||
|
||||
if old is None and new is None:
|
||||
return
|
||||
|
||||
timer_query = 'Timer where user.username is "{}"'.format(self.username)
|
||||
timer = self.timer_session.query(timer_query).first()
|
||||
if timer is not None:
|
||||
self.last_task = timer['context']
|
||||
|
||||
if old is None:
|
||||
self.signal_timer_started.emit(
|
||||
self.get_data_from_task(self.last_task)
|
||||
)
|
||||
elif new is None:
|
||||
self.signal_timer_stopped.emit()
|
||||
|
||||
def ftrack_stop_timer(self):
|
||||
try:
|
||||
self.user.stop_timer()
|
||||
self.timer_session.commit()
|
||||
self.signal_timer_stopped.emit()
|
||||
except Exception as e:
|
||||
log.debug("Timer stop had issues: {}".format(e))
|
||||
|
||||
def ftrack_start_timer(self, input_data):
|
||||
if self.user is None:
|
||||
return
|
||||
if (
|
||||
input_data['task_name'] == self.last_task['name'] and
|
||||
input_data['hierarchy'][-1] == self.last_task['parent']['name']
|
||||
):
|
||||
return
|
||||
task_query = (
|
||||
'Task where name is "{task_name}"'
|
||||
' and parent.name is "{entity_name}"'
|
||||
' and project.full_name is "{project_name}"'
|
||||
).format(**input_data)
|
||||
|
||||
task = self.timer_session.query(task_query).one()
|
||||
self.last_task = task
|
||||
self.user.start_timer(task)
|
||||
self.timer_session.commit()
|
||||
self.signal_timer_started.emit(
|
||||
self.get_data_from_task(self.last_task)
|
||||
)
|
||||
|
|
@ -1,624 +0,0 @@
|
|||
import os
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
import ftrack_api
|
||||
from app import style
|
||||
from app.vendor.Qt import QtCore, QtGui, QtWidgets
|
||||
|
||||
from pype.ftrack import credentials, login_dialog as login_dialog
|
||||
|
||||
from pype.vendor.pynput import mouse, keyboard
|
||||
from . import FtrackServer
|
||||
|
||||
from pype import api as pype
|
||||
|
||||
|
||||
# load data from templates
|
||||
pype.load_data_from_templates()
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "ftrack")
|
||||
|
||||
|
||||
class FtrackRunner:
|
||||
def __init__(self, main_parent=None, parent=None):
|
||||
|
||||
self.parent = parent
|
||||
self.widget_login = login_dialog.Login_Dialog_ui(self)
|
||||
self.widget_timer = StopTimer(self)
|
||||
self.action_server = FtrackServer('action')
|
||||
self.thread_action_server = None
|
||||
self.thread_timer = None
|
||||
self.thread_timer_coundown = None
|
||||
|
||||
# self.signal_start_timer.connect(self.timerStart)
|
||||
|
||||
self.bool_logged = False
|
||||
self.bool_action_server = False
|
||||
self.bool_timer_event = False
|
||||
|
||||
def show_login_widget(self):
|
||||
self.widget_login.show()
|
||||
|
||||
def validate(self):
|
||||
validation = False
|
||||
cred = credentials._get_credentials()
|
||||
try:
|
||||
if 'username' in cred and 'apiKey' in cred:
|
||||
validation = credentials._check_credentials(
|
||||
cred['username'],
|
||||
cred['apiKey']
|
||||
)
|
||||
if validation is False:
|
||||
self.show_login_widget()
|
||||
else:
|
||||
self.show_login_widget()
|
||||
|
||||
except Exception as e:
|
||||
log.error("We are unable to connect to Ftrack: {0}".format(e))
|
||||
|
||||
validation = credentials._check_credentials()
|
||||
if validation is True:
|
||||
log.info("Connected to Ftrack successfully")
|
||||
self.loginChange()
|
||||
else:
|
||||
log.warning("Please sign in to Ftrack")
|
||||
self.bool_logged = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
return validation
|
||||
|
||||
# Necessary - login_dialog works with this method after logging in
|
||||
def loginChange(self):
|
||||
self.bool_logged = True
|
||||
self.set_menu_visibility()
|
||||
self.start_action_server()
|
||||
|
||||
def logout(self):
|
||||
credentials._clear_credentials()
|
||||
self.stop_action_server()
|
||||
|
||||
log.info("Logged out of Ftrack")
|
||||
self.bool_logged = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
# Actions part
|
||||
def start_action_server(self):
|
||||
if self.thread_action_server is None:
|
||||
self.thread_action_server = threading.Thread(
|
||||
target=self.set_action_server
|
||||
)
|
||||
self.thread_action_server.daemon = True
|
||||
self.thread_action_server.start()
|
||||
|
||||
log.info("Ftrack action server launched")
|
||||
self.bool_action_server = True
|
||||
self.set_menu_visibility()
|
||||
|
||||
def set_action_server(self):
|
||||
try:
|
||||
self.action_server.run_server()
|
||||
except Exception:
|
||||
msg = 'Ftrack Action server crashed! Please try to start again.'
|
||||
log.error(msg)
|
||||
# TODO show message to user
|
||||
self.bool_action_server = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
def reset_action_server(self):
|
||||
self.stop_action_server()
|
||||
self.start_action_server()
|
||||
|
||||
def stop_action_server(self):
|
||||
try:
|
||||
self.action_server.stop_session()
|
||||
if self.thread_action_server is not None:
|
||||
self.thread_action_server.join()
|
||||
self.thread_action_server = None
|
||||
|
||||
log.info("Ftrack action server stopped")
|
||||
self.bool_action_server = False
|
||||
self.set_menu_visibility()
|
||||
except Exception as e:
|
||||
log.error("During Killing action server: {0}".format(e))
|
||||
|
||||
# Definition of Tray menu
|
||||
def trayMenu(self, parent):
|
||||
# Menu for Tray App
|
||||
self.menu = QtWidgets.QMenu('Ftrack', parent)
|
||||
self.menu.setProperty('submenu', 'on')
|
||||
self.menu.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
# Actions - server
|
||||
self.smActionS = self.menu.addMenu("Action server")
|
||||
|
||||
self.aRunActionS = QtWidgets.QAction(
|
||||
"Run action server", self.smActionS
|
||||
)
|
||||
self.aResetActionS = QtWidgets.QAction(
|
||||
"Reset action server", self.smActionS
|
||||
)
|
||||
self.aStopActionS = QtWidgets.QAction(
|
||||
"Stop action server", self.smActionS
|
||||
)
|
||||
|
||||
self.aRunActionS.triggered.connect(self.start_action_server)
|
||||
self.aResetActionS.triggered.connect(self.reset_action_server)
|
||||
self.aStopActionS.triggered.connect(self.stop_action_server)
|
||||
|
||||
self.smActionS.addAction(self.aRunActionS)
|
||||
self.smActionS.addAction(self.aResetActionS)
|
||||
self.smActionS.addAction(self.aStopActionS)
|
||||
|
||||
# Actions - basic
|
||||
self.aLogin = QtWidgets.QAction("Login", self.menu)
|
||||
self.aLogin.triggered.connect(self.validate)
|
||||
self.aLogout = QtWidgets.QAction("Logout", self.menu)
|
||||
self.aLogout.triggered.connect(self.logout)
|
||||
|
||||
self.menu.addAction(self.aLogin)
|
||||
self.menu.addAction(self.aLogout)
|
||||
|
||||
self.bool_logged = False
|
||||
self.set_menu_visibility()
|
||||
|
||||
return self.menu
|
||||
|
||||
# Definition of visibility of each menu actions
|
||||
def set_menu_visibility(self):
|
||||
|
||||
self.smActionS.menuAction().setVisible(self.bool_logged)
|
||||
self.aLogin.setVisible(not self.bool_logged)
|
||||
self.aLogout.setVisible(self.bool_logged)
|
||||
|
||||
if self.bool_logged is False:
|
||||
if self.bool_timer_event is True:
|
||||
self.stop_timer_thread()
|
||||
return
|
||||
|
||||
self.aRunActionS.setVisible(not self.bool_action_server)
|
||||
self.aResetActionS.setVisible(self.bool_action_server)
|
||||
self.aStopActionS.setVisible(self.bool_action_server)
|
||||
|
||||
if self.bool_timer_event is False:
|
||||
self.start_timer_thread()
|
||||
|
||||
def start_timer_thread(self):
|
||||
try:
|
||||
if self.thread_timer is None:
|
||||
self.thread_timer = FtrackEventsThread(self)
|
||||
self.bool_timer_event = True
|
||||
self.thread_timer.signal_timer_started.connect(
|
||||
self.timer_started
|
||||
)
|
||||
self.thread_timer.signal_timer_stopped.connect(
|
||||
self.timer_stopped
|
||||
)
|
||||
self.thread_timer.start()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def stop_timer_thread(self):
|
||||
try:
|
||||
if self.thread_timer is not None:
|
||||
self.thread_timer.terminate()
|
||||
self.thread_timer.wait()
|
||||
self.thread_timer = None
|
||||
|
||||
except Exception as e:
|
||||
log.error("During Killing Timer event server: {0}".format(e))
|
||||
|
||||
def start_countdown_thread(self):
|
||||
if self.thread_timer_coundown is None:
|
||||
self.thread_timer_coundown = CountdownThread(self)
|
||||
self.thread_timer_coundown.signal_show_question.connect(
|
||||
self.show_widget_timer
|
||||
)
|
||||
self.thread_timer_coundown.signal_send_time.connect(
|
||||
self.change_count_widget
|
||||
)
|
||||
self.thread_timer_coundown.signal_stop_timer.connect(
|
||||
self.timer_stop
|
||||
)
|
||||
self.thread_timer_coundown.start()
|
||||
|
||||
def stop_countdown_thread(self):
|
||||
if self.thread_timer_coundown is not None:
|
||||
self.thread_timer_coundown.runs = False
|
||||
self.thread_timer_coundown.terminate()
|
||||
self.thread_timer_coundown.wait()
|
||||
self.thread_timer_coundown = None
|
||||
|
||||
def show_widget_timer(self):
|
||||
self.widget_timer.show()
|
||||
self.widget_timer.setWindowState(QtCore.Qt.WindowMinimized)
|
||||
self.widget_timer.setWindowState(QtCore.Qt.WindowActive)
|
||||
# self.widget_timer.activateWindow()
|
||||
|
||||
def change_count_widget(self, time):
|
||||
str_time = str(time).replace(".0", "")
|
||||
self.widget_timer.lbl_rest_time.setText(str_time)
|
||||
|
||||
def timer_started(self):
|
||||
self.start_countdown_thread()
|
||||
|
||||
def timer_stopped(self):
|
||||
self.stop_countdown_thread()
|
||||
|
||||
def timer_stop(self):
|
||||
if self.thread_timer is not None:
|
||||
self.widget_timer.main_context = False
|
||||
self.widget_timer.refresh_context()
|
||||
self.thread_timer.signal_stop_timer.emit()
|
||||
if self.thread_timer_coundown is not None:
|
||||
self.stop_countdown_thread()
|
||||
|
||||
def timer_restart(self):
|
||||
if self.thread_timer is not None:
|
||||
self.thread_timer.signal_restart_timer.emit()
|
||||
|
||||
self.timer_started()
|
||||
|
||||
def timer_continue(self):
|
||||
if self.thread_timer_coundown is not None:
|
||||
self.thread_timer_coundown.signal_continue_timer.emit()
|
||||
|
||||
|
||||
class FtrackEventsThread(QtCore.QThread):
|
||||
# Senders
|
||||
signal_timer_started = QtCore.Signal()
|
||||
signal_timer_stopped = QtCore.Signal()
|
||||
# Listeners
|
||||
signal_stop_timer = QtCore.Signal()
|
||||
signal_restart_timer = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(FtrackEventsThread, self).__init__()
|
||||
cred = credentials._get_credentials()
|
||||
self.username = cred['username']
|
||||
self.signal_stop_timer.connect(self.ftrack_stop_timer)
|
||||
self.signal_restart_timer.connect(self.ftrack_restart_timer)
|
||||
self.user = None
|
||||
self.last_task = None
|
||||
|
||||
def run(self):
|
||||
self.timer_session = ftrack_api.Session(auto_connect_event_hub=True)
|
||||
self.timer_session.event_hub.subscribe(
|
||||
'topic=ftrack.update and source.user.username={}'.format(
|
||||
self.username
|
||||
),
|
||||
self.event_handler)
|
||||
|
||||
user_query = 'User where username is "{}"'.format(self.username)
|
||||
self.user = self.timer_session.query(user_query).one()
|
||||
|
||||
timer_query = 'Timer where user.username is "{}"'.format(self.username)
|
||||
timer = self.timer_session.query(timer_query).first()
|
||||
if timer is not None:
|
||||
self.last_task = timer['context']
|
||||
self.signal_timer_started.emit()
|
||||
|
||||
self.timer_session.event_hub.wait()
|
||||
|
||||
def event_handler(self, event):
|
||||
try:
|
||||
if event['data']['entities'][0]['objectTypeId'] != 'timer':
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
|
||||
new = event['data']['entities'][0]['changes']['start']['new']
|
||||
old = event['data']['entities'][0]['changes']['start']['old']
|
||||
|
||||
if old is None and new is None:
|
||||
return
|
||||
|
||||
timer_query = 'Timer where user.username is "{}"'.format(self.username)
|
||||
timer = self.timer_session.query(timer_query).first()
|
||||
if timer is not None:
|
||||
self.last_task = timer['context']
|
||||
|
||||
if old is None:
|
||||
self.signal_timer_started.emit()
|
||||
elif new is None:
|
||||
self.signal_timer_stopped.emit()
|
||||
|
||||
def ftrack_stop_timer(self):
|
||||
try:
|
||||
self.user.stop_timer()
|
||||
self.timer_session.commit()
|
||||
except Exception as e:
|
||||
log.debug("Timer stop had issues: {}".format(e))
|
||||
|
||||
def ftrack_restart_timer(self):
|
||||
try:
|
||||
if (self.last_task is not None) and (self.user is not None):
|
||||
self.user.start_timer(self.last_task)
|
||||
self.timer_session.commit()
|
||||
except Exception as e:
|
||||
log.debug("Timer stop had issues: {}".format(e))
|
||||
|
||||
|
||||
class CountdownThread(QtCore.QThread):
|
||||
# Senders
|
||||
signal_show_question = QtCore.Signal()
|
||||
signal_send_time = QtCore.Signal(object)
|
||||
signal_stop_timer = QtCore.Signal()
|
||||
signal_stop_countdown = QtCore.Signal()
|
||||
# Listeners
|
||||
signal_reset_timer = QtCore.Signal()
|
||||
signal_continue_timer = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(CountdownThread, self).__init__()
|
||||
|
||||
self.runs = True
|
||||
self.over_line = False
|
||||
config_data = self.load_timer_values()
|
||||
self.count_length = config_data['full_time']*60
|
||||
self.border_line = config_data['message_time']*60 + 1
|
||||
self.reset_count()
|
||||
self.signal_reset_timer.connect(self.reset_count)
|
||||
self.signal_continue_timer.connect(self.continue_timer)
|
||||
|
||||
def continue_timer(self):
|
||||
self.over_line = False
|
||||
self.reset_count()
|
||||
|
||||
def reset_count(self):
|
||||
if self.over_line is True:
|
||||
self.actual = self.border_line
|
||||
else:
|
||||
self.actual = self.count_length
|
||||
|
||||
def stop(self):
|
||||
self.runs = False
|
||||
|
||||
def run(self):
|
||||
thread_mouse = MouseThread(self)
|
||||
thread_mouse.start()
|
||||
thread_keyboard = KeyboardThread(self)
|
||||
thread_keyboard.start()
|
||||
while self.runs:
|
||||
if self.actual == self.border_line:
|
||||
self.signal_show_question.emit()
|
||||
self.over_line = True
|
||||
|
||||
if self.actual <= self.border_line:
|
||||
self.signal_send_time.emit(self.actual)
|
||||
|
||||
time.sleep(1)
|
||||
self.actual -= 1
|
||||
|
||||
if self.actual == 0:
|
||||
self.runs = False
|
||||
self.signal_stop_timer.emit()
|
||||
|
||||
thread_mouse.signal_stop.emit()
|
||||
thread_mouse.terminate()
|
||||
thread_mouse.wait()
|
||||
thread_keyboard.signal_stop.emit()
|
||||
thread_keyboard.terminate()
|
||||
thread_keyboard.wait()
|
||||
|
||||
def load_timer_values(self):
|
||||
templates = os.environ['PYPE_STUDIO_TEMPLATES']
|
||||
path_items = [templates, 'presets', 'ftrack', 'ftrack_config.json']
|
||||
filepath = os.path.sep.join(path_items)
|
||||
data = dict()
|
||||
try:
|
||||
with open(filepath) as data_file:
|
||||
json_dict = json.load(data_file)
|
||||
data = json_dict['timer']
|
||||
except Exception as e:
|
||||
msg = (
|
||||
'Loading "Ftrack Config file" Failed.'
|
||||
' Please check log for more information.'
|
||||
' Times are set to default.'
|
||||
)
|
||||
log.warning("{} - {}".format(msg, str(e)))
|
||||
|
||||
data = self.validate_timer_values(data)
|
||||
|
||||
return data
|
||||
|
||||
def validate_timer_values(self, data):
|
||||
# default values
|
||||
if 'full_time' not in data:
|
||||
data['full_time'] = 15
|
||||
if 'message_time' not in data:
|
||||
data['message_time'] = 0.5
|
||||
|
||||
# minimum values
|
||||
if data['full_time'] < 2:
|
||||
data['full_time'] = 2
|
||||
# message time is earlier that full time
|
||||
if data['message_time'] > data['full_time']:
|
||||
data['message_time'] = data['full_time'] - 0.5
|
||||
return data
|
||||
|
||||
|
||||
class MouseThread(QtCore.QThread):
|
||||
signal_stop = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(MouseThread, self).__init__()
|
||||
self.parent = parent
|
||||
self.signal_stop.connect(self.stop)
|
||||
self.m_listener = None
|
||||
|
||||
def stop(self):
|
||||
if self.m_listener is not None:
|
||||
self.m_listener.stop()
|
||||
|
||||
def on_move(self, posx, posy):
|
||||
self.parent.signal_reset_timer.emit()
|
||||
|
||||
def run(self):
|
||||
self.m_listener = mouse.Listener(on_move=self.on_move)
|
||||
self.m_listener.start()
|
||||
|
||||
|
||||
class KeyboardThread(QtCore.QThread):
|
||||
signal_stop = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(KeyboardThread, self).__init__()
|
||||
self.parent = parent
|
||||
self.signal_stop.connect(self.stop)
|
||||
self.k_listener = None
|
||||
|
||||
def stop(self):
|
||||
if self.k_listener is not None:
|
||||
self.k_listener.stop()
|
||||
|
||||
def on_press(self, key):
|
||||
self.parent.signal_reset_timer.emit()
|
||||
|
||||
def run(self):
|
||||
self.k_listener = keyboard.Listener(on_press=self.on_press)
|
||||
self.k_listener.start()
|
||||
|
||||
|
||||
class StopTimer(QtWidgets.QWidget):
|
||||
|
||||
SIZE_W = 300
|
||||
SIZE_H = 160
|
||||
|
||||
def __init__(self, parent=None):
|
||||
|
||||
super(StopTimer, self).__init__()
|
||||
|
||||
self.main_context = True
|
||||
self.parent = parent
|
||||
self.setWindowIcon(self.parent.parent.icon)
|
||||
self.setWindowFlags(
|
||||
QtCore.Qt.WindowCloseButtonHint |
|
||||
QtCore.Qt.WindowMinimizeButtonHint
|
||||
)
|
||||
|
||||
self._translate = QtCore.QCoreApplication.translate
|
||||
|
||||
self.font = QtGui.QFont()
|
||||
self.font.setFamily("DejaVu Sans Condensed")
|
||||
self.font.setPointSize(9)
|
||||
self.font.setBold(True)
|
||||
self.font.setWeight(50)
|
||||
self.font.setKerning(True)
|
||||
|
||||
self.resize(self.SIZE_W, self.SIZE_H)
|
||||
self.setMinimumSize(QtCore.QSize(self.SIZE_W, self.SIZE_H))
|
||||
self.setMaximumSize(QtCore.QSize(self.SIZE_W+100, self.SIZE_H+100))
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
self.setLayout(self._main())
|
||||
self.refresh_context()
|
||||
self.setWindowTitle('Pype - Stop Ftrack timer')
|
||||
|
||||
def _main(self):
|
||||
self.main = QtWidgets.QVBoxLayout()
|
||||
self.main.setObjectName('main')
|
||||
|
||||
self.form = QtWidgets.QFormLayout()
|
||||
self.form.setContentsMargins(10, 15, 10, 5)
|
||||
self.form.setObjectName('form')
|
||||
|
||||
msg_info = 'You didn\'t work for a long time.'
|
||||
msg_question = 'Would you like to stop Ftrack timer?'
|
||||
msg_stopped = (
|
||||
'Your Ftrack timer was stopped. Do you want to start again?'
|
||||
)
|
||||
|
||||
self.lbl_info = QtWidgets.QLabel(msg_info)
|
||||
self.lbl_info.setFont(self.font)
|
||||
self.lbl_info.setTextFormat(QtCore.Qt.RichText)
|
||||
self.lbl_info.setObjectName("lbl_info")
|
||||
self.lbl_info.setWordWrap(True)
|
||||
|
||||
self.lbl_question = QtWidgets.QLabel(msg_question)
|
||||
self.lbl_question.setFont(self.font)
|
||||
self.lbl_question.setTextFormat(QtCore.Qt.RichText)
|
||||
self.lbl_question.setObjectName("lbl_question")
|
||||
self.lbl_question.setWordWrap(True)
|
||||
|
||||
self.lbl_stopped = QtWidgets.QLabel(msg_stopped)
|
||||
self.lbl_stopped.setFont(self.font)
|
||||
self.lbl_stopped.setTextFormat(QtCore.Qt.RichText)
|
||||
self.lbl_stopped.setObjectName("lbl_stopped")
|
||||
self.lbl_stopped.setWordWrap(True)
|
||||
|
||||
self.lbl_rest_time = QtWidgets.QLabel("")
|
||||
self.lbl_rest_time.setFont(self.font)
|
||||
self.lbl_rest_time.setTextFormat(QtCore.Qt.RichText)
|
||||
self.lbl_rest_time.setObjectName("lbl_rest_time")
|
||||
self.lbl_rest_time.setWordWrap(True)
|
||||
self.lbl_rest_time.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
self.form.addRow(self.lbl_info)
|
||||
self.form.addRow(self.lbl_question)
|
||||
self.form.addRow(self.lbl_stopped)
|
||||
self.form.addRow(self.lbl_rest_time)
|
||||
|
||||
self.group_btn = QtWidgets.QHBoxLayout()
|
||||
self.group_btn.addStretch(1)
|
||||
self.group_btn.setObjectName("group_btn")
|
||||
|
||||
self.btn_stop = QtWidgets.QPushButton("Stop timer")
|
||||
self.btn_stop.setToolTip('Stop\'s Ftrack timer')
|
||||
self.btn_stop.clicked.connect(self.stop_timer)
|
||||
|
||||
self.btn_continue = QtWidgets.QPushButton("Continue")
|
||||
self.btn_continue.setToolTip('Timer will continue')
|
||||
self.btn_continue.clicked.connect(self.continue_timer)
|
||||
|
||||
self.btn_close = QtWidgets.QPushButton("Close")
|
||||
self.btn_close.setToolTip('Close window')
|
||||
self.btn_close.clicked.connect(self.close_widget)
|
||||
|
||||
self.btn_restart = QtWidgets.QPushButton("Start timer")
|
||||
self.btn_restart.setToolTip('Timer will be started again')
|
||||
self.btn_restart.clicked.connect(self.restart_timer)
|
||||
|
||||
self.group_btn.addWidget(self.btn_continue)
|
||||
self.group_btn.addWidget(self.btn_stop)
|
||||
self.group_btn.addWidget(self.btn_restart)
|
||||
self.group_btn.addWidget(self.btn_close)
|
||||
|
||||
self.main.addLayout(self.form)
|
||||
self.main.addLayout(self.group_btn)
|
||||
|
||||
return self.main
|
||||
|
||||
def refresh_context(self):
|
||||
self.lbl_question.setVisible(self.main_context)
|
||||
self.lbl_rest_time.setVisible(self.main_context)
|
||||
self.lbl_stopped.setVisible(not self.main_context)
|
||||
|
||||
self.btn_continue.setVisible(self.main_context)
|
||||
self.btn_stop.setVisible(self.main_context)
|
||||
self.btn_restart.setVisible(not self.main_context)
|
||||
self.btn_close.setVisible(not self.main_context)
|
||||
|
||||
def stop_timer(self):
|
||||
self.parent.timer_stop()
|
||||
self.close_widget()
|
||||
|
||||
def restart_timer(self):
|
||||
self.parent.timer_restart()
|
||||
self.close_widget()
|
||||
|
||||
def continue_timer(self):
|
||||
self.parent.timer_continue()
|
||||
self.close_widget()
|
||||
|
||||
def closeEvent(self, event):
|
||||
event.ignore()
|
||||
if self.main_context is True:
|
||||
self.continue_timer()
|
||||
else:
|
||||
self.close_widget()
|
||||
|
||||
def close_widget(self):
|
||||
self.main_context = True
|
||||
self.refresh_context()
|
||||
self.hide()
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import sys
|
||||
from pype.ftrack import credentials, login_dialog as login_dialog
|
||||
from pype.ftrack.ftrack_server import FtrackServer
|
||||
from app.vendor.Qt import QtWidgets
|
||||
from Qt import QtWidgets
|
||||
from pype import api
|
||||
|
||||
log = api.Logger.getLogger(__name__, "ftrack-event-server")
|
||||
log = api.Logger().get_logger(__name__, "ftrack-event-server")
|
||||
|
||||
|
||||
class EventServer:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import sys
|
||||
from pype.ftrack import credentials
|
||||
from pype.ftrack.ftrack_server import FtrackServer
|
||||
from app import api
|
||||
from pypeapp import Logger
|
||||
|
||||
log = api.Logger.getLogger(__name__, "ftrack-event-server-cli")
|
||||
log = Logger().get_logger(__name__, "ftrack-event-server-cli")
|
||||
|
||||
possible_yes = ['y', 'yes']
|
||||
possible_no = ['n', 'no']
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import os
|
|||
import sys
|
||||
import types
|
||||
import importlib
|
||||
import ftrack_api
|
||||
from pype.vendor import ftrack_api
|
||||
import time
|
||||
import logging
|
||||
from app.api import Logger
|
||||
from pypeapp import Logger
|
||||
|
||||
log = Logger.getLogger(__name__)
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
"""
|
||||
# Required - Needed for connection to Ftrack
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ import avalon
|
|||
import avalon.api
|
||||
from avalon import schema
|
||||
from avalon.vendor import toml, jsonschema
|
||||
from app.api import Logger
|
||||
from pypeapp import Logger
|
||||
|
||||
ValidationError = jsonschema.ValidationError
|
||||
|
||||
log = Logger.getLogger(__name__)
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
def get_ca_mongoid():
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ from pype import lib as pypelib
|
|||
from .avalon_sync import get_config_data
|
||||
from .ftrack_base_handler import BaseHandler
|
||||
|
||||
from pypeapp import Anatomy
|
||||
|
||||
|
||||
class AppAction(BaseHandler):
|
||||
'''Custom Action base class
|
||||
|
|
@ -177,7 +179,8 @@ class AppAction(BaseHandler):
|
|||
os.environ["AVALON_APP"] = self.identifier.split("_")[0]
|
||||
os.environ["AVALON_APP_NAME"] = self.identifier
|
||||
|
||||
anatomy = pype.Anatomy
|
||||
anatomy = Anatomy()
|
||||
|
||||
hierarchy = ""
|
||||
parents = database[project_name].find_one({
|
||||
"type": 'asset',
|
||||
|
|
@ -190,7 +193,7 @@ class AppAction(BaseHandler):
|
|||
application = avalonlib.get_application(os.environ["AVALON_APP_NAME"])
|
||||
|
||||
data = {
|
||||
"root": os.environ["AVALON_PROJECTS"],
|
||||
"root": os.environ.get("PYPE_STUDIO_PROJECTS_MOUNT"),
|
||||
"project": {
|
||||
"name": entity['project']['full_name'],
|
||||
"code": entity['project']['name']
|
||||
|
|
@ -213,12 +216,10 @@ class AppAction(BaseHandler):
|
|||
except Exception:
|
||||
try:
|
||||
anatomy = anatomy.format(data)
|
||||
work_template = os.path.join(
|
||||
anatomy.work.root,
|
||||
anatomy.work.folder
|
||||
)
|
||||
work_template = anatomy["work"]["folder"]
|
||||
|
||||
except Exception as e:
|
||||
self.log.error(
|
||||
self.log.exception(
|
||||
"{0} Error in anatomy.format: {1}".format(__name__, e)
|
||||
)
|
||||
os.environ["AVALON_WORKDIR"] = os.path.normpath(work_template)
|
||||
|
|
@ -239,13 +240,22 @@ class AppAction(BaseHandler):
|
|||
tools_env = acre.get_tools(tools_attr)
|
||||
env = acre.compute(tools_env)
|
||||
env = acre.merge(env, current_env=dict(os.environ))
|
||||
env = acre.append(dict(os.environ), env)
|
||||
|
||||
|
||||
#
|
||||
# tools_env = acre.get_tools(tools)
|
||||
# env = acre.compute(dict(tools_env))
|
||||
# env = acre.merge(env, dict(os.environ))
|
||||
# os.environ = acre.append(dict(os.environ), env)
|
||||
# os.environ = acre.compute(os.environ)
|
||||
|
||||
# Get path to execute
|
||||
st_temp_path = os.environ['PYPE_STUDIO_TEMPLATES']
|
||||
st_temp_path = os.environ['PYPE_CONFIG']
|
||||
os_plat = platform.system().lower()
|
||||
|
||||
# Path to folder with launchers
|
||||
path = os.path.join(st_temp_path, 'bin', os_plat)
|
||||
path = os.path.join(st_temp_path, 'launchers', os_plat)
|
||||
# Full path to executable launcher
|
||||
execfile = None
|
||||
|
||||
|
|
@ -275,7 +285,7 @@ class AppAction(BaseHandler):
|
|||
try:
|
||||
fp = open(execfile)
|
||||
except PermissionError as p:
|
||||
self.log.error('Access denied on {0} - {1}'.format(
|
||||
self.log.exception('Access denied on {0} - {1}'.format(
|
||||
execfile, p))
|
||||
return {
|
||||
'success': False,
|
||||
|
|
@ -344,6 +354,8 @@ class AppAction(BaseHandler):
|
|||
|
||||
# Set origin avalon environments
|
||||
for key, value in env_origin.items():
|
||||
if value == None:
|
||||
value = ""
|
||||
os.environ[key] = value
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import ftrack_api
|
||||
import functools
|
||||
import time
|
||||
from pype import api as pype
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.vendor.ftrack_api import session as fa_session
|
||||
|
||||
|
||||
class MissingPermision(Exception):
|
||||
|
|
@ -30,7 +31,7 @@ class BaseHandler(object):
|
|||
def __init__(self, session):
|
||||
'''Expects a ftrack_api.Session instance'''
|
||||
self._session = session
|
||||
self.log = pype.Logger.getLogger(self.__class__.__name__)
|
||||
self.log = pype.Logger().get_logger(self.__class__.__name__)
|
||||
|
||||
# Using decorator
|
||||
self.register = self.register_decorator(self.register)
|
||||
|
|
@ -71,7 +72,7 @@ class BaseHandler(object):
|
|||
self.type, label)
|
||||
)
|
||||
except Exception as e:
|
||||
self.log.error('{} "{}" - Registration failed ({})'.format(
|
||||
self.log.exception('{} "{}" - Registration failed ({})'.format(
|
||||
self.type, label, str(e))
|
||||
)
|
||||
return wrapper_register
|
||||
|
|
@ -94,7 +95,7 @@ class BaseHandler(object):
|
|||
return result
|
||||
except Exception as e:
|
||||
msg = '{} "{}": Failed ({})'.format(self.type, label, str(e))
|
||||
self.log.error(msg)
|
||||
self.log.exception(msg)
|
||||
return {
|
||||
'success': False,
|
||||
'message': msg
|
||||
|
|
@ -110,7 +111,6 @@ class BaseHandler(object):
|
|||
self.session.reset()
|
||||
|
||||
def _preregister(self):
|
||||
# Rolecheck
|
||||
if hasattr(self, "role_list") and len(self.role_list) > 0:
|
||||
username = self.session.api_user
|
||||
user = self.session.query(
|
||||
|
|
@ -197,7 +197,9 @@ class BaseHandler(object):
|
|||
_entities = event['data'].get('entities_object', None)
|
||||
if (
|
||||
_entities is None or
|
||||
_entities[0].get('link', None) == ftrack_api.symbol.NOT_SET
|
||||
_entities[0].get(
|
||||
'link', None
|
||||
) == fa_session.ftrack_api.symbol.NOT_SET
|
||||
):
|
||||
_entities = self._get_entities(event)
|
||||
|
||||
|
|
@ -302,7 +304,7 @@ class BaseHandler(object):
|
|||
|
||||
# Launch preactions
|
||||
for preaction in self.preactions:
|
||||
event = ftrack_api.event.base.Event(
|
||||
event = fa_session.ftrack_api.event.base.Event(
|
||||
topic='ftrack.action.launch',
|
||||
data=dict(
|
||||
actionIdentifier=preaction,
|
||||
|
|
@ -314,7 +316,7 @@ class BaseHandler(object):
|
|||
)
|
||||
session.event_hub.publish(event, on_error='ignore')
|
||||
# Relaunch this action
|
||||
event = ftrack_api.event.base.Event(
|
||||
event = fa_session.ftrack_api.event.base.Event(
|
||||
topic='ftrack.action.launch',
|
||||
data=dict(
|
||||
actionIdentifier=self.identifier,
|
||||
|
|
@ -415,7 +417,7 @@ class BaseHandler(object):
|
|||
'applicationId=ftrack.client.web and user.id="{0}"'
|
||||
).format(user_id)
|
||||
self.session.event_hub.publish(
|
||||
ftrack_api.event.base.Event(
|
||||
fa_session.ftrack_api.event.base.Event(
|
||||
topic='ftrack.action.trigger-user-interface',
|
||||
data=dict(
|
||||
type='message',
|
||||
|
|
@ -438,7 +440,7 @@ class BaseHandler(object):
|
|||
).format(user_id)
|
||||
|
||||
self.session.event_hub.publish(
|
||||
ftrack_api.event.base.Event(
|
||||
fa_session.ftrack_api.event.base.Event(
|
||||
topic='ftrack.action.trigger-user-interface',
|
||||
data=dict(
|
||||
type='widget',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
import requests
|
||||
from app.vendor.Qt import QtCore, QtGui, QtWidgets
|
||||
from app import style
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
from pypeapp import style
|
||||
from . import credentials, login_tools
|
||||
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ class Login_Dialog_ui(QtWidgets.QWidget):
|
|||
elif hasattr(parent, 'parent') and hasattr(parent.parent, 'icon'):
|
||||
self.setWindowIcon(self.parent.parent.icon)
|
||||
else:
|
||||
pype_setup = os.getenv('PYPE_SETUP_ROOT')
|
||||
pype_setup = os.getenv('PYPE_ROOT')
|
||||
items = [pype_setup, "app", "resources", "icon.png"]
|
||||
fname = os.path.sep.join(items)
|
||||
icon = QtGui.QIcon(fname)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import webbrowser
|
|||
import functools
|
||||
import pype
|
||||
import inspect
|
||||
from app.vendor.Qt import QtCore
|
||||
from Qt import QtCore
|
||||
|
||||
|
||||
class LoginServerHandler(BaseHTTPRequestHandler):
|
||||
|
|
|
|||
12
pype/lib.py
12
pype/lib.py
|
|
@ -420,7 +420,7 @@ def get_avalon_project_template_schema():
|
|||
|
||||
|
||||
def get_avalon_project_template():
|
||||
from app.api import Templates
|
||||
from pypeapp import Anatomy
|
||||
|
||||
"""
|
||||
Get avalon template
|
||||
|
|
@ -428,11 +428,11 @@ def get_avalon_project_template():
|
|||
Returns:
|
||||
dictionary with templates
|
||||
"""
|
||||
template = Templates(type=["anatomy"])
|
||||
templates = Anatomy().templates
|
||||
proj_template = {}
|
||||
proj_template['workfile'] = template.anatomy.avalon.workfile
|
||||
proj_template['work'] = template.anatomy.avalon.work
|
||||
proj_template['publish'] = template.anatomy.avalon.publish
|
||||
proj_template['workfile'] = templates["avalon"]["workfile"]
|
||||
proj_template['work'] = templates["avalon"]["work"]
|
||||
proj_template['publish'] = templates["avalon"]["publish"]
|
||||
return proj_template
|
||||
|
||||
|
||||
|
|
@ -467,7 +467,7 @@ def get_all_avalon_projects():
|
|||
|
||||
|
||||
def get_presets_path():
|
||||
templates = os.environ['PYPE_STUDIO_TEMPLATES']
|
||||
templates = os.environ['PYPE_CONFIG']
|
||||
path_items = [templates, 'presets']
|
||||
filepath = os.path.sep.join(path_items)
|
||||
return filepath
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@ import os
|
|||
import logging
|
||||
import weakref
|
||||
|
||||
from maya import utils, cmds, mel
|
||||
from maya import utils, cmds
|
||||
|
||||
from avalon import api as avalon, pipeline, maya
|
||||
from avalon.maya.pipeline import IS_HEADLESS
|
||||
from avalon.tools import workfiles
|
||||
from pyblish import api as pyblish
|
||||
from pypeapp import config
|
||||
|
||||
from ..lib import (
|
||||
update_task_from_path,
|
||||
any_outdated
|
||||
)
|
||||
from . import menu
|
||||
|
|
@ -107,19 +108,39 @@ def on_init(_):
|
|||
# Force load objExport plug-in (requested by artists)
|
||||
cmds.loadPlugin("objExport", quiet=True)
|
||||
|
||||
# Force load objExport plug-in (requested by artists)
|
||||
cmds.loadPlugin("spore", quiet=True)
|
||||
|
||||
from .customize import (
|
||||
override_component_mask_commands,
|
||||
override_toolbox_ui
|
||||
)
|
||||
safe_deferred(override_component_mask_commands)
|
||||
|
||||
launch_workfiles = True
|
||||
try:
|
||||
presets = config.get_presets()
|
||||
launch_workfiles = presets['tools']['workfiles']['start_on_app_launch']
|
||||
except KeyError:
|
||||
log.info(
|
||||
"Workfiles app start on launch configuration was not found."
|
||||
" Defaulting to False."
|
||||
)
|
||||
launch_workfiles = False
|
||||
|
||||
if launch_workfiles:
|
||||
safe_deferred(launch_workfiles_app)
|
||||
|
||||
if not IS_HEADLESS:
|
||||
safe_deferred(override_toolbox_ui)
|
||||
|
||||
|
||||
def launch_workfiles_app(*args):
|
||||
workfiles.show(
|
||||
os.path.join(
|
||||
cmds.workspace(query=True, rootDirectory=True),
|
||||
cmds.workspace(fileRuleEntry="scene")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def on_before_save(return_code, _):
|
||||
"""Run validation for scene's FPS prior to saving"""
|
||||
return lib.validate_fps()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from pyblish import api as pyblish
|
|||
from .. import api
|
||||
|
||||
from pype.nuke import menu
|
||||
import logging
|
||||
|
||||
from .lib import (
|
||||
create_write_node
|
||||
|
|
@ -13,14 +14,16 @@ from .lib import (
|
|||
|
||||
import nuke
|
||||
|
||||
# removing logger handler created in avalon_core
|
||||
for name, handler in [(handler.get_name(), handler)
|
||||
for handler in api.Logger.logging.root.handlers[:]]:
|
||||
if "pype" not in str(name).lower():
|
||||
api.Logger.logging.root.removeHandler(handler)
|
||||
from pypeapp import Logger
|
||||
|
||||
# #removing logger handler created in avalon_core
|
||||
# for name, handler in [(handler.get_name(), handler)
|
||||
# for handler in Logger.logging.root.handlers[:]]:
|
||||
# if "pype" not in str(name).lower():
|
||||
# Logger.logging.root.removeHandler(handler)
|
||||
|
||||
|
||||
log = api.Logger.getLogger(__name__, "nuke")
|
||||
log = Logger().get_logger(__name__, "nuke")
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
|
||||
|
|
@ -33,14 +36,13 @@ LOAD_PATH = os.path.join(PLUGINS_DIR, "nuke", "load")
|
|||
CREATE_PATH = os.path.join(PLUGINS_DIR, "nuke", "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "nuke", "inventory")
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self.nLogger = None
|
||||
|
||||
# registering pyblish gui regarding settings in presets
|
||||
if os.getenv("PYBLISH_GUI", None):
|
||||
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
|
||||
|
||||
|
||||
class NukeHandler(api.Logger.logging.Handler):
|
||||
class NukeHandler(logging.Handler):
|
||||
'''
|
||||
Nuke Handler - emits logs into nuke's script editor.
|
||||
warning will emit nuke.warning()
|
||||
|
|
@ -48,7 +50,7 @@ class NukeHandler(api.Logger.logging.Handler):
|
|||
'''
|
||||
|
||||
def __init__(self):
|
||||
api.Logger.logging.Handler.__init__(self)
|
||||
logging.Handler.__init__(self)
|
||||
self.set_name("Pype_Nuke_Handler")
|
||||
|
||||
def emit(self, record):
|
||||
|
|
@ -61,6 +63,7 @@ class NukeHandler(api.Logger.logging.Handler):
|
|||
"fatal",
|
||||
"error"
|
||||
]:
|
||||
msg = self.format(record)
|
||||
nuke.message(msg)
|
||||
|
||||
|
||||
|
|
@ -68,12 +71,9 @@ class NukeHandler(api.Logger.logging.Handler):
|
|||
nuke_handler = NukeHandler()
|
||||
if nuke_handler.get_name() \
|
||||
not in [handler.get_name()
|
||||
for handler in api.Logger.logging.root.handlers[:]]:
|
||||
api.Logger.logging.getLogger().addHandler(nuke_handler)
|
||||
api.Logger.logging.getLogger().setLevel(api.Logger.logging.INFO)
|
||||
|
||||
if not self.nLogger:
|
||||
self.nLogger = api.Logger
|
||||
for handler in logging.root.handlers[:]]:
|
||||
logging.getLogger().addHandler(nuke_handler)
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
|
||||
def reload_config():
|
||||
|
|
@ -106,8 +106,14 @@ def reload_config():
|
|||
|
||||
def install():
|
||||
|
||||
api.set_avalon_workdir()
|
||||
reload_config()
|
||||
# api.set_avalon_workdir()
|
||||
# reload_config()
|
||||
|
||||
# import sys
|
||||
|
||||
# for path in sys.path:
|
||||
# if path.startswith("C:\\Users\\Public"):
|
||||
# sys.path.remove(path)
|
||||
|
||||
log.info("Registering Nuke plug-ins..")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
|
|
@ -146,7 +152,7 @@ def uninstall():
|
|||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
"""Toggle node passthrough states on instance toggles."""
|
||||
self.log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
instance, old_value, new_value))
|
||||
|
||||
from avalon.nuke import (
|
||||
|
|
|
|||
108
pype/nuke/lib.py
108
pype/nuke/lib.py
|
|
@ -13,7 +13,9 @@ from .templates import (
|
|||
get_colorspace
|
||||
)
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "nuke")
|
||||
from pypeapp import Logger
|
||||
log = Logger().get_logger(__name__, "nuke")
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self._project = None
|
||||
|
||||
|
|
@ -27,6 +29,53 @@ def onScriptLoad():
|
|||
nuke.tcl('load movWriter')
|
||||
|
||||
|
||||
def checkInventoryVersions():
|
||||
"""
|
||||
Actiual version idetifier of Loaded containers
|
||||
|
||||
Any time this function is run it will check all nodes and filter only Loader nodes for its version. It will get all versions from database
|
||||
and check if the node is having actual version. If not then it will color it to red.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# get all Loader nodes by avalon attribute metadata
|
||||
for each in nuke.allNodes():
|
||||
if each.Class() == 'Read':
|
||||
container = avalon.nuke.parse_container(each)
|
||||
|
||||
if container:
|
||||
node = container["_tool"]
|
||||
avalon_knob_data = get_avalon_knob_data(node)
|
||||
|
||||
# get representation from io
|
||||
representation = io.find_one({
|
||||
"type": "representation",
|
||||
"_id": io.ObjectId(avalon_knob_data["representation"])
|
||||
})
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# check the available version and do match
|
||||
# change color of node if not max verion
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
|
||||
def writes_version_sync():
|
||||
try:
|
||||
rootVersion = pype.get_version_from_path(nuke.root().name())
|
||||
|
|
@ -56,7 +105,8 @@ def writes_version_sync():
|
|||
node_new_file = node_file.replace(node_version, new_version)
|
||||
each['file'].setValue(node_new_file)
|
||||
except Exception as e:
|
||||
log.debug("Write node: `{}` has no version in path: {}".format(each.name(), e))
|
||||
log.debug(
|
||||
"Write node: `{}` has no version in path: {}".format(each.name(), e))
|
||||
|
||||
|
||||
def version_up_script():
|
||||
|
|
@ -72,7 +122,7 @@ def get_render_path(node):
|
|||
data_preset = {
|
||||
"class": data['avalon']['family'],
|
||||
"preset": data['avalon']['families']
|
||||
}
|
||||
}
|
||||
|
||||
nuke_dataflow_writes = get_dataflow(**data_preset)
|
||||
nuke_colorspace_writes = get_colorspace(**data_preset)
|
||||
|
|
@ -85,7 +135,8 @@ def get_render_path(node):
|
|||
})
|
||||
|
||||
anatomy_filled = format_anatomy(data)
|
||||
return anatomy_filled.render.path.replace("\\", "/")
|
||||
return anatomy_filled["render"]["path"].replace("\\", "/")
|
||||
|
||||
|
||||
def format_anatomy(data):
|
||||
from .templates import (
|
||||
|
|
@ -93,28 +144,29 @@ def format_anatomy(data):
|
|||
)
|
||||
|
||||
anatomy = get_anatomy()
|
||||
|
||||
log.info("__ anatomy.templates: {}".format(anatomy.templates))
|
||||
# TODO: perhaps should be in try!
|
||||
padding = anatomy.render.padding
|
||||
padding = int(anatomy.templates['render']['padding'])
|
||||
version = data.get("version", None)
|
||||
if not version:
|
||||
file = script_name()
|
||||
data["version"] = pype.get_version_from_path(file)
|
||||
|
||||
data.update({
|
||||
"root": api.Session["AVALON_PROJECTS"],
|
||||
"subset": data["avalon"]["subset"],
|
||||
"asset": data["avalon"]["asset"],
|
||||
"task": str(pype.get_task()).lower(),
|
||||
"family": data["avalon"]["family"],
|
||||
"project": {"name": pype.get_project_name(),
|
||||
"code": pype.get_project_code()},
|
||||
"representation": data["nuke_dataflow_writes"].file_type,
|
||||
"representation": data["nuke_dataflow_writes"]["file_type"],
|
||||
"app": data["application"]["application_dir"],
|
||||
"hierarchy": pype.get_hierarchy(),
|
||||
"frame": "#" * padding,
|
||||
})
|
||||
|
||||
# log.info("format_anatomy:anatomy: {}".format(anatomy))
|
||||
log.info("__ data: {}".format(data))
|
||||
log.info("__ format_anatomy: {}".format(anatomy.format(data)))
|
||||
return anatomy.format(data)
|
||||
|
||||
|
||||
|
|
@ -139,10 +191,8 @@ def create_write_node(name, data):
|
|||
except Exception as e:
|
||||
log.error("problem with resolving anatomy tepmlate: {}".format(e))
|
||||
|
||||
log.debug("anatomy_filled.render: {}".format(anatomy_filled.render))
|
||||
|
||||
_data = OrderedDict({
|
||||
"file": str(anatomy_filled.render.path).replace("\\", "/")
|
||||
"file": str(anatomy_filled["render"]["path"]).replace("\\", "/")
|
||||
})
|
||||
|
||||
# adding dataflow template
|
||||
|
|
@ -159,7 +209,7 @@ def create_write_node(name, data):
|
|||
log.debug(_data)
|
||||
|
||||
_data["frame_range"] = data.get("frame_range", None)
|
||||
|
||||
log.info("__ _data3: {}".format(_data))
|
||||
instance = avalon.nuke.lib.add_write_node(
|
||||
name,
|
||||
**_data
|
||||
|
|
@ -168,6 +218,7 @@ def create_write_node(name, data):
|
|||
add_rendering_knobs(instance)
|
||||
return instance
|
||||
|
||||
|
||||
def add_rendering_knobs(node):
|
||||
if "render" not in node.knobs():
|
||||
knob = nuke.Boolean_Knob("render", "Render")
|
||||
|
|
@ -193,8 +244,8 @@ def set_viewers_colorspace(viewer):
|
|||
erased_viewers = []
|
||||
|
||||
for v in viewers:
|
||||
v['viewerProcess'].setValue(str(viewer.viewerProcess))
|
||||
if str(viewer.viewerProcess) not in v['viewerProcess'].value():
|
||||
v['viewerProcess'].setValue(str(viewer["viewerProcess"]))
|
||||
if str(viewer["viewerProcess"]) not in v['viewerProcess'].value():
|
||||
copy_inputs = v.dependencies()
|
||||
copy_knobs = {k: v[k].value() for k in v.knobs()
|
||||
if k not in filter_knobs}
|
||||
|
|
@ -216,7 +267,7 @@ def set_viewers_colorspace(viewer):
|
|||
nv[k].setValue(v)
|
||||
|
||||
# set viewerProcess
|
||||
nv['viewerProcess'].setValue(str(viewer.viewerProcess))
|
||||
nv['viewerProcess'].setValue(str(viewer["viewerProcess"]))
|
||||
|
||||
if erased_viewers:
|
||||
log.warning(
|
||||
|
|
@ -227,6 +278,17 @@ def set_viewers_colorspace(viewer):
|
|||
def set_root_colorspace(root_dict):
|
||||
assert isinstance(root_dict, dict), log.error(
|
||||
"set_root_colorspace(): argument should be dictionary")
|
||||
|
||||
# first set OCIO
|
||||
if nuke.root()["colorManagement"].value() not in str(root_dict["colorManagement"]):
|
||||
nuke.root()["colorManagement"].setValue(
|
||||
str(root_dict["colorManagement"]))
|
||||
|
||||
# second set ocio version
|
||||
if nuke.root()["OCIO_config"].value() not in str(root_dict["OCIO_config"]):
|
||||
nuke.root()["OCIO_config"].setValue(str(root_dict["OCIO_config"]))
|
||||
|
||||
# then set the rest
|
||||
for knob, value in root_dict.items():
|
||||
if nuke.root()[knob].value() not in value:
|
||||
nuke.root()[knob].setValue(str(value))
|
||||
|
|
@ -242,20 +304,20 @@ def set_writes_colorspace(write_dict):
|
|||
def set_colorspace():
|
||||
from pype import api as pype
|
||||
|
||||
nuke_colorspace = getattr(pype.Colorspace, "nuke", None)
|
||||
nuke_colorspace = pype.Colorspace.get("nuke", None)
|
||||
|
||||
try:
|
||||
set_root_colorspace(nuke_colorspace.root)
|
||||
set_root_colorspace(nuke_colorspace["root"])
|
||||
except AttributeError:
|
||||
log.error(
|
||||
"set_colorspace(): missing `root` settings in template")
|
||||
try:
|
||||
set_viewers_colorspace(nuke_colorspace.viewer)
|
||||
set_viewers_colorspace(nuke_colorspace["viewer"])
|
||||
except AttributeError:
|
||||
log.error(
|
||||
"set_colorspace(): missing `viewer` settings in template")
|
||||
try:
|
||||
set_writes_colorspace(nuke_colorspace.write)
|
||||
set_writes_colorspace(nuke_colorspace["write"])
|
||||
except AttributeError:
|
||||
log.error(
|
||||
"set_colorspace(): missing `write` settings in template")
|
||||
|
|
@ -320,7 +382,7 @@ def reset_resolution():
|
|||
check_format = used_formats[-1]
|
||||
format_name = "{}_{}".format(
|
||||
project["name"],
|
||||
int(used_formats[-1].name()[-1])+1
|
||||
int(used_formats[-1].name()[-1]) + 1
|
||||
)
|
||||
log.info(
|
||||
"Format exists: {}. "
|
||||
|
|
@ -438,7 +500,7 @@ def get_additional_data(container):
|
|||
|
||||
def get_write_node_template_attr(node):
|
||||
''' Gets all defined data from presets
|
||||
|
||||
|
||||
'''
|
||||
# get avalon data from node
|
||||
data = dict()
|
||||
|
|
@ -446,7 +508,7 @@ def get_write_node_template_attr(node):
|
|||
data_preset = {
|
||||
"class": data['avalon']['family'],
|
||||
"preset": data['avalon']['families']
|
||||
}
|
||||
}
|
||||
|
||||
# get template data
|
||||
nuke_dataflow_writes = get_dataflow(**data_preset)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from pype import api as pype
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "nuke")
|
||||
log = pype.Logger().get_logger(__name__, "nuke")
|
||||
|
||||
|
||||
def get_anatomy(**kwarg):
|
||||
|
|
@ -15,10 +15,12 @@ def get_dataflow(**kwarg):
|
|||
assert any([host, cls]), log.error("nuke.templates.get_dataflow():"
|
||||
"Missing mandatory kwargs `host`, `cls`")
|
||||
|
||||
nuke_dataflow = getattr(pype.Dataflow, str(host), None)
|
||||
nuke_dataflow_node = getattr(nuke_dataflow.nodes, str(cls), None)
|
||||
nuke_dataflow = pype.Dataflow.get(str(host), None)
|
||||
nuke_dataflow_nodes = nuke_dataflow.get('nodes', None)
|
||||
nuke_dataflow_node = nuke_dataflow_nodes.get(str(cls), None)
|
||||
|
||||
if preset:
|
||||
nuke_dataflow_node = getattr(nuke_dataflow_node, str(preset), None)
|
||||
nuke_dataflow_node = nuke_dataflow_node.get(str(preset), None)
|
||||
|
||||
log.info("Dataflow: {}".format(nuke_dataflow_node))
|
||||
return nuke_dataflow_node
|
||||
|
|
@ -32,10 +34,10 @@ def get_colorspace(**kwarg):
|
|||
assert any([host, cls]), log.error("nuke.templates.get_colorspace():"
|
||||
"Missing mandatory kwargs `host`, `cls`")
|
||||
|
||||
nuke_colorspace = getattr(pype.Colorspace, str(host), None)
|
||||
nuke_colorspace_node = getattr(nuke_colorspace, str(cls), None)
|
||||
nuke_colorspace = pype.Colorspace.get(str(host), None)
|
||||
nuke_colorspace_node = nuke_colorspace.get(str(cls), None)
|
||||
if preset:
|
||||
nuke_colorspace_node = getattr(nuke_colorspace_node, str(preset), None)
|
||||
nuke_colorspace_node = nuke_colorspace_node.get(str(preset), None)
|
||||
|
||||
log.info("Colorspace: {}".format(nuke_colorspace_node))
|
||||
return nuke_colorspace_node
|
||||
|
|
|
|||
117
pype/nukestudio/__init__.py
Normal file
117
pype/nukestudio/__init__.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import os
|
||||
import sys
|
||||
from avalon import api as avalon
|
||||
from pyblish import api as pyblish
|
||||
|
||||
from .. import api
|
||||
|
||||
from .menu import install as menu_install
|
||||
|
||||
from .lib import (
|
||||
show,
|
||||
setup,
|
||||
add_to_filemenu
|
||||
)
|
||||
|
||||
|
||||
from pypeapp import Logger
|
||||
|
||||
|
||||
log = Logger().get_logger(__name__, "nukestudio")
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
|
||||
PARENT_DIR = os.path.dirname(__file__)
|
||||
PACKAGE_DIR = os.path.dirname(PARENT_DIR)
|
||||
PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")
|
||||
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "inventory")
|
||||
|
||||
|
||||
if os.getenv("PYBLISH_GUI", None):
|
||||
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
|
||||
|
||||
|
||||
def reload_config():
|
||||
"""Attempt to reload pipeline at run-time.
|
||||
|
||||
CAUTION: This is primarily for development and debugging purposes.
|
||||
|
||||
"""
|
||||
|
||||
import importlib
|
||||
|
||||
for module in (
|
||||
"pypeapp",
|
||||
"{}.api".format(AVALON_CONFIG),
|
||||
"{}.templates".format(AVALON_CONFIG),
|
||||
"{}.nukestudio.inventory".format(AVALON_CONFIG),
|
||||
"{}.nukestudio.lib".format(AVALON_CONFIG),
|
||||
"{}.nukestudio.menu".format(AVALON_CONFIG),
|
||||
):
|
||||
log.info("Reloading module: {}...".format(module))
|
||||
try:
|
||||
module = importlib.import_module(module)
|
||||
reload(module)
|
||||
except Exception as e:
|
||||
log.warning("Cannot reload module: {}".format(e))
|
||||
importlib.reload(module)
|
||||
|
||||
|
||||
def install(config):
|
||||
|
||||
# api.set_avalon_workdir()
|
||||
# reload_config()
|
||||
|
||||
# import sys
|
||||
# for path in sys.path:
|
||||
# if path.startswith("C:\\Users\\Public"):
|
||||
# sys.path.remove(path)
|
||||
|
||||
log.info("Registering NukeStudio plug-ins..")
|
||||
pyblish.register_host("nukestudio")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# Disable all families except for the ones we explicitly want to see
|
||||
family_states = [
|
||||
"write",
|
||||
"review"
|
||||
]
|
||||
|
||||
avalon.data["familiesStateDefault"] = False
|
||||
avalon.data["familiesStateToggled"] = family_states
|
||||
|
||||
menu_install()
|
||||
|
||||
# load data from templates
|
||||
api.load_data_from_templates()
|
||||
|
||||
|
||||
def uninstall():
|
||||
log.info("Deregistering NukeStudio plug-ins..")
|
||||
pyblish.deregister_host("nukestudio")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
|
||||
# reset data from templates
|
||||
api.reset_data_from_templates()
|
||||
|
||||
|
||||
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`
|
||||
"""
|
||||
return
|
||||
205
pype/nukestudio/lib.py
Normal file
205
pype/nukestudio/lib.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# Standard library
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Pyblish libraries
|
||||
import pyblish.api
|
||||
|
||||
# Host libraries
|
||||
import hiero
|
||||
|
||||
from PySide2 import (QtWidgets, QtGui)
|
||||
|
||||
|
||||
cached_process = None
|
||||
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self._has_been_setup = False
|
||||
self._has_menu = False
|
||||
self._registered_gui = None
|
||||
|
||||
|
||||
def setup(console=False, port=None, menu=True):
|
||||
"""Setup integration
|
||||
|
||||
Registers Pyblish for Hiero plug-ins and appends an item to the File-menu
|
||||
|
||||
Arguments:
|
||||
console (bool): Display console with GUI
|
||||
port (int, optional): Port from which to start looking for an
|
||||
available port to connect with Pyblish QML, default
|
||||
provided by Pyblish Integration.
|
||||
menu (bool, optional): Display file menu in Hiero.
|
||||
"""
|
||||
|
||||
if self._has_been_setup:
|
||||
teardown()
|
||||
|
||||
add_submission()
|
||||
|
||||
if menu:
|
||||
add_to_filemenu()
|
||||
self._has_menu = True
|
||||
|
||||
self._has_been_setup = True
|
||||
print("pyblish: Loaded successfully.")
|
||||
|
||||
|
||||
def show():
|
||||
"""Try showing the most desirable GUI
|
||||
This function cycles through the currently registered
|
||||
graphical user interfaces, if any, and presents it to
|
||||
the user.
|
||||
"""
|
||||
|
||||
return (_discover_gui() or _show_no_gui)()
|
||||
|
||||
|
||||
def _discover_gui():
|
||||
"""Return the most desirable of the currently registered GUIs"""
|
||||
|
||||
# Prefer last registered
|
||||
guis = reversed(pyblish.api.registered_guis())
|
||||
|
||||
for gui in list(guis) + ["pyblish_lite"]:
|
||||
try:
|
||||
gui = __import__(gui).show
|
||||
except (ImportError, AttributeError):
|
||||
continue
|
||||
else:
|
||||
return gui
|
||||
|
||||
|
||||
def teardown():
|
||||
"""Remove integration"""
|
||||
if not self._has_been_setup:
|
||||
return
|
||||
|
||||
if self._has_menu:
|
||||
remove_from_filemenu()
|
||||
self._has_menu = False
|
||||
|
||||
self._has_been_setup = False
|
||||
print("pyblish: Integration torn down successfully")
|
||||
|
||||
|
||||
def remove_from_filemenu():
|
||||
raise NotImplementedError("Implement me please.")
|
||||
|
||||
|
||||
def add_to_filemenu():
|
||||
PublishAction()
|
||||
|
||||
|
||||
class PyblishSubmission(hiero.exporters.FnSubmission.Submission):
|
||||
|
||||
def __init__(self):
|
||||
hiero.exporters.FnSubmission.Submission.__init__(self)
|
||||
|
||||
def addToQueue(self):
|
||||
# Add submission to Hiero module for retrieval in plugins.
|
||||
hiero.submission = self
|
||||
show()
|
||||
|
||||
|
||||
def add_submission():
|
||||
registry = hiero.core.taskRegistry
|
||||
registry.addSubmission("Pyblish", PyblishSubmission)
|
||||
|
||||
|
||||
class PublishAction(QtWidgets.QAction):
|
||||
def __init__(self):
|
||||
QtWidgets.QAction.__init__(self, "Publish", None)
|
||||
self.triggered.connect(self.publish)
|
||||
|
||||
for interest in ["kShowContextMenu/kTimeline",
|
||||
"kShowContextMenukBin",
|
||||
"kShowContextMenu/kSpreadsheet"]:
|
||||
hiero.core.events.registerInterest(interest, self.eventHandler)
|
||||
|
||||
self.setShortcut("Ctrl+Alt+P")
|
||||
|
||||
def publish(self):
|
||||
# Removing "submission" attribute from hiero module, to prevent tasks
|
||||
# from getting picked up when not using the "Export" dialog.
|
||||
if hasattr(hiero, "submission"):
|
||||
del hiero.submission
|
||||
show()
|
||||
|
||||
def eventHandler(self, event):
|
||||
# Add the Menu to the right-click menu
|
||||
event.menu.addAction(self)
|
||||
|
||||
|
||||
def _show_no_gui():
|
||||
"""Popup with information about how to register a new GUI
|
||||
In the event of no GUI being registered or available,
|
||||
this information dialog will appear to guide the user
|
||||
through how to get set up with one.
|
||||
"""
|
||||
|
||||
messagebox = QtWidgets.QMessageBox()
|
||||
messagebox.setIcon(messagebox.Warning)
|
||||
messagebox.setWindowIcon(QtGui.QIcon(os.path.join(
|
||||
os.path.dirname(pyblish.__file__),
|
||||
"icons",
|
||||
"logo-32x32.svg"))
|
||||
)
|
||||
|
||||
spacer = QtWidgets.QWidget()
|
||||
spacer.setMinimumSize(400, 0)
|
||||
spacer.setSizePolicy(QtWidgets.QSizePolicy.Minimum,
|
||||
QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
layout = messagebox.layout()
|
||||
layout.addWidget(spacer, layout.rowCount(), 0, 1, layout.columnCount())
|
||||
|
||||
messagebox.setWindowTitle("Uh oh")
|
||||
messagebox.setText("No registered GUI found.")
|
||||
|
||||
if not pyblish.api.registered_guis():
|
||||
messagebox.setInformativeText(
|
||||
"In order to show you a GUI, one must first be registered. "
|
||||
"Press \"Show details...\" below for information on how to "
|
||||
"do that.")
|
||||
|
||||
messagebox.setDetailedText(
|
||||
"Pyblish supports one or more graphical user interfaces "
|
||||
"to be registered at once, the next acting as a fallback to "
|
||||
"the previous."
|
||||
"\n"
|
||||
"\n"
|
||||
"For example, to use Pyblish Lite, first install it:"
|
||||
"\n"
|
||||
"\n"
|
||||
"$ pip install pyblish-lite"
|
||||
"\n"
|
||||
"\n"
|
||||
"Then register it, like so:"
|
||||
"\n"
|
||||
"\n"
|
||||
">>> import pyblish.api\n"
|
||||
">>> pyblish.api.register_gui(\"pyblish_lite\")"
|
||||
"\n"
|
||||
"\n"
|
||||
"The next time you try running this, Lite will appear."
|
||||
"\n"
|
||||
"See http://api.pyblish.com/register_gui.html for "
|
||||
"more information.")
|
||||
|
||||
else:
|
||||
messagebox.setInformativeText(
|
||||
"None of the registered graphical user interfaces "
|
||||
"could be found."
|
||||
"\n"
|
||||
"\n"
|
||||
"Press \"Show details\" for more information.")
|
||||
|
||||
messagebox.setDetailedText(
|
||||
"These interfaces are currently registered."
|
||||
"\n"
|
||||
"%s" % "\n".join(pyblish.api.registered_guis()))
|
||||
|
||||
messagebox.setStandardButtons(messagebox.Ok)
|
||||
messagebox.exec_()
|
||||
92
pype/nukestudio/menu.py
Normal file
92
pype/nukestudio/menu.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import os
|
||||
from avalon.api import Session
|
||||
from pprint import pprint
|
||||
|
||||
import hiero.core
|
||||
|
||||
try:
|
||||
from PySide.QtGui import *
|
||||
except Exception:
|
||||
from PySide2.QtGui import *
|
||||
from PySide2.QtWidgets import *
|
||||
|
||||
from hiero.ui import findMenuAction
|
||||
|
||||
|
||||
#
|
||||
def install():
|
||||
# here is the best place to add menu
|
||||
from avalon.tools import (
|
||||
creator,
|
||||
publish,
|
||||
workfiles,
|
||||
cbloader,
|
||||
cbsceneinventory,
|
||||
contextmanager,
|
||||
libraryloader
|
||||
)
|
||||
|
||||
menu_name = os.environ['PYPE_STUDIO_NAME']
|
||||
# Grab Hiero's MenuBar
|
||||
M = hiero.ui.menuBar()
|
||||
|
||||
# Add a Menu to the MenuBar
|
||||
file_action = None
|
||||
|
||||
try:
|
||||
check_made_menu = findMenuAction(menu_name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not check_made_menu:
|
||||
menu = M.addMenu(menu_name)
|
||||
else:
|
||||
menu = check_made_menu.menu()
|
||||
|
||||
actions = [{
|
||||
'action': QAction('Set Context', None),
|
||||
'function': contextmanager.show,
|
||||
'icon': QIcon('icons:Position.png')
|
||||
},
|
||||
{
|
||||
'action': QAction('Create...', None),
|
||||
'function': creator.show,
|
||||
'icon': QIcon('icons:ColorAdd.png')
|
||||
},
|
||||
{
|
||||
'action': QAction('Load...', None),
|
||||
'function': cbloader.show,
|
||||
'icon': QIcon('icons:CopyRectangle.png')
|
||||
},
|
||||
{
|
||||
'action': QAction('Publish...', None),
|
||||
'function': publish.show,
|
||||
'icon': QIcon('icons:Output.png')
|
||||
},
|
||||
{
|
||||
'action': QAction('Manage...', None),
|
||||
'function': cbsceneinventory.show,
|
||||
'icon': QIcon('icons:ModifyMetaData.png')
|
||||
},
|
||||
{
|
||||
'action': QAction('Library...', None),
|
||||
'function': libraryloader.show,
|
||||
'icon': QIcon('icons:ColorAdd.png')
|
||||
}]
|
||||
|
||||
|
||||
# Create menu items
|
||||
for a in actions:
|
||||
pprint(a)
|
||||
# create action
|
||||
for k in a.keys():
|
||||
if 'action' in k:
|
||||
action = a[k]
|
||||
elif 'function' in k:
|
||||
action.triggered.connect(a[k])
|
||||
elif 'icon' in k:
|
||||
action.setIcon(a[k])
|
||||
|
||||
# add action to menu
|
||||
menu.addAction(action)
|
||||
hiero.ui.registerAction(action)
|
||||
|
|
@ -63,8 +63,8 @@ class CollectContextDataFromAport(pyblish.api.ContextPlugin):
|
|||
pyblish.api.register_host(host)
|
||||
|
||||
# get path to studio templates
|
||||
templates_dir = os.getenv("PYPE_STUDIO_TEMPLATES", None)
|
||||
assert templates_dir, "Missing `PYPE_STUDIO_TEMPLATES` in os.environ"
|
||||
templates_dir = os.getenv("PYPE_CONFIG", None)
|
||||
assert templates_dir, "Missing `PYPE_CONFIG` in os.environ"
|
||||
|
||||
# get presets for host
|
||||
presets_dir = os.path.join(templates_dir, "presets", host)
|
||||
|
|
|
|||
|
|
@ -26,15 +26,9 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
|
|||
'render': 'render',
|
||||
'nukescript': 'comp',
|
||||
'review': 'mov'}
|
||||
exclude = []
|
||||
|
||||
def process(self, instance):
|
||||
for ex in self.exclude:
|
||||
if ex in instance.data['families']:
|
||||
return
|
||||
|
||||
self.log.debug('instance {}'.format(instance))
|
||||
|
||||
assumed_data = instance.data["assumedTemplateData"]
|
||||
assumed_version = assumed_data["version"]
|
||||
version_number = int(assumed_version)
|
||||
|
|
@ -60,8 +54,6 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
|
|||
self.log.debug('dest ext: ' + ext)
|
||||
thumbnail = False
|
||||
|
||||
|
||||
|
||||
if ext in ['.mov']:
|
||||
if not instance.data.get('startFrameReview'):
|
||||
instance.data['startFrameReview'] = instance.data['startFrame']
|
||||
|
|
@ -70,12 +62,13 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
|
|||
location = ft_session.query(
|
||||
'Location where name is "ftrack.server"').one()
|
||||
component_data = {
|
||||
"name": "ftrackreview-mp4", # Default component name is "main".
|
||||
# Default component name is "main".
|
||||
"name": "ftrackreview-mp4",
|
||||
"metadata": {'ftr_meta': json.dumps({
|
||||
'frameIn': int(instance.data['startFrameReview']),
|
||||
'frameOut': int(instance.data['startFrameReview']),
|
||||
'frameRate': 25})}
|
||||
}
|
||||
}
|
||||
elif ext in [".jpg", ".jpeg"]:
|
||||
component_data = {
|
||||
"name": "thumbnail" # Default component name is "main".
|
||||
|
|
|
|||
58
pype/plugins/global/load/open_file.py
Normal file
58
pype/plugins/global/load/open_file.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from avalon import api
|
||||
|
||||
|
||||
def open(filepath):
|
||||
"""Open file with system default executable"""
|
||||
if sys.platform.startswith('darwin'):
|
||||
subprocess.call(('open', filepath))
|
||||
elif os.name == 'nt':
|
||||
os.startfile(filepath)
|
||||
elif os.name == 'posix':
|
||||
subprocess.call(('xdg-open', filepath))
|
||||
|
||||
|
||||
class Openfile(api.Loader):
|
||||
"""Open Image Sequence with system default"""
|
||||
|
||||
families = ["write"]
|
||||
representations = ["*"]
|
||||
|
||||
label = "Open"
|
||||
order = -10
|
||||
icon = "play-circle"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
from avalon.vendor import clique
|
||||
|
||||
directory = os.path.dirname(self.fname)
|
||||
pattern = clique.PATTERNS["frames"]
|
||||
|
||||
files = os.listdir(directory)
|
||||
representation = context["representation"]
|
||||
|
||||
ext = representation["name"]
|
||||
path = representation["data"]["path"]
|
||||
|
||||
if ext in ["#"]:
|
||||
collections, remainder = clique.assemble(files,
|
||||
patterns=[pattern],
|
||||
minimum_items=1)
|
||||
|
||||
seqeunce = collections[0]
|
||||
|
||||
first_image = list(seqeunce)[0]
|
||||
filepath = os.path.normpath(os.path.join(directory, first_image))
|
||||
else:
|
||||
file = [f for f in files
|
||||
if ext in f
|
||||
if "#" not in f][0]
|
||||
filepath = os.path.normpath(os.path.join(directory, file))
|
||||
|
||||
self.log.info("Opening : {}".format(filepath))
|
||||
|
||||
open(filepath)
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from avalon import api
|
||||
|
||||
|
||||
def open(filepath):
|
||||
"""Open file with system default executable"""
|
||||
if sys.platform.startswith('darwin'):
|
||||
subprocess.call(('open', filepath))
|
||||
elif os.name == 'nt':
|
||||
os.startfile(filepath)
|
||||
elif os.name == 'posix':
|
||||
subprocess.call(('xdg-open', filepath))
|
||||
|
||||
|
||||
class PlayImageSequence(api.Loader):
|
||||
"""Open Image Sequence with system default"""
|
||||
|
||||
families = ["write"]
|
||||
representations = ["*"]
|
||||
|
||||
label = "Play sequence"
|
||||
order = -10
|
||||
icon = "play-circle"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
directory = self.fname
|
||||
from avalon.vendor import clique
|
||||
|
||||
pattern = clique.PATTERNS["frames"]
|
||||
files = os.listdir(directory)
|
||||
collections, remainder = clique.assemble(files,
|
||||
patterns=[pattern],
|
||||
minimum_items=1)
|
||||
|
||||
assert not remainder, ("There shouldn't have been a remainder for "
|
||||
"'%s': %s" % (directory, remainder))
|
||||
|
||||
seqeunce = collections[0]
|
||||
first_image = list(seqeunce)[0]
|
||||
filepath = os.path.normpath(os.path.join(directory, first_image))
|
||||
|
||||
self.log.info("Opening : {}".format(filepath))
|
||||
|
||||
open(filepath)
|
||||
|
|
@ -4,15 +4,71 @@ import pyblish.api
|
|||
from avalon import io, api
|
||||
|
||||
|
||||
class CollectAssumedDestination(pyblish.api.InstancePlugin):
|
||||
class CollectAssumedDestination(pyblish.api.ContextPlugin):
|
||||
"""Generate the assumed destination path where the file will be stored"""
|
||||
|
||||
label = "Collect Assumed Destination"
|
||||
order = pyblish.api.CollectorOrder + 0.498
|
||||
exclude_families = ["clip"]
|
||||
|
||||
def process(self, instance):
|
||||
"""Create a destination filepath based on the current data available
|
||||
def process(self, context):
|
||||
for instance in context:
|
||||
self.process_item(instance)
|
||||
|
||||
def process_item(self, instance):
|
||||
if [ef for ef in self.exclude_families
|
||||
if instance.data["family"] in ef]:
|
||||
return
|
||||
|
||||
self.create_destination_template(instance)
|
||||
|
||||
template_data = instance.data["assumedTemplateData"]
|
||||
|
||||
anatomy = instance.context.data['anatomy']
|
||||
# self.log.info(anatomy.anatomy())
|
||||
self.log.info(anatomy.templates)
|
||||
# template = anatomy.publish.path
|
||||
anatomy_filled = anatomy.format(template_data)
|
||||
self.log.info(anatomy_filled)
|
||||
mock_template = anatomy_filled["publish"]["path"]
|
||||
|
||||
# For now assume resources end up in a "resources" folder in the
|
||||
# published folder
|
||||
mock_destination = os.path.join(os.path.dirname(mock_template),
|
||||
"resources")
|
||||
|
||||
# Clean the path
|
||||
mock_destination = os.path.abspath(os.path.normpath(mock_destination))
|
||||
|
||||
# Define resource destination and transfers
|
||||
resources = instance.data.get("resources", list())
|
||||
transfers = instance.data.get("transfers", list())
|
||||
for resource in resources:
|
||||
|
||||
# Add destination to the resource
|
||||
source_filename = os.path.basename(resource["source"])
|
||||
destination = os.path.join(mock_destination, source_filename)
|
||||
|
||||
# Force forward slashes to fix issue with software unable
|
||||
# to work correctly with backslashes in specific scenarios
|
||||
# (e.g. escape characters in PLN-151 V-Ray UDIM)
|
||||
destination = destination.replace("\\", "/")
|
||||
|
||||
resource['destination'] = destination
|
||||
|
||||
# Collect transfers for the individual files of the resource
|
||||
# e.g. all individual files of a cache or UDIM textures.
|
||||
files = resource['files']
|
||||
for fsrc in files:
|
||||
fname = os.path.basename(fsrc)
|
||||
fdest = os.path.join(mock_destination, fname)
|
||||
transfers.append([fsrc, fdest])
|
||||
|
||||
instance.data["resources"] = resources
|
||||
instance.data["transfers"] = transfers
|
||||
|
||||
def create_destination_template(self, instance):
|
||||
"""Create a filepath based on the current data available
|
||||
|
||||
Example template:
|
||||
{root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/
|
||||
|
|
@ -84,5 +140,5 @@ class CollectAssumedDestination(pyblish.api.InstancePlugin):
|
|||
|
||||
# We take the parent folder of representation 'filepath'
|
||||
instance.data["assumedDestination"] = os.path.dirname(
|
||||
(anatomy.format(template_data)).publish.path
|
||||
(anatomy.format(template_data))["publish"]["path"]
|
||||
)
|
||||
|
|
|
|||
14
pype/plugins/global/publish/collect_presets.py
Normal file
14
pype/plugins/global/publish/collect_presets.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from pyblish import api
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class CollectPresets(api.ContextPlugin):
|
||||
"""Collect Presets."""
|
||||
|
||||
order = api.CollectorOrder
|
||||
label = "Collect Presets"
|
||||
|
||||
def process(self, context):
|
||||
context.data["presets"] = config.get_presets()
|
||||
self.log.info(context.data["presets"])
|
||||
return
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
import os
|
||||
import pype.api as pype
|
||||
|
||||
|
||||
class CollectSceneVersion(pyblish.api.ContextPlugin):
|
||||
"""Finds version in the filename or passes the one found in the context
|
||||
Arguments:
|
||||
|
|
@ -16,8 +16,10 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
|
|||
|
||||
filename = os.path.basename(context.data.get('currentFile'))
|
||||
|
||||
rootVersion = pype.get_version_from_path(filename)
|
||||
if '<shell>' in filename:
|
||||
return
|
||||
|
||||
rootVersion = pype.get_version_from_path(filename)
|
||||
context.data['version'] = rootVersion
|
||||
|
||||
self.log.info('Scene Version: %s' % context.data('version'))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
import pype.api as pype
|
||||
from pypeapp import Anatomy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
|
@ -11,6 +12,6 @@ class CollectTemplates(pyblish.api.ContextPlugin):
|
|||
label = "Collect Templates"
|
||||
|
||||
def process(self, context):
|
||||
pype.load_data_from_templates()
|
||||
context.data['anatomy'] = pype.Anatomy
|
||||
# pype.load_data_from_templates()
|
||||
context.data['anatomy'] = Anatomy()
|
||||
self.log.info("Anatomy templates collected...")
|
||||
|
|
|
|||
|
|
@ -210,10 +210,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
|
|||
|
||||
src = os.path.join(stagingdir, fname)
|
||||
anatomy_filled = anatomy.format(template_data)
|
||||
dst = anatomy_filled.publish.path
|
||||
dst = anatomy_filled["publish"]["path"]
|
||||
|
||||
instance.data["transfers"].append([src, dst])
|
||||
template = anatomy.publish.path
|
||||
template = anatomy.templates["publish"]["path"]
|
||||
|
||||
else:
|
||||
# Single file
|
||||
|
|
@ -234,10 +234,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
|
|||
|
||||
src = os.path.join(stagingdir, fname)
|
||||
anatomy_filled = anatomy.format(template_data)
|
||||
dst = anatomy_filled.publish.path
|
||||
dst = anatomy_filled["publish"]["path"]
|
||||
|
||||
instance.data["transfers"].append([src, dst])
|
||||
template = anatomy.publish.path
|
||||
template = anatomy.templates["publish"]["path"]
|
||||
|
||||
representation = {
|
||||
"schema": "pype:representation-2.0",
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin):
|
|||
template_data["frame"] = src_collection.format(
|
||||
"{padding}") % i
|
||||
anatomy_filled = anatomy.format(template_data)
|
||||
test_dest_files.append(anatomy_filled.render.path)
|
||||
test_dest_files.append(anatomy_filled["render"]["path"])
|
||||
|
||||
dst_collections, remainder = clique.assemble(test_dest_files)
|
||||
dst_collection = dst_collections[0]
|
||||
|
|
@ -223,7 +223,6 @@ class IntegrateFrames(pyblish.api.InstancePlugin):
|
|||
#
|
||||
|
||||
template_data.pop("frame", None)
|
||||
anatomy.pop("frame", None)
|
||||
|
||||
fname = files
|
||||
|
||||
|
|
@ -239,15 +238,21 @@ class IntegrateFrames(pyblish.api.InstancePlugin):
|
|||
src = os.path.join(stagingdir, fname)
|
||||
|
||||
anatomy_filled = anatomy.format(template_data)
|
||||
dst = anatomy_filled.render.path
|
||||
dst = anatomy_filled["render"]["path"]
|
||||
|
||||
instance.data["transfers"].append([src, dst])
|
||||
|
||||
template_data["frame"] = "#" * anatomy.render.padding
|
||||
if ext[1:] not in ["jpeg", "jpg", "mov", "mp4", "wav"]:
|
||||
template_data["frame"] = "#" * int(anatomy_filled["render"]["padding"])
|
||||
|
||||
anatomy_filled = anatomy.format(template_data)
|
||||
path_to_save = anatomy_filled.render.path
|
||||
template = anatomy.render.fullpath
|
||||
self.log.debug('ext[1:]: {}'.format(ext[1:]))
|
||||
path_to_save = anatomy_filled["render"]["path"]
|
||||
template = anatomy.templates["render"]["path"]
|
||||
|
||||
self.log.debug("path_to_save: {}".format(path_to_save))
|
||||
|
||||
|
||||
|
||||
|
||||
representation = {
|
||||
"schema": "pype:representation-2.0",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import pyblish.api
|
||||
from app.api import (
|
||||
Templates
|
||||
)
|
||||
import os
|
||||
|
||||
class ValidateTemplates(pyblish.api.ContextPlugin):
|
||||
"""Check if all templates were filed"""
|
||||
|
|
@ -14,27 +12,30 @@ class ValidateTemplates(pyblish.api.ContextPlugin):
|
|||
|
||||
anatomy = context.data["anatomy"]
|
||||
if not anatomy:
|
||||
raise RuntimeError("Did not find templates")
|
||||
raise RuntimeError("Did not find anatomy")
|
||||
else:
|
||||
data = { "project": {"name": "D001_projectsx",
|
||||
data = {
|
||||
"root": os.environ["PYPE_STUDIO_PROJECTS_PATH"],
|
||||
"project": {"name": "D001_projectsx",
|
||||
"code": "prjX"},
|
||||
"representation": "exr",
|
||||
"ext": "exr",
|
||||
"version": 3,
|
||||
"task": "animation",
|
||||
"asset": "sh001",
|
||||
"hierarchy": "ep101/sq01/sh010"}
|
||||
|
||||
|
||||
anatomy = context.data["anatomy"].format(data)
|
||||
self.log.info(anatomy.work.path)
|
||||
anatomy_filled = anatomy.format(data)
|
||||
self.log.info(anatomy_filled)
|
||||
|
||||
data = { "project": {"name": "D001_projectsy",
|
||||
data = {"root": os.environ["PYPE_STUDIO_PROJECTS_PATH"],
|
||||
"project": {"name": "D001_projectsy",
|
||||
"code": "prjY"},
|
||||
"representation": "abc",
|
||||
"ext": "abc",
|
||||
"version": 1,
|
||||
"task": "lookdev",
|
||||
"asset": "bob",
|
||||
"hierarchy": "ep101/sq01/bob"}
|
||||
|
||||
anatomy = context.data["anatomy"].format(data)
|
||||
self.log.info(anatomy.work.file)
|
||||
anatomy_filled = context.data["anatomy"].format(data)
|
||||
self.log.info(anatomy_filled["work"]["folder"])
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import pype.api as pype
|
|||
|
||||
from pype.api import Logger
|
||||
|
||||
log = Logger.getLogger(__name__, "aport")
|
||||
log = Logger().get_logger(__name__, "aport")
|
||||
|
||||
|
||||
class Aport(api.Action):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
import os
|
||||
import sys
|
||||
import acre
|
||||
|
||||
from avalon import api, lib
|
||||
from pype.tools import assetcreator
|
||||
|
||||
from pype.api import Logger
|
||||
|
||||
log = Logger.getLogger(__name__, "asset_creator")
|
||||
log = Logger().get_logger(__name__, "asset_creator")
|
||||
|
||||
|
||||
class AssetCreator(api.Action):
|
||||
|
|
@ -19,9 +14,23 @@ class AssetCreator(api.Action):
|
|||
|
||||
def is_compatible(self, session):
|
||||
"""Return whether the action is compatible with the session"""
|
||||
if "AVALON_PROJECT" in session:
|
||||
return True
|
||||
return False
|
||||
compatible = True
|
||||
|
||||
# Check required modules.
|
||||
module_names = [
|
||||
"ftrack_api", "ftrack_api_old", "pype.tools.assetcreator"
|
||||
]
|
||||
for name in module_names:
|
||||
try:
|
||||
__import__(name)
|
||||
except ImportError:
|
||||
compatible = False
|
||||
|
||||
# Check session environment.
|
||||
if "AVALON_PROJECT" not in session:
|
||||
compatible = False
|
||||
|
||||
return compatible
|
||||
|
||||
def process(self, session, **kwargs):
|
||||
asset = ''
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pype.maya.plugin
|
||||
import os
|
||||
import json
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class AbcLoader(pype.maya.plugin.ReferenceLoader):
|
||||
|
|
@ -36,14 +36,8 @@ class AbcLoader(pype.maya.plugin.ReferenceLoader):
|
|||
cmds.makeIdentity(groupName, apply=False, rotate=True,
|
||||
translate=True, scale=True)
|
||||
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
cmds.setAttr(groupName + ".useOutlinerColor", 1)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from avalon import api
|
|||
import pype.maya.plugin
|
||||
import os
|
||||
import pymel.core as pm
|
||||
import json
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class AssProxyLoader(pype.maya.plugin.ReferenceLoader):
|
||||
|
|
@ -50,13 +50,8 @@ class AssProxyLoader(pype.maya.plugin.ReferenceLoader):
|
|||
proxyShape.dso.set(path)
|
||||
proxyShape.aiOverrideShaders.set(0)
|
||||
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
@ -165,13 +160,8 @@ class AssStandinLoader(api.Loader):
|
|||
label = "{}:{}".format(namespace, name)
|
||||
root = pm.group(name=label, empty=True)
|
||||
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get('ass')
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pype.maya.plugin
|
||||
import os
|
||||
import json
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class CameraLoader(pype.maya.plugin.ReferenceLoader):
|
||||
|
|
@ -35,13 +35,8 @@ class CameraLoader(pype.maya.plugin.ReferenceLoader):
|
|||
|
||||
cameras = cmds.ls(nodes, type="camera")
|
||||
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pype.maya.plugin
|
||||
import os
|
||||
import json
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class FBXLoader(pype.maya.plugin.ReferenceLoader):
|
||||
|
|
@ -36,13 +36,9 @@ class FBXLoader(pype.maya.plugin.ReferenceLoader):
|
|||
groupName="{}:{}".format(namespace, name))
|
||||
|
||||
groupName = "{}:{}".format(namespace, name)
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pype.maya.plugin
|
||||
import json
|
||||
from pypeapp import config
|
||||
import os
|
||||
|
||||
|
||||
|
|
@ -36,13 +36,9 @@ class MayaAsciiLoader(pype.maya.plugin.ReferenceLoader):
|
|||
|
||||
self[:] = nodes
|
||||
groupName = "{}:{}".format(namespace, name)
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from avalon import api
|
||||
import pype.maya.plugin
|
||||
import json
|
||||
import os
|
||||
from pypeapp import config
|
||||
reload(config)
|
||||
|
||||
|
||||
class ModelLoader(pype.maya.plugin.ReferenceLoader):
|
||||
|
|
@ -21,18 +22,6 @@ class ModelLoader(pype.maya.plugin.ReferenceLoader):
|
|||
import maya.cmds as cmds
|
||||
from avalon import maya
|
||||
|
||||
try:
|
||||
family = context["representation"]["context"]["family"]
|
||||
except ValueError:
|
||||
family = "model"
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
with maya.maintained_selection():
|
||||
|
||||
groupName = "{}:{}".format(namespace, name)
|
||||
|
|
@ -46,7 +35,9 @@ class ModelLoader(pype.maya.plugin.ReferenceLoader):
|
|||
cmds.makeIdentity(groupName, apply=False, rotate=True,
|
||||
translate=True, scale=True)
|
||||
|
||||
c = colors.get(family)
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
c = colors.get('model')
|
||||
if c is not None:
|
||||
cmds.setAttr(groupName + ".useOutlinerColor", 1)
|
||||
cmds.setAttr(groupName + ".outlinerColor",
|
||||
|
|
@ -89,14 +80,9 @@ class GpuCacheLoader(api.Loader):
|
|||
# Root group
|
||||
label = "{}:{}".format(namespace, name)
|
||||
root = cmds.group(name=label, empty=True)
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
c = colors.get('model')
|
||||
if c is not None:
|
||||
cmds.setAttr(root + ".useOutlinerColor", 1)
|
||||
|
|
@ -196,14 +182,8 @@ class AbcModelLoader(pype.maya.plugin.ReferenceLoader):
|
|||
cmds.makeIdentity(groupName, apply=False, rotate=True,
|
||||
translate=True, scale=True)
|
||||
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
c = colors.get('model')
|
||||
if c is not None:
|
||||
cmds.setAttr(groupName + ".useOutlinerColor", 1)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from maya import cmds
|
|||
import pype.maya.plugin
|
||||
from avalon import api, maya
|
||||
import os
|
||||
import json
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class RigLoader(pype.maya.plugin.ReferenceLoader):
|
||||
|
|
@ -39,13 +39,8 @@ class RigLoader(pype.maya.plugin.ReferenceLoader):
|
|||
cmds.makeIdentity(groupName, apply=False, rotate=True,
|
||||
translate=True, scale=True)
|
||||
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from avalon import api
|
||||
import os
|
||||
import json
|
||||
|
||||
from pypeapp import config
|
||||
|
||||
class LoadVDBtoRedShift(api.Loader):
|
||||
"""Load OpenVDB in a Redshift Volume Shape"""
|
||||
|
|
@ -55,13 +54,9 @@ class LoadVDBtoRedShift(api.Loader):
|
|||
# Root group
|
||||
label = "{}:{}".format(namespace, name)
|
||||
root = cmds.group(name=label, empty=True)
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from avalon import api
|
||||
import json
|
||||
from pypeapp import config
|
||||
import os
|
||||
|
||||
|
||||
|
|
@ -47,13 +47,9 @@ class LoadVDBtoVRay(api.Loader):
|
|||
# Root group
|
||||
label = "{}:{}".format(namespace, name)
|
||||
root = cmds.group(name=label, empty=True)
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from avalon.maya import lib
|
||||
from avalon import api
|
||||
import json
|
||||
from pypeapp import config
|
||||
import os
|
||||
import maya.cmds as cmds
|
||||
|
||||
|
|
@ -26,14 +26,6 @@ class VRayProxyLoader(api.Loader):
|
|||
except ValueError:
|
||||
family = "vrayproxy"
|
||||
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
asset_name = context['asset']["name"]
|
||||
namespace = namespace or lib.unique_namespace(
|
||||
asset_name + "_",
|
||||
|
|
@ -54,6 +46,9 @@ class VRayProxyLoader(api.Loader):
|
|||
if not nodes:
|
||||
return
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
cmds.setAttr("{0}_{1}.useOutlinerColor".format(name, "GRP"), 1)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from maya import cmds
|
|||
from avalon import api
|
||||
from avalon.maya import lib as avalon_lib, pipeline
|
||||
from pype.maya import lib
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class YetiCacheLoader(api.Loader):
|
||||
|
|
@ -54,13 +55,9 @@ class YetiCacheLoader(api.Loader):
|
|||
|
||||
group_name = "{}:{}".format(namespace, name)
|
||||
group_node = cmds.group(nodes, name=group_name)
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pype.maya.plugin
|
||||
import os
|
||||
import json
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
class YetiRigLoader(pype.maya.plugin.ReferenceLoader):
|
||||
|
|
@ -27,13 +27,9 @@ class YetiRigLoader(pype.maya.plugin.ReferenceLoader):
|
|||
groupName="{}:{}".format(namespace, name))
|
||||
|
||||
groupName = "{}:{}".format(namespace, name)
|
||||
preset_file = os.path.join(
|
||||
os.environ.get('PYPE_STUDIO_TEMPLATES'),
|
||||
'presets', 'tools',
|
||||
'family_colors.json'
|
||||
)
|
||||
with open(preset_file, 'r') as cfile:
|
||||
colors = json.load(cfile)
|
||||
|
||||
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
|
||||
colors = presets['plugins']['maya']['load']['colors']
|
||||
|
||||
c = colors.get('yetiRig')
|
||||
if c is not None:
|
||||
|
|
|
|||
|
|
@ -21,11 +21,14 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin):
|
|||
# Get render globals node
|
||||
try:
|
||||
render_globals = cmds.ls("renderglobalsMain")[0]
|
||||
for instance in context:
|
||||
self.log.debug(instance.name)
|
||||
if instance.data['family'] == 'workfile':
|
||||
instance.data['publish'] = True
|
||||
except IndexError:
|
||||
self.log.info("Skipping renderlayer collection, no "
|
||||
"renderGlobalsDefault found..")
|
||||
return
|
||||
|
||||
# Get all valid renderlayers
|
||||
# This is how Maya populates the renderlayer display
|
||||
rlm_attribute = "renderLayerManager.renderLayerId"
|
||||
|
|
@ -51,7 +54,7 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin):
|
|||
continue
|
||||
|
||||
if layer.endswith("defaultRenderLayer"):
|
||||
layername = "masterLayer"
|
||||
continue
|
||||
else:
|
||||
# Remove Maya render setup prefix `rs_`
|
||||
layername = layer.split("rs_", 1)[-1]
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin):
|
|||
clean_path = clean_path.replace('python2', 'python3')
|
||||
clean_path = clean_path.replace(
|
||||
os.path.normpath(environment['PYPE_STUDIO_CORE_MOUNT']),
|
||||
os.path.normpath(environment['PYPE_STUDIO_CORE']))
|
||||
os.path.normpath(environment['PYPE_STUDIO_CORE_PATH']))
|
||||
clean_environment[key] = clean_path
|
||||
|
||||
environment = clean_environment
|
||||
|
|
|
|||
24
pype/plugins/nuke/_publish_unused/test_instances.py
Normal file
24
pype/plugins/nuke/_publish_unused/test_instances.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import pyblish.api
|
||||
|
||||
|
||||
class IncrementTestPlugin(pyblish.api.ContextPlugin):
|
||||
"""Increment current script version."""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.5
|
||||
label = "Test Plugin"
|
||||
hosts = ['nuke']
|
||||
|
||||
def process(self, context):
|
||||
instances = context[:]
|
||||
|
||||
prerender_check = list()
|
||||
families_check = list()
|
||||
for instance in instances:
|
||||
if ("prerender" in str(instance)):
|
||||
prerender_check.append(instance)
|
||||
if instance.data.get("families", None):
|
||||
families_check.append(True)
|
||||
|
||||
if len(prerender_check) != len(families_check):
|
||||
self.log.info(prerender_check)
|
||||
self.log.info(families_check)
|
||||
|
|
@ -6,7 +6,7 @@ from pype import api as pype
|
|||
import nuke
|
||||
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "nuke")
|
||||
log = pype.Logger().get_logger(__name__, "nuke")
|
||||
|
||||
|
||||
class CrateRead(avalon.nuke.Creator):
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ from pype.nuke import (
|
|||
create_write_node
|
||||
)
|
||||
from pype import api as pype
|
||||
# from pypeapp import Logger
|
||||
|
||||
import nuke
|
||||
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "nuke")
|
||||
log = pype.Logger().get_logger(__name__, "nuke")
|
||||
|
||||
|
||||
def subset_to_families(subset, family, families):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
from avalon import api
|
||||
from pype.api import Logger
|
||||
|
||||
log = Logger.getLogger(__name__, "nuke")
|
||||
log = Logger().get_logger(__name__, "nuke")
|
||||
|
||||
|
||||
class SetFrameRangeLoader(api.Loader):
|
||||
|
|
|
|||
170
pype/plugins/nuke/load/load_script_precomp.py
Normal file
170
pype/plugins/nuke/load/load_script_precomp.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
from avalon import api, style, io
|
||||
from pype.nuke.lib import get_avalon_knob_data
|
||||
import nuke
|
||||
import os
|
||||
from pype.api import Logger
|
||||
log = Logger().get_logger(__name__, "nuke")
|
||||
|
||||
|
||||
|
||||
class LinkAsGroup(api.Loader):
|
||||
"""Copy the published file to be pasted at the desired location"""
|
||||
|
||||
representations = ["nk"]
|
||||
families = ["*"]
|
||||
|
||||
label = "Load Precomp"
|
||||
order = 10
|
||||
icon = "file"
|
||||
color = style.colors.dark
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from avalon.nuke import containerise
|
||||
# for k, v in context.items():
|
||||
# log.info("key: `{}`, value: {}\n".format(k, v))
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("startFrame", None)
|
||||
last = version_data.get("endFrame", None)
|
||||
|
||||
# Fallback to asset name when namespace is None
|
||||
if namespace is None:
|
||||
namespace = context['asset']['name']
|
||||
|
||||
file = self.fname.replace("\\", "/")
|
||||
self.log.info("file: {}\n".format(self.fname))
|
||||
|
||||
precomp_name = context["representation"]["context"]["subset"]
|
||||
|
||||
# Set global in point to start frame (if in version.data)
|
||||
start = context["version"]["data"].get("startFrame", None)
|
||||
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["startFrame", "endFrame", "handles",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {
|
||||
"start_frame": start,
|
||||
"fstart": first,
|
||||
"fend": last,
|
||||
"version": vname
|
||||
}
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: context["version"]['data'][k]})
|
||||
data_imprint.update({"objectName": precomp_name})
|
||||
|
||||
# group context is set to precomp, so back up one level.
|
||||
nuke.endGroup()
|
||||
|
||||
# P = nuke.nodes.LiveGroup("file {}".format(file))
|
||||
P = nuke.createNode(
|
||||
"Precomp",
|
||||
"file {}".format(file))
|
||||
|
||||
# Set colorspace defined in version data
|
||||
colorspace = context["version"]["data"].get("colorspace", None)
|
||||
self.log.info("colorspace: {}\n".format(colorspace))
|
||||
|
||||
|
||||
# ['version', 'file', 'reading', 'output', 'useOutput']
|
||||
|
||||
P["name"].setValue("{}_{}".format(name, namespace))
|
||||
P["useOutput"].setValue(True)
|
||||
|
||||
with P:
|
||||
# iterate trough all nodes in group node and find pype writes
|
||||
writes = [n.name() for n in nuke.allNodes()
|
||||
if n.Class() == "Write"
|
||||
if get_avalon_knob_data(n)]
|
||||
|
||||
# create panel for selecting output
|
||||
panel_choices = " ".join(writes)
|
||||
panel_label = "Select write node for output"
|
||||
p = nuke.Panel("Select Write Node")
|
||||
p.addEnumerationPulldown(
|
||||
panel_label, panel_choices)
|
||||
p.show()
|
||||
P["output"].setValue(p.value(panel_label))
|
||||
|
||||
P["tile_color"].setValue(0xff0ff0ff)
|
||||
|
||||
return containerise(
|
||||
node=P,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
|
||||
root = api.get_representation_path(representation).replace("\\","/")
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
updated_dict = {}
|
||||
updated_dict.update({
|
||||
"representation": str(representation["_id"]),
|
||||
"endFrame": version["data"].get("endFrame"),
|
||||
"version": version.get("name"),
|
||||
"colorspace": version["data"].get("colorspace"),
|
||||
"source": version["data"].get("source"),
|
||||
"handles": version["data"].get("handles"),
|
||||
"fps": version["data"].get("fps"),
|
||||
"author": version["data"].get("author"),
|
||||
"outputDir": version["data"].get("outputDir"),
|
||||
})
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
node,
|
||||
updated_dict
|
||||
)
|
||||
|
||||
node["file"].setValue(root)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int("0xff0ff0ff", 16))
|
||||
|
||||
log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
@ -8,7 +8,7 @@ import avalon.io as io
|
|||
import nuke
|
||||
|
||||
from pype.api import Logger
|
||||
log = Logger.getLogger(__name__, "nuke")
|
||||
log = Logger().get_logger(__name__, "nuke")
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
|
@ -128,11 +128,15 @@ class LoadSequence(api.Loader):
|
|||
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["startFrame", "endFrame", "handles",
|
||||
"source", "colorspace", "author", "fps"]
|
||||
"source", "colorspace", "author", "fps", "version"]
|
||||
|
||||
data_imprint = {}
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: context["version"]['data'][k]})
|
||||
if k is 'version':
|
||||
data_imprint.update({k: context["version"]['name']})
|
||||
else:
|
||||
data_imprint.update({k: context["version"]['data'][k]})
|
||||
|
||||
data_imprint.update({"objectName": read_name})
|
||||
|
||||
r["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
|
@ -226,6 +230,7 @@ class LoadSequence(api.Loader):
|
|||
node,
|
||||
updated_dict
|
||||
)
|
||||
log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def remove(self, container):
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ class CollectNukeInstances(pyblish.api.ContextPlugin):
|
|||
def process(self, context):
|
||||
asset_data = io.find_one({"type": "asset",
|
||||
"name": api.Session["AVALON_ASSET"]})
|
||||
|
||||
# add handles into context
|
||||
context.data['handles'] = int(asset_data["data"].get("handles", 0))
|
||||
|
||||
self.log.debug("asset_data: {}".format(asset_data["data"]))
|
||||
instances = []
|
||||
# creating instances per write node
|
||||
|
|
@ -51,7 +55,6 @@ class CollectNukeInstances(pyblish.api.ContextPlugin):
|
|||
"family": avalon_knob_data["family"],
|
||||
"avalonKnob": avalon_knob_data,
|
||||
"publish": node.knob('publish').value(),
|
||||
"handles": int(asset_data["data"].get("handles", 0)),
|
||||
"step": 1,
|
||||
"fps": int(nuke.root()['fps'].value())
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pyblish.api
|
|||
import logging
|
||||
from avalon import io, api
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log = logging.get_logger(__name__)
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import os
|
||||
import nuke
|
||||
import pyblish.api
|
||||
import logging
|
||||
import pype.api as pype
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class CollectNukeWrites(pyblish.api.ContextPlugin):
|
||||
|
|
@ -38,10 +35,12 @@ class CollectNukeWrites(pyblish.api.ContextPlugin):
|
|||
output_type = "mov"
|
||||
|
||||
# Get frame range
|
||||
handles = instance.context.data.get('handles', 0)
|
||||
first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
last_frame = int(nuke.root()["last_frame"].getValue())
|
||||
|
||||
if node["use_limit"].getValue():
|
||||
handles = 0
|
||||
first_frame = int(node["first"].getValue())
|
||||
last_frame = int(node["last"].getValue())
|
||||
|
||||
|
|
@ -79,6 +78,7 @@ class CollectNukeWrites(pyblish.api.ContextPlugin):
|
|||
"outputDir": output_dir,
|
||||
"ext": ext,
|
||||
"label": label,
|
||||
"handles": handles,
|
||||
"startFrame": first_frame,
|
||||
"endFrame": last_frame,
|
||||
"outputType": output_type,
|
||||
|
|
|
|||
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