Skip to content

Commit

Permalink
Merge branch '1.3_maintenance'
Browse files Browse the repository at this point in the history
  • Loading branch information
johnhaddon committed Dec 12, 2023
2 parents a3bd99d + 9fe2e9c commit 6fcbee1
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 10 deletions.
10 changes: 9 additions & 1 deletion Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,21 @@ Build

- PsUtil : Added version 5.9.6.

1.3.x.x (relative to 1.3.8.0)
1.3.x.x (relative to 1.3.9.0)
=======



1.3.9.0 (relative to 1.3.8.0)
=======

Improvements
------------

- 3Delight : Added support for subdivision corners and creases.
- SetEditor :
- Added "Selection" column displaying the number of currently selected members for each set.
- Added "Hide Empty Selection" checkbox. When on, the SetEditor will only display sets with currently selected members.

Fixes
-----
Expand Down
23 changes: 20 additions & 3 deletions python/GafferSceneUI/SetEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,33 @@ def __init__( self, scriptNode, **kw ) :

searchFilter = _GafferSceneUI._SetEditor.SearchFilter()
emptySetFilter = _GafferSceneUI._SetEditor.EmptySetFilter()
emptySetFilter.userData()["UI"] = { "label" : "Hide Empty" }
emptySetFilter.userData()["UI"] = { "label" : "Hide Empty Members", "toolTip" : "Hide sets with no members" }
emptySetFilter.setEnabled( False )

self.__filter = Gaffer.CompoundPathFilter( [ searchFilter, emptySetFilter ] )
emptySelectionFilter = _GafferSceneUI._SetEditor.EmptySetFilter( propertyName = "setPath:selectedMemberCount" )
emptySelectionFilter.userData()["UI"] = { "label" : "Hide Empty Selection", "toolTip" : "Hide sets with no selected members or descendants" }
emptySelectionFilter.setEnabled( False )

self.__filter = Gaffer.CompoundPathFilter( [ searchFilter, emptySetFilter, emptySelectionFilter ] )

with mainColumn :

with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing = 4 ) :

self.__searchFilterWidget = _SearchFilterWidget( searchFilter )
GafferUI.BasicPathFilterWidget( emptySetFilter )
GafferUI.BasicPathFilterWidget( emptySelectionFilter )

