Adding Presets to Global Context Variables: Difference between revisions

From Gaffer Wiki
Jump to navigation Jump to search
No edit summary
mNo edit summary
Line 15: Line 15:


==== Set the widget type ====
==== Set the widget type ====
Next we need to tell Gaffer to use a presets dropdown widget for our value plug. That is done by setting <code>plugValueWidget:type</code> to <code>"GafferUI.PresetsPlugValueWidget"</code> : <pre>
Next we need to tell Gaffer to use a presets dropdown widget for our value plug. That is done by setting <code>plugValueWidget:type</code> to <code>"GafferUI.PresetsPlugValueWidget"</code> : <syntaxhighlight lang="python">
Gaffer.Metadata.registerValue( variablePlug, "plugValueWidget:type", "GafferUI.PresetsPlugValueWidget" )
Gaffer.Metadata.registerValue( variablePlug, "plugValueWidget:type", "GafferUI.PresetsPlugValueWidget" )
</pre>
</syntaxhighlight>


==== Set the preset names and values ====
==== Set the preset names and values ====
Now we add the presets themselves. This can be done in one of two ways. You can add a metadata entry starting with <code>preset:</code> followed by the display string of your preset set to the preset value. For example, <code>Gaffer.Metadata.registerValue( variablePlug, "preset:Shot 1004", "shot1004")</code>will create a preset showing in the UI as <code>Shot 1004</code>. When the user chooses that preset, the actual value of the context variable will be <code>shot1004</code>.
Now we add the presets themselves. This can be done in one of two ways. You can add a metadata entry starting with <code>preset:</code> followed by the display string of your preset set to the preset value. For example, <code>Gaffer.Metadata.registerValue( variablePlug, "preset:Shot 1004", "shot1004")</code>will create a preset showing in the UI as <code>Shot 1004</code>. When the user chooses that preset, the actual value of the context variable will be <code>shot1004</code>.


The second method sets all of the preset names and all of the values in one step each. This can be done by setting the <code>presetNames</code> metadata entry to a Cortex string vector (<code>IECore.StringVectorData</code>)holding all of the preset names. Setting <code>presetValues</code> to a Cortex vector of some type, such as <code>IECoreStringVectorData</code> or <code>IECoreFloatVectorData</code>.<pre>
The second method sets all of the preset names and all of the values in one step each. This can be done by setting the <code>presetNames</code> metadata entry to a Cortex string vector (<code>IECore.StringVectorData</code>)holding all of the preset names. Setting <code>presetValues</code> to a Cortex vector of some type, such as <code>IECoreStringVectorData</code> or <code>IECoreFloatVectorData</code>.<syntaxhighlight lang="python">
Gaffer.Metadata.registerValue( variablePlug, "presetNames", IECore.StringVectorData( presetNames ) )
Gaffer.Metadata.registerValue( variablePlug, "presetNames", IECore.StringVectorData( presetNames ) )
Gaffer.Metadata.registerValue( variablePlug, "presetValues", IECore.StringVectorData( presetValues ) )
Gaffer.Metadata.registerValue( variablePlug, "presetValues", IECore.StringVectorData( presetValues ) )
</pre>
</syntaxhighlight>


==== Resetting the <code>Settings</code> dialog ====
==== Resetting the <code>Settings</code> dialog ====


Note that the <code>Settings</code> UI is built once and then reused for subsequent showings. This means after adding presets, you need to force Gaffer to refresh the <code>Settings</code> dialog. You can force Gaffer to rebuild the <code>Settings</code> dialog by running the following code in the Python editor:<pre>
Note that the <code>Settings</code> UI is built once and then reused for subsequent showings. This means after adding presets, you need to force Gaffer to refresh the <code>Settings</code> dialog. You can force Gaffer to rebuild the <code>Settings</code> dialog by running the following code in the Python editor:<syntaxhighlight lang="python">
sw = GafferUI.ScriptWindow.acquire(root)
sw = GafferUI.ScriptWindow.acquire(root)
for window in sw.childWindows():
for window in sw.childWindows():
if hasattr( window, "_settingsEditor" ) :
if hasattr( window, "_settingsEditor" ) :
sw.removeChild(window)
sw.removeChild(window)
</pre>
</syntaxhighlight>


Once you have registered the metadata for a plug in this way, it will be saved with the script so there is no need to register the metadata again.
Once you have registered the metadata for a plug in this way, it will be saved with the script so there is no need to register the metadata again.
Line 40: Line 40:
==== Example Script ====
==== Example Script ====


The following script is an example of all of the above steps condensed into a reusable function for adding presets for a global context variable.<pre>
The following script is an example of all of the above steps condensed into a reusable function for adding presets for a global context variable.
<syntaxhighlight lang="python">
# Sets the preset names and values for a global context variable.
# Sets the preset names and values for a global context variable.
# `scriptNode` is the script node which you want to set global context variables for.
# `scriptNode` is the script node which you want to set global context variables for.
Line 76: Line 77:
if hasattr( window, "_settingsEditor" ) :
if hasattr( window, "_settingsEditor" ) :
sw.removeChild(window)
sw.removeChild(window)
</pre>For example, to set a few presets for a global context variable named <code>shot</code> run the script above in the Python editor, then run the line below.<pre>
</syntaxhighlight>For example, to set a few presets for a global context variable named <code>shot</code> run the script above in the Python editor, then run the line below.<syntaxhighlight lang="python">
globalContextPresets( root, "shot", ["shot A", "shot B", "shot C"], IECore.StringVectorData( [ "shotA", "shotB", "shotC" ] ) )
globalContextPresets( root, "shot", ["shot A", "shot B", "shot C"], IECore.StringVectorData( [ "shotA", "shotB", "shotC" ] ) )
</pre>
</syntaxhighlight>

