From 6fd45486d8eb740a2047709cc374f3dcb0b5108f Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Thu, 23 May 2024 16:22:53 -0700 Subject: [PATCH] SpreadsheetUI : Forward metadata from connected plugs --- Changes.md | 1 + python/GafferUI/SpreadsheetUI/_Metadata.py | 78 ++++++++++++--- python/GafferUITest/SpreadsheetUITest.py | 109 +++++++++++++++++++++ 3 files changed, 176 insertions(+), 12 deletions(-) diff --git a/Changes.md b/Changes.md index 0d0be615320..88b48f9c1d6 100644 --- a/Changes.md +++ b/Changes.md @@ -9,6 +9,7 @@ Improvements - CodeWidget : Added highlighting of braces and operators. - RenderPassEditor : Added preset menu for choosing a render pass type from the list of available registered types. An "auto" type is included in the list when an auto type function has been registered. - OptionTweaks : Tweak `value` plugs can now access metadata registered globally to `option:{tweakName}`, where `{tweakName}` is the value of the tweak's `name` plug. +- Spreadsheet : Added support for metadata to be automatically forwarded from plugs downstream of a column's `out` plug to the column's default row. Fixes ----- diff --git a/python/GafferUI/SpreadsheetUI/_Metadata.py b/python/GafferUI/SpreadsheetUI/_Metadata.py index 84517c87516..7e30eca5916 100644 --- a/python/GafferUI/SpreadsheetUI/_Metadata.py +++ b/python/GafferUI/SpreadsheetUI/_Metadata.py @@ -249,10 +249,26 @@ def __correspondingDefaultPlug( plug ) : rowsPlug = rowPlug.parent() return rowsPlug.defaultRow().descendant( plug.relativeName( rowPlug ) ) +def __correspondingOutPlug( plug ) : + + return Gaffer.PlugAlgo.findDestination( + plug, + lambda p : p if isinstance( p.node(), Gaffer.Spreadsheet ) and p.node()["out"].isAncestorOf( p ) else None + ) + def __defaultCellMetadata( plug, key ) : return Gaffer.Metadata.value( __correspondingDefaultPlug( plug ), key ) +def __forwardedMetadata( plug, key ) : + + source = Gaffer.PlugAlgo.findDestination( + __correspondingOutPlug( plug ), + lambda p : p if Gaffer.Metadata.value( p, key ) else None + ) + + return Gaffer.Metadata.value( source, key ) if source else None + for key in [ "description", "spreadsheet:columnLabel", @@ -263,6 +279,10 @@ def __defaultCellMetadata( plug, key ) : "tweakPlugValueWidget:allowCreate", ] : + Gaffer.Metadata.registerValue( + Gaffer.Spreadsheet.RowsPlug, "default.*...", key, + functools.partial( __forwardedMetadata, key = key ), + ) Gaffer.Metadata.registerValue( Gaffer.Spreadsheet.RowsPlug, "row*.*...", key, functools.partial( __defaultCellMetadata, key = key ), @@ -286,36 +306,70 @@ def __defaultCellMetadata( plug, key ) : } +def __presetSourcePlug( plug ) : + + def predicate( p ) : + + if ( + ( Gaffer.Metadata.value( p, "presetNames" ) and Gaffer.Metadata.value( p, "presetValues" ) ) + or any( v.startswith( "preset:" ) for v in Gaffer.Metadata.registeredValues( p ) ) + ) : + return p + + source = Gaffer.PlugAlgo.findDestination( + __correspondingOutPlug( plug ), + lambda p : predicate( p ) + ) + + return source + def __presetNamesMetadata( plug ) : - if plug.__class__ not in __plugPresetTypes : + if not plug or plug.__class__ not in __plugPresetTypes : return None - source = __correspondingDefaultPlug( plug ) - result = IECore.StringVectorData() - for n in Gaffer.Metadata.registeredValues( source ) : + for n in Gaffer.Metadata.registeredValues( plug ) : if n.startswith( "preset:" ) : result.append( n[7:] ) - result.extend( Gaffer.Metadata.value( source, "presetNames" ) or [] ) + result.extend( Gaffer.Metadata.value( plug, "presetNames" ) or [] ) return result def __presetValuesMetadata( plug ) : + if not plug : + return None + dataType = __plugPresetTypes.get( plug.__class__ ) if dataType is None : return None - source = __correspondingDefaultPlug( plug ) - result = dataType() - for n in Gaffer.Metadata.registeredValues( source ) : + for n in Gaffer.Metadata.registeredValues( plug ) : if n.startswith( "preset:" ) : - result.append( Gaffer.Metadata.value( source, n ) ) + result.append( Gaffer.Metadata.value( plug, n ) ) - result.extend( Gaffer.Metadata.value( source, "presetValues" ) or [] ) + result.extend( Gaffer.Metadata.value( plug, "presetValues" ) or [] ) return result -Gaffer.Metadata.registerValue( Gaffer.Spreadsheet.RowsPlug, "row*.*...", "presetNames", __presetNamesMetadata ) -Gaffer.Metadata.registerValue( Gaffer.Spreadsheet.RowsPlug, "row*.*...", "presetValues", __presetValuesMetadata ) +def __defaultPlugPresetNamesMetadata( plug ) : + + return __presetNamesMetadata( __correspondingDefaultPlug( plug ) ) + +def __defaultPlugPresetValuesMetadata( plug ) : + + return __presetValuesMetadata( __correspondingDefaultPlug( plug ) ) + +def __forwardedPresetNamesMetadata( plug ) : + + return __presetNamesMetadata( __presetSourcePlug( plug ) ) + +def __forwardedPresetValuesMetadata( plug ) : + + return __presetValuesMetadata( __presetSourcePlug( plug ) ) + +Gaffer.Metadata.registerValue( Gaffer.Spreadsheet.RowsPlug, "default.*...", "presetNames", __forwardedPresetNamesMetadata ) +Gaffer.Metadata.registerValue( Gaffer.Spreadsheet.RowsPlug, "default.*...", "presetValues", __forwardedPresetValuesMetadata ) +Gaffer.Metadata.registerValue( Gaffer.Spreadsheet.RowsPlug, "row*.*...", "presetNames", __defaultPlugPresetNamesMetadata ) +Gaffer.Metadata.registerValue( Gaffer.Spreadsheet.RowsPlug, "row*.*...", "presetValues", __defaultPlugPresetValuesMetadata ) diff --git a/python/GafferUITest/SpreadsheetUITest.py b/python/GafferUITest/SpreadsheetUITest.py index 821719145b1..b12e339dfce 100644 --- a/python/GafferUITest/SpreadsheetUITest.py +++ b/python/GafferUITest/SpreadsheetUITest.py @@ -914,5 +914,114 @@ def testMetadataAlgoRemovesNonDefaultRowMetadata( self ) : [] ) + def testMetadataForwarding( self ) : + + s = Gaffer.Spreadsheet() + s["rows"].addColumn( Gaffer.StringPlug( "a" ) ) + s["rows"].addColumn( Gaffer.StringPlug( "b" ) ) + s["rows"].addRows( 1 ) + + n = Gaffer.Node() + n.addChild( Gaffer.StringPlug( "a" ) ) + + n["a"].setInput( s["out"]["a"] ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "plugValueWidget:type" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "plugValueWidget:type" ), None ) + + Gaffer.Metadata.registerValue( n["a"], "plugValueWidget:type", "Test" ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "plugValueWidget:type" ), "Test" ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "plugValueWidget:type" ), None ) + + Gaffer.Metadata.registerValue( s["rows"].defaultRow()["cells"]["b"]["value"], "plugValueWidget:type", "Test2" ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "plugValueWidget:type" ), "Test" ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "plugValueWidget:type" ), "Test2" ) + + Gaffer.Metadata.registerValue( s["rows"].defaultRow()["cells"]["a"]["value"], "plugValueWidget:type", "Test2" ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "plugValueWidget:type" ), "Test2" ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "plugValueWidget:type" ), "Test2" ) + + def testPresetForwarding( self ) : + + s = Gaffer.Spreadsheet() + s["rows"].addColumn( Gaffer.StringPlug( "a" ) ) + s["rows"].addColumn( Gaffer.StringPlug( "b" ) ) + s["rows"].addRows( 1 ) + + n = Gaffer.Node() + n.addChild( Gaffer.StringPlug( "a" ) ) + n.addChild( Gaffer.StringPlug( "b" ) ) + + n["a"].setInput( s["out"]["a"] ) + n["b"].setInput( s["out"]["b"] ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetNames" ), IECore.StringVectorData( [] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetValues" ), IECore.StringVectorData( [] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetNames" ), IECore.StringVectorData( [] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetValues" ), IECore.StringVectorData( [] ) ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetNames" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetValues" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetNames" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetValues" ), None ) + + # Registering only "presetNames" on the destination plug doesn't forward to the cell. + Gaffer.Metadata.registerValue( n["a"], "presetNames", IECore.StringVectorData( [ "Test", "Test2" ] ) ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetNames" ), IECore.StringVectorData( [] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetValues" ), IECore.StringVectorData( [] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetNames" ), IECore.StringVectorData( [] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetValues" ), IECore.StringVectorData( [] ) ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetNames" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetValues" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetNames" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetValues" ), None ) + + # Registering "presetValues" alongside "presetNames" will forward. + Gaffer.Metadata.registerValue( n["a"], "presetValues", IECore.StringVectorData( [ "test", "test2" ] ) ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetNames" ), IECore.StringVectorData( [ "Test", "Test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetValues" ), IECore.StringVectorData( [ "test", "test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetNames" ), None ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetValues" ), None ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetNames" ), IECore.StringVectorData( [ "Test", "Test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetValues" ), IECore.StringVectorData( [ "test", "test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetNames" ), IECore.StringVectorData( [] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetValues" ), IECore.StringVectorData( [] ) ) + + # Registering "preset:" metadata also forwards, and combines with "presetNames" & "presetValues" + Gaffer.Metadata.registerValue( n["a"], "preset:Test3", "test3" ) + Gaffer.Metadata.registerValue( n["b"], "preset:Test", "test" ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetNames" ), IECore.StringVectorData( [ "Test3", "Test", "Test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetValues" ), IECore.StringVectorData( [ "test3", "test", "test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetNames" ), IECore.StringVectorData( [ "Test" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetValues" ), IECore.StringVectorData( [ "test" ] ) ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetNames" ), IECore.StringVectorData( [ "Test3", "Test", "Test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["a"]["value"], "presetValues" ), IECore.StringVectorData( [ "test3", "test", "test2" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetNames" ), IECore.StringVectorData( [ "Test" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"].defaultRow()["cells"]["b"]["value"], "presetValues" ), IECore.StringVectorData( [ "test" ] ) ) + + # Registering either metadata on the default row will override the metadata from the destination. + Gaffer.Metadata.registerValue( s["rows"].defaultRow()["cells"]["b"]["value"], "preset:TestDefault", "testDefault" ) + Gaffer.Metadata.registerValue( s["rows"].defaultRow()["cells"]["b"]["value"], "presetNames", IECore.StringVectorData( [ "TestDefault2", "TestDefault3" ] ) ) + Gaffer.Metadata.registerValue( s["rows"].defaultRow()["cells"]["b"]["value"], "presetValues", IECore.StringVectorData( [ "testDefault2", "testDefault3" ] ) ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetNames" ), IECore.StringVectorData( [ "TestDefault", "TestDefault2", "TestDefault3" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["b"]["value"], "presetValues" ), IECore.StringVectorData( [ "testDefault", "testDefault2", "testDefault3" ] ) ) + + # Registering "presetNames" and "presetValues" on the default row will override the destination presets. + Gaffer.Metadata.registerValue( s["rows"].defaultRow()["cells"]["a"]["value"], "presetNames", IECore.StringVectorData( [ "TestDefault2", "TestDefault3" ] ) ) + Gaffer.Metadata.registerValue( s["rows"].defaultRow()["cells"]["a"]["value"], "presetValues", IECore.StringVectorData( [ "testDefault2", "testDefault3" ] ) ) + + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetNames" ), IECore.StringVectorData( [ "TestDefault2", "TestDefault3" ] ) ) + self.assertEqual( Gaffer.Metadata.value( s["rows"][1]["cells"]["a"]["value"], "presetValues" ), IECore.StringVectorData( [ "testDefault2", "testDefault3" ] ) ) + if __name__ == "__main__": unittest.main()