From 3741e16c9f962dc61408a0617dbbe275234378db Mon Sep 17 00:00:00 2001 From: Andrew Kaufman Date: Thu, 9 Oct 2014 14:25:38 -0700 Subject: [PATCH 1/4] FrameRange UI displays the value that will actually be dispatched. This overrides the widget text on the Dispatcher frameRange plug to show the value that will be dispatched, even when not in CustomRange mode. The actual value of the plug remains the same, but the widget displays the CurrentFrame or FullRange when in those modes, and the PlaybackRange updates automatically in that mode (which does change the plug value). --- python/GafferUI/DispatcherUI.py | 103 +++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/python/GafferUI/DispatcherUI.py b/python/GafferUI/DispatcherUI.py index d7bfefadd3b..19a9c012c26 100644 --- a/python/GafferUI/DispatcherUI.py +++ b/python/GafferUI/DispatcherUI.py @@ -121,15 +121,8 @@ def setNodesToDispatch( self, nodes ) : self.__updateTitle() def __update( self ) : - - nodeUI = GafferUI.NodeUI.create( self.__dispatcher ) - - # force the framesMode widget to update so we start with - # the correct enabled state on the frameRange plug. - framesModeWidget = nodeUI.plugValueWidget( self.__dispatcher["framesMode"], lazy = False ) - if framesModeWidget : - framesModeWidget.selectionMenu().setSelection( framesModeWidget.selectionMenu().getSelection() ) + nodeUI = GafferUI.NodeUI.create( self.__dispatcher ) self.__frame.setChild( nodeUI ) self.__updateTitle() @@ -182,16 +175,16 @@ def __executeClicked( self, button ) : _showDispatcherWindow( [ self.getPlug().node() ] ) -################################# -# PlugValueWidget for framesMode -################################# +######################################## +# PlugValueWidgets for frame range plugs +######################################## # Much of this is copied from EnumPlugValueWidget, but we're not deriving because we # want the ability to add in menu items that don't correspond to plug values directly. class __FramesModePlugValueWidget( GafferUI.PlugValueWidget ) : def __init__( self, plug, **kw ) : - + self.__selectionMenu = GafferUI.MultiSelectionMenu( allowMultipleSelection = False, allowEmptySelection = False ) GafferUI.PlugValueWidget.__init__( self, self.__selectionMenu, plug, **kw ) @@ -205,6 +198,8 @@ def __init__( self, plug, **kw ) : for label, value in self.__labelsAndValues : self.__selectionMenu.append( label ) + self.__updateFrameRangeConnection = None + self.__visibilityChangedConnection = self.visibilityChangedSignal().connect( Gaffer.WeakMethod( self.__visibilityChanged ) ) self.__selectionChangedConnection = self.__selectionMenu.selectionChangedSignal().connect( Gaffer.WeakMethod( self.__selectionChanged ) ) self._addPopupMenu( self.__selectionMenu ) @@ -231,6 +226,14 @@ def _updateFromPlug( self ) : self.__selectionMenu.setSelection( labelAndValue[0] ) break + def __frameRangeWidget( self ) : + + nodeUI = self.ancestor( GafferUI.NodeUI ) + if nodeUI : + return nodeUI.plugValueWidget( self.getPlug().node()["frameRange"], lazy = False ) + + return None + def __selectionChanged( self, selectionMenu ) : label = selectionMenu.getSelection()[0] @@ -239,19 +242,72 @@ def __selectionChanged( self, selectionMenu ) : with Gaffer.BlockedConnection( self._plugConnections() ) : self.getPlug().setValue( value ) - nodeUI = self.ancestor( GafferUI.NodeUI ) - if nodeUI : - frameRangeWidget = nodeUI.plugValueWidget( self.getPlug().node()["frameRange"], lazy = False ) + frameRangeWidget = self.__frameRangeWidget() + if frameRangeWidget : + ## \todo: This should be managed by activator metadata once we've ported + # that functionality out of RenderManShaderUI and into PlugLayout. + frameRangeWidget.setReadOnly( value != Gaffer.Dispatcher.FramesMode.CustomRange ) + + self.__updateFrameRangeConnection = None + + window = self.ancestor( GafferUI.ScriptWindow ) + if not window : + return + + script = window.scriptNode() + context = script.context() + + if label == "CurrentFrame" : + self.__updateFrameRangeConnection = context.changedSignal().connect( Gaffer.WeakMethod( self.__contextChanged ) ) + self.__contextChanged( context, "frame" ) + elif label == "FullRange" : + self.__updateFrameRangeConnection = script.plugDirtiedSignal().connect( Gaffer.WeakMethod( self.__scriptPlugDirtied ) ) + self.__scriptPlugDirtied( script["frameRange"] ) + elif label == "PlaybackRange" : + playback = GafferUI.Playback.acquire( context ) + self.__updateFrameRangeConnection = playback.frameRangeChangedSignal().connect( Gaffer.WeakMethod( self.__playbackFrameRangeChanged ) ) + self.__playbackFrameRangeChanged( playback ) + + def __visibilityChanged( self, widget ) : + + if self.visible() : + self.__selectionChanged( self.__selectionMenu ) + else : + self.__updateFrameRangeConnection = None + + def __contextChanged( self, context, key ) : + + if key == "frame" : + frameRangeWidget = self.__frameRangeWidget() + if frameRangeWidget : + frameRangeWidget.textWidget().setText( str(int(context.getFrame())) ) + + def __playbackFrameRangeChanged( self, playback ) : + + frameRange = playback.getFrameRange() + frameRange = str(IECore.frameListFromList( range(frameRange[0], frameRange[1]+1) )) + self.getPlug().node()["frameRange"].setValue( frameRange ) + + def __scriptPlugDirtied( self, plug ) : + + script = plug.ancestor( Gaffer.ScriptNode ) + if script and plug.isSame( script["frameRange"] ) or plug.parent().isSame( script["frameRange"] ) : + frameRangeWidget = self.__frameRangeWidget() if frameRangeWidget : - ## \todo: This should be managed by activator metadata once we've ported - # that functionality out of RenderManShaderUI and into PlugLayout. - frameRangeWidget.setReadOnly( value != Gaffer.Dispatcher.FramesMode.CustomRange ) + frameRangeWidget.textWidget().setText( str(IECore.FrameRange( script["frameRange"]["start"].getValue(), script["frameRange"]["end"].getValue() )) ) + +class __FrameRangePlugValueWidget( GafferUI.StringPlugValueWidget ) : + + def _updateFromPlug( self ) : + + with self.getContext() : + framesMode = self.getPlug().node()["framesMode"].getValue() + + # we need to disable the normal update in CurrentFrame and FullRange modes + if framesMode == Gaffer.Dispatcher.FramesMode.CustomRange : + GafferUI.StringPlugValueWidget._updateFromPlug( self ) - if label == "PlaybackRange" : - window = self.ancestor( GafferUI.ScriptWindow ) - frameRange = GafferUI.Playback.acquire( window.scriptNode().context() ).getFrameRange() - frameRange = str(IECore.frameListFromList( range(frameRange[0], frameRange[1]+1) )) - self.getPlug().node()["frameRange"].setValue( frameRange ) + self.textWidget().setEditable( self._editable() ) ########################################################################## # Metadata, PlugValueWidgets and Nodules @@ -283,6 +339,7 @@ def __selectionChanged( self, selectionMenu ) : GafferUI.PlugValueWidget.registerCreator( Gaffer.Dispatcher, "user", None ) GafferUI.PlugValueWidget.registerCreator( Gaffer.Dispatcher, "framesMode", __FramesModePlugValueWidget ) +GafferUI.PlugValueWidget.registerCreator( Gaffer.Dispatcher, "frameRange", __FrameRangePlugValueWidget ) GafferUI.PlugValueWidget.registerCreator( Gaffer.ExecutableNode, "requirement", __RequirementPlugValueWidget ) GafferUI.PlugValueWidget.registerCreator( Gaffer.ExecutableNode, "dispatcher", GafferUI.CompoundPlugValueWidget, collapsed = None ) From 44e55c21d4a62014c79dccb44ef02a8ba81068f4 Mon Sep 17 00:00:00 2001 From: Andrew Kaufman Date: Fri, 10 Oct 2014 14:55:50 -0700 Subject: [PATCH 2/4] Setting default value for CustomRange mode to demonstrate syntax. --- src/Gaffer/Dispatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gaffer/Dispatcher.cpp b/src/Gaffer/Dispatcher.cpp index 0cacb16e90d..ac891af5c0f 100644 --- a/src/Gaffer/Dispatcher.cpp +++ b/src/Gaffer/Dispatcher.cpp @@ -64,7 +64,7 @@ Dispatcher::Dispatcher( const std::string &name ) storeIndexOfNextChild( g_firstPlugIndex ); addChild( new IntPlug( "framesMode", Plug::In, CurrentFrame, CurrentFrame ) ); - addChild( new StringPlug( "frameRange", Plug::In, "" ) ); + addChild( new StringPlug( "frameRange", Plug::In, "1-100x10" ) ); addChild( new StringPlug( "jobName", Plug::In, "" ) ); addChild( new StringPlug( "jobsDirectory", Plug::In, "" ) ); } From 6ce70ce8739696e8877b4efd58207b9e2ee2dbf0 Mon Sep 17 00:00:00 2001 From: Andrew Kaufman Date: Fri, 10 Oct 2014 09:27:14 -0700 Subject: [PATCH 3/4] PlaybackRange mode is read only as well. I changed it's order in the list because I think it's more natural next to FullRange --- python/GafferUI/DispatcherUI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/GafferUI/DispatcherUI.py b/python/GafferUI/DispatcherUI.py index 19a9c012c26..d65f05361ff 100644 --- a/python/GafferUI/DispatcherUI.py +++ b/python/GafferUI/DispatcherUI.py @@ -191,8 +191,8 @@ def __init__( self, plug, **kw ) : self.__labelsAndValues = ( ( "CurrentFrame", Gaffer.Dispatcher.FramesMode.CurrentFrame ), ( "FullRange", Gaffer.Dispatcher.FramesMode.FullRange ), - ( "CustomRange", Gaffer.Dispatcher.FramesMode.CustomRange ), ( "PlaybackRange", Gaffer.Dispatcher.FramesMode.CustomRange ), + ( "CustomRange", Gaffer.Dispatcher.FramesMode.CustomRange ), ) for label, value in self.__labelsAndValues : @@ -246,7 +246,7 @@ def __selectionChanged( self, selectionMenu ) : if frameRangeWidget : ## \todo: This should be managed by activator metadata once we've ported # that functionality out of RenderManShaderUI and into PlugLayout. - frameRangeWidget.setReadOnly( value != Gaffer.Dispatcher.FramesMode.CustomRange ) + frameRangeWidget.setReadOnly( label in [ "CurrentFrame", "FullRange", "PlaybackRange" ] ) self.__updateFrameRangeConnection = None From 4d8ff3b368719a954ed117d4eca4eaa79cabc0d4 Mon Sep 17 00:00:00 2001 From: Andrew Kaufman Date: Fri, 10 Oct 2014 14:50:29 -0700 Subject: [PATCH 4/4] Using metadata to store the actual CustomRange value. This allowed PlaybackRange to set the plug value without losing the alternate value. --- python/GafferUI/DispatcherUI.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/python/GafferUI/DispatcherUI.py b/python/GafferUI/DispatcherUI.py index d65f05361ff..373ebdba701 100644 --- a/python/GafferUI/DispatcherUI.py +++ b/python/GafferUI/DispatcherUI.py @@ -204,6 +204,9 @@ def __init__( self, plug, **kw ) : self._addPopupMenu( self.__selectionMenu ) + # save the metadata in case the frameRange plug is set prior to enabling CustomRange mode + self.__customFrameRangeChanged( self.getPlug().node()["frameRange"] ) + self._updateFromPlug() def selectionMenu( self ) : @@ -267,6 +270,11 @@ def __selectionChanged( self, selectionMenu ) : playback = GafferUI.Playback.acquire( context ) self.__updateFrameRangeConnection = playback.frameRangeChangedSignal().connect( Gaffer.WeakMethod( self.__playbackFrameRangeChanged ) ) self.__playbackFrameRangeChanged( playback ) + else : + frameRange = Gaffer.Metadata.plugValue( self.getPlug(), "dispatcherWindow:frameRange", inherit=False ) + if frameRange is not None : + self.getPlug().node()["frameRange"].setValue( frameRange ) + self.__updateFrameRangeConnection = self.getPlug().node().plugDirtiedSignal().connect( Gaffer.WeakMethod( self.__customFrameRangeChanged ) ) def __visibilityChanged( self, widget ) : @@ -295,6 +303,12 @@ def __scriptPlugDirtied( self, plug ) : frameRangeWidget = self.__frameRangeWidget() if frameRangeWidget : frameRangeWidget.textWidget().setText( str(IECore.FrameRange( script["frameRange"]["start"].getValue(), script["frameRange"]["end"].getValue() )) ) + + def __customFrameRangeChanged( self, plug ) : + + if plug.isSame( self.getPlug().node()["frameRange"] ) : + with self.getContext() : + Gaffer.Metadata.registerPlugValue( self.getPlug(), "dispatcherWindow:frameRange", plug.getValue() ) class __FrameRangePlugValueWidget( GafferUI.StringPlugValueWidget ) :