Revision as of 10:23, 22 July 2024

Global context variables can be useful for ensuring a context variable is always accessible and set for the entire script. Unless it is overridden by a ContextVariables or ContextVariablesTweaks node in the node graph, all nodes will have access to the same global context variable. There are some global context variables added by Gaffer by default when creating a new script. You can see these, and add your own, from the Variables tab in the Settings dialog accessed from the File.Settingsmenu item.

The default user interface for a global context variable can be useful for many situations, but like all plugs in Gaffer, it is possible to customize how the plugs for global context variables are presented to the user. One such customization is adding presets for the user to choose from. This ensures only valid values can be assigned to the context variable. This guide will describe how to add presets to the widget controlling a global context variable.

Adding Presets to the Settings Dialog

Presets can be added to plugs in the Settings dialog by assigning metadata to the value plug for a global context variable. There are three steps to adding the needed metadata.

Identify the target plug

Before we assign metadata, we need to identify which plug represents the context variable you want to add presets to. Global context variables are stored in the variables child of the script node. In the Python editor, these are accessed by root["variables"].

The entries in root["variables"] are of the type NameValuePlug. These plugs themselves have three child plugs: name, enabled and value. The name plug sets the name of the context variable and the value sets the value of the variable. The enabled plug is used to enable or disable the variable. Note that the value of name may be different from the name of the actual NameValuePlug.

To determine which child of root["variables"] you want to add metadata to, loop through all the children of root["variables"] checking the value of the name plug until you find the variable. The corresponding value child plug will be the plug we add metadata to.

Set the widget type

Next we need to tell Gaffer to use a presets dropdown widget for our value plug. That is done by setting plugValueWidget:type to "GafferUI.PresetsPlugValueWidget" :

Gaffer.Metadata.registerValue( variablePlug, "plugValueWidget:type", "GafferUI.PresetsPlugValueWidget" )

Set the preset names and values

Now we add the presets themselves. This can be done in one of two ways. You can add a metadata entry starting with preset: followed by the display string of your preset set to the preset value. For example, Gaffer.Metadata.registerValue( variablePlug, "preset:Shot 1004", "shot1004")will create a preset showing in the UI as Shot 1004. When the user chooses that preset, the actual value of the context variable will be shot1004.

The second method sets all of the preset names and all of the values in one step each. This can be done by setting the presetNames metadata entry to a Cortex string vector (IECore.StringVectorData)holding all of the preset names. Setting presetValues to a Cortex vector of some type, such as IECoreStringVectorData or IECoreFloatVectorData.

Gaffer.Metadata.registerValue( variablePlug, "presetNames", IECore.StringVectorData( presetNames ) )
Gaffer.Metadata.registerValue( variablePlug, "presetValues", IECore.StringVectorData( presetValues ) )

Resetting the Settings dialog

Note that the Settings UI is built once and then reused for subsequent showings. This means after adding presets, you need to force Gaffer to refresh the Settings dialog. You can force Gaffer to rebuild the Settings dialog by running the following code in the Python editor:

sw = GafferUI.ScriptWindow.acquire(root)
for window in sw.childWindows():
	if hasattr( window, "_settingsEditor" ) :
		sw.removeChild(window)

Once you have registered the metadata for a plug in this way, it will be saved with the script so there is no need to register the metadata again.

Example Script

The following script is an example of all of the above steps condensed into a reusable function for adding presets for a global context variable.

# Sets the preset names and values for a global context variable.
# `scriptNode` is the script node which you want to set global context variables for.
# `variableName` is the name of the variable to set presets for.
# `presetNames` is a list of strings to set the preset names to.
# `presetValues` is an `IECore.*VectorData` holding a list of preset values
# for each corresponding item in `presetNames`
def globalContextPresets( scriptNode, variableName, presetNames, presetValues ) :
	if len( presetNames ) != len( presetValues ) :
		raise ValueError( "`presetNames` and `presetValues` must be the same length" )
	
	# Find the value plug for `variableName` global context variable.
	variablePlug = None
	for p in scriptNode["variables"] :
		if p["name"].getValue() == variableName :
			variablePlug = p["value"]
			break

	if variablePlug is None :
		raise RuntimeError( "\"{}\" not found in global context variables".format( variableName ) )
	
	# Set the widget to a presets dropdown.
	Gaffer.Metadata.registerValue( variablePlug, "plugValueWidget:type", "GafferUI.PresetsPlugValueWidget" )

	# Register the names and values.
	Gaffer.Metadata.registerValue( variablePlug, "presetNames", IECore.StringVectorData( presetNames ) )
	Gaffer.Metadata.registerValue( variablePlug, "presetValues", presetValues )

	# Set the value to the first preset.
	variablePlug.setValue( presetValues[0] )

	# Tell Gaffer to rebuild the `Settings` dialog.
	sw = GafferUI.ScriptWindow.acquire( scriptNode )
	for window in sw.childWindows():
		if hasattr( window, "_settingsEditor" ) :
			sw.removeChild(window)

For example, to set a few presets for a global context variable named shot run the script above in the Python editor, then run the line below.

globalContextPresets( root, "shot", ["shot A", "shot B", "shot C"], IECore.StringVectorData( [ "shotA", "shotB", "shotC" ] ) )