self.__setMembersColumn = GafferUI.StandardPathColumn( "Members", "setPath:memberCount" )
self.__setMembersColumn = _GafferSceneUI._SetEditor.SetMembersColumn()
self.__selectedSetMembersColumn = _GafferSceneUI._SetEditor.SetSelectionColumn()
self.__includedSetMembersColumn = _GafferSceneUI._SetEditor.VisibleSetInclusionsColumn( scriptNode.context() )
self.__excludedSetMembersColumn = _GafferSceneUI._SetEditor.VisibleSetExclusionsColumn( scriptNode.context() )
self.__pathListing = GafferUI.PathListingWidget(
Gaffer.DictPath( {}, "/" ), # temp till we make a SetPath
columns = [
_GafferSceneUI._SetEditor.SetNameColumn(),
self.__setMembersColumn,
self.__selectedSetMembersColumn,
self.__includedSetMembersColumn,
self.__excludedSetMembersColumn,
],
Expand Down Expand Up @@ -186,6 +193,8 @@ def __dragBegin( self, widget, event ) :
column = self.__pathListing.columnAt( imath.V2f( event.line.p0.x, event.line.p0.y ) )
if column == self.__setMembersColumn :
return IECore.StringVectorData( self.__getSetMembers( setNames ).paths() )
elif column == self.__selectedSetMembersColumn :
return IECore.StringVectorData( self.__getSelectedSetMembers( setNames ).paths() )
elif column == self.__includedSetMembersColumn :
return IECore.StringVectorData( self.__getIncludedSetMembers( setNames ).paths() )
elif column == self.__excludedSetMembersColumn :
Expand Down Expand Up @@ -264,6 +273,14 @@ def __getSetMembers( self, setNames, *unused ) :

return result

def __getSelectedSetMembers( self, setNames, *unused ) :

setMembers = self.__getSetMembers( setNames )
return IECore.PathMatcher( [
p for p in ContextAlgo.getSelectedPaths( self.getContext() ).paths()
if setMembers.match( p ) & ( IECore.PathMatcher.Result.ExactMatch | IECore.PathMatcher.Result.AncestorMatch )
] )

def __getIncludedSetMembers( self, setNames, *unused ) :

return self.__getSetMembers( setNames ).intersection( ContextAlgo.getVisibleSet( self.getContext() ).inclusions )
Expand Down
162 changes: 162 additions & 0 deletions python/GafferSceneUITest/SetEditorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

import Gaffer
import GafferScene
import GafferSceneUI
import GafferUITest

from GafferSceneUI import _GafferSceneUI
Expand Down Expand Up @@ -182,6 +183,113 @@ def testSetPathMemberCount( self ) :
path.setFromString( parent )
self.assertEqual( path.property( "setPath:memberCount" ), count )

def testSetPathSelectedMemberCount( self ) :

plane = GafferScene.Plane()
plane["sets"].setValue( "A A:B A:C D E:F:G" )

planeB = GafferScene.Plane()
planeB["name"].setValue( "planeB" )
planeB["sets"].setValue( "A A:C D F" )

p = GafferScene.Parent()
p["parent"].setValue( "/" )
p["in"].setInput( plane["out"] )
p["children"]["child0"].setInput( planeB["out"] )

context = Gaffer.Context()
path = _GafferSceneUI._SetEditor.SetPath( p["out"], context, "/" )
self.assertTrue( path.isValid() )
self.assertFalse( path.isLeaf() )

for parent, selection, count in [
( "/", [], None ),
( "/", [ "/plane" ], None ),
( "/A", [ "/plane", "/planeB" ], 2 ),
( "/A", [ "/plane" ], 1 ),
( "/A", [ "/planeB" ], 1 ),
( "/A/A:B", [ "/plane" ], 1 ),
( "/A/A:B", [ "/planeB" ], 0 ),
( "/A/A:B", [], 0 ),
( "/A/A:C", [ "/plane", "/planeB" ], 2 ),
( "/A/A:C", [ "/plane" ], 1 ),
( "/A/A:C", [ "/planeB" ], 1 ),
( "/D", [ "/plane", "/planeB" ], 2 ),
( "/D", [ "/plane" ], 1 ),
( "/D", [ "/planeB" ], 1 ),
( "/E", [ "/plane", "/planeB" ], None ),
( "/E/F", [ "/plane" ], None ),
( "/E/F/E:F:G", [ "/plane", "/planeB" ], 1 ),
( "/E/F/E:F:G", [ "/plane" ], 1 ),
( "/E/F/E:F:G", [ "/planeB" ], 0 ),
( "/F", [ "/plane", "/planeB" ], 1 ),
( "/F", [ "/plane" ], 0 ),
( "/F", [ "/planeB" ], 1 ),
( "/A/A:D", [ "/plane", "/planeB" ], None ),
( "/D/D:A", [ "/planeB" ], None ),
] :

path.setFromString( parent )
GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( selection ) )
self.assertEqual( path.property( "setPath:selectedMemberCount" ), count )

def testSetPathSelectedMemberCountWithInheritance( self ) :

plane = GafferScene.Plane()
plane["sets"].setValue( "A" )

sphere = GafferScene.Sphere()

p = GafferScene.Parent()
p["parent"].setValue( "/plane" )
p["in"].setInput( plane["out"] )
p["children"]["child0"].setInput( sphere["out"] )

planeB = GafferScene.Plane()
planeB["name"].setValue( "planeB" )
planeB["sets"].setValue( "B" )

g = GafferScene.Group()
g["in"]["in0"].setInput( p["out"] )
g["in"]["in1"].setInput( planeB["out"] )

f = GafferScene.PathFilter()
f["paths"].setValue( IECore.StringVectorData( [ "/group" ] ) )

s = GafferScene.Set()
s["name"].setValue( "AB" )
s["in"].setInput( g["out"] )
s["filter"].setInput( f["out"] )

context = Gaffer.Context()
path = _GafferSceneUI._SetEditor.SetPath( s["out"], context, "/" )
self.assertTrue( path.isValid() )
self.assertFalse( path.isLeaf() )

