Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Initial Photoshop integration. #232

Merged
merged 4 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions pype/hosts/photoshop/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os

from avalon import api
import pyblish.api


def install():
print("Installing Pype config...")

plugins_directory = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
"plugins",
"photoshop"
)

pyblish.api.register_plugin_path(
os.path.join(plugins_directory, "publish")
)
api.register_plugin_path(
api.Loader, os.path.join(plugins_directory, "load")
)
api.register_plugin_path(
api.Creator, os.path.join(plugins_directory, "create")
)

pyblish.api.register_callback(
"instanceToggled", on_pyblish_instance_toggled
)


def on_pyblish_instance_toggled(instance, old_value, new_value):
"""Toggle layer visibility on instance toggles."""
instance[0].Visible = new_value
2 changes: 1 addition & 1 deletion pype/plugins/global/publish/integrate_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
"gizmo",
"source",
"matchmove",
"image"
"image",
"source",
"assembly",
"fbx",
Expand Down
12 changes: 12 additions & 0 deletions pype/plugins/photoshop/create/create_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from avalon import photoshop


class CreateImage(photoshop.Creator):
"""Image folder for publish."""

name = "imageDefault"
label = "Image"
family = "image"

def __init__(self, *args, **kwargs):
super(CreateImage, self).__init__(*args, **kwargs)
43 changes: 43 additions & 0 deletions pype/plugins/photoshop/load/load_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from avalon import api, photoshop


class ImageLoader(api.Loader):
"""Load images

Stores the imported asset in a container named after the asset.
"""

families = ["image"]
representations = ["*"]

def load(self, context, name=None, namespace=None, data=None):
with photoshop.maintained_selection():
layer = photoshop.import_smart_object(self.fname)

self[:] = [layer]

return photoshop.containerise(
name,
namespace,
layer,
context,
self.__class__.__name__
)

def update(self, container, representation):
layer = container.pop("layer")

with photoshop.maintained_selection():
photoshop.replace_smart_object(
layer, api.get_representation_path(representation)
)

photoshop.imprint(
layer, {"representation": str(representation["_id"])}
)

def remove(self, container):
container["layer"].Delete()

def switch(self, container, representation):
self.update(container, representation)
17 changes: 17 additions & 0 deletions pype/plugins/photoshop/publish/collect_current_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import os

import pyblish.api
from avalon import photoshop


class CollectCurrentFile(pyblish.api.ContextPlugin):
"""Inject the current working file into context"""

order = pyblish.api.CollectorOrder - 0.5
label = "Current File"
hosts = ["photoshop"]

def process(self, context):
context.data["currentFile"] = os.path.normpath(
photoshop.app().ActiveDocument.FullName
).replace("\\", "/")
56 changes: 56 additions & 0 deletions pype/plugins/photoshop/publish/collect_instances.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import pythoncom

from avalon import photoshop

import pyblish.api


class CollectInstances(pyblish.api.ContextPlugin):
"""Gather instances by LayerSet and file metadata

This collector takes into account assets that are associated with
an LayerSet and marked with a unique identifier;

Identifier:
id (str): "pyblish.avalon.instance"
"""

label = "Instances"
order = pyblish.api.CollectorOrder
hosts = ["photoshop"]
families_mapping = {
"image": []
}

def process(self, context):
# Necessary call when running in a different thread which pyblish-qml
# can be.
pythoncom.CoInitialize()

for layer in photoshop.get_layers_in_document():
layer_data = photoshop.read(layer)

# Skip layers without metadata.
if layer_data is None:
continue

# Skip containers.
if "container" in layer_data["id"]:
continue

child_layers = [*layer.Layers]
if not child_layers:
self.log.info("%s skipped, it was empty." % layer.Name)
continue

instance = context.create_instance(layer.Name)
instance.append(layer)
instance.data.update(layer_data)
instance.data["families"] = self.families_mapping[
layer_data["family"]
]
instance.data["publish"] = layer.Visible

# Produce diagnostic message for any graphical
# user interface interested in visualising it.
self.log.info("Found: \"%s\" " % instance.data["name"])
39 changes: 39 additions & 0 deletions pype/plugins/photoshop/publish/collect_workfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pyblish.api
import os


