diff --git a/Changes.md b/Changes.md index 8eca76454d..d7b6e155bc 100644 --- a/Changes.md +++ b/Changes.md @@ -141,7 +141,15 @@ Build 1.4.x.x (relative to 1.4.12.0) ======= +Fixes +----- + +- Constraint : The `target` browser now shows locations from the `targetScene` if it has an input connection. Before it always showed locations from the main input. + +API +--- +- ScenePathPlugValueWidget : The `scenePathPlugValueWidget:scene` metadata now accepts a space-separated list of plugs, taking the first plug which has an input connection. 1.4.12.0 (relative to 1.4.11.0) ======== diff --git a/python/GafferSceneUI/ConstraintUI.py b/python/GafferSceneUI/ConstraintUI.py index d3460f6579..4686e20ada 100644 --- a/python/GafferSceneUI/ConstraintUI.py +++ b/python/GafferSceneUI/ConstraintUI.py @@ -82,6 +82,7 @@ """, "plugValueWidget:type", "GafferSceneUI.ScenePathPlugValueWidget", + "scenePathPlugValueWidget:scene", "targetScene in", ], diff --git a/python/GafferSceneUI/FramingConstraintUI.py b/python/GafferSceneUI/FramingConstraintUI.py index 77949af95f..9c277c87fc 100644 --- a/python/GafferSceneUI/FramingConstraintUI.py +++ b/python/GafferSceneUI/FramingConstraintUI.py @@ -75,6 +75,7 @@ """, "plugValueWidget:type", "GafferSceneUI.ScenePathPlugValueWidget", + "scenePathPlugValueWidget:scene", "targetScene in", ], diff --git a/python/GafferSceneUI/ScenePathPlugValueWidget.py b/python/GafferSceneUI/ScenePathPlugValueWidget.py index 0774f8b6a0..02050b6157 100644 --- a/python/GafferSceneUI/ScenePathPlugValueWidget.py +++ b/python/GafferSceneUI/ScenePathPlugValueWidget.py @@ -39,6 +39,15 @@ import GafferImage import GafferScene +# Supported metadata : +# +# - `scenePathPlugValueWidget:scene` : The name of a plug on the same node, used to +# provide a scene browser for path selection. Also accepts a space-separated list +# of names, taking the first plug with an input connection. +# - `scenePathPlugValueWidget:setNames` : Limits the scene browser to include only +# locations in the specified sets. +# - `scenePathPlugValueWidget:setsLabel` : A UI label for turning on and off the +# set filter. class ScenePathPlugValueWidget( GafferUI.PathPlugValueWidget ) : def __init__( self, plug, path = None, **kw ) : @@ -54,7 +63,7 @@ def __init__( self, plug, path = None, **kw ) : ) path = GafferScene.ScenePath( - self.__scenePlug( plug ), + None, plug.node().scriptNode().context(), "/", filter = filter @@ -62,7 +71,59 @@ def __init__( self, plug, path = None, **kw ) : GafferUI.PathPlugValueWidget.__init__( self, plug, path, **kw ) - plug.ancestor( Gaffer.ScriptNode ).focusChangedSignal().connect( Gaffer.WeakMethod( self.__focusChanged ) ) + def _auxiliaryPlugs( self, plug ) : + + scenePlugNames = Gaffer.Metadata.value( plug, "scenePathPlugValueWidget:scene" ) or "in" + return [ + plug.node().descendant( n ) + for n in scenePlugNames.split() + if isinstance( plug.node().descendant( n ), GafferScene.ScenePlug ) + ] + + @staticmethod + def _valuesForUpdate( plugs, auxiliaryPlugs ) : + + values = GafferUI.PathPlugValueWidget._valuesForUpdate( plugs, auxiliaryPlugs ) + + result = [] + for value, scenePlugs in zip( values, auxiliaryPlugs ) : + + # Find the first ScenePlug with an input, falling back to the last + # one in the list. + scenePlug = None + for scenePlug in scenePlugs : + if scenePlug.getInput() : + break + + result.append( { + "value" : value, + "scenePlug" : scenePlug, + } ) + + return result + + def _updateFromValues( self, values, exception ) : + + GafferUI.PathPlugValueWidget._updateFromValues( + self, [ v["value"] for v in values ], exception + ) + + scenePlug = next( ( v["scenePlug"] for v in values if v["scenePlug"] is not None ), None ) + if scenePlug is not None : + self.path().setScene( scenePlug ) + self.__focusChangedConnection = None + else : + # The `_auxiliaryPlugs()` search doesn't work well for ShaderNodes, + # since they don't have ScenePlug inputs. We _could_ traverse outputs + # from the shader looking for a ShaderAssignment node to get a ScenePlug + # from. But this wouldn't be useful if the scene hierarchy was + # manipulated downstream of the ShaderAssignment as shaders need the + # final paths as seen by the renderer. So instead we use the focus node, as + # it is more likely to be pointed at the final render node. + self.path().setScene( self.__scenePlugFromFocus() ) + self.__focusChangedConnection = self.getPlug().ancestor( Gaffer.ScriptNode ).focusChangedSignal().connect( + Gaffer.WeakMethod( self.__focusChanged ), scoped = True + ) def _pathChooserDialogue( self ) : @@ -86,31 +147,9 @@ def _pathChooserDialogue( self ) : return dialogue - def __scenePlug( self, plug ) : - - # Search for a suitable ScenePlug input on the same node as this plug, - # or on the node of another plug being driven by this plug. - - def predicate( plug ) : - - scenePlugName = Gaffer.Metadata.value( plug, "scenePathPlugValueWidget:scene" ) or "in" - scenePlug = plug.node().descendant( scenePlugName ) - if scenePlug and isinstance( scenePlug, GafferScene.ScenePlug ) : - return scenePlug - - scenePlug = Gaffer.PlugAlgo.findDestination( plug, predicate ) - if scenePlug is not None : - return scenePlug - - # The above doesn't work well for ShaderNodes, since they don't have - # ScenePlug inputs. We _could_ traverse outputs from the shader looking - # for a ShaderAssignment node to get a ScenePlug from. But this wouldn't - # be useful if the scene hierarchy was manipulated downstream of the - # ShaderAssignment as shaders need the final paths as seen by the - # renderer. So instead use the focus node, as it is more likely to - # be pointed at the final render node. + def __scenePlugFromFocus( self ) : - focusNode = plug.ancestor( Gaffer.ScriptNode ).getFocus() + focusNode = self.getPlug().ancestor( Gaffer.ScriptNode ).getFocus() if focusNode is not None : outputScene = next( GafferScene.ScenePlug.RecursiveOutputRange( focusNode ), None ) if outputScene is not None : @@ -121,6 +160,6 @@ def predicate( plug ) : def __focusChanged( self, scriptNode, node ) : - scenePlug = self.__scenePlug( self.getPlug() ) + scenePlug = self.__scenePlugFromFocus() if scenePlug is not None : self.path().setScene( scenePlug )