To be able to inject the instance argument to Actions and get the relevant instances.
Motivation
Currently you have to repeat a lot of code to get the relevant instances. Example to get the failed instances:
class Texture(pyblish.api.Action):
label = "Print Failed Instance"
on = "failed"
def process(self, context):
# get all failed instances
failed = list()
for result in context.data["results"]:
if result["error"] is not None:
if result["instance"] not in failed:
failed.append(result["instance"])
for instance in failed:
# context seem to return None
if not instance:
continue
# filter to family specific instance
if instance.data['family'] != 'texture':
continue
self.log.info(instance)
Implementation
You already specify what statuses of instances the action should operate on, from the on property ei. all, failed etc.
You also know the families of instances, which is coming from the plugin executing the action.
With the status and family of the instance, we should be able to find the correct instances to operate on, Unless I’m missing something?
I have also been looking for a way to access this easier.
By the way, filtering the instances to the family of the plugin can be done through the API. As such it uses the logic that pyblish uses, and possibly other filtering for a plugin in the future aside from just the family.
# pseudocode
def process(self, context, plugin):
# Get the errored instances
failed = []
for result in context.data["results"]:
if result["error"] is not None:
failed.append(result["instance"])
# Apply pyblish.logic to get the instances for the plug-in
instances = pyblish.api.instances_by_plugin(failed, plugin)
Yet this also makes me think about the way the Actions currently work in the UI. Since the Artist cannot run an Action only for a specific instance.
This is where instance actions could come in, but the logic for having an action on an instance would be different than an action on a plugin iterating over instances. Is that right, @marcus?
I don’t know, there are probably more than one way of going at it. Feel free to propose your ideas. Maybe start with a few examples of actions you would expect to find on an instance?
class SelectInstance(pyblish.api.InstanceAction):
label = 'Select'
on = 'all'
def process(self, instance):
pymel.core.select(instance[0])
You would assign the action when collecting the instance:
class CollectLocators(pyblish.api.ContextPlugin):
order = pyblish.api.CollectorOrder
def process(self, context):
for node in pymel.core.ls(type='locator'):
instance = context.add_instance(node.name(), family='locator')
instance.actions.append(SelectInstance)
It also might make sense that it is an InstanceAction, in which instance in the argument signature is the right-clicked instance in the view. A ContextAction could take context, and pretty much still make sense, right?
The assignment also works for me.
I think let’s mock it up. It’d need the ContextAction and InstanceAction classes in pyblish-base, to be formatted in pyblish-rpc and finally drawn in pyblish-qml. There is already actions in each project to use for reference and in all should be quite straightforward to implement.
Does it make sense as well that if an InstanceAction assigned to an instance is run once, and an InstanceAction assigned to a plugin will potentially be run multiple times?
Slight adjustment to @BigRoy code, since the context returns None.
# pseudocode
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:
failed.append(result["instance"])
# Apply pyblish.logic to get the instances for the plug-in
instances = pyblish.api.instances_by_plugin(failed, plugin)