class CollectWorkfile(pyblish.api.ContextPlugin):
"""Collect current script for publish."""

order = pyblish.api.CollectorOrder + 0.1
label = "Collect Workfile"
hosts = ["photoshop"]

def process(self, context):
family = "workfile"
task = os.getenv("AVALON_TASK", None)
subset = family + task.capitalize()

file_path = context.data["currentFile"]
staging_dir = os.path.dirname(file_path)
base_name = os.path.basename(file_path)

# Create instance
instance = context.create_instance(subset)
instance.data.update({
"subset": subset,
"label": base_name,
"name": base_name,
"family": family,
"families": [],
"representations": [],
"asset": os.environ["AVALON_ASSET"]
})

# creating representation
instance.data["representations"].append({
"name": "psd",
"ext": "psd",
"files": base_name,
"stagingDir": staging_dir,
})
62 changes: 62 additions & 0 deletions pype/plugins/photoshop/publish/extract_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os

import pype.api
from avalon import photoshop


class ExtractImage(pype.api.Extractor):
"""Produce a flattened image file from instance

This plug-in takes into account only the layers in the group.
"""

label = "Extract Image"
hosts = ["photoshop"]
families = ["image"]

def process(self, instance):

staging_dir = self.staging_dir(instance)
self.log.info("Outputting image to {}".format(staging_dir))

# Perform extraction
files = {}
with photoshop.maintained_selection():
self.log.info("Extracting %s" % str(list(instance)))
with photoshop.maintained_visibility():
# Hide all other layers.
extract_ids = [
x.id for x in photoshop.get_layers_in_layers([instance[0]])
]
for layer in photoshop.get_layers_in_document():
if layer.id not in extract_ids:
layer.Visible = False

save_options = {
"png": photoshop.com_objects.PNGSaveOptions(),
"jpg": photoshop.com_objects.JPEGSaveOptions()
}

for extension, save_option in save_options.items():
photoshop.app().ActiveDocument.SaveAs(
staging_dir, save_option, True
)
files[extension] = "{} copy.{}".format(
os.path.splitext(
photoshop.app().ActiveDocument.Name
)[0],
extension
)

representations = []
for extension, filename in files.items():
representations.append({
"name": extension,
"ext": extension,
"files": filename,
"stagingDir": staging_dir
})
instance.data["representations"] = representations
instance.data["stagingDir"] = staging_dir

self.log.info(f"Extracted {instance} to {staging_dir}")
14 changes: 14 additions & 0 deletions pype/plugins/photoshop/publish/extract_save_scene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pype.api
from avalon import photoshop


class ExtractSaveScene(pype.api.Extractor):
"""Save scene before extraction."""

order = pype.api.Extractor.order - 0.49
label = "Extract Save Scene"
hosts = ["photoshop"]
families = ["workfile"]

def process(self, instance):
photoshop.app().ActiveDocument.Save()
48 changes: 48 additions & 0 deletions pype/plugins/photoshop/publish/validate_instance_asset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import os

import pyblish.api
import pype.api
from avalon import photoshop


class ValidateInstanceAssetRepair(pyblish.api.Action):
"""Repair the instance asset."""

label = "Repair"
icon = "wrench"
on = "failed"

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
and result["instance"] not in failed):
failed.append(result["instance"])

# Apply pyblish.logic to get the instances for the plug-in
instances = pyblish.api.instances_by_plugin(failed, plugin)

for instance in instances:
data = photoshop.read(instance[0])
data["asset"] = os.environ["AVALON_ASSET"]
photoshop.imprint(instance[0], data)


class ValidateInstanceAsset(pyblish.api.InstancePlugin):
"""Validate the instance asset is the current asset."""

label = "Validate Instance Asset"
hosts = ["photoshop"]
actions = [ValidateInstanceAssetRepair]
order = pype.api.ValidateContentsOrder

def process(self, instance):
instance_asset = instance.data["asset"]
current_asset = os.environ["AVALON_ASSET"]
msg = (
"Instance asset is not the same as current asset:"
f"\nInstance: {instance_asset}\nCurrent: {current_asset}"
)
assert instance_asset == current_asset, msg
Binary file added res/app_icons/photoshop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.