Photoshop Integration
Issue
This is about a Photoshop (PS) Integration for Avalon.
Implementation
- PS does not support Python, but we can communicate with PS through its DOM (Document Object Model). Best starting resource is; https://github.com/lohriialo/photoshop-scripting-python.
- DOM communication with PS may not provide all the access we need. This needs to be investigated.
- Another communication method would be through socket/port, where PS invokes a server/client relationship with Avalon on demand.
Workfiles prototype

- This is using the DOM workflow, which is quite easy to get into.
- One issue with with Workfiles app, is that it was not intended for DCCs where you can have multiple files open. You can see that I've just resorted to opening
v002instead of closingv001. Maybe the Workfiles app needs to have anClose active workfileoption?
Menu Items
- Menu items are not possible in Photoshop (and I assume the rest of the Adobe products).
- Could provide a script for each menu item, and access them through
File > Scripts. This would certainly be the quickest way to get going, but it would not allow for very easy updates. If we add a menu item, a new script will need to be deployed to each workstation. - Make a CEP extension, which can be accessed from
Window > Extensions. Again updates to the extension would need to be distributed and installed on each workstation. But CEP can render html, so we could potentially host a local server and have the CEP extension be a passthrough.
Extension

- The
Workfilesbutton is hosted in the extension, so not easily updatable but its a start.
Looks like a good start indeed !!
Separate Flask Server

- Simplified the extension to only host a button for "Refresh" and an Iframe for the external process.
- Running a Flask server with the
Workfiles/Create...etc. buttons. Which means no hacked execution of batch scripts;
@app.route("/workfiles_route")
def workfiles_route():
io.install()
api.register_host(avalon.photoshop)
workfiles.show()
# Required return statement.
return "nothing"
- There is an issue where the Flask server exits when you try opening tools for the second time.
- The tools (except for pyblish-qml) appear behind the Photoshop window. Will need to get them in front.
- You dont have to restart Photoshop (just refresh the website), and when using Flask's debug mode you dont even have to restart the server.

- The context of the server determines the tools' context. Will need to have a "Launch Photoshop server" action or similar to get started.

- Tools are appearing ontop of Photoshop. Quite possibly a loophole since apps arent supposed to appear infront of the active window. Done by launching a new process, which is how Pyblish-QML was the only one appearing ontop.
- There are no icons when launching the tools standalone. When trying to investigate how the avalon-launcher fetches the icons, I noticed that its fetching it from outside the
avalonmodule folder; https://github.com/getavalon/launcher/blame/1cec0a92e6af6e627072486d3f38bd53786981b3/launcher/lib.py#L17. Is this not "dangerous" since we are assuming the avalon module is pulled as a git repository? @davidlatwe - Note to self; since the server can be launched in any context, it might be nice to see the context along with the buttons.

- Reverted using separate processes to launch the tools. This was introducing a world of trouble, when using the context manager because parent processes do not get the environment from their children. Added bonus is that the server only needs to be launched once and the context can be switched.
- Switched to a vendorized Bottle.py webserver, so there are no external dependencies.
- The tools are appearing ontop of Photoshop because of this PR; https://github.com/getavalon/core/pull/520
- Photoshop has "groups" or
LayerSetsas they are known as in the Javascripting. This is a good candidate for a container for output. - Photoshop can store metadata per layer since CS4; https://blogs.adobe.com/jnack/2008/10/per-layer_metadata_comes_to_photoshop.html
Unfortunately LayerSets cannot store metadata, so a possible solution is to have a layer within the LayerSet that stores the metadata for the container.
Certainly not ideal since the user can move the metadata layer around, but we can also link the layer and LayerSet to ensure we can get the metadata.
One issue with using metadata on a layer, is that the user cannot actually interact with the data.
For example in Maya data is imprinted on the container, which allows users to manually change this data after creation.
It might be useful with a tools that reads/writes this imprinted data, so DCCs like Photoshop can have same interaction as Maya.
- Reverted back to using the DOM method for communicating with Photoshop.
When getting deeper into the integration it got apparent that passing objects around to different functions (imprint for example), communicating with Photoshop over socket/port became very tricky. So I took the "cheap" way out, and added the pywin32 (on Windows) dependency.
Maybe in the future there will be python library we can make use of.

- Implemented
avalon.photoshop.Creator
This involved other required methods; imprint and maintained_selection. Getting deeper into XMPMetadata turned out to be needlessly tricky. Instead I've opted to store the metadata in the file info Headline field. There may be a better place for this, but I chose this because (1) it provides easy access for debugging and (2) the Headline field in Photoshop is a multi-line edit so its a easy to copy.
Since I'm not storing the metadata on each layer instance, I've resorted to using each layers id which is unique. The data is a dictionary with these ids:
{
"189": {
"id": "pyblish.avalon.instance",
"family": "image",
"asset": "Bruce",
"subset": "imageDefault",
"active": true
}
}
The nice thing about this approach is that LayerSets have ids as well, so there no need for additional layers as described here, making is cleaner and less user error prone.
Documenting this to be fixed later.
When opening the Creator tool for the second time after creating an instance, this message appears:
Traceback (most recent call last):
File "C:\Users\admin\avalon-docker\volume\git\avalon-core\avalon\tools\creator\app.py", line 465, in <lambda>
lib.schedule(lambda: widget.setText(""), 5000, channel="message")
RuntimeError: wrapped C/C++ object of type QLabel has been deleted
Publishing and Loading

Being able to go full circle marks the completion of the draft version of the Photoshop integration. Will look at documentation and PR tomorrow.