Merge branch 'develop' into bugfix/nuke_reset_resolution

# Conflicts:
#	pype/nuke/lib.py
This commit is contained in:
Jakub Jezek 2019-08-29 12:25:40 +02:00
commit c51ea9049a
88 changed files with 1896 additions and 1027 deletions

54
changelog.md Normal file
View file

@ -0,0 +1,54 @@
# Pype changelog #
Welcome to pype changelog
## 2.1 ##
A large cleanup release. Most of the change are under the hood.
**new**:
- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts
- _(pype)_ Added configurable option to add burnins to any generated quicktimes
- _(ftrack)_ Action that identifies what machines pype is running on.
- _(system)_ unify subprocess calls
- _(maya)_ add audio to review quicktimes
- _(nuke)_ add crop before write node to prevent overscan problems in ffmpeg
- **Nuke Studio** publishing and workfiles support
- **Muster** render manager support
- _(nuke)_ Framerange, FPS and Resolution are set automatically at startup
- _(maya)_ Ability to load published sequences as image planes
- _(system)_ Ftrack event that sets asset folder permissions based on task assignees in ftrack.
- _(maya)_ Pyblish plugin that allow validation of maya attributes
- _(system)_ added better startup logging to tray debug, including basic connection information
- _(avalon)_ option to group published subsets to groups in the loader
- _(avalon)_ loader family filters are working now
**changed**:
- change multiple key attributes to unify their behaviour across the pipeline
- `frameRate` to `fps`
- `startFrame` to `frameStart`
- `endFrame` to `frameEnd`
- `fstart` to `frameStart`
- `fend` to `frameEnd`
- `handle_start` to `handleStart`
- `handle_end` to `handleEnd`
- `resolution_width` to `resolutionWidth`
- `resolution_height` to `resolutionHeight`
- `pixel_aspect` to `pixelAspect`
- _(nuke)_ write nodes are now created inside group with only some attributes editable by the artist
- rendered frames are now deleted from temporary location after their publishing is finished.
- _(ftrack)_ RV action can now be launched from any entity
- after publishing only refresh button is now available in pyblish UI
- added context instance pyblish-lite so that artist knows if context plugin fails
- _(avalon)_ allow opening selected files using enter key
- _(avalon)_ core updated to v5.2.9 with our forked changes on top
**fix**:
- faster hierarchy retrieval from db
- _(nuke)_ A lot of stability enhancements
- _(nuke studio)_ A lot of stability enhancements
- _(nuke)_ now only renders a single write node on farm
- _(ftrack)_ pype would crash when launcher project level task
- work directory was sometimes not being created correctly
- major pype.lib cleanup. Removing of unused functions, merging those that were doing the same and general house cleaning.
- _(avalon)_ subsets in maya 2019 weren't behaving correctly in the outliner

View file

@ -7,6 +7,8 @@ from .lib import filter_pyblish_plugins
import logging
log = logging.getLogger(__name__)
__version__ = "2.1.0"
PACKAGE_DIR = os.path.dirname(__file__)
PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")

View file

@ -7,3 +7,6 @@ __all__ = [
'ClockifySettings',
'ClockifyModule'
]
def tray_init(tray_widget, main_widget):
return ClockifyModule(main_widget, tray_widget)

View file

@ -1,3 +1,4 @@
import os
import threading
from pypeapp import style
from Qt import QtWidgets
@ -20,9 +21,10 @@ class ClockifyModule:
self.bool_workspace_set = False
self.bool_timer_run = False
def start_up(self):
self.clockapi.set_master(self)
self.bool_api_key_set = self.clockapi.set_api()
def tray_start(self):
if self.bool_api_key_set is False:
self.show_settings()
return
@ -41,7 +43,7 @@ class ClockifyModule:
os.path.dirname(__file__),
'ftrack_actions'
])
current = os.environ('FTRACK_ACTIONS_PATH', '')
current = os.environ.get('FTRACK_ACTIONS_PATH', '')
if current:
current += os.pathsep
os.environ['FTRACK_ACTIONS_PATH'] = current + actions_path
@ -57,6 +59,24 @@ class ClockifyModule:
current += os.pathsep
os.environ['AVALON_ACTIONS'] = current + actions_path
if 'TimersManager' in modules:
self.timer_manager = modules['TimersManager']
self.timer_manager.add_module(self)
def start_timer_manager(self, data):
self.start_timer(data)
def stop_timer_manager(self):
self.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()
def start_timer_check(self):
self.bool_thread_check_running = True
if self.thread_timer_check is None:
@ -75,21 +95,81 @@ class ClockifyModule:
def check_running(self):
import time
while self.bool_thread_check_running is True:
bool_timer_run = False
if self.clockapi.get_in_progress() is not None:
self.bool_timer_run = True
else:
self.bool_timer_run = False
self.set_menu_visibility()
bool_timer_run = True
if self.bool_timer_run != bool_timer_run:
if self.bool_timer_run is True:
self.timer_stopped()
else:
actual_timer = self.clockapi.get_in_progress()
if not actual_timer:
continue
actual_project_id = actual_timer["projectId"]
project = self.clockapi.get_project_by_id(
actual_project_id
)
project_name = project["name"]
actual_timer_hierarchy = actual_timer["description"]
hierarchy_items = actual_timer_hierarchy.split("/")
task_name = hierarchy_items[-1]
hierarchy = hierarchy_items[:-1]
data = {
"task_name": task_name,
"hierarchy": hierarchy,
"project_name": project_name
}
self.timer_started(data)
self.bool_timer_run = bool_timer_run
self.set_menu_visibility()
time.sleep(5)
def stop_timer(self):
self.clockapi.finish_time_entry()
if self.bool_timer_run:
self.timer_stopped()
self.bool_timer_run = False
def start_timer(self, input_data):
actual_timer = self.clockapi.get_in_progress()
actual_timer_hierarchy = None
actual_project_id = None
if actual_timer is not None:
actual_timer_hierarchy = actual_timer.get("description")
actual_project_id = actual_timer.get("projectId")
desc_items = [val for val in input_data.get("hierarchy", [])]
desc_items.append(input_data["task_name"])
description = "/".join(desc_items)
project_id = self.clockapi.get_project_id(input_data["project_name"])
if (
actual_timer is not None and
description == actual_timer_hierarchy and
project_id == actual_project_id
):
return
tag_ids = []
task_tag_id = self.clockapi.get_tag_id(input_data["task_name"])
if task_tag_id is not None:
tag_ids.append(task_tag_id)
self.clockapi.start_time_entry(
description, project_id, tag_ids=tag_ids
)
# Definition of Tray menu
def tray_menu(self, parent):
def tray_menu(self, parent_menu):
# Menu for Tray App
self.menu = QtWidgets.QMenu('Clockify', parent)
self.menu = QtWidgets.QMenu('Clockify', parent_menu)
self.menu.setProperty('submenu', 'on')
self.menu.setStyleSheet(style.load_stylesheet())
@ -109,7 +189,7 @@ class ClockifyModule:
self.set_menu_visibility()
return self.menu
parent_menu.addMenu(self.menu)
def show_settings(self):
self.widget_settings.input_api_key.setText(self.clockapi.get_api_key())

View file

