# Action

Process once, with [Context](https://github.com/pyblish/api/tree/9d41e509649f2dfb91bc4d595aebabed2dc0512f/pages/context.md) and [Plugin](https://github.com/pyblish/api/tree/9d41e509649f2dfb91bc4d595aebabed2dc0512f/pages/plugin.md) as input.

| Source                                                                                          | Added |
| ----------------------------------------------------------------------------------------------- | ----- |
| [Link](https://github.com/pyblish/pyblish-base/commit/ac83f2a94bbde95bc2f6c4000d7c50e36a3d3b5f) | 1.2.0 |

Inherits [Plugin](https://api.pyblish.com/pyblish.api/plugin)

## Public Functions

| Output | Method                                                                                                                                                                                                                                             |
| -----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|        | [process](https://api.pyblish.com/pyblish.api/contextplugin/contextplugin.process)([context](https://api.pyblish.com/pyblish.api/context), [plugin](https://github.com/pyblish/api/tree/9d41e509649f2dfb91bc4d595aebabed2dc0512f/pages/plugin.md)) |

## Properties

| property                                                       | type |
| -------------------------------------------------------------- | ---- |
| [icon](https://api.pyblish.com/pyblish.api/action/action.icon) | str  |
| [on](https://api.pyblish.com/pyblish.api/action/action.on)     | str  |

## Usage

Attach any functionality to a plug-in and tailor it to a particular state; like an action only available via a failed validator, or a successful extraction, or just all-round functionality associated with a particular plug-in.

![image](https://cloud.githubusercontent.com/assets/2152766/13439097/da8cf9da-dfe3-11e5-8db1-7c33f31046fd.png)

Each action is passed both the Context and it's parent plug-in at run-time and can be accessed via their argument signature, similar to plug-ins.

Actions in QML are arranged in a menu with optional customisable groups and separators. Actions with any kind of implementation error show up as well, including a helpful error message for simplified debugging.

**Argument signature**

These objects are available via the argument signature.

* `context`: The global context
* `plugin`: The parent plug-in

**Full list of features**

* Per-plugin actions
* Action API \~= Plug-in API, it is more or less a 1-1 match between their interfaces, including `process()` and `label`.
* Standard logging and exception reporting, identical to plug-ins
* Customisable icon per action, from [Awesome Icon](http://fortawesome.github.io/Font-Awesome/icons/)
* Customisable availability
  * `all`: Always
  * `processed`: After plug-in has been processed
  * `failed`: After plug-in has been processed, and failed
  * `succeeded`: After plug-in has been processed, and succeeded

## Example

```python
class OpenInExplorer(pyblish.api.Action):
    label = "Open in Explorer"
    on = "failed"  # This action is only available on a failed plug-in
    icon = "hand-o-up"  # Icon from Awesome Icon

    def process(self, context, plugin):
        import subprocess
        subprocess.call("start .", shell=True)  # Launch explorer at the cwd


class Validate(pyblish.api.InstancePlugin):
    order = pyblish.api.ValidatorOrder
    actions = [
        # Order of items is preserved
        pyblish.api.Category("My Actions"),
        MyAction,
        pyblish.api.Separator,
    ]

    def process(self, instance):
        self.log.info("Standard log messages apply here.")
        raise Exception("Exceptions too.")
```

## Extended Example

Every possible combination of an action.

```python
class ContextAction(pyblish.api.Action):
    label = "Context action"

    def process(self, context):
        self.log.info("I have access to the context")
        self.log.info("Context.instances: %s" % str(list(context)))


class FailingAction(pyblish.api.Action):
    label = "Failing action"

    def process(self, context, plugin):
        self.log.info("About to fail..")
        raise Exception("I failed")


class LongRunningAction(pyblish.api.Action):
    label = "Long-running action"

    def process(self, context, plugin):
        self.log.info("Sleeping for 2 seconds..")
        time.sleep(2)
        self.log.info("Ah, that's better")


class IconAction(pyblish.api.Action):
    label = "Icon action"
    icon = "crop"

    def process(self, context, plugin):
        self.log.info("I have an icon")


class PluginAction(pyblish.api.Action):
    label = "Plugin action"

    def process(self, context, plugin):
        self.log.info("I have access to my parent plug-in")
        self.log.info("Which is %s" % plugin.id)


class LaunchExplorerAction(pyblish.api.Action):
    label = "Open in Explorer"
    icon = "folder-open"

    def process(self, context, plugin):
        import os
        import subprocess

        cwd = context.data["cwd"]
        self.log.info("Opening %s in Explorer" % cwd)
        result = subprocess.call("start .", cwd=cwd, shell=True)
        self.log.debug(result)


class ProcessedAction(pyblish.api.Action):
    label = "Success action"
    icon = "check"
    on = "processed"

    def process(self, context, plugin):
        self.log.info("I am only available on a successful plug-in")


class FailedAction(pyblish.api.Action):
    label = "Failure action"
    icon = "close"
    on = "failed"


class SucceededAction(pyblish.api.Action):
    label = "Success action"
    icon = "check"
    on = "succeeded"

    def process(self, context, plugin):
        self.log.info("I am only available on a successful plug-in")


class BadEventAction(pyblish.api.Action):
    label = "Bad event action"
    on = "not exist"


class InactiveAction(pyblish.api.Action):
    active = False


class PluginWithActions(pyblish.api.InstancePlugin):
    order = pyblish.api.ValidatorOrder
    optional = True
    actions = [
        pyblish.api.Category("General"),
        ContextAction,
        FailingAction,
        LongRunningAction,
        IconAction,
        PluginAction,
        pyblish.api.Category("OS"),
        LaunchExplorerAction,
        pyblish.api.Separator,
        FailedAction,
        SucceededAction,
        pyblish.api.Category("Debug"),
        BadEventAction,
        InactiveAction,
    ]

    def process(self, instance):
        self.log.info("Ran PluginWithActions")
```

## Maya Example

```python
import time
import pyblish.api
import pyblish_qml


class Collect(pyblish.api.Collector):
    def process(self, context):
        i = context.create_instance("MyInstance")
        i.data["family"] = "default"
        i.append("pCube1")


class SelectInvalidNodes(pyblish.api.Action):
    label = "Select broken nodes"
    on = "failed"
    icon = "hand-o-up"

    def process(self, context):
        self.log.info("Finding bad nodes..")
        nodes = []
        for result in context.data["results"]:
            if result["error"]:
                instance = result["instance"]
                nodes.extend(instance)

        self.log.info("Selecting bad nodes: %s" % ", ".join(nodes))
        cmds.select(deselect=True)
        cmds.select(nodes)


class Validate(pyblish.api.Validator):
    actions = [
        pyblish.api.Category("Scene"),
        SelectInvalidNodes
    ]

    def process(self, instance):
        raise Exception("I failed")


pyblish.api.register_plugin(Collect)
pyblish.api.register_plugin(Validate)

import pyblish_maya
pyblish_maya.show()
```

{{ file.mtime }}
