Skip to content

Releases: pyblish/pyblish-win

100

23 Feb 19:33
Compare
Choose a tag to compare

Automated release from AppVeyor

1.3.2

18 Feb 19:27
Compare
Choose a tag to compare

Fixing obscure logical bug when publishing from pyblish-qml using InstancePlugin as Collector.

This would previously fail, by not processing CollectorB due to the previously created MyInstance not being visible.

count = {"#": 0}
history = []

class CollectorA(pyblish.api.ContextPlugin):
    order = pyblish.api.CollectorOrder

    def process(self, context):
        history.append(type(self).__name__)
        context.create_instance("MyInstance")
        count["#"] += 1

class CollectorB(pyblish.api.InstancePlugin):
    order = pyblish.api.CollectorOrder + 0.1

    def process(self, instance):
        history.append(type(self).__name__)
        count["#"] += 10

pyblish.api.register_plugin(CollectorA)
pyblish.api.register_plugin(CollectorB)

c = reset()

check_present("CollectorA", c.item_model)
check_present("CollectorB", c.item_model)
check_present("MyInstance", c.item_model)

assert count["#"] == 11, count
assert history == ["CollectorA",

1.3.1

07 Feb 15:14
Compare
Choose a tag to compare

This release marks a significant overhaul of the underlying logic of Pyblish.

Release notes

1.2.6

22 Dec 08:36
Compare
Choose a tag to compare

Added pyblish/pyblish-qml#135, Quick Toggles, to quickly toggle a series of instances with a given family.

untitled

1.2.5

09 Dec 13:11
Compare
Choose a tag to compare
  • pyblish-maya now supports an icon in the File-menu and shelf
  • Added support for instances with multiple families (#231)

Multi-family support

This update adds the ability to provide instances with more than a single family.

Traditionally, an Instance would only support having a single family. This family is then matched against supported families of each plug-in. Now, an instance can register multiple families; essentially changing places of how plug-ins and instances are matched. That is, instead of building a plug-in with support for multiple families, you can now think in terms of building instances with multiple families, and in that way register support for more than a single plug-in.

See the issue for more details.

1.2.4

19 Nov 09:55
Compare
Choose a tag to compare

Maintenance update.

1.2.2

05 Nov 12:04
Compare
Choose a tag to compare

Added Pyblish Tray, a standalone management application for Pyblish to gain an overview of it's state and current output. It sits quietly in the system tray of the given operating system, collecting output and providing options for debugging and manipulating the currently active instance of the Pyblish front-end, QML.

untitled

Usage

Tray is designed to run during startup of your OS.

Once started, it will sit quietly in the background and listen for interested hosts looking to publish and provide them with a handle to the Pyblish QML front-end.

Once Pyblish has been installed, look for the corresponding start menu item called simply "Pyblish". Clicking on it will launch Pyblish Tray.

You can also launch Tray via the command-line, by running the associated launcher.

$ cd pyblish-win\bin
$ pyblish-tray

This will have the same effect as clicking on the start-menu item.

1.2.1

27 Oct 15:47
Compare
Choose a tag to compare

1.2.0

23 Oct 15:46
Compare
Choose a tag to compare

Summary of new features

  • Integrations
  • Core
    • Feature: Actions (see #142)
    • Feature: Pure-dict data (see #117)
    • Enhancement: Same-name Instances allowed
  • GUI
    • Enhancement: Collectors now visible
    • Actions accessible via right-click on plug-ins.



Overview

The new version focuses on Actions, a flexible method of adding any functionality to plug-ins whilst reaping the benefits of a context-sensitive environment. Associated actions show up alongside plug-ins that already limit themselves to various constraints, such as which project, shot, artist and asset is currently active.

The actions can be further tailored to only appear at the success, failure or done state of a plug-in, providing you with much flexibility in terms of which actions to provide when, such as only enabling the selection of affected nodes in the viewport on a plug-in that had just found a problem with an asset.

Furthermore, you will also notice that collectors are now visible in the GUI as unmodifiable, unchecked items, making it more easy to spot what was actually run and to inspect the various properties of each collector, such as it's description.




Installation

Whether you are installing anew or updating, it is recommended that you install from scratch.

If you run into any issues, feel free to post below.




Transition Guide

The only change requiring any additional thought is pure-dict data and the deprecation of add() on Context and Instance objects. Both of which are fully backwards compatible.

The deprecation of add means that both Context and Instance are more closely resembling the pure Python list.

  1. Instead of using instance.data("key"), use instance.data["key"]
  2. Instead of using instance.add("MyObject"), useinstance.append("MyObject")`



Actions

This functionality is meant to replace "repair", along with adding an abundance of flexibility in terms of context-sensitive functionality. 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.

Each action have access to both the Context and it's parent plug-in via dependency injection, along with any other custom dependencies already available to plug-ins in general.

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.

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
  • Standard dependency injection still applies; can still inject custom functionality
  • Customisable icon per action, from Awesome Icon
  • 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

Basic use

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):
        import subprocess
        subprocess.call("start .", shell=True)  # Launch explorer at the cwd


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

    def process(self, context, plugin):
        """The Context and parent Plug-in are available via dependency injection"""
        self.log.info("Standard log messages apply here.")
        raise Exception("Exceptions too.")

Showcase

Every possible combination of an action.

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):
        self.log.info("About to fail..")
        raise Exception("I failed")


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

    def process(self):
        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):
        self.log.info("I have an icon")


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

    def process(self, 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):
        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):
        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):
        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.Validator):
    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):
        self.log.info("Ran PluginWithActions")

Maya example

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()



Pure-dict

The data property of Context and Instance objects is now a standard Python dictionary, with backwards compatibility for being called directly.

import pyblish.api as pyblish

context = pyblish.Context()

# Old behaviour preserved
context.set_data("key", "value")

# New behaviour preferred
context.data["key"] = "value"
if "key" in context.data:
  context.data.pop("key")

assert context.data.get("key") == None

# The same applies to Instances
instance = context.create_instance("MyInstance")
instance.data["key"] = "value"

The recommended way forwards is to start transitioning to using pure-dict data, not worrying too much about going back to refactor the old ways. Down the line, there will be a warning printed for each call to a deprecated member so as to easily find and refactor things.

From now on, guides and discussion will start to assume this new way of working.




Same name instances

Initially, you could store multiple instances of the same name in the Context. Later on, a feature was implemented to allow referencing an Instance within a Context by name, meaning this ability had to be removed.

Turns out, allowing same-name instances is useful for when you have the same logical asset in your scene, being treated differently by one or more plug-ins; examples include collecting a character rig for extraction to an animator, but then also collecting it for playblasting of a turntable. The turntable instance would only interest itself with plain meshes, and optional overrides for playblast settings being passed onto the relevant p...

Read more

1.1.1

08 Sep 08:35
Compare
Choose a tag to compare

Added Pyblish Standalone and Pyblish Hiero.

The start-menu item "Pyblish" now refers to Pyblish Standalone. It is normally run by hand, via /bin/pyblish-standalone.bat such that command-line arguments may be passed to it.

$ cd bin
$ pyblish-standalone.bat --data key value

More changes

  • BACKWARD INCOMPATIBLE: Directly registered plug-ins are now also checked for duplicates; before, they overwrite those discovered on disk.
  • BACKWARD INCOMPATIBLE: register_plugin now checks the proposed registered plug-in for correctness, and throws an exception otherwise.
  • BACKWARD INCOMPATIBLE: regex has been deprecated from discover() and does nothing.
  • BACKWARD INCOMPATIBLE: type has been deprecated from discover() and does nothing.
  • Enhancement: Implementation of discover() has been greatly simplified, made more robust and reusable.