diff --git a/Changes.md b/Changes.md index cff482c8334..dc7d25211bd 100644 --- a/Changes.md +++ b/Changes.md @@ -74,6 +74,7 @@ API - Added `shuffleWithExtraSources()` method. - ShufflePlugValueWidget : Widgets for the `source` and `destination` plugs can now be customised using standard `plugValueWidget:type` metadata. - ImageTestCase : in `assertImageEqual` function, maxDifference may now be a tuple, to specify an asymmetric range. +- Editor : Added `Settings` class, which should be used to store settings for subclasses. See LightEditor and ImageInspector for examples. Breaking Changes ---------------- diff --git a/python/GafferImageUI/ImageInspector.py b/python/GafferImageUI/ImageInspector.py index 1b71e495b4b..d55e6dffd22 100644 --- a/python/GafferImageUI/ImageInspector.py +++ b/python/GafferImageUI/ImageInspector.py @@ -47,11 +47,11 @@ class ImageInspector( GafferUI.NodeSetEditor ) : # for the ImageInspector. ## \todo Eventually we want `GafferUI.Editor` to derive from Node itself, # in which case we wouldn't need a separate settings object. - class Settings( Gaffer.Node ) : + class Settings( GafferUI.Editor.Settings ) : - def __init__( self ) : + def __init__( self, script ) : - Gaffer.Node.__init__( self, "ImageInspectorSettings" ) + GafferUI.Editor.Settings.__init__( self, "ImageInspectorSettings", script ) self["in"] = GafferImage.ImagePlug() self["view"] = Gaffer.StringPlug( defaultValue = "default" ) @@ -85,7 +85,7 @@ def __init__( self ) : self["__sampleStats"]["in"].setInput( self["__sampleCounts"]["out"] ) self["__sampleStats"]["area"].setInput( self["__formatQuery"]["format"]["displayWindow"] ) - IECore.registerRunTimeTyped( Settings, typeName = "ImageInspector::Settings" ) + IECore.registerRunTimeTyped( Settings, typeName = "GafferImageUI::ImageInspector::Settings" ) def __init__( self, scriptNode, **kw ) : @@ -93,7 +93,7 @@ def __init__( self, scriptNode, **kw ) : GafferUI.NodeSetEditor.__init__( self, column, scriptNode, nodeSet = scriptNode.focusSet(), **kw ) - self.__settingsNode = self.Settings() + self.__settingsNode = self.Settings( scriptNode ) Gaffer.NodeAlgo.applyUserDefaults( self.__settingsNode ) with column : @@ -262,9 +262,7 @@ def copy( self ) : def cancellationSubject( self ) : - ## \todo We really just want to return `self._image` here, but we - # can't because BackgroundTask can't find the right ScriptNode from it. - return self.__scriptNode["fileName"] + return self._image def _children( self, canceller ) : diff --git a/python/GafferSceneUI/LightEditor.py b/python/GafferSceneUI/LightEditor.py index 40f4448ec2e..c0b9af0099a 100644 --- a/python/GafferSceneUI/LightEditor.py +++ b/python/GafferSceneUI/LightEditor.py @@ -60,29 +60,18 @@ # with HierarchyView. class LightEditor( GafferUI.NodeSetEditor ) : - # We store our settings as plugs on a node for a few reasons : - # - # - We want to use an EditScopePlugValueWidget, and that requires it. - # - We get a bunch of useful widgets and signals for free. - # - Longer term we want to refactor all Editors to derive from Node, - # in the same way that View does already. This will let us serialise - # _all_ layout state in the same format we serialise node graphs in. - # - The `userDefault` metadata provides a convenient way of configuring - # defaults. - # - The PlugLayout we use to display the settings allows users to add - # their own widgets to the UI. - class Settings( Gaffer.Node ) : - - def __init__( self ) : - - Gaffer.Node.__init__( self, "LightEditorSettings" ) + class Settings( GafferUI.Editor.Settings ) : + + def __init__( self, script ) : + + GafferUI.Editor.Settings.__init__( self, "LightEditorSettings", script ) self["in"] = GafferScene.ScenePlug() self["attribute"] = Gaffer.StringPlug( defaultValue = "light" ) self["section"] = Gaffer.StringPlug( defaultValue = "" ) self["editScope"] = Gaffer.Plug() - IECore.registerRunTimeTyped( Settings, typeName = "LightEditor::Settings" ) + IECore.registerRunTimeTyped( Settings, typeName = "GafferSceneUI::LightEditor::Settings" ) def __init__( self, scriptNode, **kw ) : @@ -90,7 +79,7 @@ def __init__( self, scriptNode, **kw ) : GafferUI.NodeSetEditor.__init__( self, column, scriptNode, nodeSet = scriptNode.focusSet(), **kw ) - self.__settingsNode = self.Settings() + self.__settingsNode = self.Settings( scriptNode ) Gaffer.NodeAlgo.applyUserDefaults( self.__settingsNode ) self.__setFilter = _GafferSceneUI._HierarchyViewSetFilter() diff --git a/python/GafferUI/Editor.py b/python/GafferUI/Editor.py index 48529b5f4d8..ed62d94be45 100644 --- a/python/GafferUI/Editor.py +++ b/python/GafferUI/Editor.py @@ -49,6 +49,34 @@ # or its children. These make up the tabs in the UI layout. class Editor( GafferUI.Widget ) : + ## Base class used to store settings for an Editor. We store our settings + # as plugs on a node for a few reasons : + # + # - Some editors want to use an EditScopePlugValueWidget, and that requires + # it. + # - We get a bunch of useful widgets and signals for free. + # - Longer term we want to refactor all Editors to derive from Node, in the + # same way that View does already. This will let us serialise _all_ layout + # state in the same format we serialise node graphs in. + # - The `userDefault` metadata provides a convenient way of configuring + # defaults. + # - The PlugLayout we use to display the settings allows users to add their + # own widgets to the UI. + class Settings( Gaffer.Node ) : + + def __init__( self, name, script ) : + + Gaffer.Node.__init__( self, name ) + + # Hack to allow BackgroundTask to recover ScriptNode for + # cancellation support - see `BackgroundTask.cpp`. + ## \todo Perhaps we can make this more natural at the point we derive + # Editor from Node? + self["__scriptNode"] = Gaffer.Plug( flags = Gaffer.Plug.Flags.Default & ~Gaffer.Plug.Flags.Serialisable ) + self["__scriptNode"].setInput( script["fileName"] ) + + IECore.registerRunTimeTyped( Settings, typeName = "GafferUI::Editor::Settings" ) + def __init__( self, topLevelWidget, scriptNode, **kw ) : GafferUI.Widget.__init__( self, topLevelWidget, **kw ) diff --git a/src/Gaffer/BackgroundTask.cpp b/src/Gaffer/BackgroundTask.cpp index bceebb131ee..9d88197ed72 100644 --- a/src/Gaffer/BackgroundTask.cpp +++ b/src/Gaffer/BackgroundTask.cpp @@ -90,9 +90,9 @@ const ScriptNode *scriptNode( const GraphComponent *subject ) } } - // The `GafferUI::View` classes house internal nodes which might not be - // directly connected to the graph. This hack recovers the ScriptNode from - // the node the view is currently connected to. + // The `GafferUI::View` and `GafferUI.Editor` classes house internal nodes + // which might not be directly connected to the graph. This hack recovers + // the ScriptNode from such classes. while( subject ) { if( subject->isInstanceOf( "GafferUI::View" ) ) @@ -102,6 +102,13 @@ const ScriptNode *scriptNode( const GraphComponent *subject ) return scriptNode( inPlug->getInput() ); } } + else if( subject->isInstanceOf( "GafferUI::Editor::Settings" ) ) + { + if( auto scriptPlug = subject->getChild( "__scriptNode" ) ) + { + return scriptNode( scriptPlug->getInput() ); + } + } subject = subject->parent(); } return nullptr;