@ -1,4 +1,5 @@
import os
import re
import requests
import json
import datetime
@ -22,6 +23,7 @@ class ClockifyAPI(metaclass=Singleton):
app_dir = os.path.normpath(appdirs.user_data_dir('pype-app', 'pype'))
file_name = 'clockify.json'
fpath = os.path.join(app_dir, file_name)
admin_permission_names = ['WORKSPACE_OWN', 'WORKSPACE_ADMIN']
master_parent = None
workspace_id = None
@ -55,31 +57,41 @@ class ClockifyAPI(metaclass=Singleton):
return False
return True
def validate_workspace_perm(self):
test_project = '__test__'
action_url = 'workspaces/{}/projects/'.format(self.workspace_id)
body = {
"name": test_project, "clientId": "", "isPublic": "false",
"estimate": {"type": "AUTO"},
"color": "#f44336", "billable": "true"
}
response = requests.post(
self.endpoint + action_url,
headers=self.headers, json=body
def validate_workspace_perm(self, workspace_id=None):
user_id = self.get_user_id()
if user_id is None:
return False
if workspace_id is None:
workspace_id = self.workspace_id
action_url = "/workspaces/{}/users/{}/permissions".format(
workspace_id, user_id
)
if response.status_code == 201:
self.delete_project(self.get_project_id(test_project))
return True
else:
projects = self.get_projects()
if test_project in projects:
try:
self.delete_project(self.get_project_id(test_project))
return True
except json.decoder.JSONDecodeError:
return False
response = requests.get(
self.endpoint + action_url,
headers=self.headers
)
user_permissions = response.json()
for perm in user_permissions:
if perm['name'] in self.admin_permission_names:
return True
return False
def get_user_id(self):
action_url = 'v1/user/'
response = requests.get(
self.endpoint + action_url,
headers=self.headers
)
# this regex is neccessary: UNICODE strings are crashing
# during json serialization
id_regex ='\"{1}id\"{1}\:{1}\"{1}\w+\"{1}'
result = re.findall(id_regex, str(response.content))
if len(result) != 1:
# replace with log and better message?
print('User ID was not found (this is a BUG!!!)')
return None
return json.loads('{'+result[0]+'}')['id']
def set_workspace(self, name=None):
if name is None:
name = os.environ.get('CLOCKIFY_WORKSPACE', None)
@ -147,6 +159,19 @@ class ClockifyAPI(metaclass=Singleton):
project["name"]: project["id"] for project in response.json()
}
def get_project_by_id(self, project_id, workspace_id=None):
if workspace_id is None:
workspace_id = self.workspace_id
action_url = 'workspaces/{}/projects/{}/'.format(
workspace_id, project_id
)
response = requests.get(
self.endpoint + action_url,
headers=self.headers
)
return response.json()
def get_tags(self, workspace_id=None):
if workspace_id is None:
workspace_id = self.workspace_id
@ -279,6 +304,9 @@ class ClockifyAPI(metaclass=Singleton):
if workspace_id is None:
workspace_id = self.workspace_id
current = self.get_in_progress(workspace_id)
if current is None:
return
current_id = current["id"]
action_url = 'workspaces/{}/timeEntries/{}'.format(
workspace_id, current_id

View file

@ -14,7 +14,7 @@ class StartClockify(BaseAction):
#: Action identifier.
identifier = 'clockify.start.timer'
#: Action label.
label = 'Start timer'
label = 'Clockify - Start timer'
#: Action description.
description = 'Starts timer on clockify'
#: roles that are allowed to register this action
@ -67,42 +67,3 @@ def register(session, **kw):
return
StartClockify(session).register()
def main(arguments=None):
'''Set up logging and register action.'''
if arguments is None:
arguments = []
parser = argparse.ArgumentParser()
# Allow setting of logging level from arguments.
loggingLevels = {}
for level in (
logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING,
logging.ERROR, logging.CRITICAL
):
loggingLevels[logging.getLevelName(level).lower()] = level
parser.add_argument(
'-v', '--verbosity',
help='Set the logging output verbosity.',
choices=loggingLevels.keys(),
default='info'
)
namespace = parser.parse_args(arguments)
# Set up basic logging
logging.basicConfig(level=loggingLevels[namespace.verbosity])
session = ftrack_api.Session()
register(session)
# Wait for events
logging.info(
'Registered actions and listening for events. Use Ctrl-C to abort.'
)
session.event_hub.wait()
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -4,11 +4,12 @@ import time
from pype.ftrack import AppAction
from avalon import lib
from pypeapp import Logger
from pype.lib import get_all_avalon_projects
log = Logger().get_logger(__name__)
def registerApp(app, session):
def registerApp(app, session, plugins_presets):
name = app['name']
variant = ""
try:
@ -40,14 +41,18 @@ def registerApp(app, session):
# register action
AppAction(
session, label, name, executable, variant,
icon, description, preactions
icon, description, preactions, plugins_presets
).register()
if not variant:
log.info('- Variant is not set')
def register(session):
def register(session, plugins_presets={}):
# WARNING getting projects only helps to check connection to mongo
# - without will `discover` of ftrack apps actions take ages
result = get_all_avalon_projects()
apps = []
launchers_path = os.path.join(os.environ["PYPE_CONFIG"], "launchers")
@ -66,7 +71,7 @@ def register(session):
app_counter = 0
for app in apps:
try:
registerApp(app, session)
registerApp(app, session, plugins_presets)
if app_counter%5 == 0:
time.sleep(0.1)
app_counter += 1

View file

@ -78,7 +78,7 @@ class AssetDelete(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register action. Called when used as an event plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -87,7 +87,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
AssetDelete(session).register()
AssetDelete(session, plugins_presets).register()
def main(arguments=None):

View file

@ -11,13 +11,14 @@ class AttributesRemapper(BaseAction):
#: Action identifier.
identifier = 'attributes.remapper'
#: Action label.
label = 'Attributes Remapper'
label = "Pype Doctor"
variant = '- Attributes Remapper'
#: Action description.
description = 'Remaps attributes in avalon DB'
#: roles that are allowed to register this action
role_list = ["Pypeclub", "Administrator"]
icon = '{}/ftrack/action_icons/AttributesRemapper.svg'.format(
icon = '{}/ftrack/action_icons/PypeDoctor.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
@ -249,7 +250,9 @@ class AttributesRemapper(BaseAction):
if interface_messages:
self.show_interface_from_dict(
event, interface_messages, "Errors during remapping attributes"
messages=interface_messages,
title="Errors during remapping attributes",
event=event
)
return True
@ -274,10 +277,10 @@ class AttributesRemapper(BaseAction):
self.show_interface(event, items, title)
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
AttributesRemapper(session).register()
AttributesRemapper(session, plugins_presets).register()

View file

@ -53,12 +53,12 @@ class ClientReviewSort(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register action. Called when used as an event plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
action_handler = ClientReviewSort(session)
action_handler = ClientReviewSort(session, plugins_presets)
action_handler.register()

View file

@ -65,7 +65,7 @@ class ComponentOpen(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register action. Called when used as an event plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -74,7 +74,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
ComponentOpen(session).register()
ComponentOpen(session, plugins_presets).register()
def main(arguments=None):

View file

@ -110,12 +110,13 @@ class CustomAttributes(BaseAction):
#: Action identifier.
identifier = 'create.update.attributes'
#: Action label.
label = 'Create/Update Avalon Attributes'
label = "Pype Admin"
variant = '- Create/Update Avalon Attributes'
#: Action description.
description = 'Creates Avalon/Mongo ID for double check'
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
icon = '{}/ftrack/action_icons/CustomAttributes.svg'.format(
icon = '{}/ftrack/action_icons/PypeAdmin.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
@ -568,7 +569,7 @@ class CustomAttributes(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -577,7 +578,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
CustomAttributes(session).register()
CustomAttributes(session, plugins_presets).register()
def main(arguments=None):

View file

@ -30,11 +30,13 @@ class CreateFolders(BaseAction):
def discover(self, session, entities, event):
''' Validation '''
not_allowed = ['assetversion']
if len(entities) != 1:
return False
not_allowed = ['assetversion', 'project']
if entities[0].entity_type.lower() in not_allowed:
return False
return True
def interface(self, session, entities, event):
@ -322,13 +324,13 @@ class PartialDict(dict):
return '{'+key+'}'
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
CreateFolders(session).register()
CreateFolders(session, plugins_presets).register()
def main(arguments=None):

View file

@ -13,9 +13,9 @@ class CreateProjectFolders(BaseAction):
'''Edit meta data action.'''
#: Action identifier.
identifier = 'create.project.folders'
identifier = 'create.project.structure'
#: Action label.
label = 'Create Project Folders'
label = 'Create Project Structure'
#: Action description.
description = 'Creates folder structure'
#: roles that are allowed to register this action
@ -31,6 +31,11 @@ class CreateProjectFolders(BaseAction):
def discover(self, session, entities, event):
''' Validation '''
if len(entities) != 1:
return False
if entities[0].entity_type.lower() != "project":
return False
return True
@ -190,13 +195,13 @@ class CreateProjectFolders(BaseAction):
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
CreateProjectFolders(session).register()
CreateProjectFolders(session, plugins_presets).register()
def main(arguments=None):

View file

@ -12,14 +12,15 @@ class CustomAttributeDoctor(BaseAction):
#: Action identifier.
identifier = 'custom.attributes.doctor'
#: Action label.
label = 'Custom Attributes Doctor'
label = "Pype Doctor"
variant = '- Custom Attributes Doctor'
#: Action description.
description = (
'Fix hierarchical custom attributes mainly handles, fstart'
' and fend'
)
icon = '{}/ftrack/action_icons/TestAction.svg'.format(
icon = '{}/ftrack/action_icons/PypeDoctor.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
hierarchical_ca = ['handle_start', 'handle_end', 'fstart', 'fend']
@ -286,13 +287,13 @@ class CustomAttributeDoctor(BaseAction):
return all_roles
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
CustomAttributeDoctor(session).register()
CustomAttributeDoctor(session, plugins_presets).register()
def main(arguments=None):

View file

@ -311,7 +311,7 @@ class DeleteAsset(BaseAction):
return assets
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -320,7 +320,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
DeleteAsset(session).register()
DeleteAsset(session, plugins_presets).register()
def main(arguments=None):

View file

@ -13,12 +13,13 @@ class AssetsRemover(BaseAction):
#: Action identifier.
identifier = 'remove.assets'
#: Action label.
label = 'Delete Assets by Name'
label = "Pype Admin"
variant = '- Delete Assets by Name'
#: Action description.
description = 'Removes assets from Ftrack and Avalon db with all childs'
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
icon = '{}/ftrack/action_icons/AssetsRemover.svg'.format(
icon = '{}/ftrack/action_icons/PypeAdmin.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
#: Db
@ -131,7 +132,7 @@ class AssetsRemover(BaseAction):
return assets
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -140,7 +141,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
AssetsRemover(session).register()
AssetsRemover(session, plugins_presets).register()
def main(arguments=None):

View file

@ -42,7 +42,7 @@ class VersionsCleanup(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register action. Called when used as an event plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -51,7 +51,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
VersionsCleanup(session).register()
VersionsCleanup(session, plugins_presets).register()
def main(arguments=None):

View file

@ -21,9 +21,9 @@ class DJVViewAction(BaseAction):
)
type = 'Application'
def __init__(self, session):
def __init__(self, session, plugins_presets):
'''Expects a ftrack_api.Session instance'''
super().__init__(session)
super().__init__(session, plugins_presets)
self.djv_path = None
self.config_data = config.get_presets()['djv_view']['config']
@ -218,12 +218,12 @@ class DJVViewAction(BaseAction):
return True
def register(session):
def register(session, plugins_presets={}):
"""Register hooks."""
if not isinstance(session, ftrack_api.session.Session):
return
DJVViewAction(session).register()
DJVViewAction(session, plugins_presets).register()
def main(arguments=None):

View file

@ -14,12 +14,13 @@ class JobKiller(BaseAction):
#: Action identifier.
identifier = 'job.killer'
#: Action label.
label = 'Job Killer'
label = "Pype Admin"
variant = '- Job Killer'
#: Action description.
description = 'Killing selected running jobs'
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
icon = '{}/ftrack/action_icons/JobKiller.svg'.format(
icon = '{}/ftrack/action_icons/PypeAdmin.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
@ -117,7 +118,7 @@ class JobKiller(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -126,7 +127,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
JobKiller(session).register()
JobKiller(session, plugins_presets).register()
def main(arguments=None):

View file

@ -112,13 +112,13 @@ class MultipleNotes(BaseAction):
return True
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
MultipleNotes(session).register()
MultipleNotes(session, plugins_presets).register()
def main(arguments=None):

View file

@ -0,0 +1,240 @@
import os
import json
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction
from pypeapp import config
from pype.ftrack.lib import get_avalon_attr
class PrepareProject(BaseAction):
'''Edit meta data action.'''
#: Action identifier.
identifier = 'prepare.project'
#: Action label.
label = 'Prepare Project'
#: Action description.
description = 'Set basic attributes on the project'
#: roles that are allowed to register this action
role_list = ["Pypeclub", "Administrator", "Project manager"]
icon = '{}/ftrack/action_icons/PrepareProject.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
def discover(self, session, entities, event):
''' Validation '''
if len(entities) != 1:
return False
if entities[0].entity_type.lower() != "project":
return False
return True
def interface(self, session, entities, event):
if event['data'].get('values', {}):
return
# Inform user that this may take a while
self.show_message(event, "Preparing data... Please wait", True)
self.log.debug("Loading custom attributes")
cust_attrs, hier_cust_attrs = get_avalon_attr(session, True)
project_defaults = config.get_presets().get("ftrack", {}).get(
"project_defaults", {}
)
self.log.debug("Preparing data which will be shown")
attributes_to_set = {}
for attr in hier_cust_attrs:
key = attr["key"]
attributes_to_set[key] = {
"label": attr["label"],
"object": attr,
"default": project_defaults.get(key)
}
for attr in cust_attrs:
if attr["entity_type"].lower() != "show":
continue
key = attr["key"]
attributes_to_set[key] = {
"label": attr["label"],
"object": attr,
"default": project_defaults.get(key)
}
# Sort by label
attributes_to_set = dict(sorted(
attributes_to_set.items(),
key=lambda x: x[1]["label"]
))
self.log.debug("Preparing interface for keys: \"{}\"".format(
str([key for key in attributes_to_set])
))
title = "Set Attribute values"
items = []
multiselect_enumerators = []
# This item will be last (before enumerators)
# - sets value of auto synchronization
auto_sync_name = "avalon_auto_sync"
auto_sync_item = {
"name": auto_sync_name,
"type": "boolean",
"value": project_defaults.get(auto_sync_name, False),
"label": "AutoSync to Avalon"
}
item_splitter = {'type': 'label', 'value': '---'}
for key, in_data in attributes_to_set.items():
attr = in_data["object"]
# initial item definition
item = {
"name": key,
"label": in_data["label"]
}
# cust attr type - may have different visualization
type_name = attr["type"]["name"].lower()
easy_types = ["text", "boolean", "date", "number"]
easy_type = False
if type_name in easy_types:
easy_type = True
elif type_name == "enumerator":
attr_config = json.loads(attr["config"])
attr_config_data = json.loads(attr_config["data"])
if attr_config["multiSelect"] is True:
multiselect_enumerators.append(item_splitter)
multiselect_enumerators.append({
"type": "label",
"value": in_data["label"]
})
default = in_data["default"]
names = []
for option in sorted(
attr_config_data, key=lambda x: x["menu"]
):
name = option["value"]
new_name = "__{}__{}".format(key, name)
names.append(new_name)
item = {
"name": new_name,
"type": "boolean",
"label": "- {}".format(option["menu"])
}
if default:
if (
isinstance(default, list) or
isinstance(default, tuple)
):
if name in default:
item["value"] = True
else:
if name == default:
item["value"] = True
multiselect_enumerators.append(item)
multiselect_enumerators.append({
"type": "hidden",
"name": "__hidden__{}".format(key),
"value": json.dumps(names)
})
else:
easy_type = True
item["data"] = attr_config_data
else:
self.log.warning((
"Custom attribute \"{}\" has type \"{}\"."
" I don't know how to handle"
).format(key, type_name))
items.append({
"type": "label",
"value": (
"!!! Can't handle Custom attritubte type \"{}\""
" (key: \"{}\")"
).format(type_name, key)
})
if easy_type:
item["type"] = type_name
# default value in interface
default = in_data["default"]
if default is not None:
item["value"] = default
items.append(item)
# Add autosync attribute
items.append(auto_sync_item)
# Add enumerator items at the end
for item in multiselect_enumerators:
items.append(item)
return {
'items': items,
'title': title
}
def launch(self, session, entities, event):
if not event['data'].get('values', {}):
return
in_data = event['data']['values']
# Find hidden items for multiselect enumerators
keys_to_process = []
for key in in_data:
if key.startswith("__hidden__"):
keys_to_process.append(key)
self.log.debug("Preparing data for Multiselect Enumerators")
enumerators = {}
for key in keys_to_process:
new_key = key.replace("__hidden__", "")
enumerator_items = in_data.pop(key)
enumerators[new_key] = json.loads(enumerator_items)
# find values set for multiselect enumerator
for key, enumerator_items in enumerators.items():
in_data[key] = []
name = "__{}__".format(key)
for item in enumerator_items:
value = in_data.pop(item)
if value is True:
new_key = item.replace(name, "")
in_data[key].append(new_key)
self.log.debug("Setting Custom Attribute values:")
entity = entities[0]
for key, value in in_data.items():
entity["custom_attributes"][key] = value
self.log.debug("- Key \"{}\" set to \"{}\"".format(key, value))
session.commit()
return True
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
PrepareProject(session, plugins_presets).register()

View file

@ -23,13 +23,13 @@ class RVAction(BaseAction):
)
type = 'Application'
def __init__(self, session):
def __init__(self, session, plugins_presets):
""" Constructor
:param session: ftrack Session
:type session: :class:`ftrack_api.Session`
"""
super().__init__(session)
super().__init__(session, plugins_presets)
self.rv_path = None
self.config_data = None
@ -326,12 +326,12 @@ class RVAction(BaseAction):
return paths
def register(session):
def register(session, plugins_presets={}):
"""Register hooks."""
if not isinstance(session, ftrack_api.session.Session):
return
RVAction(session).register()
RVAction(session, plugins_presets).register()
def main(arguments=None):

View file

@ -71,7 +71,7 @@ class SetVersion(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register action. Called when used as an event plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -80,7 +80,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
SetVersion(session).register()
SetVersion(session, plugins_presets).register()
def main(arguments=None):

View file

@ -70,10 +70,10 @@ class StartTimer(BaseAction):
self.log.info('Starting Clockify timer for task: ' + task['name'])
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
StartTimer(session).register()
StartTimer(session, plugins_presets).register()

View file

@ -19,11 +19,12 @@ class SyncHierarchicalAttrs(BaseAction):
#: Action identifier.
identifier = 'sync.hierarchical.attrs.local'
#: Action label.
label = 'Sync HierAttrs - Local'
label = "Pype Admin"
variant = '- Sync Hier Attrs (Local)'
#: Action description.
description = 'Synchronize hierarchical attributes'
#: Icon
icon = '{}/ftrack/action_icons/SyncHierarchicalAttrsLocal.svg'.format(
icon = '{}/ftrack/action_icons/PypeAdmin.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
@ -186,7 +187,10 @@ class SyncHierarchicalAttrs(BaseAction):
job['status'] = 'failed'
session.commit()
if self.interface_messages:
self.show_interface_from_dict(self.interface_messages, event)
title = "Errors during SyncHierarchicalAttrs"
self.show_interface_from_dict(
messages=self.interface_messages, title=title, event=event
)
return True
@ -302,13 +306,13 @@ class SyncHierarchicalAttrs(BaseAction):
self.update_hierarchical_attribute(child, key, value)
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
SyncHierarchicalAttrs(session).register()
SyncHierarchicalAttrs(session, plugins_presets).register()
def main(arguments=None):

View file

@ -47,11 +47,12 @@ class SyncToAvalon(BaseAction):
#: Action identifier.
identifier = 'sync.to.avalon.local'
#: Action label.
label = 'SyncToAvalon - Local'
label = "Pype Admin"
variant = '- Sync To Avalon (Local)'
#: Action description.
description = 'Send data from Ftrack to Avalon'
#: Action icon.
icon = '{}/ftrack/action_icons/SyncToAvalon-local.svg'.format(
icon = '{}/ftrack/action_icons/PypeAdmin.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
#: roles that are allowed to register this action
@ -59,7 +60,7 @@ class SyncToAvalon(BaseAction):
#: Action priority
priority = 200
def __init__(self, session):
def __init__(self, session, plugins_presets):
super(SyncToAvalon, self).__init__(session)
# reload utils on initialize (in case of server restart)
@ -212,7 +213,7 @@ class SyncToAvalon(BaseAction):
self.add_childs_to_importable(child)
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -221,7 +222,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
SyncToAvalon(session).register()
SyncToAvalon(session, plugins_presets).register()
def main(arguments=None):

View file

@ -40,13 +40,13 @@ class TestAction(BaseAction):
return True
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
TestAction(session).register()
TestAction(session, plugins_presets).register()
def main(arguments=None):

View file

@ -14,9 +14,11 @@ class ThumbToChildren(BaseAction):
# Action identifier
identifier = 'thumb.to.children'
# Action label
label = 'Thumbnail to Children'
label = 'Thumbnail'
# Action variant
variant = " to Children"
# Action icon
icon = '{}/ftrack/action_icons/thumbToChildren.svg'.format(
icon = '{}/ftrack/action_icons/Thumbnail.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
@ -64,12 +66,12 @@ class ThumbToChildren(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register action. Called when used as an event plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
ThumbToChildren(session).register()
ThumbToChildren(session, plugins_presets).register()
def main(arguments=None):

View file

@ -13,9 +13,11 @@ class ThumbToParent(BaseAction):
# Action identifier
identifier = 'thumb.to.parent'
# Action label
label = 'Thumbnail to Parent'
label = 'Thumbnail'
# Action variant
variant = " to Parent"
# Action icon
icon = '{}/ftrack/action_icons/thumbToParent.svg'.format(
icon = '{}/ftrack/action_icons/Thumbnail.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
@ -86,12 +88,12 @@ class ThumbToParent(BaseAction):
}
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register action. Called when used as an event plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
ThumbToParent(session).register()
ThumbToParent(session, plugins_presets).register()
def main(arguments=None):

View file

@ -45,10 +45,10 @@ class ActionAskWhereIRun(BaseAction):
return True
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
ActionAskWhereIRun(session).register()
ActionAskWhereIRun(session, plugins_presets).register()

View file

@ -77,10 +77,10 @@ class ActionShowWhereIRun(BaseAction):
return True
def register(session, **kw):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
ActionShowWhereIRun(session).register()
ActionShowWhereIRun(session, plugins_presets).register()

View file

@ -20,11 +20,12 @@ class SyncHierarchicalAttrs(BaseAction):
#: Action identifier.
identifier = 'sync.hierarchical.attrs'
#: Action label.
label = 'Sync HierAttrs'
label = "Pype Admin"
variant = '- Sync Hier Attrs (server)'
#: Action description.
description = 'Synchronize hierarchical attributes'
#: Icon
icon = '{}/ftrack/action_icons/SyncHierarchicalAttrs.svg'.format(
icon = '{}/ftrack/action_icons/PypeAdmin.svg'.format(
os.environ.get(
'PYPE_STATICS_SERVER',
'http://localhost:{}'.format(
@ -333,13 +334,13 @@ class SyncHierarchicalAttrs(BaseAction):
self.update_hierarchical_attribute(child, key, value)
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
SyncHierarchicalAttrs(session).register()
SyncHierarchicalAttrs(session, plugins_presets).register()
def main(arguments=None):

View file

@ -48,11 +48,12 @@ class Sync_To_Avalon(BaseAction):
#: Action identifier.
identifier = 'sync.to.avalon'
#: Action label.
label = 'SyncToAvalon'
label = "Pype Admin"
variant = "- Sync To Avalon (Server)"
#: Action description.
description = 'Send data from Ftrack to Avalon'
#: Action icon.
icon = '{}/ftrack/action_icons/SyncToAvalon.svg'.format(
icon = '{}/ftrack/action_icons/PypeAdmin.svg'.format(
os.environ.get(
'PYPE_STATICS_SERVER',
'http://localhost:{}'.format(
@ -242,7 +243,7 @@ class Sync_To_Avalon(BaseAction):
self.add_childs_to_importable(child)
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
# Validate that session is an instance of ftrack_api.Session. If not,
@ -251,7 +252,7 @@ def register(session, **kw):
if not isinstance(session, ftrack_api.session.Session):
return
Sync_To_Avalon(session).register()
SyncToAvalon(session, plugins_presets).register()
def main(arguments=None):

View file

@ -51,9 +51,9 @@ class DelAvalonIdFromNew(BaseEvent):
continue
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
DelAvalonIdFromNew(session).register()
DelAvalonIdFromNew(session, plugins_presets).register()

View file

@ -86,9 +86,9 @@ class NextTaskUpdate(BaseEvent):
session.rollback()
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
NextTaskUpdate(session).register()
NextTaskUpdate(session, plugins_presets).register()

View file

@ -34,9 +34,9 @@ class Radio_buttons(BaseEvent):
session.commit()
def register(session):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
Radio_buttons(session).register()
Radio_buttons(session, plugins_presets).register()

View file

@ -115,9 +115,9 @@ class SyncHierarchicalAttrs(BaseEvent):
self.update_hierarchical_attribute(child, key, value)
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
SyncHierarchicalAttrs(session).register()
SyncHierarchicalAttrs(session, plugins_presets).register()

View file

@ -118,10 +118,10 @@ class Sync_to_Avalon(BaseEvent):
return
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
Sync_to_Avalon(session).register()
Sync_to_Avalon(session, plugins_presets).register()

View file

@ -20,9 +20,9 @@ class Test_Event(BaseEvent):
return True
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
Test_Event(session).register()
Test_Event(session, plugins_presets).register()

View file

@ -45,9 +45,9 @@ class ThumbnailEvents(BaseEvent):
pass
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
ThumbnailEvents(session).register()
ThumbnailEvents(session, plugins_presets).register()

View file

@ -229,11 +229,11 @@ class UserAssigmentEvent(BaseEvent):
return True
def register(session, **kw):
def register(session, plugins_presets):
"""
Register plugin. Called when used as an plugin.
"""
if not isinstance(session, ftrack_api.session.Session):
return
UserAssigmentEvent(session).register()
UserAssigmentEvent(session, plugins_presets).register()

View file

@ -69,9 +69,9 @@ class VersionToTaskStatus(BaseEvent):
path, task_status['name']))
def register(session, **kw):
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''
if not isinstance(session, ftrack_api.session.Session):
return
VersionToTaskStatus(session).register()
VersionToTaskStatus(session, plugins_presets).register()

View file

@ -5,7 +5,9 @@ import importlib
from pype.vendor import ftrack_api
import time
import logging
from pypeapp import Logger
import inspect
from pypeapp import Logger, config
log = Logger().get_logger(__name__)
@ -27,8 +29,8 @@ PYTHONPATH # Path to ftrack_api and paths to all modules used in actions
"""
class FtrackServer():
def __init__(self, type='action'):
class FtrackServer:
def __init__(self, server_type='action'):
"""
- 'type' is by default set to 'action' - Runs Action server
- enter 'event' for Event server
@ -43,21 +45,12 @@ class FtrackServer():
ftrack_log = logging.getLogger("ftrack_api")
ftrack_log.setLevel(logging.WARNING)
self.type = type
self.actionsAvailable = True
self.eventsAvailable = True
# Separate all paths
if "FTRACK_ACTIONS_PATH" in os.environ:
all_action_paths = os.environ["FTRACK_ACTIONS_PATH"]
self.actionsPaths = all_action_paths.split(os.pathsep)
else:
self.actionsAvailable = False
env_key = "FTRACK_ACTIONS_PATH"
if server_type.lower() == 'event':
env_key = "FTRACK_EVENTS_PATH"
if "FTRACK_EVENTS_PATH" in os.environ:
all_event_paths = os.environ["FTRACK_EVENTS_PATH"]
self.eventsPaths = all_event_paths.split(os.pathsep)
else:
self.eventsAvailable = False
self.server_type = server_type
self.env_key = env_key
def stop_session(self):
if self.session.event_hub.connected is True:
@ -67,7 +60,7 @@ class FtrackServer():
def set_files(self, paths):
# Iterate all paths
functions = []
register_functions_dict = []
for path in paths:
# add path to PYTHON PATH
if path not in sys.path:
@ -92,13 +85,11 @@ class FtrackServer():
# separate files by register function
if 'register' not in mod_functions:
msg = (
'"{0}" - Missing register method'
).format(file, self.type)
msg = ('"{}" - Missing register method').format(file)
log.warning(msg)
continue
functions.append({
register_functions_dict.append({
'name': file,
'register': mod_functions['register']
})
@ -108,43 +99,47 @@ class FtrackServer():
)
log.warning(msg)
if len(functions) < 1:
if len(register_functions_dict) < 1:
raise Exception
# Load presets for setting plugins
key = "user"
if self.server_type.lower() == "event":
key = "server"
plugins_presets = config.get_presets().get(
"ftrack", {}
).get("plugins", {}).get(key, {})
function_counter = 0
for function in functions:
for function_dict in register_functions_dict:
register = function_dict["register"]
try:
function['register'](self.session)
if len(inspect.signature(register).parameters) == 1:
register(self.session)
else:
register(self.session, plugins_presets=plugins_presets)
if function_counter%7 == 0:
time.sleep(0.1)
function_counter += 1
except Exception as e:
except Exception as exc:
msg = '"{}" - register was not successful ({})'.format(
function['name'], str(e)
function_dict['name'], str(exc)
)
log.warning(msg)
def run_server(self):
self.session = ftrack_api.Session(auto_connect_event_hub=True,)
if self.type.lower() == 'event':
if self.eventsAvailable is False:
msg = (
'FTRACK_EVENTS_PATH is not set'
', event server won\'t launch'
)
log.error(msg)
return
self.set_files(self.eventsPaths)
else:
if self.actionsAvailable is False:
msg = (
'FTRACK_ACTIONS_PATH is not set'
', action server won\'t launch'
)
log.error(msg)
return
self.set_files(self.actionsPaths)
paths_str = os.environ.get(self.env_key)
if paths_str is None:
log.error((
"Env var \"{}\" is not set, \"{}\" server won\'t launch"
).format(self.env_key, self.server_type))
return
paths = paths_str.split(os.pathsep)
self.set_files(paths)
log.info(60*"*")
log.info('Registration of actions/events has finished!')

View file

@ -116,13 +116,13 @@ def import_to_avalon(
# not override existing templates!
templates = av_project['config'].get('template', None)
if templates is not None:
for key, value in config['template'].items():
for key, value in proj_config['template'].items():
if (
key in templates and
templates[key] is not None and
templates[key] != value
):
config['template'][key] = templates[key]
proj_config['template'][key] = templates[key]
projectId = av_project['_id']
@ -142,7 +142,7 @@ def import_to_avalon(
{'_id': ObjectId(projectId)},
{'$set': {
'name': project_name,
'config': config,
'config': proj_config,
'data': data
}}
)
@ -326,13 +326,26 @@ def import_to_avalon(
return output
def get_avalon_attr(session):
def get_avalon_attr(session, split_hierarchical=False):
custom_attributes = []
hier_custom_attributes = []
query = 'CustomAttributeGroup where name is "avalon"'
all_avalon_attr = session.query(query).one()
for cust_attr in all_avalon_attr['custom_attribute_configurations']:
if 'avalon_' not in cust_attr['key']:
custom_attributes.append(cust_attr)
if 'avalon_' in cust_attr['key']:
continue
if split_hierarchical:
if cust_attr["is_hierarchical"]:
hier_custom_attributes.append(cust_attr)
continue
custom_attributes.append(cust_attr)
if split_hierarchical:
# return tuple
return custom_attributes, hier_custom_attributes
return custom_attributes

View file

@ -21,9 +21,9 @@ class BaseAction(BaseHandler):
icon = None
type = 'Action'
def __init__(self, session):
def __init__(self, session, plugins_presets={}):
'''Expects a ftrack_api.Session instance'''
super().__init__(session)
super().__init__(session, plugins_presets)
if self.label is None:
raise ValueError(

View file

@ -26,10 +26,10 @@ class AppAction(BaseHandler):
preactions = ['start.timer']
def __init__(
self, session, label, name, executable,
variant=None, icon=None, description=None, preactions=[]
self, session, label, name, executable, variant=None,
icon=None, description=None, preactions=[], plugins_presets={}
):
super().__init__(session)
super().__init__(session, plugins_presets)
'''Expects a ftrack_api.Session instance'''
if label is None:

View file

@ -29,7 +29,7 @@ class BaseHandler(object):
ignore_me = False
preactions = []
def __init__(self, session):
def __init__(self, session, plugins_presets={}):
'''Expects a ftrack_api.Session instance'''
self._session = session
self.log = Logger().get_logger(self.__class__.__name__)
@ -37,13 +37,23 @@ class BaseHandler(object):
# Using decorator
self.register = self.register_decorator(self.register)
self.launch = self.launch_log(self.launch)
self.plugins_presets = plugins_presets
# Decorator
def register_decorator(self, func):
@functools.wraps(func)
def wrapper_register(*args, **kwargs):
presets_data = self.plugins_presets.get(self.__class__.__name__)
if presets_data:
for key, value in presets_data.items():
if not hasattr(self, key):
continue
setattr(self, key, value)
if self.ignore_me:
return
label = self.__class__.__name__
if hasattr(self, 'label'):
if self.variant is None:
@ -495,13 +505,12 @@ class BaseHandler(object):
)
def show_interface_from_dict(
self, messages, event=None, user=None, username=None, user_id=None
self, messages, title="", event=None, user=None, username=None, user_id=None
):
if not messages:
self.log.debug("No messages to show! (messages dict is empty)")
return
items = []
title = 'Errors during mirroring'
splitter = {'type': 'label', 'value': '---'}
first = True
for key, value in messages.items():

View file

@ -15,9 +15,9 @@ class BaseEvent(BaseHandler):
type = 'Event'
def __init__(self, session):
def __init__(self, session, plugins_presets={}):
'''Expects a ftrack_api.Session instance'''
super().__init__(session)
super().__init__(session, plugins_presets)
# Decorator
def launch_log(self, func):

View file

@ -88,9 +88,11 @@ class FtrackModule:
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)
except Exception as exc:
log.error(
"Ftrack Action server crashed! Please try to start again.",
exc_info=True
)
# TODO show message to user
self.bool_action_server = False
self.set_menu_visibility()

View file

@ -51,7 +51,7 @@ def get_hierarchy(asset_name=None):
})
not_set = "PARENTS_NOT_SET"
entity_parents = entity.get("data", {}).get("parents", not_set)
entity_parents = asset_entity.get("data", {}).get("parents", not_set)
# If entity already have parents then just return joined
if entity_parents != not_set:
@ -467,10 +467,18 @@ def filter_pyblish_plugins(plugins):
host = api.current_host()
presets = config.get_presets().get('plugins', {}).get(host, {}).get(
"publish", {}
)
# iterate over plugins
for plugin in plugins[:]:
# skip if there are no presets to process
if not presets:
continue
try:
config_data = config.get_presets()['plugins'][host]["publish"][plugin.__name__] # noqa: E501
config_data = presets[plugin.__name__] # noqa: E501
except KeyError:
continue
@ -483,3 +491,7 @@ def filter_pyblish_plugins(plugins):
option, value, plugin.__name__))
setattr(plugin, option, value)
# Remove already processed plugins from dictionary
# WARNING Requires plugins with unique names
presets.pop(plugin.__name__)

View file

@ -59,13 +59,14 @@ class NukeHandler(logging.Handler):
'''Adding Nuke Logging Handler'''
log.info([handler.get_name() for handler in logging.root.handlers[:]])
nuke_handler = NukeHandler()
if nuke_handler.get_name() \
not in [handler.get_name()
for handler in logging.root.handlers[:]]:
logging.getLogger().addHandler(nuke_handler)
logging.getLogger().setLevel(logging.INFO)
log.info([handler.get_name() for handler in logging.root.handlers[:]])
def reload_config():
"""Attempt to reload pipeline at run-time.
@ -77,10 +78,7 @@ def reload_config():
import importlib
for module in (
"app",
"app.api",
"{}.api".format(AVALON_CONFIG),
"{}.templates".format(AVALON_CONFIG),
"{}.nuke.actions".format(AVALON_CONFIG),
"{}.nuke.templates".format(AVALON_CONFIG),
"{}.nuke.menu".format(AVALON_CONFIG),
@ -96,9 +94,8 @@ def reload_config():
def install():
# api.set_avalon_workdir()
# reload_config()
''' Installing all requarements for Nuke host
'''
log.info("Registering Nuke plug-ins..")
pyblish.register_plugin_path(PUBLISH_PATH)
@ -117,8 +114,6 @@ def install():
avalon.data["familiesStateDefault"] = False
avalon.data["familiesStateToggled"] = family_states
menu.install()
# Workfiles.
launch_workfiles = os.environ.get("WORKFILES_STARTUP")
@ -128,14 +123,21 @@ def install():
# Set context settings.
nuke.addOnCreate(lib.set_context_settings, nodeClass="Root")
menu.install()
def launch_workfiles_app():
'''Function letting start workfiles after start of host
'''
if not self.workfiles_launched:
self.workfiles_launched = True
workfiles.show(os.environ["AVALON_WORKDIR"])
def uninstall():
'''Uninstalling host's integration
'''
log.info("Deregistering Nuke plug-ins..")
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
@ -144,8 +146,13 @@ def uninstall():
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
reload_config()
menu.uninstall()
def on_pyblish_instance_toggled(instance, old_value, new_value):
"""Toggle node passthrough states on instance toggles."""
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
instance, old_value, new_value))

View file

@ -1,6 +1,3 @@
# absolute_import is needed to counter the `module has no cmds error` in Maya
from __future__ import absolute_import
import pyblish.api
from avalon.nuke.lib import (
@ -12,7 +9,7 @@ from ..action import get_errored_instances_from_context
class SelectInvalidAction(pyblish.api.Action):
"""Select invalid nodes in Maya when plug-in failed.
"""Select invalid nodes in Nuke when plug-in failed.
To retrieve the invalid nodes this assumes a static `get_invalid()`
method is available on the plugin.

View file

@ -20,6 +20,8 @@ self._project = None
def onScriptLoad():
''' Callback for ffmpeg support
'''
if nuke.env['LINUX']:
nuke.tcl('load ffmpegReader')
nuke.tcl('load ffmpegWriter')
@ -37,6 +39,7 @@ def checkInventoryVersions():
and check if the node is having actual version. If not then it will color
it to red.
"""
# TODO: make it for all nodes not just Read (Loader
# get all Loader nodes by avalon attribute metadata
for each in nuke.allNodes():
@ -76,13 +79,16 @@ def checkInventoryVersions():
def writes_version_sync():
''' Callback synchronizing version of publishable write nodes
'''
# TODO: make it work with new write node group
try:
rootVersion = pype.get_version_from_path(nuke.root().name())
padding = len(rootVersion)
new_version = "v" + str("{" + ":0>{}".format(padding) + "}").format(
int(rootVersion)
)
log.info("new_version: {}".format(new_version))
log.debug("new_version: {}".format(new_version))
except Exception:
return
@ -92,32 +98,34 @@ def writes_version_sync():
try:
if avalon_knob_data['families'] not in ["render"]:
log.info(avalon_knob_data['families'])
log.debug(avalon_knob_data['families'])
continue
node_file = each['file'].value()
log.info("node_file: {}".format(node_file))
node_version = "v" + pype.get_version_from_path(node_file)
log.info("node_version: {}".format(node_version))
log.debug("node_version: {}".format(node_version))
node_new_file = node_file.replace(node_version, new_version)
each['file'].setValue(node_new_file)
if not os.path.isdir(os.path.dirname(node_new_file)):
log.info("path does not exist")
log.warning("Path does not exist! I am creating it.")
os.makedirs(os.path.dirname(node_new_file), 0o766)
except Exception as e:
log.debug(
log.warning(
"Write node: `{}` has no version in path: {}".format(each.name(), e))
def version_up_script():
''' Raising working script's version
'''
import nukescripts
nukescripts.script_and_write_nodes_version_up()
def get_render_path(node):
''' Generate Render path from presets regarding avalon knob data
'''
data = dict()
data['avalon'] = avalon.nuke.get_avalon_knob_data(node)
@ -141,12 +149,24 @@ def get_render_path(node):
def format_anatomy(data):
''' Helping function for formating of anatomy paths
Arguments:
data (dict): dictionary with attributes used for formating
Return:
path (str)
'''
# TODO: perhaps should be nonPublic
from .templates import (
get_anatomy
)
# TODO: remove get_anatomy and import directly Anatomy() here
anatomy = get_anatomy()
log.info("__ anatomy.templates: {}".format(anatomy.templates))
log.debug("__ anatomy.templates: {}".format(anatomy.templates))
# TODO: perhaps should be in try!
padding = int(anatomy.templates['render']['padding'])
version = data.get("version", None)
@ -167,17 +187,24 @@ def format_anatomy(data):
"hierarchy": pype.get_hierarchy(),
"frame": "#" * padding,
})
log.info("__ data: {}".format(data))
log.info("__ format_anatomy: {}".format(anatomy.format(data)))
return anatomy.format(data)
def script_name():
''' Returns nuke script path
'''
return nuke.root().knob('name').value()
def add_button_write_to_read(node):
name = "createReadNode"
label = "Create Read"
value = "import write_to_read;write_to_read.write_to_read(nuke.thisNode())"
k = nuke.PyScript_Knob(name, label, value)
k.setFlag(0x1000)
node.addKnob(k)
def create_write_node(name, data, prenodes=None):
'''Creating write node which is group node
''' Creating write node which is group node
Arguments:
name (str): name of node
@ -200,6 +227,8 @@ def create_write_node(name, data, prenodes=None):
)
]
Return:
node (obj): group node with avalon data as Knobs
'''
nuke_dataflow_writes = get_node_dataflow_preset(**data)
@ -212,7 +241,6 @@ def create_write_node(name, data, prenodes=None):
"nuke_dataflow_writes": nuke_dataflow_writes,
"nuke_colorspace_writes": nuke_colorspace_writes
})
anatomy_filled = format_anatomy(data)
except Exception as e:
@ -228,7 +256,7 @@ def create_write_node(name, data, prenodes=None):
# create directory
if not os.path.isdir(os.path.dirname(fpath)):
log.info("path does not exist")
log.warning("Path does not exist! I am creating it.")
os.makedirs(os.path.dirname(fpath), 0o766)
_data = OrderedDict({
@ -303,11 +331,15 @@ def create_write_node(name, data, prenodes=None):
# imprinting group node
GN = avalon.nuke.imprint(GN, data["avalon"])
divider = nuke.Text_Knob('')
GN.addKnob(divider)
add_rendering_knobs(GN)
# adding write to read button
add_button_write_to_read(GN)
divider = nuke.Text_Knob('')
GN.addKnob(divider)
@ -325,6 +357,14 @@ def create_write_node(name, data, prenodes=None):
def add_rendering_knobs(node):
''' Adds additional rendering knobs to given node
Arguments:
node (obj): nuke node object to be fixed
Return:
node (obj): with added knobs
'''
if "render" not in node.knobs():
knob = nuke.Boolean_Knob("render", "Render")
knob.setFlag(0x1000)
@ -338,6 +378,12 @@ def add_rendering_knobs(node):
def set_viewers_colorspace(viewer):
''' Adds correct colorspace to viewer
Arguments:
viewer (obj): nuke viewer node object to be fixed
'''
assert isinstance(viewer, dict), log.error(
"set_viewers_colorspace(): argument should be dictionary")
@ -381,6 +427,12 @@ def set_viewers_colorspace(viewer):
def set_root_colorspace(root_dict):
''' Adds correct colorspace to root
Arguments:
root_dict (dict): nuke root node as dictionary
'''
assert isinstance(root_dict, dict), log.error(
"set_root_colorspace(): argument should be dictionary")
@ -397,17 +449,26 @@ def set_root_colorspace(root_dict):
for knob, value in root_dict.items():
if nuke.root()[knob].value() not in value:
nuke.root()[knob].setValue(str(value))
log.info("nuke.root()['{}'] changed to: {}".format(knob, value))
log.debug("nuke.root()['{}'] changed to: {}".format(knob, value))
def set_writes_colorspace(write_dict):
''' Adds correct colorspace to write node dict
Arguments:
write_dict (dict): nuke write node as dictionary
'''
# TODO: complete this function so any write node in scene will have fixed colorspace following presets for the project
assert isinstance(write_dict, dict), log.error(
"set_root_colorspace(): argument should be dictionary")
log.info("set_writes_colorspace(): {}".format(write_dict))
log.debug("__ set_writes_colorspace(): {}".format(write_dict))
def set_colorspace():
''' Setting colorpace following presets
'''
nuke_colorspace = get_colorspace_preset().get("nuke", None)
try:
@ -428,7 +489,7 @@ def set_colorspace():
try:
for key in nuke_colorspace:
log.info("{}".format(key))
log.debug("Preset's colorspace key: {}".format(key))
except TypeError:
log.error("Nuke is not in templates! \n\n\n"
"contact your supervisor!")
@ -474,10 +535,6 @@ def reset_frame_range_handles():
root["first_frame"].setValue(frame_start)
root["last_frame"].setValue(frame_end)
log.info("__ handle_start: `{}`".format(handle_start))
log.info("__ handle_end: `{}`".format(handle_end))
log.info("__ fps: `{}`".format(fps))
# setting active viewers
nuke.frame(int(asset_entity["data"]["frameStart"]))
@ -488,15 +545,11 @@ def reset_frame_range_handles():
for node in nuke.allNodes(filter="Viewer"):
node['frame_range'].setValue(range)
node['frame_range_lock'].setValue(True)
log.info("_frameRange: {}".format(range))
log.info("frameRange: {}".format(node['frame_range'].value()))
node['frame_range'].setValue(range)
node['frame_range_lock'].setValue(True)
# adding handle_start/end to root avalon knob
if not avalon.nuke.set_avalon_knob_data(root, {
if not avalon.nuke.imprint(root, {
"handleStart": int(handle_start),
"handleEnd": int(handle_end)
}):

View file

@ -2,10 +2,11 @@ import nuke
from avalon.api import Session
from pype.nuke import lib
from pypeapp import Logger
log = Logger().get_logger(__name__, "nuke")
def install():
menubar = nuke.menu("Nuke")
menu = menubar.findItem(Session["AVALON_LABEL"])
@ -15,8 +16,11 @@ def install():
rm_item = [
(i, item) for i, item in enumerate(menu.items()) if name in item.name()
][0]
log.debug("Changing Item: {}".format(rm_item))
# rm_item[1].setEnabled(False)
menu.removeItem(rm_item[1].name())
menu.addCommand(new_name, lib.reset_resolution, index=rm_item[0])
menu.addCommand(new_name, lib.reset_resolution, index=(rm_item[0]))
# replace reset frame range from avalon core to pype's
name = "Reset Frame Range"
@ -24,8 +28,10 @@ def install():
rm_item = [
(i, item) for i, item in enumerate(menu.items()) if name in item.name()
][0]
log.debug("Changing Item: {}".format(rm_item))
# rm_item[1].setEnabled(False)
menu.removeItem(rm_item[1].name())
menu.addCommand(new_name, lib.reset_frame_range_handles, index=rm_item[0])
menu.addCommand(new_name, lib.reset_frame_range_handles, index=(rm_item[0]))
# add colorspace menu item
name = "Set colorspace"
@ -33,9 +39,22 @@ def install():
name, lib.set_colorspace,
index=(rm_item[0]+2)
)
log.debug("Adding menu item: {}".format(name))
# add item that applies all setting above
name = "Apply all settings"
menu.addCommand(
name, lib.set_context_settings, index=(rm_item[0]+3)
)
log.debug("Adding menu item: {}".format(name))
def uninstall():
menubar = nuke.menu("Nuke")
menu = menubar.findItem(Session["AVALON_LABEL"])
for item in menu.items():
log.info("Removing menu item: {}".format(item.name()))
menu.removeItem(item.name())

View file

@ -20,6 +20,8 @@ def get_colorspace_preset():
def get_node_dataflow_preset(**kwarg):
''' Get preset data for dataflow (fileType, compression, bitDepth)
'''
log.info(kwarg)
host = kwarg.get("host", "nuke")
cls = kwarg.get("class", None)
@ -39,6 +41,8 @@ def get_node_dataflow_preset(**kwarg):
def get_node_colorspace_preset(**kwarg):
''' Get preset data for colorspace
'''
log.info(kwarg)
host = kwarg.get("host", "nuke")
cls = kwarg.get("class", None)

View file

@ -1,5 +1,6 @@
import os
from pypeapp import Logger
import hiero
from avalon.tools import workfiles
from avalon import api as avalon
from pyblish import api as pyblish
@ -13,20 +14,12 @@ from .workio import (
work_root
)
from .. import api
from .menu import (
install as menu_install,
_update_menu_task_label
)
from .tags import add_tags_from_presets
from pypeapp import Logger
import hiero
log = Logger().get_logger(__name__, "nukestudio")
__all__ = [
# Workfiles API
"open",
@ -35,11 +28,16 @@ __all__ = [
"has_unsaved_changes",
"file_extensions",
"work_root",
]
]
# get logger
log = Logger().get_logger(__name__, "nukestudio")
''' Creating all important host related variables '''
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
# plugin root path
PARENT_DIR = os.path.dirname(__file__)
PACKAGE_DIR = os.path.dirname(PARENT_DIR)
PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")
@ -49,13 +47,21 @@ 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")
# registering particular pyblish gui but `lite` is recomended!!
if os.getenv("PYBLISH_GUI", None):
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
def install(config):
"""
Installing Nukestudio integration for avalon
Args:
config (obj): avalon config module `pype` in our case, it is not used but required by avalon.api.install()
"""
# adding all events
_register_events()
log.info("Registering NukeStudio plug-ins..")
@ -74,6 +80,7 @@ def install(config):
avalon.data["familiesStateDefault"] = False
avalon.data["familiesStateToggled"] = family_states
# install menu
menu_install()
# Workfiles.
@ -91,11 +98,26 @@ def install(config):
def add_tags(event):
"""
Event for automatic tag creation after nukestudio start
Args:
event (obj): required but unused
"""
add_tags_from_presets()
def launch_workfiles_app(event):
workfiles.show(os.environ["AVALON_WORKDIR"])
"""
Event for launching workfiles after nukestudio start
Args:
event (obj): required but unused
"""
from .lib import set_workfiles
set_workfiles()
# Closing the new project.
event.sender.close()
@ -107,6 +129,10 @@ def launch_workfiles_app(event):
def uninstall():
"""
Uninstalling Nukestudio integration for avalon
"""
log.info("Deregistering NukeStudio plug-ins..")
pyblish.deregister_host("nukestudio")
pyblish.deregister_plugin_path(PUBLISH_PATH)
@ -115,6 +141,11 @@ def uninstall():
def _register_events():
"""
Adding all callbacks.
"""
# if task changed then change notext of nukestudio
avalon.on("taskChanged", _update_menu_task_label)
log.info("Installed event callback for 'taskChanged'..")
@ -129,4 +160,5 @@ def ls():
See the `container.json` schema for details on how it should look,
and the Maya equivalent, which is in `avalon.maya.pipeline`
"""
# TODO: listing all availabe containers form sequence
return

View file

@ -1,19 +1,13 @@
# Standard library
import os
import sys
# Pyblish libraries
import pyblish.api
import avalon.api as avalon
import pype.api as pype
from avalon.vendor.Qt import (QtWidgets, QtGui)
# Host libraries
import hiero
import pyblish.api
import avalon.api as avalon
from avalon.vendor.Qt import (QtWidgets, QtGui)
import pype.api as pype
from pypeapp import Logger
log = Logger().get_logger(__name__, "nukestudio")
cached_process = None
@ -30,12 +24,18 @@ AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
def set_workfiles():
''' Wrapping function for workfiles launcher '''
from avalon.tools import workfiles
# import session to get project dir
S = avalon.Session
active_project_root = os.path.normpath(
os.path.join(S['AVALON_PROJECTS'], S['AVALON_PROJECT'])
)
workdir = os.environ["AVALON_WORKDIR"]
# show workfile gui
workfiles.show(workdir)
# getting project
project = hiero.core.projects()[-1]
# set project root with backward compatibility
@ -64,11 +64,10 @@ def set_workfiles():
# set fps to hiero project
project.setFramerate(fps)
# TODO: add auto colorspace set from project drop
log.info("Project property has been synchronised with Avalon db")
def reload_config():
"""Attempt to reload pipeline at run-time.
@ -189,6 +188,10 @@ def add_submission():
class PublishAction(QtWidgets.QAction):
"""
Action with is showing as menu item
"""
def __init__(self):
QtWidgets.QAction.__init__(self, "Publish", None)
self.triggered.connect(self.publish)
@ -213,7 +216,8 @@ class PublishAction(QtWidgets.QAction):
def _show_no_gui():
"""Popup with information about how to register a new 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.

View file

@ -1,24 +1,23 @@
import os
import sys
import hiero.core
from pypeapp import Logger
from avalon.api import Session
from hiero.ui import findMenuAction
# this way we secure compatibility between nuke 10 and 11
try:
from PySide.QtGui import *
except Exception:
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from hiero.ui import findMenuAction
from avalon.api import Session
from .tags import add_tags_from_presets
from .lib import (
reload_config,
set_workfiles
)
from pypeapp import Logger
log = Logger().get_logger(__name__, "nukestudio")
@ -45,6 +44,11 @@ def _update_menu_task_label(*args):
def install():
"""
Installing menu into Nukestudio
"""
# here is the best place to add menu
from avalon.tools import (
creator,
@ -127,8 +131,6 @@ def install():
'icon': QIcon('icons:ColorAdd.png')
}]
# Create menu items
for a in actions:
add_to_menu = menu

View file

@ -1,5 +1,6 @@
import re
import os
import hiero
from pypeapp import (
config,
@ -7,8 +8,6 @@ from pypeapp import (
)
from avalon import io
import hiero
log = Logger().get_logger(__name__, "nukestudio")

View file

@ -74,12 +74,14 @@ class ReferenceLoader(pype.maya.plugin.ReferenceLoader):
# for backwards compatibility
class AbcLoader(ReferenceLoader):
label = "Deprecated loader (don't use)"
families = ["pointcache", "animation"]
representations = ["abc"]
tool_names = []
# for backwards compatibility
class ModelLoader(ReferenceLoader):
label = "Deprecated loader (don't use)"
families = ["model", "pointcache"]
representations = ["abc"]
tool_names = []

View file

@ -64,9 +64,9 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin):
"subset": layername,
"setMembers": layer,
"publish": True,
"frameStart": self.get_render_attribute("frameStart",
"frameStart": self.get_render_attribute("startFrame",
layer=layer),
"frameEnd": self.get_render_attribute("frameEnd",
"frameEnd": self.get_render_attribute("endFrame",
layer=layer),
"byFrameStep": self.get_render_attribute("byFrameStep",
layer=layer),

View file

@ -12,7 +12,7 @@ def is_subdir(path, root_dir):
root_dir = os.path.realpath(root_dir)
# If not on same drive
if os.path.splitdrive(path)[0] != os.path.splitdrive(root_dir)[0]:
if os.path.splitdrive(path)[0].lower() != os.path.splitdrive(root_dir)[0].lower(): # noqa: E501
return False
# Get 'relative path' (can contain ../ which means going up)

View file

@ -16,7 +16,6 @@ def subset_to_families(subset, family, families):
new_subset = families + subset_sufx
return "{}.{}".format(family, new_subset)
class CreateWriteRender(avalon.nuke.Creator):
# change this to template preset
preset = "render"

View file

@ -76,7 +76,7 @@ class LoadSequence(api.Loader):
"""Load image sequence into Nuke"""
families = ["write", "source", "plate", "render"]
representations = ["exr", "dpx"]
representations = ["exr", "dpx", "jpg", "jpeg"]
label = "Load sequence"
order = -10
@ -94,20 +94,11 @@ class LoadSequence(api.Loader):
log.info("version_data: {}\n".format(version_data))
self.first_frame = int(nuke.root()["first_frame"].getValue())
self.handle_start = version_data.get("handleStart", 0)
first = version_data.get("frameStart", None)
last = version_data.get("frameEnd", None)
handles = version_data.get("handles", 0)
handle_start = version_data.get("handleStart", 0)
handle_end = version_data.get("handleEnd", 0)
# fix handle start and end if none are available
if not handle_start and not handle_end:
handle_start = handles
handle_end = handles
# # create handles offset
# first -= handle_start
# last += handle_end
# Fallback to asset name when namespace is None
if namespace is None:
@ -138,7 +129,7 @@ class LoadSequence(api.Loader):
r["last"].setValue(int(last))
# add additional metadata from the version to imprint to Avalon knob
add_keys = ["frameStart", "frameEnd", "handles",
add_keys = ["frameStart", "frameEnd",
"source", "colorspace", "author", "fps", "version",
"handleStart", "handleEnd"]
@ -147,12 +138,18 @@ class LoadSequence(api.Loader):
if k is 'version':
data_imprint.update({k: context["version"]['name']})
else:
data_imprint.update({k: context["version"]['data'].get(k, str(None))})
data_imprint.update(
{k: context["version"]['data'].get(k, str(None))})
data_imprint.update({"objectName": read_name})
r["tile_color"].setValue(int("0x4ecd25ff", 16))
if version_data.get("retime", None):
speed = version_data.get("speed", 1)
time_warp_nodes = version_data.get("timewarps", [])
self.make_retimes(r, speed, time_warp_nodes)
return containerise(r,
name=name,
namespace=namespace,
@ -160,6 +157,34 @@ class LoadSequence(api.Loader):
loader=self.__class__.__name__,
data=data_imprint)
def make_retimes(self, node, speed, time_warp_nodes):
''' Create all retime and timewarping nodes with coppied animation '''
if speed != 1:
rtn = nuke.createNode(
"Retime",
"speed {}".format(speed))
rtn["before"].setValue("continue")
rtn["after"].setValue("continue")
rtn["input.first_lock"].setValue(True)
rtn["input.first"].setValue(
self.handle_start + self.first_frame
)
if time_warp_nodes != []:
for timewarp in time_warp_nodes:
twn = nuke.createNode(timewarp["Class"],
"name {}".format(timewarp["name"]))
if isinstance(timewarp["lookup"], list):
# if array for animation
twn["lookup"].setAnimated()
for i, value in enumerate(timewarp["lookup"]):
twn["lookup"].setValueAt(
(self.first_frame + i) + value,
(self.first_frame + i))
else:
# if static value `int`
twn["lookup"].setValue(timewarp["lookup"])
def switch(self, container, representation):
self.update(container, representation)
@ -200,11 +225,11 @@ class LoadSequence(api.Loader):
version_data = version.get("data", {})
self.first_frame = int(nuke.root()["first_frame"].getValue())
self.handle_start = version_data.get("handleStart", 0)
first = version_data.get("frameStart", None)
last = version_data.get("frameEnd", None)
handles = version_data.get("handles", 0)
handle_start = version_data.get("handleStart", 0)
handle_end = version_data.get("handleEnd", 0)
if first is None:
log.warning("Missing start frame for updated version"
@ -212,15 +237,6 @@ class LoadSequence(api.Loader):
"{} ({})".format(node['name'].value(), representation))
first = 0
# fix handle start and end if none are available
if not handle_start and not handle_end:
handle_start = handles
handle_end = handles
# create handles offset
first -= handle_start
last += handle_end
# Update the loader's path whilst preserving some values
with preserve_trim(node):
node["file"].setValue(file["path"])
@ -241,7 +257,6 @@ class LoadSequence(api.Loader):
"version": version.get("name"),
"colorspace": version_data.get("colorspace"),
"source": version_data.get("source"),
"handles": version_data.get("handles"),
"handleStart": version_data.get("handleStart"),
"handleEnd": version_data.get("handleEnd"),
"fps": version_data.get("fps"),
@ -255,6 +270,11 @@ class LoadSequence(api.Loader):
else:
node["tile_color"].setValue(int("0x4ecd25ff", 16))
if version_data.get("retime", None):
speed = version_data.get("speed", 1)
time_warp_nodes = version_data.get("timewarps", [])
self.make_retimes(node, speed, time_warp_nodes)
# Update the imprinted representation
update_container(
node,

View file

@ -1,4 +1,3 @@
import nuke
from avalon import api, io
import pyblish.api
@ -19,5 +18,6 @@ class CollectAssetInfo(pyblish.api.ContextPlugin):
self.log.info("asset_data: {}".format(asset_data))
context.data['handles'] = int(asset_data["data"].get("handles", 0))
context.data["handleStart"] = int(asset_data["data"].get("handleStart", 0))
context.data["handleStart"] = int(asset_data["data"].get(
"handleStart", 0))
context.data["handleEnd"] = int(asset_data["data"].get("handleEnd", 0))

View file

@ -0,0 +1,30 @@
import toml
import nuke
import pyblish.api
class CollectReadLegacy(pyblish.api.ContextPlugin):
"""Collect legacy read nodes."""
order = pyblish.api.CollectorOrder
label = "Collect Read Legacy"
hosts = ["nuke", "nukeassist"]
def process(self, context):
for node in nuke.allNodes():
if node.Class() != "Read":
continue
if "avalon" not in node.knobs().keys():
continue
if not toml.loads(node["avalon"].value()):
return
instance = context.create_instance(
node.name(), family="read.legacy"
)
instance.append(node)

View file

@ -27,9 +27,13 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin):
def process(self, instance):
# root = nuke.root()
# node_subset_name = instance.data.get("name", None)
node = instance[1]
node = None
for x in instance:
if x.Class() == "Write":
node = x
if node is None:
return
DEADLINE_REST_URL = os.environ.get("DEADLINE_REST_URL",
"http://localhost:8082")

View file

@ -0,0 +1,83 @@
import os
import toml
import nuke
import pyblish.api
from avalon import api
from bson.objectid import ObjectId
class RepairReadLegacyAction(pyblish.api.Action):
label = "Repair"
icon = "wrench"
on = "failed"
def process(self, context, plugin):
# Get the errored instances
failed = []
for result in context.data["results"]:
if (result["error"] is not None and result["instance"] is not None
and result["instance"] not in failed):
failed.append(result["instance"])
# Apply pyblish.logic to get the instances for the plug-in
instances = pyblish.api.instances_by_plugin(failed, plugin)
for instance in instances:
data = toml.loads(instance[0]["avalon"].value())
data["name"] = instance[0].name()
data["xpos"] = instance[0].xpos()
data["ypos"] = instance[0].ypos()
data["extension"] = os.path.splitext(
instance[0]["file"].value()
)[1][1:]
data["connections"] = []
for d in instance[0].dependent():
for i in range(d.inputs()):
if d.input(i) == instance[0]:
data["connections"].append([i, d])
nuke.delete(instance[0])
loader_name = "LoadSequence"
if data["extension"] == "mov":
loader_name = "LoadMov"
loader_plugin = None
for Loader in api.discover(api.Loader):
if Loader.__name__ != loader_name:
continue
loader_plugin = Loader
api.load(
Loader=loader_plugin,
representation=ObjectId(data["representation"])
)
node = nuke.toNode(data["name"])
for connection in data["connections"]:
connection[1].setInput(connection[0], node)
node.setXYpos(data["xpos"], data["ypos"])
class ValidateReadLegacy(pyblish.api.InstancePlugin):
"""Validate legacy read instance[0]s."""
order = pyblish.api.ValidatorOrder
optional = True
families = ["read.legacy"]
label = "Read Legacy"
hosts = ["nuke"]
actions = [RepairReadLegacyAction]
def process(self, instance):
msg = "Clean up legacy read node \"{}\"".format(instance)
assert False, msg

View file

@ -0,0 +1,121 @@
from pyblish import api
import hiero
import math
class CollectCalculateRetime(api.InstancePlugin):
"""Calculate Retiming of selected track items."""
order = api.CollectorOrder + 0.02
label = "Collect Calculate Retiming"
hosts = ["nukestudio"]
families = ['retime']
def process(self, instance):
margin_in = instance.data["retimeMarginIn"]
margin_out = instance.data["retimeMarginOut"]
self.log.debug("margin_in: '{0}', margin_out: '{1}'".format(margin_in, margin_out))
handle_start = instance.data["handleStart"]
handle_end = instance.data["handleEnd"]
track_item = instance.data["item"]
# define basic clip frame range variables
timeline_in = int(track_item.timelineIn())
timeline_out = int(track_item.timelineOut())
source_in = int(track_item.sourceIn())
source_out = int(track_item.sourceOut())
speed = track_item.playbackSpeed()
self.log.debug("_BEFORE: \n timeline_in: `{0}`,\n timeline_out: `{1}`,\
\n source_in: `{2}`,\n source_out: `{3}`,\n speed: `{4}`,\n handle_start: `{5}`,\n handle_end: `{6}`".format(
timeline_in,
timeline_out,
source_in,
source_out,
speed,
handle_start,
handle_end
))
# loop withing subtrack items
source_in_change = 0
source_out_change = 0
for s_track_item in track_item.linkedItems():
if isinstance(s_track_item, hiero.core.EffectTrackItem) \
and "TimeWarp" in s_track_item.node().Class():
# adding timewarp attribute to instance
if not instance.data.get("timeWarpNodes", None):
instance.data["timeWarpNodes"] = list()
# ignore item if not enabled
if s_track_item.isEnabled():
node = s_track_item.node()
name = node["name"].value()
look_up = node["lookup"].value()
animated = node["lookup"].isAnimated()
if animated:
look_up = [((node["lookup"].getValueAt(i)) - i)
for i in range((timeline_in - handle_start), (timeline_out + handle_end) + 1)
]
# calculate differnce
diff_in = (node["lookup"].getValueAt(
timeline_in)) - timeline_in
diff_out = (node["lookup"].getValueAt(
timeline_out)) - timeline_out
# calculate source
source_in_change += diff_in
source_out_change += diff_out
# calculate speed
speed_in = (node["lookup"].getValueAt(timeline_in) / (
float(timeline_in) * .01)) * .01
speed_out = (node["lookup"].getValueAt(timeline_out) / (
float(timeline_out) * .01)) * .01
# calculate handles
handle_start = int(
math.ceil(
(handle_start * speed_in * 1000) / 1000.0)
)
handle_end = int(
math.ceil(
(handle_end * speed_out * 1000) / 1000.0)
)
self.log.debug(
("diff_in, diff_out", diff_in, diff_out))
self.log.debug(
("source_in_change, source_out_change", source_in_change, source_out_change))
instance.data["timeWarpNodes"].append({"Class": "TimeWarp",
"name": name,
"lookup": look_up})
self.log.debug((source_in_change, source_out_change))
# recalculate handles by the speed
handle_start *= speed
handle_end *= speed
self.log.debug("speed: handle_start: '{0}', handle_end: '{1}'".format(handle_start, handle_end))
source_in += int(source_in_change)
source_out += int(source_out_change * speed)
handle_start += (margin_in)
handle_end += (margin_out)
self.log.debug("margin: handle_start: '{0}', handle_end: '{1}'".format(handle_start, handle_end))
# add all data to Instance
instance.data["sourceIn"] = source_in
instance.data["sourceOut"] = source_out
instance.data["sourceInH"] = int(source_in - math.ceil(
(handle_start * 1000) / 1000.0))
instance.data["sourceOutH"] = int(source_out + math.ceil(
(handle_end * 1000) / 1000.0))
instance.data["speed"] = speed
self.log.debug("timeWarpNodes: {}".format(instance.data["timeWarpNodes"]))
self.log.debug("sourceIn: {}".format(instance.data["sourceIn"]))
self.log.debug("sourceOut: {}".format(instance.data["sourceOut"]))
self.log.debug("speed: {}".format(instance.data["speed"]))

View file

@ -1,5 +1,6 @@
import pyblish.api
class CollectClipFrameRanges(pyblish.api.InstancePlugin):
"""Collect all frame range data: source(In,Out), timeline(In,Out), edit_(in, out), f(start, end)"""
@ -15,8 +16,10 @@ class CollectClipFrameRanges(pyblish.api.InstancePlugin):
handle_start = instance.data["handleStart"]
handle_end = instance.data["handleEnd"]
source_in_h = instance.data["sourceIn"] - handle_start
source_out_h = instance.data["sourceOut"] + handle_end
source_in_h = instance.data("sourceInH",
instance.data("sourceIn") - handle_start)
source_out_h = instance.data("sourceOutH",
instance.data("sourceOut") + handle_end)
timeline_in = instance.data["clipIn"]
timeline_out = instance.data["clipOut"]

View file

@ -137,7 +137,6 @@ class CollectPlatesData(api.InstancePlugin):
"subset": name,
"fps": instance.context.data["fps"]
})
instance.data["versionData"] = version_data
try:
basename, ext = os.path.splitext(source_file)
@ -156,9 +155,11 @@ class CollectPlatesData(api.InstancePlugin):
start_frame = source_first_frame + instance.data["sourceInH"]
duration = instance.data["sourceOutH"] - instance.data["sourceInH"]
end_frame = start_frame + duration
self.log.debug("start_frame: `{}`".format(start_frame))
self.log.debug("end_frame: `{}`".format(end_frame))
files = [file % i for i in range(start_frame, (end_frame + 1), 1)]
except Exception as e:
self.log.debug("Exception in file: {}".format(e))
self.log.warning("Exception in file: {}".format(e))
head, ext = os.path.splitext(source_file)
ext = ext[1:]
files = source_file
@ -207,16 +208,41 @@ class CollectPlatesData(api.InstancePlugin):
thumb_representation)
# adding representation for plates
frame_start = instance.data["frameStart"] - \
instance.data["handleStart"]
frame_end = instance.data["frameEnd"] + instance.data["handleEnd"]
# exception for retimes
if instance.data.get("retime"):
source_in_h = instance.data["sourceInH"]
source_in = instance.data["sourceIn"]
source_handle_start = source_in_h - source_in
frame_start = instance.data["frameStart"] + source_handle_start
duration = instance.data["sourceOutH"] - instance.data["sourceInH"]
frame_end = frame_start + duration
plates_representation = {
'files': files,
'stagingDir': staging_dir,
'name': ext,
'ext': ext,
"frameStart": instance.data["frameStart"] - instance.data["handleStart"],
"frameEnd": instance.data["frameEnd"] + instance.data["handleEnd"],
"frameStart": frame_start,
"frameEnd": frame_end,
}
instance.data["representations"].append(plates_representation)
# deal with retimed clip
if instance.data.get("retime"):
version_data.update({
"retime": True,
"speed": instance.data.get("speed", 1),
"timewarps": instance.data.get("timeWarpNodes", []),
"frameStart": frame_start,
"frameEnd": frame_end,
})
instance.data["versionData"] = version_data
# testing families
family = instance.data["family"]
families = instance.data["families"]

View file

@ -1,5 +1,5 @@
from pyblish import api
import os
class CollectClipTagFrameStart(api.InstancePlugin):
"""Collect FrameStart from Tags of selected track items."""
@ -19,8 +19,20 @@ class CollectClipTagFrameStart(api.InstancePlugin):
# gets only task family tags and collect labels
if "frameStart" in t_family:
t_value = t_metadata.get("tag.value", "")
# backward compatibility
t_number = t_metadata.get("tag.number", "")
start_frame = int(t_number)
try:
start_frame = int(t_number) or int(t_value)
except ValueError:
if "source" in t_value:
source_first = instance.data["sourceFirst"]
source_in = instance.data["sourceIn"]
handle_start = instance.data["handleStart"]
start_frame = (source_first + source_in) - handle_start
instance.data["startingFrame"] = start_frame
self.log.info("Start frame on `{0}` set to `{1}`".format(
instance, start_frame

View file

@ -0,0 +1,32 @@
from pyblish import api
class CollectTagRetime(api.InstancePlugin):
"""Collect Retiming from Tags of selected track items."""
order = api.CollectorOrder + 0.014
label = "Collect Retiming Tag"
hosts = ["nukestudio"]
families = ['clip']
def process(self, instance):
# gets tags
tags = instance.data["tags"]
for t in tags:
t_metadata = dict(t["metadata"])
t_family = t_metadata.get("tag.family", "")
# gets only task family tags and collect labels
if "retiming" in t_family:
margin_in = t_metadata.get("tag.marginIn", "")
margin_out = t_metadata.get("tag.marginOut", "")
instance.data["retimeMarginIn"] = int(margin_in)
instance.data["retimeMarginOut"] = int(margin_out)
instance.data["retime"] = True
self.log.info("retimeMarginIn: `{}`".format(margin_in))
self.log.info("retimeMarginOut: `{}`".format(margin_out))
instance.data["families"] += ["retime"]

View file

@ -1 +0,0 @@
<?xml version="1.0" ?><svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g><linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256"><stop offset="0" style="stop-color:#33B49D"/><stop offset="1" style="stop-color:#00A185"/></linearGradient><circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/><linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188"><stop offset="0" style="stop-color:#00A185"/><stop offset="1" style="stop-color:#33B49D"/></linearGradient><path d="M256,469.3338623c-117.6315308,0-213.3333435-95.7023926-213.3333435-213.3333435 c0-117.6314545,95.7018051-213.333313,213.3333435-213.333313c117.6362,0,213.3333435,95.7018661,213.3333435,213.333313 C469.3333435,373.6314697,373.6362,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/></g><g><rect height="127.4010696" opacity="0.3" width="127.4021301" x="200.2975464" y="200.2986603"/><rect height="35.229332" opacity="0.3" width="10" x="302.7012634" y="144"/><rect height="35.229332" opacity="0.3" width="10" x="273.5652771" y="144"/><rect height="35.229332" opacity="0.3" width="10" x="244.4292755" y="144"/><rect height="35.229332" opacity="0.3" width="10" x="215.2985992" y="144"/><rect height="35.2341347" opacity="0.3" width="10" x="302.7012634" y="348.7658691"/><rect height="35.2341347" opacity="0.3" width="10" x="273.5652771" y="348.7658691"/><rect height="35.2341347" opacity="0.3" width="10" x="244.4292755" y="348.7658691"/><rect height="35.2341347" opacity="0.3" width="10" x="215.2985992" y="348.7658691"/><rect height="10" opacity="0.3" width="35.2292747" x="144" y="215.2986603"/><rect height="10" opacity="0.3" width="35.2292747" x="144" y="244.4341278"/><rect height="10" opacity="0.3" width="35.2292747" x="144" y="273.5653381"/><rect height="10" opacity="0.3" width="35.2292747" x="144" y="302.7013245"/><rect height="10" opacity="0.3" width="35.2347298" x="348.7652588" y="215.2986603"/><rect height="10" opacity="0.3" width="35.2347298" x="348.7652588" y="244.4341278"/><rect height="10" opacity="0.3" width="35.2347298" x="348.7652588" y="273.5653381"/><rect height="10" opacity="0.3" width="35.2347298" x="348.7652588" y="302.7013245"/></g><g><rect fill="#FFFFFF" height="127.4010696" width="127.4021301" x="192.2975464" y="192.2986603"/><rect fill="#FFFFFF" height="35.229332" width="10" x="294.7012634" y="136"/><rect fill="#FFFFFF" height="35.229332" width="10" x="265.5652771" y="136"/><rect fill="#FFFFFF" height="35.229332" width="10" x="236.4292755" y="136"/><rect fill="#FFFFFF" height="35.229332" width="10" x="207.2985992" y="136"/><rect fill="#FFFFFF" height="35.2341347" width="10" x="294.7012634" y="340.7658691"/><rect fill="#FFFFFF" height="35.2341347" width="10" x="265.5652771" y="340.7658691"/><rect fill="#FFFFFF" height="35.2341347" width="10" x="236.4292755" y="340.7658691"/><rect fill="#FFFFFF" height="35.2341347" width="10" x="207.2985992" y="340.7658691"/><rect fill="#FFFFFF" height="10" width="35.2292747" x="136" y="207.2986603"/><rect fill="#FFFFFF" height="10" width="35.2292747" x="136" y="236.4341278"/><rect fill="#FFFFFF" height="10" width="35.2292747" x="136" y="265.5653381"/><rect fill="#FFFFFF" height="10" width="35.2292747" x="136" y="294.7013245"/><rect fill="#FFFFFF" height="10" width="35.2347298" x="340.7652588" y="207.2986603"/><rect fill="#FFFFFF" height="10" width="35.2347298" x="340.7652588" y="236.4341278"/><rect fill="#FFFFFF" height="10" width="35.2347298" x="340.7652588" y="265.5653381"/><rect fill="#FFFFFF" height="10" width="35.2347298" x="340.7652588" y="294.7013245"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -1,41 +0,0 @@
<?xml version="1.0" ?>
<svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#33B49D"/>
<stop offset="1" style="stop-color:#008165"/>
</linearGradient><circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#008165"/>
<stop offset="1" style="stop-color:#33B49D"/>
</linearGradient>
<path
d="
M 256,469.3338623
c -117.6315308,0-213.3333435-95.7023926-213.3333435-213.3333435
c 0-117.6314545,95.7018051 -213.333313,213.3333435 -213.333313
c 117.6362,0,213.3333435,95.7018661,213.3333435,213.333313
C 469.3333435,373.6314697,373.6362,469.3338623,256,469.3338623
z"
fill="url(#SVGID_2_)"
/>
</g>
<g>
<circle cx="170.0324707" cy="174.964798" opacity="0.3" r="26.0319996"/>
<rect height="10" opacity="0.3" width="161.7552032" x="222.2442017" y="169.965332"/>
<circle cx="170.0324707" cy="264" opacity="0.3" r="26.0319996"/>
<rect height="10" opacity="0.3" width="161.7552032" x="222.2442017" y="259.0010681"/>
<circle cx="170.0324707" cy="353.034668" opacity="0.3" r="26.0319996"/>
<rect height="10" opacity="0.3" width="161.7552032" x="222.2442017" y="348.0341492"/>
</g>
<g>
<circle cx="162.0324707" cy="166.964798" fill="#FFFFFF" r="26.0319996"/>
<rect fill="#FFFFFF" height="10" width="161.7552032" x="214.2442017" y="161.965332"/>
<circle cx="162.0324707" cy="256" fill="#FFFFFF" r="26.0319996"/>
<rect fill="#FFFFFF" height="10" width="161.7552032" x="214.2442017" y="251.0010681"/>
<circle cx="162.0324707" cy="345.034668" fill="#FFFFFF" r="26.0319996"/>
<rect fill="#FFFFFF" height="10" width="161.7552032" x="214.2442017" y="340.0341492"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -1,374 +0,0 @@
<?xml version="1.0" ?>
<svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#ffffff"/>
<stop offset="1" style="stop-color:#aaaaaa"/>
</linearGradient><circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#aaaaaa"/>
<stop offset="1" style="stop-color:#ffffff"/>
</linearGradient>
<path d="M256,469.3338623c-117.6314697,0-213.3333435-95.7023926-213.3333435-213.3333435 c0-117.6314545,95.7018661-213.333313,213.3333435-213.333313c117.6357422,0,213.3333435,95.7018661,213.3333435,213.333313 C469.3333435,373.6314697,373.6357422,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/>
</g>
<g transform="translate(80 80) scale(0.5 0.5)">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M642.761,7.857c27.524-13.874,20.688-7.637,36.396,5.242
c21.389,17.531-47.403,113.702-85.56,101.273C597.209,66.095,628.271,15.159,642.761,7.857z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M388.764,16.05c10.221,28.978,21.564,101.561-24.58,98.322
C347.148,88.668,329.25,7.829,388.764,16.05z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M303.417,128.713c-9.341,33.239-52.357,26.405-65.547,0
C209.087,71.091,320.382,68.365,303.417,128.713z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M519.854,524.039c-24.021-0.559-30.431-18.727-40.963-32.773
c-15.603,3.518-20.585,17.65-40.968,16.385c-21.542-2.428-6.449,31.787-32.771,24.582c8.412-27.529-25.482-12.754-40.968-16.387
c-12.178,6.941-11.856,26.383-16.386,40.969c-14.608-19.396,16.189-66.479-40.968-57.357c-1.641-31.131-34.453-31.09-32.772-65.547
c-50.24,12.574-80.853,44.779-131.094,57.355c26.667-44.338,87.719-54.301,131.094-81.934c-5.64-32.949,7.849-46.771,8.193-73.74
c-16.702-18.804-68.042-2.965-98.322-8.193c21.309-16.93,59.1-17.375,98.322-16.386c23.035-10.227,27.083-33.584,16.386-57.354
c10.475,0.45,11.347,10.5,24.579,8.193c2.721-32.761-14.605-45.48-8.193-81.933c21.864,5.446,30.003,24.619,40.968,40.965
c10.298-3.356,16.748-10.566,16.386-24.579c6.384,1.809,9.526,6.86,8.193,16.386C464.654,198.51,397.173,40.41,495.275,81.598
c16.984,7.13,16.283,16.491,16.386,32.775c-3.441,15.665-2.963,35.265-24.576,32.772c-7.125,8.614-11.859,18.676-24.582,32.775
c51.397,11.42,94.795,30.834,106.515,81.933c32.949,5.641,46.771-7.848,73.743-8.193c-13.629,21.878-56.536,14.474-73.743,32.772
c-1.532,68.441,5.806,95.405-32.777,122.901c1.401,12.258-2.917,30.236,8.198,32.771c-11.423,13.158-40.435,8.727-49.163,24.582
C503.696,485.572,523.483,493.105,519.854,524.039z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M560.825,147.145
C591.118,171.985,535.943,174.746,560.825,147.145L560.825,147.145z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M749.27,220.884c17.286,2.513,8.66,15.924,0,16.389
c-8.66,0.461-23.257,1.407-24.579-8.196C726.069,219.534,731.983,218.372,749.27,220.884z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M708.305,237.273c1.556-9.304,8.222-4.549,8.193,0
C714.942,246.574,708.276,241.82,708.305,237.273z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M183.93,327.399C184.764,337.301,172.022,332.436,183.93,327.399
L183.93,327.399z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M85.611,335.592c3.008,24.861-26.687,17.007-40.968,24.579
C43.455,337.136,64.712,336.541,85.611,335.592z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M642.761,360.171c0.217-9.093,45.956-3.205,65.544-8.193
c-2.609,21.727,46.896,10.463,32.771,24.583C726.274,392.045,642.544,369.264,642.761,360.171z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M199.897,398.458c-12.073,32.979-66.239,39.39-68.1-9.286
C129.943,340.492,211.973,365.481,199.897,398.458z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M642.761,458.492c17.845,3.156,22.146,16.09,24.579,32.773
c31.985-16.23,70.102,20.02,65.544,40.967c-4.559,20.947-37.74,31.988-49.158,8.193c3.703,27.465-35.017,31.943-49.158,32.775
c-6.717,0.387-26.334-3.59-24.579-24.58c-24.311-3.002-42.555,0.217-49.163-32.775
C554.216,482.852,595.181,450.078,642.761,458.492z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M142.964,491.266
C143.796,501.166,131.054,496.301,142.964,491.266L142.964,491.266z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M298.637,507.65c0.464-4.996,1.225-9.697,8.193-8.193
C306.366,504.455,305.605,509.154,298.637,507.65z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M290.444,515.846c0.464-4.998,1.225-9.699,8.193-8.195
C298.173,512.646,297.413,517.352,290.444,515.846z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M101.997,540.426c-3.196,13.438-27.527,6.361-32.772-8.193
C63.98,517.672,105.193,526.988,101.997,540.426z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M290.444,515.846
C291.276,525.746,278.534,520.883,290.444,515.846L290.444,515.846z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M519.854,524.039
C529.757,523.207,524.891,535.947,519.854,524.039L519.854,524.039z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M3.678,573.201c-3.196-5.688-6.361-5.926,0-16.387
s67.646-29.844,57.354,16.387C55.537,602.986,6.874,578.881,3.678,573.201z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M405.152,556.814c2.823-9.891,21.799-6.627,24.579,0
C437.16,574.533,399.726,575.799,405.152,556.814z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M667.34,573.201c11.993,7.121,27.775,24.111,24.579,32.771
s-21.389,16.602-32.772,0C647.752,589.365,664.765,584.277,667.34,573.201z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M749.27,622.359c5.003,5.24,4.313,7.291,0,8.191
c-9.64-1.287-23.257,1.414-24.579-8.191C736.382,619.168,744.278,617.117,749.27,622.359z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M347.798,646.938c14.554,8.66,18.898,32.994,0,32.771
C328.899,679.492,333.244,638.277,347.798,646.938z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M577.211,646.938c12.928,14.381,37.62,17.002,40.971,40.965
C587.939,690.836,583.939,667.527,577.211,646.938z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M175.736,679.709
C174.905,669.807,187.647,674.674,175.736,679.709L175.736,679.709z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M175.736,679.709c-1,20.854-19.334,40.51-32.772,32.773
C129.527,704.756,150.086,675.914,175.736,679.709z"/>
</g>
<g transform="translate(80 70) scale(0.7 0.7)">
<path id="Selection"
fill="#e5e5e5"
d="M 413.00,255.00
C 413.00,255.00 408.00,255.00 408.00,255.00
408.00,255.00 408.00,439.00 408.00,439.00
401.94,437.78 374.70,423.25 373.82,420.55
373.14,418.48 374.09,98.55 374.09,98.55
374.09,95.17 374.22,92.97 373.36,89.64
368.51,70.71 345.00,57.64 328.82,57.27
328.82,57.27 249.00,56.00 249.00,56.00
249.00,56.00 105.64,57.09 105.64,57.09
90.17,57.11 75.87,66.25 69.64,80.82
67.20,86.51 64.76,93.81 64.73,100.00
64.73,100.00 64.91,397.09 64.91,397.09
64.95,422.07 85.64,446.45 110.64,446.73
110.64,446.73 214.00,448.00 214.00,448.00
214.00,448.00 404.91,446.91 404.91,446.91
426.82,447.00 454.59,424.12 454.64,395.64
454.64,395.64 454.73,225.45 454.73,225.45
454.71,212.87 449.50,203.71 437.09,199.27
431.28,197.20 416.91,197.64 412.73,202.82
411.27,204.73 408.19,206.11 408.18,208.36
408.18,208.36 413.00,255.00 413.00,255.00 Z" />
<g>
<linearGradient id="fadeGrad" y2="1" x2="0">
<stop offset="0.5" stop-color="#e5e5e5" stop-opacity="0"/>
<stop offset="1" stop-color="#e5e5e5" stop-opacity="1"/>
</linearGradient><path id="grey_fill"
fill="url(#fadeGrad)"
d="M 372.00,204.00
C 372.00,204.00 372.00,437.00 372.00,437.00
372.00,437.00 408.00,437.00 408.00,437.00
408.00,437.00 408.00,204.00 408.00,204.00
408.00,204.00 372.00,204.00 372.00,204.00 Z" />
</g>
<path id="border"
fill="#2c3e50"
d="M 97.00,56.12
C 92.47,57.54 89.03,58.99 85.00,61.35
74.99,67.21 66.62,78.52 64.12,90.00
64.12,90.00 64.12,112.00 64.12,112.00
64.12,112.00 64.12,141.00 64.12,141.00
64.12,141.00 64.12,399.00 64.12,399.00
64.04,421.84 82.68,447.71 107.00,448.00
107.00,448.00 214.00,448.00 214.00,448.00
214.00,448.00 407.00,448.00 407.00,448.00
432.43,447.96 455.96,423.15 456.00,398.00
456.00,398.00 456.00,225.00 456.00,225.00
455.97,208.44 444.97,196.00 428.00,196.00
422.37,196.00 418.95,196.42 414.01,199.48
403.91,205.73 400.02,217.75 400.00,229.00
400.00,229.00 400.00,385.00 400.00,385.00
400.00,388.14 399.82,392.11 401.31,394.95
404.11,400.27 410.69,400.91 414.15,395.87
416.23,392.83 415.99,388.53 416.00,385.00
416.00,385.00 416.00,227.00 416.00,227.00
416.01,223.53 415.73,220.08 417.74,217.04
422.00,210.58 432.71,210.16 437.57,216.11
440.32,219.47 439.99,222.95 440.00,227.00
440.00,227.00 440.00,396.00 440.00,396.00
439.99,399.36 440.10,401.68 439.24,405.00
435.84,418.21 422.05,431.64 408.00,431.99
387.86,432.47 376.09,417.15 376.00,398.00
376.00,398.00 376.00,356.00 376.00,356.00
376.00,356.00 376.00,98.00 376.00,98.00
375.96,73.58 350.92,56.04 328.00,56.12
328.00,56.12 175.00,56.12 175.00,56.12
175.00,56.12 125.00,56.12 125.00,56.12
125.00,56.12 97.00,56.12 97.00,56.12 Z
M 371.00,432.00
C 371.00,432.00 110.00,432.00 110.00,432.00
106.92,432.00 103.99,432.10 101.00,431.20
88.05,427.30 80.16,412.89 80.00,400.00
80.00,400.00 80.00,355.00 80.00,355.00
80.00,355.00 80.00,99.00 80.00,99.00
80.01,95.19 79.89,92.73 81.05,89.00
82.31,84.96 84.15,82.08 87.09,79.04
95.54,70.33 104.02,72.00 115.00,72.00
115.00,72.00 137.00,72.00 137.00,72.00
137.00,72.00 327.00,72.00 327.00,72.00
343.88,72.03 359.91,80.22 360.00,99.00
360.00,99.00 360.00,158.00 360.00,158.00
360.00,158.00 360.00,329.00 360.00,329.00
360.00,329.00 360.00,376.00 360.00,376.00
360.00,387.70 359.54,399.52 362.24,411.00
364.74,421.66 367.08,423.20 371.00,432.00 Z" />
<path id="text"
fill="#2c3e50"
d="M 183.00,110.64
C 179.64,111.42 176.99,112.23 174.00,114.13
157.51,124.60 159.97,141.24 160.00,158.00
160.01,163.24 160.33,167.99 162.10,173.00
166.51,185.43 179.93,193.80 193.00,190.98
208.62,187.62 215.82,175.17 216.00,160.00
216.23,140.44 218.24,117.01 194.00,110.64
190.07,109.75 186.95,109.89 183.00,110.64 Z
M 229.10,110.77
C 227.21,111.91 226.64,111.99 225.45,114.11
223.13,118.24 224.00,132.64 224.00,138.00
224.00,138.00 224.00,179.00 224.00,179.00
224.05,182.84 224.15,186.73 227.28,189.49
230.97,192.75 238.29,192.01 243.00,192.00
258.52,191.98 277.71,186.71 274.56,167.00
273.97,163.32 273.04,160.18 271.03,157.00
269.78,155.02 266.97,152.06 266.51,150.00
266.00,147.74 267.57,145.18 268.20,143.00
269.04,140.08 269.06,137.01 268.99,134.00
268.63,119.33 257.94,113.28 245.00,110.77
239.84,109.82 234.22,109.45 229.10,110.77 Z
M 320.00,164.00
C 319.29,166.34 318.67,168.92 317.28,170.93
312.51,177.90 302.17,177.38 298.30,170.93
296.06,167.14 296.74,162.14 294.41,159.28
291.16,155.30 285.55,156.17 282.74,160.13
277.83,167.04 285.08,179.04 290.04,183.96
300.83,194.65 316.52,193.80 326.90,182.96
329.03,180.72 330.34,178.74 331.72,176.00
340.59,158.44 327.85,149.91 313.00,143.42
309.43,141.86 296.86,137.96 296.66,133.89
296.37,128.22 304.01,124.60 309.00,125.57
313.67,126.48 316.77,129.55 318.20,134.00
319.62,138.40 318.92,143.38 325.00,144.67
334.12,146.59 334.63,135.91 332.78,130.00
329.78,120.39 321.94,112.67 312.00,110.52
299.03,107.70 285.65,116.60 281.55,129.00
280.62,132.69 279.59,137.37 281.55,141.00
283.97,146.59 289.75,149.61 295.00,152.25
295.00,152.25 320.00,164.00 320.00,164.00 Z
M 136.00,128.00
C 136.00,128.00 136.00,162.00 136.00,162.00
135.99,167.25 136.21,172.61 130.90,175.55
120.70,181.19 117.27,168.68 112.90,165.86
108.58,163.08 103.38,165.23 101.78,170.02
100.74,173.13 102.36,176.34 103.88,179.00
108.07,186.34 115.38,191.75 124.00,191.98
127.40,192.08 130.76,192.02 134.00,190.89
137.66,189.62 140.24,187.67 142.96,184.96
154.71,173.20 152.00,155.19 152.00,140.00
152.00,140.00 152.00,127.00 152.00,127.00
151.98,116.47 150.64,112.14 139.00,112.00
134.77,111.95 127.63,111.45 124.13,113.85
119.09,117.31 119.73,123.89 125.05,126.69
128.06,128.27 132.65,128.00 136.00,128.00 Z
M 186.00,125.32
C 190.79,125.04 195.35,125.73 198.26,130.06
200.13,132.83 199.98,135.82 200.00,139.00
200.00,139.00 200.00,164.00 200.00,164.00
199.92,169.87 198.61,174.57 192.00,175.88
192.00,175.88 187.00,175.88 187.00,175.88
177.07,175.73 176.01,170.43 176.00,162.00
176.00,162.00 176.00,148.00 176.00,148.00
176.00,143.84 175.41,134.40 177.17,131.02
179.19,127.16 182.05,126.16 186.00,125.32 Z
M 240.00,144.00
C 240.00,144.00 240.00,125.00 240.00,125.00
243.48,125.70 247.87,126.53 250.66,128.85
255.54,132.93 254.41,138.65 248.98,141.55
246.56,142.85 242.73,143.48 240.00,144.00 Z
M 240.00,177.00
C 240.00,177.00 240.00,160.00 240.00,160.00
243.26,160.15 253.37,160.99 255.86,162.45
260.04,164.92 261.00,171.96 256.77,174.91
252.86,177.64 244.73,177.00 240.00,177.00 Z
M 119.00,216.57
C 113.27,218.89 109.82,224.65 115.28,229.49
117.99,231.89 121.59,231.95 125.00,232.00
125.00,232.00 161.00,232.00 161.00,232.00
164.53,231.99 168.83,232.23 171.87,230.15
176.46,227.00 176.46,221.00 171.87,217.85
169.27,216.07 166.02,216.04 163.00,216.00
163.00,216.00 119.00,216.57 119.00,216.57 Z
M 239.00,216.57
C 232.65,219.14 229.74,225.77 236.13,230.15
239.17,232.23 243.47,231.99 247.00,232.00
247.00,232.00 289.00,232.00 289.00,232.00
292.53,231.99 296.83,232.23 299.87,230.15
304.46,227.00 304.46,221.00 299.87,217.85
297.03,215.91 293.29,216.02 290.00,216.00
290.00,216.00 239.00,216.57 239.00,216.57 Z
M 119.00,240.57
C 112.65,243.14 109.74,249.77 116.13,254.15
119.17,256.23 123.47,255.99 127.00,256.00
127.00,256.00 193.00,256.00 193.00,256.00
196.14,256.00 200.11,256.18 202.95,254.69
208.27,251.89 208.91,245.31 203.87,241.85
200.83,239.77 196.53,240.01 193.00,240.00
193.00,240.00 145.00,240.00 145.00,240.00
145.00,240.00 119.00,240.57 119.00,240.57 Z
M 239.00,240.57
C 232.65,243.14 229.74,249.77 236.13,254.15
239.17,256.23 243.47,255.99 247.00,256.00
247.00,256.00 313.00,256.00 313.00,256.00
316.53,255.99 320.83,256.23 323.87,254.15
328.91,250.69 328.27,244.11 322.95,241.31
320.11,239.82 316.14,240.00 313.00,240.00
313.00,240.00 265.00,240.00 265.00,240.00
265.00,240.00 239.00,240.57 239.00,240.57 Z
M 119.00,264.57
C 112.65,267.14 109.74,273.77 116.13,278.15
119.17,280.23 123.47,279.99 127.00,280.00
127.00,280.00 185.00,280.00 185.00,280.00
188.14,280.00 192.11,280.18 194.95,278.69
200.27,275.89 200.91,269.31 195.87,265.85
192.83,263.77 188.53,264.01 185.00,264.00
185.00,264.00 143.00,264.00 143.00,264.00
136.67,264.00 124.68,263.54 119.00,264.57 Z
M 239.00,264.57
C 232.65,267.14 229.74,273.77 236.13,278.15
239.17,280.23 243.47,279.99 247.00,280.00
247.00,280.00 305.00,280.00 305.00,280.00
308.14,280.00 312.11,280.18 314.95,278.69
320.27,275.89 320.91,269.31 315.87,265.85
312.83,263.77 308.53,264.01 305.00,264.00
305.00,264.00 263.00,264.00 263.00,264.00
256.67,264.00 244.68,263.54 239.00,264.57 Z
M 239.00,288.57
C 232.65,291.14 229.74,297.77 236.13,302.15
239.17,304.23 243.47,303.99 247.00,304.00
247.00,304.00 313.00,304.00 313.00,304.00
316.14,304.00 320.11,304.18 322.95,302.69
328.27,299.89 328.91,293.31 323.87,289.85
320.83,287.77 316.53,288.01 313.00,288.00
313.00,288.00 265.00,288.00 265.00,288.00
265.00,288.00 239.00,288.57 239.00,288.57 Z
M 119.00,312.57
C 112.65,315.14 109.74,321.77 116.13,326.15
119.17,328.23 123.47,327.99 127.00,328.00
127.00,328.00 169.00,328.00 169.00,328.00
172.53,327.99 176.83,328.23 179.87,326.15
184.46,323.00 184.46,317.00 179.87,313.85
177.03,311.91 173.29,312.02 170.00,312.00
170.00,312.00 119.00,312.57 119.00,312.57 Z
M 119.00,336.57
C 112.65,339.14 109.74,345.77 116.13,350.15
119.17,352.23 123.47,351.99 127.00,352.00
127.00,352.00 193.00,352.00 193.00,352.00
196.14,352.00 200.11,352.18 202.95,350.69
208.27,347.89 208.91,341.31 203.87,337.85
200.83,335.77 196.53,336.01 193.00,336.00
193.00,336.00 145.00,336.00 145.00,336.00
145.00,336.00 119.00,336.57 119.00,336.57 Z
M 239.00,336.57
C 233.27,338.89 229.82,344.65 235.28,349.49
237.99,351.89 241.59,351.95 245.00,352.00
245.00,352.00 281.00,352.00 281.00,352.00
284.53,351.99 288.83,352.23 291.87,350.15
296.33,347.09 296.33,340.91 291.87,337.85
289.27,336.07 286.02,336.04 283.00,336.00
283.00,336.00 239.00,336.57 239.00,336.57 Z
M 119.00,360.57
C 112.65,363.14 109.74,369.77 116.13,374.15
119.17,376.23 123.47,375.99 127.00,376.00
127.00,376.00 185.00,376.00 185.00,376.00
188.14,376.00 192.11,376.18 194.95,374.69
200.27,371.89 200.91,365.31 195.87,361.85
192.83,359.77 188.53,360.01 185.00,360.00
185.00,360.00 143.00,360.00 143.00,360.00
136.67,360.00 124.68,359.54 119.00,360.57 Z
M 239.00,360.57
C 232.65,363.14 229.74,369.77 236.13,374.15
239.17,376.23 243.47,375.99 247.00,376.00
247.00,376.00 313.00,376.00 313.00,376.00
316.14,376.00 320.11,376.18 322.95,374.69
328.27,371.89 328.91,365.31 323.87,361.85
320.83,359.77 316.53,360.01 313.00,360.00
313.00,360.00 265.00,360.00 265.00,360.00
265.00,360.00 239.00,360.57 239.00,360.57 Z
M 119.00,384.57
C 112.65,387.14 109.74,393.77 116.13,398.15
119.17,400.23 123.47,399.99 127.00,400.00
127.00,400.00 193.00,400.00 193.00,400.00
196.14,400.00 200.11,400.18 202.95,398.69
208.27,395.89 208.91,389.31 203.87,385.85
200.83,383.77 196.53,384.01 193.00,384.00
193.00,384.00 145.00,384.00 145.00,384.00
145.00,384.00 119.00,384.57 119.00,384.57 Z
M 239.00,384.57
C 232.65,387.14 229.74,393.77 236.13,398.15
239.17,400.23 243.47,399.99 247.00,400.00
247.00,400.00 305.00,400.00 305.00,400.00
308.53,399.99 312.83,400.23 315.87,398.15
320.91,394.69 320.27,388.11 314.95,385.31
312.11,383.82 308.14,384.00 305.00,384.00
305.00,384.00 263.00,384.00 263.00,384.00
256.67,384.00 244.68,383.54 239.00,384.57 Z" />
</g>
<g>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#9C141C" d="M126.579,753.453c-7.296,8.654-15.924,8.414-24.582,0
c-8.657-8.416-11.982-46.715,16.386-40.971C139.233,713.313,133.872,744.797,126.579,753.453z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1,88 @@
<?xml version="1.0" ?>
<svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#A364AB"/>
<stop offset="1" style="stop-color:#8C3D96"/>
</linearGradient>
<circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#8C3D96"/>
<stop offset="1" style="stop-color:#A364AB"/>
</linearGradient>
<path d="M256,469.3338623c-117.6315155,0-213.3333435-95.7018738-213.3333435-213.3333435 S138.3684845,42.6671982,256,42.6671982c117.6356812,0,213.3333435,95.7018661,213.3333435,213.333313 S373.6356812,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/>
</g>
<path id="Shadow"
opacity="0.3"
d="M 320.92,166.77
C 320.92,166.77 282.98,166.77 282.98,166.77
282.98,166.77 282.98,258.00 282.98,258.00
282.98,258.00 236.32,258.00 236.32,258.00
236.30,258.09 236.35,268.00 236.32,268.00
236.32,268.00 282.98,268.00 282.98,268.00
282.98,268.00 282.98,359.23 282.98,359.23
282.98,359.23 320.92,359.23 320.92,359.23
320.92,359.23 320.92,380.76 320.92,380.76
320.92,380.76 374.00,380.76 374.00,380.76
374.00,380.76 374.00,327.69 374.00,327.69
374.00,327.69 320.92,327.69 320.92,327.69
320.92,327.69 320.92,349.23 320.92,349.23
320.92,349.23 292.98,349.23 292.98,349.23
292.98,349.23 292.98,268.00 292.98,268.00
292.98,268.00 320.92,268.00 320.92,268.00
320.92,268.00 320.92,289.54 320.92,289.54
320.92,289.54 374.00,289.54 374.00,289.54
374.00,289.54 374.00,236.46 374.00,236.46
374.00,236.46 320.92,236.46 320.92,236.46
320.92,236.46 320.92,258.00 320.92,258.00
320.92,258.00 292.98,258.00 292.98,258.00
292.98,258.00 292.98,176.77 292.98,176.77
292.98,176.77 320.92,176.77 320.92,176.77
320.92,176.77 320.92,198.31 320.92,198.31
320.92,198.31 374.00,198.31 374.00,198.31
374.00,198.31 374.00,145.24 374.00,145.24
374.00,145.24 320.92,145.24 320.92,145.24
320.92,145.24 320.92,166.77 320.92,166.77 Z"
/>
<path
id="Ico"
fill="#fff"
d="M 312.92,159.77
C 312.92,159.77 274.98,159.77 274.98,159.77
274.98,159.77 274.98,251.00 274.98,251.00
274.98,251.00 228.32,251.00 228.32,251.00
228.38,251.00 228.38,260.94 228.32,261.00
228.32,261.00 274.98,261.00 274.98,261.00
274.98,261.00 274.98,352.23 274.98,352.23
274.98,352.23 312.92,352.23 312.92,352.23
312.92,352.23 312.92,373.76 312.92,373.76
312.92,373.76 366.00,373.76 366.00,373.76
366.00,373.76 366.00,320.69 366.00,320.69
366.00,320.69 312.92,320.69 312.92,320.69
312.92,320.69 312.92,342.23 312.92,342.23
312.92,342.23 284.98,342.23 284.98,342.23
284.98,342.23 284.98,261.00 284.98,261.00
284.98,261.00 312.92,261.00 312.92,261.00
312.92,261.00 312.92,282.54 312.92,282.54
312.92,282.54 366.00,282.54 366.00,282.54
366.00,282.54 366.00,229.46 366.00,229.46
366.00,229.46 312.92,229.46 312.92,229.46
312.92,229.46 312.92,251.00 312.92,251.00
312.92,251.00 284.98,251.00 284.98,251.00
284.98,251.00 284.98,169.77 284.98,169.77
284.98,169.77 312.92,169.77 312.92,169.77
312.92,169.77 312.92,191.31 312.92,191.31
312.92,191.31 366.00,191.31 366.00,191.31
366.00,191.31 366.00,138.24 366.00,138.24
366.00,138.24 312.92,138.24 312.92,138.24
312.92,138.24 312.92,159.77 312.92,159.77 Z"
/>
<g transform="translate(78.00 56.00) scale(0.6) rotate(22)">
<path
d="M383.9994202,278.4917297v-28.9818573l-26.3045349-4.4810791 c-2.4773254-12.3055878-7.3322144-23.741333-14.0432129-33.8426666l15.4474792-21.79039l-20.4943848-20.4949341 l-21.7909546,15.447998c-10.1013184-6.7114716-21.5359802-11.5664063-33.8426514-14.0442657L278.4906616,144h-28.9829712 l-4.4794769,26.3045349c-12.3061218,2.4778595-23.7423859,7.3327942-33.8437195,14.0442657l-21.7909393-15.447998 l-20.493866,20.4949341l15.447998,21.79039c-6.7109375,10.1013336-11.5658722,21.5370789-14.0442657,33.8426666 l-26.3029327,4.4810791v28.9818573l26.3029327,4.480011c2.4783936,12.3061218,7.3333282,23.7429199,14.0442657,33.8426514 l-15.447998,21.7920227l20.493866,20.4933167l21.7925262-15.447998 c10.0997467,6.7120056,21.5360107,11.5664063,33.8421326,14.044281L249.5076904,384h28.9829712l4.4804993-26.3039856 c12.3066711-2.4778748,23.741333-7.3322754,33.8426514-14.044281l21.7909546,15.447998l20.4943848-20.4933167 l-15.4474792-21.7920227c6.7109985-10.0997314,11.5658875-21.5365295,14.0432129-33.8426514L383.9994202,278.4917297z M264,313.1525269c-27.1466675,0-49.1531067-22.0053406-49.1531067-49.1525269 c0-27.1461334,22.0064392-49.1519928,49.1531067-49.1519928c27.1460876,0,49.1524963,22.0058594,49.1524963,49.1519928 C313.1524963,291.1471863,291.1460876,313.1525269,264,313.1525269z" opacity="0.3"/><path d="M375.9994202,270.4917297v-28.9818573l-26.3045349-4.4810791 c-2.4773254-12.3055878-7.3322144-23.741333-14.0432129-33.8426666l15.4474792-21.79039l-20.4943848-20.4949341 l-21.7909546,15.447998c-10.1013184-6.7114716-21.5359802-11.5664063-33.8426514-14.0442657L270.4906616,136h-28.9829712 l-4.4794617,26.3045349c-12.3061371,2.4778595-23.7424011,7.3327942-33.8437347,14.0442657l-21.7909393-15.447998 l-20.493866,20.4949341l15.447998,21.79039c-6.7109375,10.1013336-11.5658569,21.5370789-14.0442657,33.8426666 l-26.3029327,4.4810791v28.9818573l26.3029327,4.480011c2.4784088,12.3061218,7.3333282,23.7429199,14.0442657,33.8426514 l-15.447998,21.7920227l20.493866,20.4933167l21.7925262-15.447998 c10.0997467,6.7120056,21.5360107,11.5664063,33.8421478,14.044281L241.5076904,376h28.9829712l4.4804993-26.3039856 c12.3066711-2.4778748,23.741333-7.3322754,33.8426514-14.044281l21.7909546,15.447998l20.4943848-20.4933167 l-15.4474792-21.7920227c6.7109985-10.0997314,11.5658875-21.5365295,14.0432129-33.8426514L375.9994202,270.4917297z M256,305.1525269c-27.1466675,0-49.1531067-22.0053406-49.1531067-49.1525269 c0-27.1461334,22.0064392-49.1520081,49.1531067-49.1520081c27.1460876,0,49.1524963,22.0058746,49.1524963,49.1520081 C305.1524963,283.1471863,283.1460876,305.1525269,256,305.1525269z" fill="#FFFFFF"
/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#33B49D"/>
<stop offset="1" style="stop-color:#008165"/>
</linearGradient><circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#008165"/>
<stop offset="1" style="stop-color:#33B49D"/>
</linearGradient>
<path
d="
M 256,469.3338623
c -117.6315308,0-213.3333435-95.7023926-213.3333435-213.3333435
c 0-117.6314545,95.7018051 -213.333313,213.3333435 -213.333313
c 117.6362,0,213.3333435,95.7018661,213.3333435,213.333313
C 469.3333435,373.6314697,373.6362,469.3338623,256,469.3338623
z"
fill="url(#SVGID_2_)"
/>
</g>
<g transform="translate(114.00 60.00) scale(0.36)">
<path id="Selection"
fill="#fff"
d="
M 458.00,960.00
C 450.88,953.38 442.00,950.89 432.43,939.99
424.10,930.49 415.98,914.44 409.70,903.00
409.70,903.00 387.22,863.00 387.22,863.00
379.45,848.37 368.40,834.14 368.01,817.00
367.68,802.29 373.94,787.57 384.04,777.00
388.72,772.10 392.22,768.68 398.00,765.06
401.36,762.96 406.75,760.75 408.23,756.91
409.23,754.39 408.04,748.24 408.23,745.00
408.23,745.00 408.23,728.00 408.23,728.00
408.77,725.44 409.86,719.43 408.81,717.21
407.54,714.52 394.42,706.83 391.00,703.82
378.11,692.49 369.21,677.53 369.00,660.00
368.74,637.82 378.18,625.54 388.72,607.00
388.72,607.00 404.58,579.00 404.58,579.00
419.04,553.47 430.38,524.76 461.00,515.03
466.51,513.28 473.21,511.81 479.00,512.04
487.55,512.37 497.34,515.55 505.00,519.26
508.57,520.99 516.36,525.59 520.00,525.45
524.42,525.28 536.68,517.19 541.00,514.60
550.83,508.70 554.24,508.66 553.99,498.00
553.92,495.14 553.75,492.58 552.15,490.09
549.35,485.74 533.16,475.67 528.00,472.60
506.88,460.04 485.49,452.03 462.00,445.29
452.70,442.62 438.39,438.43 429.00,438.00
429.00,438.00 429.00,417.00 429.00,417.00
429.01,414.41 428.81,411.37 430.02,409.02
432.91,403.42 446.25,397.85 452.00,394.69
464.79,387.66 485.94,369.70 495.91,359.00
527.77,324.81 548.67,279.79 552.09,233.00
552.09,233.00 553.00,223.00 553.00,223.00
553.00,223.00 553.00,205.00 553.00,205.00
552.99,196.67 551.62,188.21 550.25,180.00
542.09,131.16 517.05,84.54 479.00,52.44
457.48,34.29 433.59,21.86 407.00,13.00
391.73,7.91 364.92,2.02 349.00,2.00
349.00,2.00 327.00,2.00 327.00,2.00
306.93,2.03 274.74,10.01 256.00,17.45
190.76,43.34 140.60,101.93 127.00,171.00
125.43,178.97 123.04,195.15 123.00,203.00
123.00,203.00 123.00,221.00 123.00,221.00
123.00,221.00 124.87,242.00 124.87,242.00
130.70,283.98 147.23,322.01 175.29,354.00
186.40,366.68 200.07,378.48 214.00,387.97
214.00,387.97 233.00,399.69 233.00,399.69
237.27,402.07 242.48,404.45 244.83,409.01
245.92,411.15 246.92,422.15 246.99,425.00
247.08,429.22 247.31,433.36 243.77,436.35
240.37,439.23 230.76,440.63 226.00,442.00
226.00,442.00 202.00,449.66 202.00,449.66
202.00,449.66 187.00,454.42 187.00,454.42
158.57,465.16 122.49,483.86 100.00,504.17
82.80,519.70 68.90,538.95 57.43,559.00
32.23,603.04 14.06,661.71 7.28,712.00
4.04,736.04 2.04,760.77 2.00,785.00
2.00,785.00 1.00,802.00 1.00,802.00
1.00,802.00 1.00,856.00 1.00,856.00
1.06,891.28 37.59,906.11 66.00,916.00
66.00,916.00 83.00,921.86 83.00,921.86
83.00,921.86 104.00,927.87 104.00,927.87
152.12,939.98 187.83,945.13 237.00,949.83
237.00,949.83 256.00,951.17 256.00,951.17
256.00,951.17 295.00,954.09 295.00,954.09
295.00,954.09 324.00,956.00 324.00,956.00
324.00,956.00 343.00,957.00 343.00,957.00
343.00,957.00 358.00,958.00 358.00,958.00
358.00,958.00 372.00,958.00 372.00,958.00
372.00,958.00 387.00,959.00 387.00,959.00
387.00,959.00 407.00,959.00 407.00,959.00
407.00,959.00 422.00,960.00 422.00,960.00
422.00,960.00 458.00,960.00 458.00,960.00
Z
M 597.00,494.00
C 597.00,494.00 597.00,538.00 597.00,538.00
597.00,540.78 597.21,544.42 595.83,546.89
593.17,551.65 579.34,555.10 574.00,557.05
558.16,562.86 541.56,572.75 528.00,582.73
523.78,585.84 510.63,597.85 507.00,598.67
502.87,599.60 495.59,594.53 492.00,592.40
482.64,586.85 468.27,577.41 458.00,575.00
458.00,575.00 431.72,620.00 431.72,620.00
431.72,620.00 411.00,658.00 411.00,658.00
411.00,658.00 459.00,685.00 459.00,685.00
455.62,697.75 451.98,717.92 452.00,731.00
452.00,731.00 453.00,751.00 453.00,751.00
453.13,762.00 455.58,778.56 459.00,789.00
459.00,789.00 426.00,807.58 426.00,807.58
422.31,809.67 414.78,813.03 413.21,817.04
411.47,821.45 420.54,834.72 423.00,839.00
423.00,839.00 459.00,901.00 459.00,901.00
459.00,901.00 491.00,882.85 491.00,882.85
493.72,881.29 501.87,876.24 504.58,876.16
508.36,876.05 513.34,881.46 516.00,883.84
522.42,889.55 530.71,896.03 538.00,900.58
548.81,907.32 560.10,913.34 572.00,917.94
576.42,919.65 591.11,924.37 593.77,926.65
597.41,929.75 596.99,933.68 597.00,938.00
597.00,938.00 597.00,980.00 597.00,980.00
597.00,980.00 693.00,980.00 693.00,980.00
693.00,980.00 693.00,926.00 693.00,926.00
711.77,923.35 741.60,908.84 757.00,897.86
765.57,891.74 768.35,889.04 776.00,882.17
778.42,880.00 782.65,876.06 786.00,875.96
789.74,875.85 797.64,881.49 801.00,883.58
810.62,889.54 821.70,896.56 832.00,901.00
832.00,901.00 854.85,861.00 854.85,861.00
854.85,861.00 880.00,816.00 880.00,816.00
880.00,816.00 848.00,797.42 848.00,797.42
844.21,795.25 837.01,791.80 835.03,787.96
833.22,784.46 835.27,777.88 835.92,774.00
837.31,765.75 838.99,753.22 839.00,745.00
839.03,722.99 837.69,706.48 832.00,685.00
832.00,685.00 879.00,659.00 879.00,659.00
879.00,659.00 858.30,620.00 858.30,620.00
858.30,620.00 842.42,592.00 842.42,592.00
842.42,592.00 832.00,574.00 832.00,574.00
821.43,577.69 809.70,585.32 800.00,591.20
796.39,593.39 789.09,598.68 785.00,598.56
781.12,598.45 776.87,594.02 774.00,591.58
766.22,584.96 759.75,579.50 751.00,574.06
739.95,567.20 728.14,561.41 716.00,556.81
711.43,555.07 699.37,551.65 696.43,548.59
693.64,545.68 694.01,541.72 694.00,538.00
694.00,538.00 694.00,494.00 694.00,494.00
694.00,494.00 597.00,494.00 597.00,494.00
Z
M 634.00,619.29
C 667.81,616.13 706.02,628.51 729.91,653.04
770.54,694.74 774.57,766.57 737.54,812.00
725.64,826.59 714.66,834.43 698.00,842.75
677.41,853.04 660.74,855.26 638.00,855.00
631.50,854.92 623.34,853.21 617.00,851.63
583.30,843.20 554.03,820.90 539.31,789.00
529.16,767.01 527.89,753.66 528.00,730.00
528.17,693.08 552.71,654.07 584.00,635.45
600.28,625.76 615.54,622.10 634.00,619.29
Z
"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB

View file

@ -0,0 +1,114 @@
<?xml version="1.0" ?>
<svg id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#ffffff"/>
<stop offset="1" style="stop-color:#aaaaaa"/>
</linearGradient><circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#aaaaaa"/>
<stop offset="1" style="stop-color:#ffffff"/>
</linearGradient>
<path d="M256,469.3338623c-117.6314697,0-213.3333435-95.7023926-213.3333435-213.3333435 c0-117.6314545,95.7018661-213.333313,213.3333435-213.333313c117.6357422,0,213.3333435,95.7018661,213.3333435,213.333313 C469.3333435,373.6314697,373.6357422,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/>
</g>
<g>
<path
fill="#007dce"
d="
M386.6,277.5
c-16.3-16.2-42.7-16.2-58.9,0
c-2.3,2.3-4.2,4.8-5.9,7.4
c0,0,0,0,0,0
c-0.1,0.2-0.1,0.3-0.2,0.5
c-21.4-2.3-53.7,4.9-74.4,44.4
c-13.7,26-35.8,44.3-57.9,47.7
c-9.4,1.4-22.9,0.7-34.4-10.7
c-14.8-14.7-21.6-37.1-16.7-54.4
c1.8-6.4,6.2-14.5,16.4-16.8
c21.2-4.9,38.3-13,48.7-18.9
c0.3,0.3,0.6,0.5,0.9,0.8
c5.5,4,11.7,7.1,18.6,9.1
c6.3,1.9,12.8,0.5,17.6-3.2
c37.1,1.9,77.8-20.8,80.1-22.1
c4.8-2.8,7-8.4,5.4-13.5
c0,0,0,0,0,0
l-3-4.2
l3,4.2
c26.4-21,29.7-41.6,29.2-51.8
c0,0,0,0,0,0
c2.7-1.9,4.2-5.2,3.5-8.5
l-3.4-16.1
c-0.9-4.2-4.9-6.6-9-5.2
l-13.7,4.3
c-4.1,1.3-5.8,5.4-3.7,9.2
l7.8,14.2
c2,3.6,6.7,5.6,10.7,4.8
c0,0.1,0,0.1,0,0.2
c-0.3,9-4.3,25.5-26,42.8
c0,0,0,0-0.1,0
c-3.6-2.4-8.3-2.8-12.4-0.5
c-12.2,6.8-39.5,18.8-62.7,19.2
c-2.2-5-6.4-9.1-12.1-10.8
c-3.7-1.1-6.7-2.8-9.2-5.2
l-0.5-0.5
c-2.5-2.6-4.3-5.8-5.4-9.7
c-1.6-5.6-5.5-9.9-10.4-12.2
c0.7-22.9,12.5-49.5,19.3-61.4
c2.2-3.9,1.9-8.5-0.4-12
l-0.7-0.5
l0.7,0.5
c-0.1-0.1-0.1-0.2-0.2-0.3
c17.5-21.8,34.1-25.7,43.2-26
v0
c0,0,0,0,0,0
c-0.9,4,1.1,8.7,4.8,10.7
l14.3,7.8
c3.8,2,7.9,0.4,9.3-3.7
l4.3-13.6
c1.3-4.1-1-8.1-5.3-9
l-16.1-3.4
c-3.2-0.7-6.5,0.8-8.4,3.3
c-0.1,0.1-0.1,0.1-0.2,0.2
c-10.2-0.4-31,2.7-52.1,29.1
l-0.1,0.1
c-5.1-1.6-10.8,0.6-13.6,5.4
c-1.3,2.3-23.3,41.5-22.2,77.9
c-4,4.8-5.7,11.3-3.8,17.7
c1.3,4.4,2.9,8.5,4.9,12.3
c0.4,0.6,0.9,1.2,1.3,1.9
c-9,4.6-22.6,10.4-38.4,14
c-16.4,3.8-28.7,15.8-33.7,33.1
c-7.3,25.5,1.8,56.6,22.6,77.3
c14.3,14.3,33.7,20.4,54.5,17.2
c29.7-4.6,57.8-27,75.1-59.9
c15.2-29,36-32.9,48.2-32.2
c0.4,9,3.4,18,9.4,25.4
c0.7,0.8,1.4,1.6,2.2,2.4
c16.3,16.2,42.7,16.2,58.9,0
C402.9,319.9,402.9,293.7,386.6,277.5
z
M368.7,318.2
c-6.4,6.3-16.7,6.3-23,0
c-6.3-6.3-6.3-16.6,0-22.9
c6.4-6.3,16.7-6.3,23,0
C375,301.7,375,311.9,368.7,318.2
z"
/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1 +0,0 @@
<?xml version="1.0" ?><svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g><linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="0.0000038" x2="512" y1="256" y2="256"><stop offset="0" style="stop-color:#00AEEE"/><stop offset="1" style="stop-color:#0095DA"/></linearGradient><circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/><linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666679" x2="469.3333435" y1="256.0005188" y2="256.0005188"><stop offset="0" style="stop-color:#0095DA"/><stop offset="1" style="stop-color:#00AEEE"/></linearGradient><path d="M256,469.3338623c-117.6314697,0-213.3333282-95.7023926-213.3333282-213.3333435 c0-117.6314545,95.7018585-213.333313,213.3333282-213.333313c117.636261,0,213.3333435,95.7018661,213.3333435,213.333313 C469.3333435,373.6314697,373.636261,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/></g><path d="M360.2250671,330.9226685v-37.9413452h-91.2255859v-46.6655884 c25.976532-2.5151978,46.2854309-24.3957367,46.2854309-51.0314636C315.2849121,166.9605255,292.3237305,144,264,144 s-51.2848053,22.9605255-51.2848053,51.2842712c0,26.6357269,20.3088074,48.5162659,46.2842865,51.0314636v46.6655884h-91.2256165 v37.9413452h-21.5375977V384h53.0762634v-53.0773315h-21.5386658v-27.9413452h81.2256165v27.9413452h-21.5376129V384h53.0763702 v-53.0773315h-21.5387573v-27.9413452h81.2255859v27.9413452H328.687561V384h53.0761719v-53.0773315H360.2250671z" opacity="0.3"/><path d="M352.2250671,322.9226685v-37.9413452h-91.2255859v-46.6655884 c25.976532-2.5151978,46.2854309-24.3957367,46.2854309-51.0314636C307.2849121,158.9605255,284.3237305,136,256,136 s-51.2848053,22.9605255-51.2848053,51.2842712c0,26.6357269,20.3088074,48.5162659,46.2842712,51.0314636v46.6655884h-91.2256012 v37.9413452h-21.5375977V376h53.0762634v-53.0773315h-21.5386658v-27.9413452h81.2256012v27.9413452h-21.5375977V376h53.0763702 v-53.0773315h-21.5387573v-27.9413452h81.2255859v27.9413452H320.687561V376h53.0761719v-53.0773315H352.2250671z" fill="#FFFFFF"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1 +0,0 @@
<?xml version="1.0" ?><svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g><linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="0.0000038" x2="512" y1="256" y2="256"><stop offset="0" style="stop-color:#F35CB5"/><stop offset="1" style="stop-color:#EC008C"/></linearGradient><circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/><linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666679" x2="469.3333435" y1="256.0005188" y2="256.0005188"><stop offset="0" style="stop-color:#EC008C"/><stop offset="1" style="stop-color:#F35CB5"/></linearGradient><path d="M256,469.3338623c-117.6314697,0-213.3333282-95.7018738-213.3333282-213.3333435 c0-117.6309204,95.7018585-213.333313,213.3333282-213.333313c117.6357422,0,213.3333435,95.7024002,213.3333435,213.333313 C469.3333435,373.6319885,373.6357422,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/></g><g><polygon opacity="0.3" points="341.5125427,245.7781372 334.3989258,238.6645355 269.0289001,304.0325317 269.0289001,148.352005 258.9679871,148.352005 258.9679871,304.0325317 193.6026611,238.6645355 186.4895935,245.7781372 263.9984131,323.2896118 "/><polygon opacity="0.3" points="337.4869385,265.3551941 264.3935852,339.2314758 190.517334,265.3551941 144,265.3551941 144,379.6485291 384,379.6485291 384,265.3551941 "/></g><g><polygon fill="#FFFFFF" points="333.5125427,237.7781372 326.3989258,230.6645355 261.0289001,296.0325317 261.0289001,140.352005 250.9680023,140.352005 250.9680023,296.0325317 185.6026611,230.6645355 178.4896088,237.7781372 255.9983978,315.2896118 "/><polygon fill="#FFFFFF" points="329.4869385,257.3551941 256.3935852,331.2314758 182.517334,257.3551941 136,257.3551941 136,371.6485291 376,371.6485291 376,257.3551941 "/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,136 @@
<?xml version="1.0" ?>
<svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#000"/>
<stop offset="1" style="stop-color:#456"/>
</linearGradient>
<circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#456"/>
<stop offset="1" style="stop-color:#000"/>
</linearGradient>
<path d="M256,469.3338623c-117.6314697,0-213.3333435-95.7023926-213.3333435-213.3333435 c0-117.6314545,95.7018661-213.333313,213.3333435-213.333313c117.636261,0,213.3333435,95.7018661,213.3333435,213.333313 C469.3333435,373.6314697,373.636261,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/>
</g>
<g id="cover-shadow"
transform="translate(124.00 124.00) scale(2)">
<g transform="translate(-8 -8) scale(1.15)">
<path id="Selection"
fill="#fff"
d="
M 0.00,0.00
C 0.00,0.00 0.00,24.00 0.00,24.00
0.00,24.00 8.00,24.00 8.00,24.00
8.00,24.00 8.00,8.00 8.00,8.00
8.00,8.00 24.00,8.00 24.00,8.00
24.00,8.00 24.00,0.00 24.00,0.00
24.00,0.00 0.00,0.00 0.00,0.00
Z
M 40.00,0.00
C 40.00,0.00 40.00,8.00 40.00,8.00
40.00,8.00 56.00,8.00 56.00,8.00
56.00,8.00 56.00,0.00 56.00,0.00
56.00,0.00 40.00,0.00 40.00,0.00
Z
M 72.00,0.00
C 72.00,0.00 72.00,8.00 72.00,8.00
72.00,8.00 88.00,8.00 88.00,8.00
88.00,8.00 88.00,0.00 88.00,0.00
88.00,0.00 72.00,0.00 72.00,0.00
Z
M 104.00,0.00
C 104.00,0.00 104.00,8.00 104.00,8.00
104.00,8.00 120.00,8.00 120.00,8.00
120.00,8.00 120.00,24.00 120.00,24.00
120.00,24.00 128.00,24.00 128.00,24.00
128.00,24.00 128.00,0.00 128.00,0.00
128.00,0.00 104.00,0.00 104.00,0.00
Z
M 0.00,40.00
C 0.00,40.00 0.00,56.00 0.00,56.00
0.00,56.00 8.00,56.00 8.00,56.00
8.00,56.00 8.00,40.00 8.00,40.00
8.00,40.00 0.00,40.00 0.00,40.00
Z
M 120.00,40.00
C 120.00,40.00 120.00,56.00 120.00,56.00
120.00,56.00 128.00,56.00 128.00,56.00
128.00,56.00 128.00,40.00 128.00,40.00
128.00,40.00 120.00,40.00 120.00,40.00
Z
M 0.00,72.00
C 0.00,72.00 0.00,88.00 0.00,88.00
0.00,88.00 8.00,88.00 8.00,88.00
8.00,88.00 8.00,72.00 8.00,72.00
8.00,72.00 0.00,72.00 0.00,72.00
Z
M 120.00,72.00
C 120.00,72.00 120.00,88.00 120.00,88.00
120.00,88.00 128.00,88.00 128.00,88.00
128.00,88.00 128.00,72.00 128.00,72.00
128.00,72.00 120.00,72.00 120.00,72.00
Z
M 0.00,104.00
C 0.00,104.00 0.00,128.00 0.00,128.00
0.00,128.00 16.00,128.00 16.00,128.00
16.00,128.00 16.00,120.00 16.00,120.00
16.00,120.00 8.00,120.00 8.00,120.00
8.00,120.00 8.00,104.00 8.00,104.00
8.00,104.00 0.00,104.00 0.00,104.00
Z
M 120.00,104.00
C 120.00,104.00 120.00,120.00 120.00,120.00
120.00,120.00 104.00,120.00 104.00,120.00
104.00,120.00 104.00,128.00 104.00,128.00
104.00,128.00 128.00,128.00 128.00,128.00
128.00,128.00 128.00,104.00 128.00,104.00
128.00,104.00 120.00,104.00 120.00,104.00
Z
M 40.00,120.00
C 40.00,120.00 40.00,128.00 40.00,128.00
40.00,128.00 56.00,128.00 56.00,128.00
56.00,128.00 56.00,120.00 56.00,120.00
56.00,120.00 40.00,120.00 40.00,120.00
Z
M 72.00,120.00
C 72.00,120.00 72.00,128.00 72.00,128.00
72.00,128.00 88.00,128.00 88.00,128.00
88.00,128.00 88.00,120.00 88.00,120.00
88.00,120.00 72.00,120.00 72.00,120.00
Z"
/>
</g>
<g transform="translate(-21.00 24.00)">
<path id="Selection"
fill="#fff"
d="
M 48.00,24.00
C 48.00,24.00 48.00,64.00 48.00,64.00
48.00,64.00 24.00,64.00 24.00,64.00
24.00,64.00 64.00,102.80 64.00,102.80
64.00,102.80 104.00,64.00 104.00,64.00
104.00,64.00 80.00,64.00 80.00,64.00
80.00,64.00 80.00,24.00 80.00,24.00
80.00,24.00 48.00,24.00 48.00,24.00
Z"
/>
</g>
<g transform="translate(24.00 -21.00) scale(0.25)">
<path id="Selection"
fill="#fff"
d="
M 96.00,256.00
C 96.00,256.00 192.00,256.00 192.00,256.00
192.00,256.00 192.00,416.00 192.00,416.00
192.00,416.00 320.00,416.00 320.00,416.00
320.00,416.00 320.00,256.00 320.00,256.00
320.00,256.00 416.00,256.00 416.00,256.00
416.00,256.00 256.00,97.20 256.00,97.20
256.00,97.20 96.00,256.00 96.00,256.00
Z"
/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

@ -1,113 +0,0 @@
<?xml version="1.0" ?>
<svg enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#000"/>
<stop offset="1" style="stop-color:#456"/>
</linearGradient>
<circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#456"/>
<stop offset="1" style="stop-color:#000"/>
</linearGradient>
<path d="M256,469.3338623c-117.6314697,0-213.3333435-95.7023926-213.3333435-213.3333435 c0-117.6314545,95.7018661-213.333313,213.3333435-213.333313c117.636261,0,213.3333435,95.7018661,213.3333435,213.333313 C469.3333435,373.6314697,373.636261,469.3338623,256,469.3338623z" fill="url(#SVGID_2_)"/>
</g>
<g id="cover-shadow"
transform="translate(124.00 124.00) scale(2)">
<path id="Selection"
fill="#fff"
d="
M 0.00,0.00
C 0.00,0.00 0.00,24.00 0.00,24.00
0.00,24.00 8.00,24.00 8.00,24.00
8.00,24.00 8.00,8.00 8.00,8.00
8.00,8.00 24.00,8.00 24.00,8.00
24.00,8.00 24.00,0.00 24.00,0.00
24.00,0.00 0.00,0.00 0.00,0.00
Z
M 40.00,0.00
C 40.00,0.00 40.00,8.00 40.00,8.00
40.00,8.00 56.00,8.00 56.00,8.00
56.00,8.00 56.00,0.00 56.00,0.00
56.00,0.00 40.00,0.00 40.00,0.00
Z
M 72.00,0.00
C 72.00,0.00 72.00,8.00 72.00,8.00
72.00,8.00 88.00,8.00 88.00,8.00
88.00,8.00 88.00,0.00 88.00,0.00
88.00,0.00 72.00,0.00 72.00,0.00
Z
M 104.00,0.00
C 104.00,0.00 104.00,8.00 104.00,8.00
104.00,8.00 120.00,8.00 120.00,8.00
120.00,8.00 120.00,24.00 120.00,24.00
120.00,24.00 128.00,24.00 128.00,24.00
128.00,24.00 128.00,0.00 128.00,0.00
128.00,0.00 104.00,0.00 104.00,0.00
Z
M 48.00,24.00
C 48.00,24.00 48.00,64.00 48.00,64.00
48.00,64.00 24.00,64.00 24.00,64.00
24.00,64.00 64.00,102.80 64.00,102.80
64.00,102.80 104.00,64.00 104.00,64.00
104.00,64.00 80.00,64.00 80.00,64.00
80.00,64.00 80.00,24.00 80.00,24.00
80.00,24.00 48.00,24.00 48.00,24.00
Z
M 0.00,40.00
C 0.00,40.00 0.00,56.00 0.00,56.00
0.00,56.00 8.00,56.00 8.00,56.00
8.00,56.00 8.00,40.00 8.00,40.00
8.00,40.00 0.00,40.00 0.00,40.00
Z
M 120.00,40.00
C 120.00,40.00 120.00,56.00 120.00,56.00
120.00,56.00 128.00,56.00 128.00,56.00
128.00,56.00 128.00,40.00 128.00,40.00
128.00,40.00 120.00,40.00 120.00,40.00
Z
M 0.00,72.00
C 0.00,72.00 0.00,88.00 0.00,88.00
0.00,88.00 8.00,88.00 8.00,88.00
8.00,88.00 8.00,72.00 8.00,72.00
8.00,72.00 0.00,72.00 0.00,72.00
Z
M 120.00,72.00
C 120.00,72.00 120.00,88.00 120.00,88.00
120.00,88.00 128.00,88.00 128.00,88.00
128.00,88.00 128.00,72.00 128.00,72.00
128.00,72.00 120.00,72.00 120.00,72.00
Z
M 0.00,104.00
C 0.00,104.00 0.00,128.00 0.00,128.00
0.00,128.00 16.00,128.00 16.00,128.00
16.00,128.00 16.00,120.00 16.00,120.00
16.00,120.00 8.00,120.00 8.00,120.00
8.00,120.00 8.00,104.00 8.00,104.00
8.00,104.00 0.00,104.00 0.00,104.00
Z
M 120.00,104.00
C 120.00,104.00 120.00,120.00 120.00,120.00
120.00,120.00 104.00,120.00 104.00,120.00
104.00,120.00 104.00,128.00 104.00,128.00
104.00,128.00 128.00,128.00 128.00,128.00
128.00,128.00 128.00,104.00 128.00,104.00
128.00,104.00 120.00,104.00 120.00,104.00
Z
M 40.00,120.00
C 40.00,120.00 40.00,128.00 40.00,128.00
40.00,128.00 56.00,128.00 56.00,128.00
56.00,128.00 56.00,120.00 56.00,120.00
56.00,120.00 40.00,120.00 40.00,120.00
Z
M 72.00,120.00
C 72.00,120.00 72.00,128.00 72.00,128.00
72.00,128.00 88.00,128.00 88.00,128.00
88.00,128.00 88.00,120.00 88.00,120.00
88.00,120.00 72.00,120.00 72.00,120.00
Z"
/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5 KiB

View file

@ -1,130 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
width="1.70667in" height="1.70667in"
viewBox="0 0 512 512">
<g>
<g>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_1_" x1="-0.0000027" x2="512" y1="256" y2="256">
<stop offset="0" style="stop-color:#456"/>
<stop offset="1" style="stop-color:#000"/>
</linearGradient>
<circle cx="256" cy="256" fill="url(#SVGID_1_)" r="256"/>
<linearGradient gradientUnits="userSpaceOnUse" id="SVGID_2_" x1="42.6666641" x2="469.3333435" y1="256.0005188" y2="256.0005188">
<stop offset="0" style="stop-color:#000"/>
<stop offset="1" style="stop-color:#456"/>
</linearGradient>
<path
d="
M 256,469.3338623
c -117.6314697,0-213.3333435-95.7023926-213.3333435-213.3333435
c 0-117.6314545,95.7018661-213.333313,213.3333435-213.333313
c 117.636261,0,213.3333435,95.7018661,213.3333435,213.333313
C 469.3333435,373.6314697,373.636261,469.3338623,256,469.3338623
z"
fill="url(#SVGID_2_)"/>
</g>
<g id="cover-shadow"
transform="translate(124.00 124.00) scale(0.5)">
<path
id="Selection"
fill="#fff"
d="
M 0.00,0.00
C 0.00,0.00 0.00,97.00 0.00,97.00
0.00,97.00 32.00,97.00 32.00,97.00
32.00,97.00 32.00,32.00 32.00,32.00
32.00,32.00 96.00,32.00 96.00,32.00
96.00,32.00 96.00,0.00 96.00,0.00
96.00,0.00 0.00,0.00 0.00,0.00
Z
M 160.00,0.00
C 160.00,0.00 160.00,32.00 160.00,32.00
160.00,32.00 224.00,32.00 224.00,32.00
224.00,32.00 224.00,0.00 224.00,0.00
224.00,0.00 160.00,0.00 160.00,0.00
Z
M 288.00,0.00
C 288.00,0.00 288.00,32.00 288.00,32.00
288.00,32.00 352.00,32.00 352.00,32.00
352.00,32.00 352.00,0.00 352.00,0.00
352.00,0.00 288.00,0.00 288.00,0.00
Z
M 416.00,0.00
C 416.00,0.00 416.00,32.00 416.00,32.00
416.00,32.00 480.00,32.00 480.00,32.00
480.00,32.00 480.00,96.00 480.00,96.00
480.00,96.00 512.00,96.00 512.00,96.00
512.00,96.00 512.00,0.00 512.00,0.00
512.00,0.00 416.00,0.00 416.00,0.00
Z
M 96.00,256.00
C 96.00,256.00 192.00,256.00 192.00,256.00
192.00,256.00 192.00,416.00 192.00,416.00
192.00,416.00 320.00,416.00 320.00,416.00
320.00,416.00 320.00,256.00 320.00,256.00
320.00,256.00 416.00,256.00 416.00,256.00
416.00,256.00 256.00,97.20 256.00,97.20
256.00,97.20 96.00,256.00 96.00,256.00
Z
M 0.00,160.00
C 0.00,160.00 0.00,224.00 0.00,224.00
0.00,224.00 32.00,224.00 32.00,224.00
32.00,224.00 32.00,160.00 32.00,160.00
32.00,160.00 0.00,160.00 0.00,160.00
Z
M 480.00,160.00
C 480.00,160.00 480.00,224.00 480.00,224.00
480.00,224.00 512.00,224.00 512.00,224.00
512.00,224.00 512.00,160.00 512.00,160.00
512.00,160.00 480.00,160.00 480.00,160.00
Z
M 0.00,288.00
C 0.00,288.00 0.00,352.00 0.00,352.00
0.00,352.00 32.00,352.00 32.00,352.00
32.00,352.00 32.00,288.00 32.00,288.00
32.00,288.00 0.00,288.00 0.00,288.00
Z
M 480.00,288.00
C 480.00,288.00 480.00,352.00 480.00,352.00
480.00,352.00 512.00,352.00 512.00,352.00
512.00,352.00 512.00,288.00 512.00,288.00
512.00,288.00 480.00,288.00 480.00,288.00
Z
M 0.00,416.00
C 0.00,416.00 0.00,512.00 0.00,512.00
0.00,512.00 64.00,512.00 64.00,512.00
64.00,512.00 64.00,480.00 64.00,480.00
64.00,480.00 32.00,480.00 32.00,480.00
32.00,480.00 32.00,416.00 32.00,416.00
32.00,416.00 0.00,416.00 0.00,416.00
Z
M 480.00,416.00
C 480.00,416.00 480.00,480.00 480.00,480.00
480.00,480.00 450.00,480.00 450.00,480.00
450.00,480.00 432.00,479.01 432.00,479.01
432.00,479.01 416.00,479.01 416.00,479.01
416.00,479.01 415.00,480.00 415.00,480.00
415.00,480.00 416.00,512.00 416.00,512.00
416.00,512.00 512.00,512.00 512.00,512.00
512.00,512.00 512.00,416.00 512.00,416.00
512.00,416.00 480.00,416.00 480.00,416.00
Z
M 160.00,480.00
C 160.00,480.00 160.00,512.00 160.00,512.00
160.00,512.00 224.00,512.00 224.00,512.00
224.00,512.00 224.00,480.00 224.00,480.00
224.00,480.00 160.00,480.00 160.00,480.00
Z
M 288.00,480.00
C 288.00,480.00 288.00,512.00 288.00,512.00
288.00,512.00 352.00,512.00 352.00,512.00
352.00,512.00 352.00,480.00 352.00,480.00
352.00,480.00 288.00,480.00 288.00,480.00
Z"
/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -0,0 +1,141 @@
import re
import os
import glob
import nuke
from pype import api as pype
log = pype.Logger().get_logger(__name__, "nuke")
SINGLE_FILE_FORMATS = ['avi', 'mp4', 'mxf', 'mov', 'mpg', 'mpeg', 'wmv', 'm4v',
'm2v']
def evaluate_filepath_new(k_value, k_eval, project_dir, first_frame):
# get combined relative path
combined_relative_path = None
if k_eval is not None and project_dir is not None:
combined_relative_path = os.path.abspath(
os.path.join(project_dir, k_eval))
combined_relative_path = combined_relative_path.replace('\\', '/')
filetype = combined_relative_path.split('.')[-1]
frame_number = re.findall(r'\d+', combined_relative_path)[-1]
basename = combined_relative_path[: combined_relative_path.rfind(
frame_number)]
filepath_glob = basename + '*' + filetype
glob_search_results = glob.glob(filepath_glob)
if len(glob_search_results) <= 0:
combined_relative_path = None
try:
k_value = k_value % first_frame
if os.path.exists(k_value):
filepath = k_value
elif os.path.exists(k_eval):
filepath = k_eval
elif not isinstance(project_dir, type(None)) and \
not isinstance(combined_relative_path, type(None)):
filepath = combined_relative_path
filepath = os.path.abspath(filepath)
except Exception as E:
log.error("Cannot create Read node. Perhaps it needs to be rendered first :) Error: `{}`".format(E))
return
filepath = filepath.replace('\\', '/')
current_frame = re.findall(r'\d+', filepath)[-1]
padding = len(current_frame)
basename = filepath[: filepath.rfind(current_frame)]
filetype = filepath.split('.')[-1]
# sequence or not?
if filetype in SINGLE_FILE_FORMATS:
pass
else:
# Image sequence needs hashes
filepath = basename + '#' * padding + '.' + filetype
# relative path? make it relative again
if not isinstance(project_dir, type(None)):
filepath = filepath.replace(project_dir, '.')
# get first and last frame from disk
frames = []
firstframe = 0
lastframe = 0
filepath_glob = basename + '*' + filetype
glob_search_results = glob.glob(filepath_glob)
for f in glob_search_results:
frame = re.findall(r'\d+', f)[-1]
frames.append(frame)
frames = sorted(frames)
firstframe = frames[0]
lastframe = frames[len(frames) - 1]
if lastframe < 0:
lastframe = firstframe
return filepath, firstframe, lastframe
def create_read_node(ndata, comp_start):
read = nuke.createNode('Read', 'file ' + ndata['filepath'])
read.knob('colorspace').setValue(int(ndata['colorspace']))
read.knob('raw').setValue(ndata['rawdata'])
read.knob('first').setValue(int(ndata['firstframe']))
read.knob('last').setValue(int(ndata['lastframe']))
read.knob('origfirst').setValue(int(ndata['firstframe']))
read.knob('origlast').setValue(int(ndata['lastframe']))
if comp_start == int(ndata['firstframe']):
read.knob('frame_mode').setValue("1")
read.knob('frame').setValue(str(comp_start))
else:
read.knob('frame_mode').setValue("0")
read.knob('xpos').setValue(ndata['new_xpos'])
read.knob('ypos').setValue(ndata['new_ypos'])
nuke.inputs(read, 0)
return
def write_to_read(gn):
comp_start = nuke.Root().knob('first_frame').value()
comp_end = nuke.Root().knob('last_frame').value()
project_dir = nuke.Root().knob('project_directory').getValue()
if not os.path.exists(project_dir):
project_dir = nuke.Root().knob('project_directory').evaluate()
group_read_nodes = []
with gn:
height = gn.screenHeight() # get group height and position
new_xpos = int(gn.knob('xpos').value())
new_ypos = int(gn.knob('ypos').value()) + height + 20
group_writes = [n for n in nuke.allNodes() if n.Class() == "Write"]
print("__ group_writes: {}".format(group_writes))
if group_writes != []:
# there can be only 1 write node, taking first
n = group_writes[0]
if n.knob('file') is not None:
myfiletranslated, firstFrame, lastFrame = evaluate_filepath_new(
n.knob('file').getValue(),
n.knob('file').evaluate(),
project_dir,
comp_start
)
# get node data
ndata = {
'filepath': myfiletranslated,
'firstframe': firstFrame,
'lastframe': lastFrame,
'new_xpos': new_xpos,
'new_ypos': new_ypos,
'colorspace': n.knob('colorspace').getValue(),
'rawdata': n.knob('raw').value(),
'write_frame_mode': str(n.knob('frame_mode').value()),
'write_frame': n.knob('frame').value()
}
group_read_nodes.append(ndata)
# create reads in one go
for oneread in group_read_nodes:
# create read node
create_read_node(oneread, comp_start)

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.