Batch add layer to Photoshop file with tk-multi-app?

I have a tk-app for Photoshop that creates a model sheet/slate for an Asset based on context information and Shotgun fields.

I am now working on new tk-shotgun-app that that, based on a selection of published files (that are .psd files) could…

  1. Pass list of published files (and their contexts) somehow to the app/engine. Environment variables? Temp file?

  2. Somehow let the engine know it should expect a to-do list when it starts up?

  3. Launch Photoshop using tk-mutil-launcher. Need an example of launching Photoshop with a project context from a tk-shotgun app.

  4. After Photoshop launches, somehow launch a new hidden tk-photoshopcc-app that picks up the to-do list and starts a loop of; set context, open file, perform operations, save file, repeat with next item on list.

  5. Clean up env vars/temp files. Exit.

Seems like 2 and 4 are new territory?

From what I understand about the PS startup process it’s pretty much limited to opening and launching the panel, which launches the engine. All the default hooks to so something like this don’t work in the Adobe products they way they do in Nuke, Maya, Houdini, etc. You can’t launch WorkFiles2 on startup for example.

Is it at all possible to get Photoshop (or and Adobe product) to do anything other than just open up?

Any thoughts, experiences or fair warnings would be appreciated.

Daniel

2 Likes

Hey, Daniel welcome to the forums and great questions!

I still need to find out some information, but here’s what I have so far.

  1. So the for the first part I would have a custom app that can be run on multiple selection through the browser integration. The tk-shotgun-launchfolder is a good example of this:
    https://github.com/shotgunsoftware/tk-shotgun-launchfolder/blob/master/app.py#L29
    https://github.com/shotgunsoftware/tk-shotgun-launchfolder/blob/master/app.py#L69

    This app would then maybe store the PublishedFile ids or the paths to an environment variable:

    os.environ["PHOTOSHOP_PUB_FILE_IDS"] = "[1123,43242,5346,2144]"
    
  2. To get your code to run automatically once photoshop is launched you could make use of the core hook engine_init.py.
    As this is a core hook it will run in all engines, so you would first need to check if it’s being called in the correct engine, and then you would check for the presence of your env variable something like:

    if engine.name == "tk-photoshopcc" and "PHOTOSHOP_PUB_FILE_IDS" in os.environ:
        ...
    
  3. I’ve not tested this but I believe it should be a case of running the following code from your custom app to launch Photoshop:

    engine = self.parent.engine
    # the actual command string might be different 
    # you can print the engine.commands.keys() to see all the possible entries
    engine.execute_command("photoshop_cc_2019")
    
    
  4. You have two potential options here. You could run the logic for opening and processing the files directly from the engine_init.py hook or you could have a custom app which the engine_init.py hook could launch. I tested trying to run another app via the engine_init.py, but I encountered errors just logging and also trying to run the app. It’s also a bit hacky since the command to run a command is private in the photoshop engine. Maybe importing your logic in the engine_init.py and calling it directly might work better?

I’ll check with the team on point 4.

Thanks
Phil

1 Like

I cant seem to get the parent of my app. For me self.parent is built in method. There is no engine.

engine = self.parent.engine just hangs…

Using…

self.engine = sgtk.platform.current_engine()

I can see registered commands in my current engine but there only the ones I see in my GUI cascading menu.

[‘Jump to Screening Room Web Player’, ‘show_in_filesystem’, ‘launch_publish’, ‘Jump to Screening Room in RV’, ‘Add Model Sheet…’]

Do I need to get the PhotoshopCC command registered the for the Toolkit config I am using?

Should I expect to see the PhotoshopCC command if this app were in the Primary configuration?

How can my config see the same thing the browser ‘config’ sees?

Daniel

1 Like

Hey Daniel,

To answer your last question about not seeing the launcher commands in the engine’s commands list, my guess is that tk-multi-launchapp isn’t configured the same way in your “Toolkit” sandbox config (shown in your screenshot) the same way that it is in the project’s primary configuration. In short, accessing engine.commands is going to give you back the same list of available commands as is shown in the action menu in Shotgun for that configuration.

Secondly, using sgtk.platform.current_engine() to get the engine instance is perfectly acceptable, so feel free to continue using that approach.