for parent, selection, count in [
( "/", [], None ),
( "/", [ "/group" ], None ),
( "/", [ "/group/plane" ], None ),
( "/A", [ "/group/plane" ], 1 ),
( "/A", [ "/group/plane/sphere" ], 1 ),
( "/A", [ "/group/plane", "/group/plane/sphere" ], 2 ),
( "/A", [ "/group", "/group/plane", "/group/plane/sphere" ], 2 ),
( "/A", [ "/group" ], 0 ),
( "/A", [ "/group/planeB" ], 0 ),
( "/B", [ "/group/plane" ], 0 ),
( "/B", [ "/group" ], 0 ),
( "/B", [ "/group/planeB" ], 1 ),
( "/AB", [ "/group/plane" ], 1 ),
( "/AB", [ "/group" ], 1 ),
( "/AB", [ "/group/planeB" ], 1 ),
( "/AB", [ "/group", "/group/plane", "/group/planeB" ], 3 ),
( "/AB", [ "/group", "/group/plane", "/group/planeB", "/group/plane/sphere" ], 4 ),
] :

path.setFromString( parent )
GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( selection ) )
self.assertEqual( path.property( "setPath:selectedMemberCount" ), count )

def testSetPathCancellation( self ) :

plane = GafferScene.Plane()
Expand Down Expand Up @@ -267,3 +375,57 @@ def testEmptySetFilter( self ) :

emptySetFilter.setEnabled( True )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A/A:E" ] )

def testEmptySetFilterWithSelectedMemberCount( self ) :

plane = GafferScene.Plane()
plane["sets"].setValue( "A A:E B C D" )

planeB = GafferScene.Plane()
planeB["name"].setValue( "planeB" )
planeB["sets"].setValue( "A A:C D F" )

p = GafferScene.Parent()
p["parent"].setValue( "/" )
p["in"].setInput( plane["out"] )
p["children"]["child0"].setInput( planeB["out"] )

emptySet = GafferScene.Set()
emptySet["name"].setValue( "EMPTY A:EMPTY" )
emptySet["in"].setInput( p["out"] )

context = Gaffer.Context()
path = _GafferSceneUI._SetEditor.SetPath( emptySet["out"], context, "/" )

self.assertEqual( [ str( c ) for c in path.children() ], [ "/A", "/B", "/C", "/D", "/EMPTY", "/F" ] )

emptySetFilter = _GafferSceneUI._SetEditor.EmptySetFilter( propertyName = "setPath:selectedMemberCount" )
path.setFilter( emptySetFilter )

GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( [ "/plane" ] ) )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A", "/B", "/C", "/D" ] )

GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( [ "/planeB" ] ) )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A", "/D", "/F" ] )

GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( [ "/plane", "/planeB" ] ) )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A", "/B", "/C", "/D", "/F" ] )

GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( [] ) )
self.assertEqual( [ str( c ) for c in path.children() ], [] )

emptySetFilter.setEnabled( False )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A", "/B", "/C", "/D", "/EMPTY", "/F" ] )

path.setFromString( "/A" )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A/A:C", "/A/A:E", "/A/A:EMPTY" ] )

emptySetFilter.setEnabled( True )
GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( [ "/plane" ] ) )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A/A:E" ] )

GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( [ "/planeB" ] ) )
self.assertEqual( [ str( c ) for c in path.children() ], [ "/A/A:C" ] )

GafferSceneUI.ContextAlgo.setSelectedPaths( context, IECore.PathMatcher( [] ) )
self.assertEqual( [ str( c ) for c in path.children() ], [] )
5 changes: 5 additions & 0 deletions python/GafferUI/PathFilterWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ def _updateFromPathFilter( self ) :
invertEnabled = self.pathFilter().userData()["UI"]["invertEnabled"].value
self.__checkBox.setState( self.pathFilter().getEnabled() is not invertEnabled )

toolTip = ""
with IECore.IgnoredExceptions( KeyError ) :
toolTip = self.pathFilter().userData()["UI"]["toolTip"].value
self.__checkBox.setToolTip( toolTip )

def __stateChanged( self, checkBox ) :

invertEnabled = False
Expand Down
Loading

0 comments on commit 6fcbee1

Please sign in to comment.