Skip to content

Commit

Permalink
Metadata : Add targetsWithMetadata() function
Browse files Browse the repository at this point in the history
This is equivalent to `nodesWithMetadata()` and `plugsWithMetadata()`, but for string targets. Ths also switched the internal container to maintain order of insertion so that we can return targets in the order they were first created.
  • Loading branch information
johnhaddon committed Jan 22, 2025
1 parent a6b4a5f commit 23d3118
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 9 deletions.
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ API
---

- RenderPassEditor : Added optional `index` argument to `registerOption()` and `registerColumn()`. This can be used to specify the column's position in the UI.
- Metadata : Added `targetsWithMetadata()` function, returning all the string targets which match a pattern and have a specific metadata key.

1.5.3.0 (relative to 1.5.2.0)
=======
Expand Down
4 changes: 4 additions & 0 deletions include/Gaffer/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ class GAFFER_API Metadata
/// Utilities
/// =========

/// Returns the names of all matching string targets with the specified
/// metadata key.
static std::vector<IECore::InternedString> targetsWithMetadata( const IECore::StringAlgo::MatchPattern &targetPattern, IECore::InternedString key );

/// Lists all node descendants of "root" with the specified metadata key.
/// If instanceOnly is true the search is restricted to instance metadata.
static std::vector<Node*> nodesWithMetadata( GraphComponent *root, IECore::InternedString key, bool instanceOnly = false );
Expand Down
17 changes: 17 additions & 0 deletions python/GafferTest/MetadataTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,23 @@ def changed( node, key, reason ) :
Gaffer.Metadata.registerValue( node, "test", 2 )
self.assertEqual( Gaffer.Metadata.value( node, "test" ), 1 )

def testTargetsWithMetadata( self ) :

for target, key in [
[ "target1", "k1" ],
[ "target1", "k2" ],
[ "target2", "k2" ],
[ "target2", "k3" ],
[ "target3", "k4" ],
[ "targetA", "k1" ],
] :
Gaffer.Metadata.registerValue( target, key, "test" )
self.addCleanup( Gaffer.Metadata.deregisterValue, target, key )

self.assertEqual( Gaffer.Metadata.targetsWithMetadata( "target[1-3]", "k2" ), [ "target1", "target2" ] )
self.assertEqual( Gaffer.Metadata.targetsWithMetadata( "target*", "k1" ), [ "target1", "targetA" ] )
self.assertEqual( Gaffer.Metadata.targetsWithMetadata( "*", "k3" ), [ "target2" ] )

def tearDown( self ) :

GafferTest.TestCase.tearDown( self )
Expand Down
62 changes: 53 additions & 9 deletions src/Gaffer/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,17 @@ using Values = multi_index::multi_index_container<
>
>;

using MetadataMap = std::map<IECore::InternedString, Values>;
using NamedValues = std::pair<InternedString, Values>;

using MetadataMap = multi_index::multi_index_container<
NamedValues,
multi_index::indexed_by<
multi_index::ordered_unique<
multi_index::member<NamedValues, InternedString, &NamedValues::first>
>,
multi_index::sequenced<>
>
>;

MetadataMap &metadataMap()
{
Expand Down Expand Up @@ -481,12 +491,23 @@ void Metadata::registerValue( IECore::InternedString target, IECore::InternedStr

void Metadata::registerValue( IECore::InternedString target, IECore::InternedString key, ValueFunction value )
{
NamedValue namedValue( key, value );
auto &m = metadataMap()[target];
auto i = m.insert( namedValue );
if( !i.second )
auto &targetMap = metadataMap();

auto targetIt = targetMap.find( target );
if( targetIt == targetMap.end() )
{
targetIt = targetMap.insert( NamedValues( target, Values() ) ).first;
}

// Cast is safe because we don't use `second` as a key in the `multi_index_container`,
// so we can modify it without affecting indexing.
Values &values = const_cast<Values &>( targetIt->second );

const NamedValue namedValue( key, value );
auto keyIt = values.insert( namedValue );
if( !keyIt.second )
{
m.replace( i.first, namedValue );
values.replace( keyIt.first, namedValue );
}

valueChangedSignal()( target, key );
Expand All @@ -501,13 +522,17 @@ void Metadata::deregisterValue( IECore::InternedString target, IECore::InternedS
return;
}

auto vIt = mIt->second.find( key );
if( vIt == mIt->second.end() )
// Cast is safe because we don't use `second` as a key in the `multi_index_container`,
// so we can modify it without affecting indexing.
Values &values = const_cast<Values &>( mIt->second );

auto vIt = values.find( key );
if( vIt == values.end() )
{
return;
}

mIt->second.erase( vIt );
values.erase( vIt );
valueChangedSignal()( target, key );
}

Expand Down Expand Up @@ -544,6 +569,25 @@ IECore::ConstDataPtr Metadata::valueInternal( IECore::InternedString target, IEC
return nullptr;
}

std::vector<IECore::InternedString> Metadata::targetsWithMetadata( const IECore::StringAlgo::MatchPattern &targetPattern, IECore::InternedString key )
{
vector<InternedString> result;
const auto &orderedIndex = metadataMap().get<1>();
for( const auto &[target, values] : orderedIndex )
{
if( !StringAlgo::match( target.c_str(), targetPattern ) )
{
continue;
}
if( values.find( key ) != values.end() )
{
result.push_back( target );
}
}

return result;
}

void Metadata::registerValue( IECore::TypeId typeId, IECore::InternedString key, IECore::ConstDataPtr value )
{
registerValue( typeId, key, [value]( const GraphComponent * ){ return value; } );
Expand Down
18 changes: 18 additions & 0 deletions src/GafferModule/MetadataBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,16 @@ list registeredGraphComponentValuesDeprecated( const GraphComponent *target, boo
return keysToList( keys );
}

list targetsWithMetadataWrapper( const IECore::StringAlgo::MatchPattern &targetPattern, IECore::InternedString key )
{
std::vector<InternedString> targets;
{
IECorePython::ScopedGILRelease gilRelease;
targets = Metadata::targetsWithMetadata( targetPattern, key );
}
return keysToList( targets );
}

list plugsWithMetadata( GraphComponent *root, const std::string &key, bool instanceOnly )
{
std::vector<Plug*> plugs = Metadata::plugsWithMetadata( root, key, instanceOnly );
Expand Down Expand Up @@ -452,6 +462,14 @@ void GafferModule::bindMetadata()
.def( "plugValueChangedSignal", (Metadata::PlugValueChangedSignal &(*)( Gaffer::Node * ) )&Metadata::plugValueChangedSignal, return_value_policy<reference_existing_object>() )
.staticmethod( "plugValueChangedSignal" )

.def( "targetsWithMetadata", &targetsWithMetadataWrapper,
(
boost::python::arg( "targetPattern" ),
boost::python::arg( "key" )
)
)
.staticmethod( "targetsWithMetadata" )

.def( "plugsWithMetadata", &plugsWithMetadata,
(
boost::python::arg( "root" ),
Expand Down

0 comments on commit 23d3118

Please sign in to comment.