Publishing using pyblish¶
As an example we will look at the Maya geometry asset which uses pyblish for publishing. Note that the concepts described in this article is generic and can be applied to Nuke and other applications.
Let us have a look at the structure of the geometry asset plugin:
ftrack_connect_maya_publish/asset/geometry/
pyblish_plugins/
__init__.py
collect.py
extract_alembic.py
extract_mayabinary.py
__init__.py
geometry_asset.py
In the pyblish_plugins directory there are a number of pyblish plugins defined. These takes care of the publish process: collecting what to publish, validating and extracting the data, then integrating (registering) everything in ftrack.
In the geometry_asset.py we define the asset plugin, how to publish, import and switch to a different version for an already imported asset. The asset plugin does not necessarily have to publish using pyblish.
However, the asset plugin itself not registered here. Instead let us look at register_geometry_asset.py in the resource/ directory:
# :coding: utf-8
# :copyright: Copyright (c) 2016 ftrack
import functools
import ftrack_api
import ftrack_connect_pipeline.asset
from ftrack_connect_maya_publish.asset.geometry import geometry_asset
def create_asset_publish():
'''Return asset publisher.'''
return geometry_asset.PublishGeometry(
description='publish geometry to ftrack.',
asset_type_short='geo'
)
def register_asset_plugin(session, event):
'''Register asset plugin.'''
geometry = ftrack_connect_pipeline.asset.Asset(
identifier='geo',
label='Geometry',
icon='http://www.clipartbest.com/cliparts/9cz/EzE/9czEzE8yi.png',
create_asset_publish=create_asset_publish
)
geometry.register(session)
def register(session):
'''Subscribe to *session*.'''
if not isinstance(session, ftrack_api.Session):
return
session.event_hub.subscribe(
'topic=ftrack.pipeline.register-assets',
functools.partial(register_asset_plugin, session)
)
Here we find the registration of the asset plugin for geometry. The asset plugin will be registered with a unique identifier when the application’s ftrack-python-api session emits the ftrack.pipeline.register-assets. The create_asset_publish argument, PublishGeometry instance will handle publishing.
Note
In future versions there will be a import_asset and a switch_asset argument.
Let us look at a definition of the PublishGeometry class that got imported:
# :coding: utf-8
# :copyright: Copyright (c) 2016 ftrack
import ftrack_connect_pipeline.asset
import maya.cmds as cmds
class PublishGeometry(ftrack_connect_pipeline.asset.PyblishAsset):
'''Handle publish of maya geometry.'''
def get_options(self):
'''Return global options.'''
options = [
{
'type': 'group',
'label': 'Maya binary',
'name': 'maya_binary',
'options': [{
'name': 'reference',
'label': 'Reference',
'type': 'boolean',
}, {
'name': 'history',
'label': 'History',
'type': 'boolean',
}, {
'name': 'channels',
'label': 'Channels',
'type': 'boolean',
'value': True
}, {
'name': 'expressions',
'label': 'Expressions',
'type': 'boolean',
'value': True
}, {
'name': 'constraints',
'label': 'Constraints',
'type': 'boolean',
'value': True
}, {
'name': 'shaders',
'label': 'Shaders',
'type': 'boolean',
'value': True
}]
},
{
'type': 'group',
'label': 'Alembic',
'name': 'alembic',
'options': [{
'name': 'include_animation',
'label': 'Include animation',
'type': 'boolean',
'value': True
}, {
'name': 'uv_write',
'label': 'UV write',
'type': 'boolean',
'value': True
}, {
'name': 'world_space',
'label': 'World space',
'type': 'boolean',
'value': True
}, {
'name': 'write_visibility',
'label': 'Write visibility',
'type': 'boolean',
'value': True
}]
}
]
default_options = super(PublishGeometry, self).get_options()
return default_options + options
def get_publish_items(self):
'''Return list of items that can be published.'''
match = set(['geometry', 'ftrack'])
options = []
for instance in self.pyblish_context:
if match.issubset(instance.data['families']):
options.append(
{
'label': instance.name,
'name': instance.name,
'value': instance.data.get('publish', False)
}
)
return options
def get_item_options(self, name):
'''Return options for publishable item with *name*.'''
return []
def get_scene_selection(self):
'''Return a list of names for scene selection.'''
return cmds.ls(assemblies=True, long=True, sl=1)
Here we see that it inherits ftrack_connect_pipeline.asset.PyblishAsset and will, as the name suggests be using pyblish for the publishing process.
While processing pyblish plugins will be accessing options from the interface. These options are defined in the methods on the PublishGeometry class and and are following the syntax described in Developing actions user interface.
There are two additional types only available in the new tools:
- group
Visually group a number of options in the UI. Options will be saved under the name key:
{ 'type': 'group', 'label': 'Maya binary', 'name': 'maya_binary', 'options': [..] }
- qt_widget
The qt_widget type can be used to present a custom widget to the user:
{ 'widget': StartEndFrameField(start_frame=0, end_frame=1001), 'name': 'frame_range', 'type': 'qt_widget' }
The options will be saved under the name key. The StartEndFrameField being a subclass of ftrack_connect_pipeline.ui.widget.field.base.BaseField. The widget must:
Implement the value method that returns the current value of the field.
Emit value_changed signal with value when the underlying value changes.
Here is an example of a start/end frame qt based widget:
import sys from ftrack_connect_pipeline.ui.widget.field.base import BaseField from QtExt import QtWidgets class StartEndFrameField(BaseField): '''Start and end frame fields.''' def __init__(self, start_frame, end_frame): '''Instantiate start and end frame field.''' super(StartEndFrameField, self).__init__() self.setLayout(QtWidgets.QHBoxLayout()) self.start_frame = QtWidgets.QDoubleSpinBox() self.start_frame.setValue(start_frame) self.start_frame.setMaximum(sys.maxint) self.start_frame.setDecimals(0) self.start_frame.valueChanged.connect(self.notify_changed) self.end_frame = QtWidgets.QDoubleSpinBox() self.end_frame.setValue(end_frame) self.end_frame.setMaximum(sys.maxint) self.end_frame.setDecimals(0) self.end_frame.valueChanged.connect(self.notify_changed) self.layout().addWidget(QtWidgets.QLabel('Frame Range')) self.layout().addWidget(self.start_frame) self.layout().addWidget(self.end_frame) def notify_changed(self, *args, **kwargs): '''Notify the world about the changes.''' self.value_changed.emit(self.value()) def value(self): '''Return value.''' return { 'start_frame': int(self.start_frame.value()), 'end_frame': int(self.end_frame.value()) }
Notable methods that are implemented on the PublishGeometry class:
- get_publish_items
Return a list of items that can be published from the scene. In a pyblish based publish workflow this is ususally done by filtering on the pyblish instance family/families.
- get_options
Return a list of options that are general options for the publish.
- get_item_options
Return a list of options that are valid for the given item name.
These methods can access the current pyblish context on self:
for instance in self.pyblish_context:
print instance
Pyblish plugins¶
In the pyblish_plugins directory we have the plugins that will collect the geometries from the scene and extract them as a maya binary and/or alembic.
Common pyblish plugins are defined in ftrack_connect_maya_publish/shared_pyblish_plugins and will be used for collection and integration plugins that are shared between asset plugins.