Merge branch '2.0/develop' into develop

This commit is contained in:
Milan Kolar 2019-05-20 18:44:28 +02:00
commit 2460cd1fdc
236 changed files with 13976 additions and 1252 deletions

View file

@ -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.

View file

@ -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__)

View 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"
]

View file

@ -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
)

View file

@ -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

View file

@ -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)

View file

@ -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("\\", "/")

View file

@ -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):

View file

@ -0,0 +1,5 @@
from .avalon_app import AvalonApps
def tray_init(tray_widget, main_widget):
return AvalonApps(main_widget, tray_widget)

View 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
)

View file

@ -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:

View file

@ -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()

View file

@ -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()

View file

@ -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

View file

@ -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):

View file

@ -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)

View file

@ -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)

View file

@ -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))

View file

@ -1,7 +1,7 @@
import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -2,7 +2,7 @@ import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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():

View file

@ -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',

View file

@ -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()

View file

@ -1,7 +1,7 @@
import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -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):

View file

@ -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

View file

@ -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):

View file

@ -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")

View file

@ -1,7 +1,7 @@
import sys
import argparse
import logging
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -1,4 +1,4 @@
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction

View file

@ -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']

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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

View file

@ -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):

View file

@ -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

View file

@ -1,4 +1,4 @@
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseEvent
import operator

View file

@ -1,4 +1,4 @@
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseEvent

View file

@ -1,4 +1,4 @@
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseEvent, lib

View file

@ -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

View file

@ -1,4 +1,4 @@
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseEvent

View file

@ -1,4 +1,4 @@
import ftrack_api
from pype.vendor import ftrack_api
from pype.ftrack import BaseEvent

View 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)
)

View file

@ -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()

View file

@ -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:

View file

@ -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']

View file

@ -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

View file

@ -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():

View file

@ -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 {

View file

@ -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',

View file

@ -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)

View file

@ -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):

View file

@ -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

View file

@ -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()

View file

@ -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 (

View file

@ -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)

View file

@ -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
View 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
View 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
View 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)

View file

@ -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)

View file

@ -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".

View 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)

View file

@ -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)

View file

@ -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"]
)

View 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

View file

@ -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'))

View file

@ -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...")

View file

@ -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",

View file

@ -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",

View file

@ -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"])

View file

@ -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):

View file

@ -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 = ''

View file

@ -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)

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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)

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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)

View file

@ -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:

View file

@ -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:

View file

@ -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]

View file

@ -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

View 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)

View file

@ -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):

View file

@ -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):

View file

@ -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):

View 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)

View file

@ -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):

View file

@ -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())

View file

@ -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

View file

@ -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