diff --git a/Changes.md b/Changes.md index 4356c8f2c60..fce172ff5f6 100644 --- a/Changes.md +++ b/Changes.md @@ -7,6 +7,10 @@ Fixes - USDLayerWriter : - Fixed silent failures when unable to create the output file (#6197). - Fixed leak of `usdLayerWriter:fileName` context variable. +- PathFilter : + - Fixed bug preventing display of "Select Affected Objects" menu item in the row name column of promoted Spreadsheets. + - Fixed bug preventing use of "Select Affected Objects" menu item in the row name column of Spreadsheets with `enabledRowNames` connected to the `paths` plug of a PathFilter. + - Fixed error when using "Select Affected Objects" on Spreadsheet cells connected to the `paths` plug of a PathFilter. 1.4.15.3 (relative to 1.4.15.2) ======== diff --git a/python/GafferSceneUI/PathFilterUI.py b/python/GafferSceneUI/PathFilterUI.py index 5811eddde44..ea61ae0a649 100644 --- a/python/GafferSceneUI/PathFilterUI.py +++ b/python/GafferSceneUI/PathFilterUI.py @@ -123,45 +123,36 @@ # VectorDataPlugValueWidget customisation ########################################################################### -def __targetFilterPlug( plug ) : +# Searches for a PathFilter whose `paths` plug is eventually driven by `plug`, +# and returns it. +def _destinationPathFilter( plug ) : return Gaffer.PlugAlgo.findDestination( plug, - lambda plug : plug if isinstance( plug.parent(), GafferScene.PathFilter ) and plug.getName() == "paths" else None + lambda plug : plug.parent() if isinstance( plug.parent(), GafferScene.PathFilter ) and plug.getName() == "paths" else None ) -def _selectAffected( plug, selection ) : +def _filteredScenes( filter ) : - inPlugs = [] + result = set() + for node in GafferScene.SceneAlgo.filteredNodes( filter ) : + if isinstance( node["in"], Gaffer.ArrayPlug ) : + result.add( node["in"][0] ) + else : + result.add( node["in"] ) - rowPlug = plug.ancestor( Gaffer.Spreadsheet.RowPlug ) - - targetPlug = __targetFilterPlug( plug ) - - if targetPlug is not None : - inPlugs = [ n["in"] for n in GafferScene.SceneAlgo.filteredNodes( targetPlug.node() ) ] - elif rowPlug is not None : - for output in rowPlug.node()["out"] : - targetPlug = Gaffer.PlugAlgo.findDestination( - output, - lambda plug : plug.node()["out"] if isinstance( plug.node(), GafferScene.SceneNode ) else None - ) - if targetPlug is not None : - inPlugs = [ targetPlug ] - break + return list( result ) - if targetPlug is None : - return - - scenes = [ s[0] if isinstance( s, Gaffer.ArrayPlug ) else s for s in inPlugs ] +def _selectAffected( pathMatcher, scenes ) : result = IECore.PathMatcher() - context = targetPlug.ancestor( Gaffer.ScriptNode ).context() - with context : - for scene in scenes : - GafferScene.SceneAlgo.matchingPaths( selection, scene, result ) + for scene in scenes : + with scene.ancestor( Gaffer.ScriptNode ).context() : + GafferScene.SceneAlgo.matchingPaths( + pathMatcher, scene, result + ) - GafferSceneUI.ContextAlgo.setSelectedPaths( context, result ) + GafferSceneUI.ScriptNodeAlgo.setSelectedPaths( scenes[0].ancestor( Gaffer.ScriptNode ), result ) class _PathsPlugValueWidget( GafferUI.VectorDataPlugValueWidget ) : @@ -176,14 +167,15 @@ def __dataMenu( self, vectorDataWidget, menuDefinition ) : selectedIndices = vectorDataWidget.selectedIndices() filterData = vectorDataWidget.getData()[0] - selection = IECore.PathMatcher( [ filterData[row] for column, row in selectedIndices ] ) + pathMatcher = IECore.PathMatcher( [ filterData[row] for column, row in selectedIndices ] ) + scenes = _filteredScenes( _destinationPathFilter( self.getPlug() ) ) menuDefinition.append( "/selectDivider", { "divider" : True } ) menuDefinition.append( "/Select Affected Objects", { - "command" : functools.partial( _selectAffected, self.getPlug(), selection ), - "active" : len( selectedIndices ) > 0, + "command" : functools.partial( _selectAffected, pathMatcher, scenes ), + "active" : len( selectedIndices ) > 0 and len( scenes ) > 0, } ) @@ -193,36 +185,50 @@ def __dataMenu( self, vectorDataWidget, menuDefinition ) : def __popupMenu( menuDefinition, plugValueWidget ) : - selection = None - plug = plugValueWidget.getPlug() if plug is None: return - node = plug.node() - - if isinstance( node, Gaffer.Spreadsheet ) : - rowPlug = plug.ancestor( Gaffer.Spreadsheet.RowPlug ) - - with plugValueWidget.getContext() : - if __targetFilterPlug( plug ) is not None : - cellPlug = plug.ancestor( Gaffer.Spreadsheet.CellPlug ) - if cellPlug is None : - return - - selection = IECore.PathMatcher( plugValueWidget.vectorDataWidget().getData()[0] ) + rowPlug = plug.ancestor( Gaffer.Spreadsheet.RowPlug ) + if rowPlug is None : + return - elif rowPlug and plug == rowPlug["name"] and node["selector"].getValue() == "${scene:path}" : - selection = IECore.PathMatcher( [ plugValueWidget.getPlug().getValue() ] ) + spreadsheet = Gaffer.PlugAlgo.findDestination( + rowPlug, + lambda plug : plug.node() if isinstance( plug.node(), Gaffer.Spreadsheet ) else None + ) + if spreadsheet is None : + return - if selection is None : + scenes = [] + pathMatcher = None + with plugValueWidget.getContext() : + pathFilter = _destinationPathFilter( plug ) + if pathFilter is not None : + pathMatcher = IECore.PathMatcher( plug.getValue() ) + scenes = _filteredScenes( pathFilter ) + elif plug == rowPlug["name"] and spreadsheet["selector"].getValue() == "${scene:path}" : + pathMatcher = IECore.PathMatcher( [ plug.getValue() ] ) + pathFilter = _destinationPathFilter( spreadsheet["enabledRowNames"] ) + if pathFilter is not None : + scenes = _filteredScenes( pathFilter ) + else : + for output in spreadsheet["out"] : + scene = Gaffer.PlugAlgo.findDestination( + output, + lambda plug : plug.node()["out"] if isinstance( plug.node(), GafferScene.SceneNode ) else None + ) + if scene is not None : + scenes = [ scene ] + + if pathMatcher is None or len( scenes ) == 0 : return menuDefinition.prepend( "/selectAffectedDivider", { "divider" : True } ) menuDefinition.prepend( "/Select Affected Objects", { - "command" : functools.partial( _selectAffected, plug, selection ) + "command" : functools.partial( _selectAffected, pathMatcher, scenes ) } )