Gaffer Python get scene filepath, save, load and create node from external script

625 views
Skip to first unread message

Roy Nieterau

unread,
Jul 26, 2019, 2:01:44 AM7/26/19
to gaffer-dev
Hi everyone,

I'm a long time lurker but have little to no experience with Gaffer. However, I've been fiddling with seeing how easy it'd be to integrate our pipeline with Gaffer and it seems relatively smooth sailing to get a link setup and all tools to load correctly, including menu items, etc.

There are only a few things I couldn't figure out:
  • 1. Without relying on the global "root" variable in the Script console how do I add a node to the currently active scene? I was able to do:
import GafferScene
reader
= GafferScene.SceneReader()
root
.addChild(reader)

How do I do this in an external script without having the "root" variable? How do I get the current scene for example?
  • 2. I couldn't find how to save as, save or open a scene in Gaffer. Where's that one hidden?
    I'm particularly looking for load, save and a query to see if current scene has unsaved changes.

  • 3. I was able to generate a menu at startup going from the tutorial in the documentation: http://www.gafferhq.org/documentation/0.50.0.0/Tutorials/Scripting/AddingAMenuItem/index.html
    Thanks for that! However, could I also add the menu at runtime? If so, how would I do it?

  • 4. After I know the above. If I were to run a startup script that should always run as Gaffer initializes (even if it were without gui) yet I also wanted to allow that to trigger a function that adds the runtime menu? How could I detect Gaffer is running with a gui or not? Plus, how do I make sure the gui is initialized (if the startup script must reside outside of /startup/gui
Thanks in advance!

Also, I've been playing around in Eric Mehl's Windows build to test all this, so far so good. (Although I didn't actually test rendering, etc. but I'm more investiging the overall integration of things)

Cheers,
Roy

Tom Cowland

unread,
Jul 26, 2019, 10:44:23 AM7/26/19
to gaffe...@googlegroups.com
Hey Roy,

Glad things have been going well so far!

  • 1. Without relying on the global "root" variable in the Script console how do I add a node to the currently active scene? I was able to do:
import GafferScene
reader = GafferScene.SceneReader()
root.addChild(reader)

How do I do this in an external script without having the "root" variable? How do I get the current scene for example?

Are you able to elaborate a little on how you are running your code? 

The Gaffer app itself can have multiple scripts (scenes) open at once. Gaffer can also be imported as a module on its own. As such, the answer to this depends on how your code is invoked.

Within the app, the ScriptEditor for example is able to offer the root variable as it is a child of a ScriptWindow, and so has access to its scriptNode accessor (via self.ancestor( GafferUI.ScriptWindow) ). The same would be true of any menu items. 

If you’re in a stand-alone python script, and have just imported gaffer, it’s more a case of rolling your own ‘current’:

script = Gaffer.ScriptNode()
script["fileName"].setValue( "/path/to/your/file.gfr" )
script.load()

  • 2. I couldn't find how to save as, save or open a scene in Gaffer. Where's that one hidden?
    I'm particularly looking for load, save and a query to see if current scene has unsaved changes.

Have a look in FileMenu.py (https://github.com/GafferHQ/gaffer/blob/master/python/GafferUI/FileMenu.py) which shows how the menus do their thing (which also illustrates how to get back to the scriptNode In a variety of ways, and determine if there are unsaved changes).

https://github.com/GafferHQ/gaffer/blob/bbd76c537110ebd62232a4b149

588bb678a9af05/python/GafferUI/FileMenu.py#L29
You can run that code at any time. All those startup files just do it run code when gaffer starts.

You can also register menu items such that they are built on the fly whenever the menu is accessed. The Open Recent menu is an example of this (https://github.com/GafferHQ/gaffer/blob/bbd76c537110ebd62232a4b149588bb678a9af05/python/GafferUI/FileMenu.py#L53).

You should be able to do this to create the top-level menus too.

  • 4. After I know the above. If I were to run a startup script that should always run as Gaffer initializes (even if it were without gui) yet I also wanted to allow that to trigger a function that adds the runtime menu? How could I detect Gaffer is running with a gui or not? Plus, how do I make sure the gui is initialized (if the startup script must reside outside of /startup/gui
The startup script subdirectories are run at appropriate points in the apps startup cycle, or when the modules load. As such, if you have code that you want to run in the gui, the only way to do that safely is in the startup/gui folder, the other non-module folders refer to the other apps (see https://github.com/GafferHQ/gaffer/tree/master/apps).  You wouldn’t want to use use the GafferUI module folder as that is imported in to many different UI apps that aren’t the main gaffer ui.
So you'd need to split your code up so it runs at the appropriate point.

Hope that’s some help.
All the best
Tom

Roy Nieterau

unread,
Jul 26, 2019, 11:25:42 AM7/26/19
to gaffer-dev
See answers below.


On Friday, July 26, 2019 at 4:44:23 PM UTC+2, Tom Cowland wrote:
Hey Roy,

Glad things have been going well so far!

  • 1. Without relying on the global "root" variable in the Script console how do I add a node to the currently active scene? I was able to do:
import GafferScene
reader = GafferScene.SceneReader()
root.addChild(reader)

How do I do this in an external script without having the "root" variable? How do I get the current scene for example?

Are you able to elaborate a little on how you are running your code? 

The Gaffer app itself can have multiple scripts (scenes) open at once. Gaffer can also be imported as a module on its own. As such, the answer to this depends on how your code is invoked.

Within the app, the ScriptEditor for example is able to offer the root variable as it is a child of a ScriptWindow, and so has access to its scriptNode accessor (via self.ancestor( GafferUI.ScriptWindow) ). The same would be true of any menu items. 

If you’re in a stand-alone python script, and have just imported gaffer, it’s more a case of rolling your own ‘current’:

script = Gaffer.ScriptNode()
script["fileName"].setValue( "/path/to/your/file.gfr" )
script.load()

The script is basically triggering from a separate Qt user interface that wants to create a node in the current active view/scene in the Gaffer UI. I couldn't figure out how to easily find which scene was currently opened, kind of like doing a `maya.cmds.file(query=True, sceneName=True)`. In my case I triggered a User Interface from a menu bar entry that just shows a generic Qt interface (that is agnostic across multiple DCCs, as such passing along a handle wouldn't fit my needs). It's more a matter of finding "what script/scene is currently open"?

  • 2. I couldn't find how to save as, save or open a scene in Gaffer. Where's that one hidden?
    I'm particularly looking for load, save and a query to see if current scene has unsaved changes.

Have a look in FileMenu.py (https://github.com/GafferHQ/gaffer/blob/master/python/GafferUI/FileMenu.py) which shows how the menus do their thing (which also illustrates how to get back to the scriptNode In a variety of ways, and determine if there are unsaved changes).

 Thanks, interesting reference. Will delve into it!
You can run that code at any time. All those startup files just do it run code when gaffer starts.

You can also register menu items such that they are built on the fly whenever the menu is accessed. The Open Recent menu is an example of this (https://github.com/GafferHQ/gaffer/blob/bbd76c537110ebd62232a4b149588bb678a9af05/python/GafferUI/FileMenu.py#L53).

You should be able to do this to create the top-level menus too.

Ah, the real thing I couldn't find out was how to just the right "application" variable to start appending the menus as per the example. Since in my separate method/package I didn't have the global `application` variable available. How would I retrieve the "active application" or alike? Can I get that from GafferUI?

  • 4. After I know the above. If I were to run a startup script that should always run as Gaffer initializes (even if it were without gui) yet I also wanted to allow that to trigger a function that adds the runtime menu? How could I detect Gaffer is running with a gui or not? Plus, how do I make sure the gui is initialized (if the startup script must reside outside of /startup/gui
The startup script subdirectories are run at appropriate points in the apps startup cycle, or when the modules load. As such, if you have code that you want to run in the gui, the only way to do that safely is in the startup/gui folder, the other non-module folders refer to the other apps (see https://github.com/GafferHQ/gaffer/tree/master/apps).  You wouldn’t want to use use the GafferUI module folder as that is imported in to many different UI apps that aren’t the main gaffer ui.
So you'd need to split your code up so it runs at the appropriate point.

Sure, makes sense. I'll see how I go about this.

Roy Nieterau

unread,
Jul 26, 2019, 2:40:21 PM7/26/19
to gaffer-dev
For full clarity, I'm trying to implement open-source Pipeline Avalon and open-source publishing tool Pyblish for an integration with Gaffer, see: https://gitter.im/getavalon/Lobby?at=5d3b3f8204534306f331bdb9

William Eguienta

unread,
Jul 27, 2019, 7:40:33 AM7/27/19
to gaffer-dev
hey roy, fully support you to integrating gaffer in avalon :) 
bests 

John Haddon

unread,
Jul 27, 2019, 4:59:23 PM7/27/19
to gaffe...@googlegroups.com
On Fri, Jul 26, 2019 at 4:25 PM Roy Nieterau <royni...@gmail.com> wrote:
The script is basically triggering from a separate Qt user interface that wants to create a node in the current active view/scene in the Gaffer UI. I couldn't figure out how to easily find which scene was currently opened, kind of like doing a `maya.cmds.file(query=True, sceneName=True)`. In my case I triggered a User Interface from a menu bar entry that just shows a generic Qt interface (that is agnostic across multiple DCCs, as such passing along a handle wouldn't fit my needs). It's more a matter of finding "what script/scene is currently open"?

This has been covered a couple of times before on this forum :


Gaffer can open multiple files at once, so it's your responsibility to determine what script is "current" based on what part of the UI the user is interacting with. Since your Qt interface is opened from the menu bar, perhaps that is the interaction point at which you could determine which script to operate on. Alternatively, you might want to derive from Editor so that your UI can be embedded in the Gaffer layout directly, in which case each editor is associated directly with a script.

Ah, the real thing I couldn't find out was how to just the right "application" variable to start appending the menus as per the example. Since in my separate method/package I didn't have the global `application` variable available. How would I retrieve the "active application" or alike? Can I get that from GafferUI?

Gaffer can run more than one application in a process. You can find the application a script belongs to using `script.ancestor( Gaffer.ApplicationRoot )`.

Hope that helps...
Cheers...
John
 

Roy Nieterau

unread,
Jul 28, 2019, 4:25:50 AM7/28/19
to gaffer-dev
Thanks for confirming John. Seems like I definitely to take some time to think this through on how it would work for Pyblish and Avalon.

The tricky thing with Avalon/Pyblish is that the tools themselves are software-agnostic and keep no handle like this to the root application - because they never needed to for any other host. Then there are individual Python plug-ins which are just small scripts that are discovered and loaded at runtime by the avalon API or the interfaces. The design of these methods also do not allow for the parent tool/caller to send across this extra data like "root". For now I've written out a small Issue on Avalon github to describe what needs to be resolved for the Integration to be able to work: https://github.com/getavalon/core/issues/404

Thanks for providing those two links too. It's odd, I couldn't find them before so definitely appreciate providing these!
Reply all
Reply to author
Forward
0 new messages