Aside from that, a few points from the earlier conversation you and Phil were having:

  • I really think it should be on us (the Toolkit engineering team) to implement the same functionality around the run_at_startup configuration option in tk-photoshopcc that’s implemented in most of the other engines we provide. I’m actually pretty surprised that it’s not there already. I’ll get a ticket in to add that, but I can’t really say when we’ll be able to get the work done off the top of my head.
  • In the interim, Phil’s suggestions are pretty good ones to try, so if you’re already going in that direction I’d say it’s good to keep at it. One small suggestion I’ll make, just in case you’ve not already thought of it, would be to JSON encode the list of PublishedFile ids before putting them into the environment. That’ll make it easy to store the data in a way that’s quickly convertable back into a Python list from your app.
import json
import os
os.environ["my_enc_var_name"] = json.dumps([123, 456, 789])
import json
import os
publish_ids = json.loads(os.environ["my_env_var_name"])

I hope that helps!

2 Likes

Working on #2. To run it I just test for the engine and not the env vars just yet. Have not got PS to launch yet from my custom app.

engine_init.py seems to work but it gets triggered twice so my code gets run twice. Two modelsheets.

I can see it hit the if engine.name == test twice in a row in the logs…

2019-08-15 16:28:15,436 [14907 INFO sgtk.ext.tk_photoshopcc_basic.plugin_init] Bootstrapping toolkit...
2019-08-15 16:28:15,809 [14908 INFO sgtk.ext.tk_framework_adobe.plugin_init] Bootstrapping toolkit...
2019-08-15 16:28:22,004 [14907 DEBUG sgtk.env.project.tk-photoshopcc.js] Sending context about to change from client.
2019-08-15 16:28:22,005 [14908 DEBUG sgtk.env.project.tk-photoshopcc.js] Sending context about to change from client.
2019-08-15 16:28:22,013 [14907 DEBUG sgtk.env.project.tk-photoshopcc.js] Setting commands from client: {"commands": [{"type": "panel", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-shotgunpanel/v1.6.3/resources/shotgun_panel_menu_icon.png", "display_name": "Shotgun Panel...", "description": "Panel UI with Shotgun information about your scene, yourself and the things around you.", "uid": 3}], "context_menu_cmds": [{"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-photoshopcc/v1.7.1/resources/shotgun_logo.png", "display_name": "Jump to Shotgun", "description": "Open the current context in a web browser.", "uid": 1}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-photoshopcc/v1.7.1/resources/shotgun_folder.png", "display_name": "Jump to File System", "description": "Open the current context in a file browser.", "uid": 2}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-screeningroom/v0.3.3/icon_256.png", "display_name": "Jump to Screening Room Web Player", "description": "Screening Room integration right inside your application.", "uid": 5}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-screeningroom/v0.3.3/icon_256.png", "display_name": "Jump to Screening Room in RV", "description": "Screening Room integration right inside your application.", "uid": 4}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/platform/qt/folder_256.png", "display_name": "Open Log Folder", "description": "Opens the folder where log files are being stored.", "uid": 11}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-pythonconsole/v1.1.2/icon_256.png", "display_name": "Shotgun Python Console...", "description": "A Python console for Shotgun Toolkit", "uid": 9}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/platform/qt/book_256.png", "display_name": "Toggle Debug Logging", "description": "Toggles toolkit debug logging on and off. This affects all debug logging, including log files that are being written to disk.", "uid": 10}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-about/v0.2.8/icon_256.png", "display_name": "Work Area Info...", "description": "Shows a breakdown of your current environment and configuration.", "uid": 8}], "favorites": [{"display_name": "File Open...", "description": "Using this app you can browse, open and save your Work Files and Publishes.", "fav_index": 0, "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-workfiles2/v0.11.9/resources/file_open_menu_icon.png", "type": "default", "uid": 6}, {"display_name": "File Save...", "description": "Using this app you can browse, open and save your Work Files and Publishes.", "fav_index": 1, "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-workfiles2/v0.11.9/resources/file_save_menu_icon.png", "type": "default", "uid": 7}]}
2019-08-15 16:28:22,013 [14908 DEBUG sgtk.env.project.tk-photoshopcc.js] Setting commands from client: {"commands": [{"type": "panel", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-shotgunpanel/v1.6.3/resources/shotgun_panel_menu_icon.png", "display_name": "Shotgun Panel...", "description": "Panel UI with Shotgun information about your scene, yourself and the things around you.", "uid": 3}], "context_menu_cmds": [{"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-photoshopcc/v1.7.1/resources/shotgun_logo.png", "display_name": "Jump to Shotgun", "description": "Open the current context in a web browser.", "uid": 1}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-photoshopcc/v1.7.1/resources/shotgun_folder.png", "display_name": "Jump to File System", "description": "Open the current context in a file browser.", "uid": 2}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-screeningroom/v0.3.3/icon_256.png", "display_name": "Jump to Screening Room Web Player", "description": "Screening Room integration right inside your application.", "uid": 5}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-screeningroom/v0.3.3/icon_256.png", "display_name": "Jump to Screening Room in RV", "description": "Screening Room integration right inside your application.", "uid": 4}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/platform/qt/folder_256.png", "display_name": "Open Log Folder", "description": "Opens the folder where log files are being stored.", "uid": 11}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-pythonconsole/v1.1.2/icon_256.png", "display_name": "Shotgun Python Console...", "description": "A Python console for Shotgun Toolkit", "uid": 9}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/platform/qt/book_256.png", "display_name": "Toggle Debug Logging", "description": "Toggles toolkit debug logging on and off. This affects all debug logging, including log files that are being written to disk.", "uid": 10}, {"type": "context_menu", "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-about/v0.2.8/icon_256.png", "display_name": "Work Area Info...", "description": "Shows a breakdown of your current environment and configuration.", "uid": 8}], "favorites": [{"display_name": "File Open...", "description": "Using this app you can browse, open and save your Work Files and Publishes.", "fav_index": 0, "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-workfiles2/v0.11.9/resources/file_open_menu_icon.png", "type": "default", "uid": 6}, {"display_name": "File Save...", "description": "Using this app you can browse, open and save your Work Files and Publishes.", "fav_index": 1, "icon_path": "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/app_store/tk-multi-workfiles2/v0.11.9/resources/file_save_menu_icon.png", "type": "default", "uid": 7}]}
2019-08-15 16:28:22,014 [14908 DEBUG sgtk.env.project.tk-photoshopcc.js] Setting log file path from client: "/Users/danieleaton/Library/Logs/Shotgun/tk-photoshopcc.log"
2019-08-15 16:28:22,014 [14907 DEBUG sgtk.env.project.tk-photoshopcc.js] Setting log file path from client: "/Users/danieleaton/Library/Logs/Shotgun/tk-photoshopcc.log"

any thoughts?

1 Like

Regarding the duplicates, when I batch several files its only the first file that gets the duplicates. All the others work perfectly.

Strange.

1 Like

Part 1 - Working.
Part 2 - Working. Sort of. Except for the duplicates layers on the first file in the batch.
Part 3 - Still not sure how to launch photoshop from my custom app. Looks like I need to get my launchapp in order or I need to move this to the primary config?
Part 4 - Just need to get the context set correctly before I open each file. How do I do that? I have all the information I need. Asset/Task.
Part 5 - How do I quit Photoshop with the engine?

Thanks for all the help so far…

Daniel

add_model_sheet

2 Likes

And the model sheet. Still need to set the context to get the rest of the information.

1 Like

Looking really cool so far!

Part 2 - I’m not sure why the engine_init.py would be getting called twice. Is it possible you have the old and new both photoshop extensions installed?
You could work around this perhaps by setting a different env var on the first execution, and checking for it on the second so that you know you’ve already run once.

Part 3 - As Jeff mentioned it looks as though the tk-multi-launchapp is not configured in that version environment. From your screenshot it looks like it is present in your primary config, just not in your “Toolkit” config. Once the app has been added to the environment you should be good.

Part 4 - I’m personally not familiar with how the multi-document context stuff works in Photoshop. That would be a question for @Jeff_Beeland. Though you can create your own context based upon the PublishedFile entity with tk.context_from_entity("PublishedFile",1234), and pass it to sgtk.platform.change_context(context), however, there might be more to it. The Photoshop engine seems to handle this here:

Part 5 - Again I’m not sure on this, I suppose it would be done via the adobe connection, but I’m not familiar with the API.

1 Like

Part 2 - Duplicate extension to blame. Solved.

Part 3 - Looking at this next.

Part 4 - I can swith context with…

tk = sgtk.sgtk_from_path(local_path)
context = tk.context_from_entity("PublishedFile",sg_pubfile['id'])
logger.info(" ===== context: %s" % context)

But then when I…

sgtk.platform.change_context(context)
2019-08-16 10:58:21,124 [8951 DEBUG sgtk.core.pipelineconfig_factory] sgtk.from_path resolved '/Volumes/droid/PIPE/Assets/Characters/H/HarryPotterA/Design/Publish/PIPE_HarryPotterA_Ds_v01/PIPE_HarryPotterA_Ds_v01.psd' -> <Sgtk Configuration /Volumes/droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit>
2019-08-16 10:58:21,862 [8951 INFO sgtk.ext.no_current_bundle.ccc6ea09b2fd457788a01653f8c52b9b]  ===== context: Design, Asset HarryPotterA
2019-08-16 10:58:21,862 [8951 ERROR sgtk.core.pipelineconfig] Exception raised while executing hook '/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/hooks/engine_init.py'
Traceback (most recent call last):
  File "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/pipelineconfig.py", line 1174, in execute_core_hook_internal
    return_value = hook.execute_hook(hook_path, parent, **kwargs)
  File "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/hook.py", line 564, in execute_hook
    return execute_hook_method([hook_path], parent, None, **kwargs)
  File "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/hook.py", line 618, in execute_hook_method
    ret_val = hook_method(**kwargs)
  File "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/hooks/engine_init.py", line 47, in execute
    self._addModelSheetLayer(engine)
  File "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/hooks/engine_init.py", line 181, in _addModelSheetLayer
    sgtk.platform.change_context(context)
  File "/Volumes/Droid/PIPE/Shotgun/PipelineConfigurations/PIPE/Toolkit/install/core/python/tank/platform/util.py", line 109, in change_context
    raise TankError("No engine is currently running! Run start_engine instead.")
TankError: No engine is currently running! Run start_engine instead.

Why do I loose the engine? Do I need to relaunch it first with the new context? How?

Daniel

1 Like

Actually the more I think about it, now that I’m away from my desk…

I don’t really need need to change the context. The tk-photoshopcc-app has access to it so I use it there. But, for what I’m doing here in tk-shotgun I don’t really need it. I was going to set the context with the information from the published files, but assuming the task and asset information are the same, I already have it. I just need to pass that into engine
_init.py along with all the other layout and output dir information I’m passing via envars. It takes time a lot of time when doing a large amount of files too.

Problem solved. Now…

Part 3 - I just need to figure out how to launch PhotoshopCC, but that is more of a launchApp configuration issue I think. Have not even looked into this yet.

Part 5 - How do I quit Photoshop? I see some stuff on the web for .jsx but i’ve yet to crack the xlation to python engine.adobe. Hoping someone here has done this before.

So close.

Daniel

1 Like

Next question. How and where do I ‘hook’ the modified engine_init.py into my configuration?

1 Like

Hey Daniel! I haven’t been following this deep-dive thread since the beginning, but I think I can answer that last question without context: engine_init is a core hook, so there’s not actually a configuration setting for it. All you have to do to take it over is to copy your modified version into config/core/hooks/ in your pipeline config. So long as a file named engine_init.py is present in that directory, it’ll be used.

3 Likes

Thanks. Perfect. I figured there was a proper place.

Daniel

2 Likes

Adding Photoshop to my toolkit installation was pretty straightforward.

App works perfectly. I feel so much more Shotgun-empowered.

Thanks for all the help!

4 Likes

The only last thing to do was Quit Photoshop once the batch processing is done. Here is what worked…

engine.adobe.app.executeAction(engine.adobe.app.charIDToTypeID(‘quit’), engine.adobe.undefined, engine.adobe.DialogModes.ALL)

2 Likes

This should probably be a forum tag. :wink:

Nice work! There were quite a few pieces to get in place, and thanks for sharing back the exiting solution!

2 Likes