Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active Aug 29, 2015
Embed
What would you like to do?
Pyblish Customisation

Pyblish Customisation

Building upon Pyblish Search, here's an example of plug-ins being published via searched and customised plug-ins.

How it works

Plug-ins are categories by "tags" and assigned a series of "options". The tags are used during search and options provide for user customisation, either directly or via a GUI. A GUI can then build an interface towards available options via the provided "schema" which defines attribute types, ranges, enums et. cetera.

import re
import pyblish.api
import pyblish.util
disk = {}
class ValidateHeight(pyblish.api.Validator):
tags = ["character", "measurement"]
# Schema is used to build the User Interface
# and validate incoming values.
schema = {
"height": float,
"tolerance": float
}
# Options used to populate default values
# in user interface and for final evaluation
# by process().
options = {
"height": 2,
"tolerance": 0.5
}
def process(self, instance):
self.log.info("%s compared with %s" % (
instance.data("height"), self.options["height"]))
low_bound = self.options["height"] - self.options["tolerance"]
high_bound = self.options["height"] + self.options["tolerance"]
assert low_bound < instance.data("height") < high_bound, (
"Height out of bounds")
class ValidatePivot(pyblish.api.Validator):
tags = ["geometry", "character", "film"]
options = {
"predicate": "zeroed"
}
schema = {
"predicate": [
"zeroed",
"straight",
"transform_only"
]
}
def process(self, instance):
if self.options["predicate"] == "zeroed":
assert False
if self.options["predicate"] == "straight":
assert True
class ValidateNamingComplex(pyblish.api.Validator):
tags = ["naming", "games", "tv", "film"]
options = {
# Anything suffixed with an uppercase
# three letter extension
"regex": "^.*_[A-Z]{3}$"
}
schema = {
"regex": str
}
def process(self, instance):
for child in instance:
assert re.match(self.options["regex"], child), (
"Invalid name for at least %s" % child)
class ValidateNamingSimple(pyblish.api.Validator):
tags = ["naming", "games", "tv", "film"]
options = {
"prefix": False,
"prefixLength": 3,
"prefixCase": "upper",
"suffix": True,
"suffixLength": 3,
"suffixCase": "upper",
"separator": "_",
}
schema = {
"prefix": bool,
"prefixLength": int,
"prefixCase": ["upper", "lower"],
"suffix": bool,
"suffixLength": int,
"suffixCase": ["upper", "lower"],
"separator": str
}
def process(self, instance):
for child in instance:
regex = "^.*_[A-Z]{suffixLength}$".format(**self.options)
assert re.match(regex, child), (
"Invalid name for at least %s" % child)
class ValidateDefaultPose(pyblish.api.Validator):
tags = ["rigging", "animation", "consistency"]
options = {
"tolerance": 0.01,
"considerUserDefined": True,
"considerBuiltin": True
}
schema = {
"tolerance": float,
"considerUserDefined": bool,
"considerBuiltin": bool
}
def process(self, instance):
assert True
# Two basic gatekeepers
class CollectInstances(pyblish.api.Collector):
def process(self, context):
context.create_instance(
name="MyInstance",
family="rig",
height=1.86)
class ExtractCharacter(pyblish.api.Extractor):
def process(self, instance):
disk["data"] = 0x05f4
# Database of all available validators
# Typically stored elsewhere, somewhere global
validators = [
ValidatePivot,
ValidateHeight,
ValidateNamingComplex,
ValidateNamingSimple,
ValidateDefaultPose,
]
def search(queries, config=None, inclusive=False):
"""Search engine, based on tags and inclusiveness
Search amongst available validators via keywords.
Optionally exlude individual validators by name.
Arguments:
queries (list): Individual tags, e.g. "film" and "games"
config (dict): Options per plug-in
"""
plugins = list()
options = config.get("options", dict())
exclude = config.get("exclude", list())
for validator in validators:
name = validator.__name__
if name in exclude:
continue
operator = any if inclusive else all
if not operator(tag in validator.tags for tag in queries):
continue
# Produce a copy to modify with options
copy = type(name, (validator,), {})
copy.options.update(options.get(name, {}))
plugins.append(copy)
return plugins
# A full configuration file, provided externally
config = {
# Options provided by the user either a GUI or file on disk
"options": {
"ValidatePivot": {
"predicate": "straight"
},
"ValidateHeight": {
"height": 1.80,
"tolerance": 0.1
},
"ValidateNamingComplex": {
"regex": "^.*_[A-Z]{4}$"
}
},
# Provided via GUI or file, via e.g. unticking in a visual list
"exclude": [
"ValidateNamingSimple"
]
}
# Perform search
validators = search(
queries="naming film measurement".split(),
config=config,
inclusive=True)
# Register found
pyblish.api.deregister_all_plugins()
for plugin in validators + [CollectInstances, ExtractCharacter]:
pyblish.api.register_plugin(plugin)
# Perform publish
context = pyblish.util.publish()
for result in context.data("results"):
if result["instance"] is None:
continue
# Report results
print("{instance.id} + {plugin.id} = {success}".format(**result))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment