From c4933b79cd2239d21e8aeac5afe7e4cda51a8337 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Thu, 6 Aug 2026 16:30:54 +0100 Subject: [PATCH] PlugValueWidgetTest : Fix handling of background updates This fixes test failures that looked like this : ``` Traceback (most recent call last): File "D:\a\gaffer\gaffer\build\python\GafferUITest\PlugValueWidgetTest.py", line 453, in testContextTrackerUpdates self.waitForUpdate( widget ) File "D:\a\gaffer\gaffer\build\python\GafferUITest\PlugValueWidgetTest.py", line 62, in waitForUpdate handler.assertCalled() File "D:\a\gaffer\gaffer\build\python\GafferTest\ParallelAlgoTest.py", line 92, in assertCalled self.receive( timeout )() ^^^^^^^^^^^^^^^^^^^^^^^ File "D:\a\gaffer\gaffer\build\python\GafferTest\ParallelAlgoTest.py", line 86, in receive raise AssertionError( "UIThread call not made within {} seconds".format( timeout ) ) AssertionError: UIThread call not made within 30.0 seconds ``` Our old approach of calling `waitForUpdate()` after running the code to trigger the update was bogus - it was possible for the update to have finished before we even got to calling `waitForUpdate()`. By triggering the update from _inside_ the UIThreadCallHandler's scope we avoid this possibility. --- Changes.md | 5 + .../GafferUITest/BoolPlugValueWidgetTest.py | 63 +++--- python/GafferUITest/ColorChooserTest.py | 114 +++++----- .../NumericPlugValueWidgetTest.py | 122 ++++++----- python/GafferUITest/PlugValueWidgetTest.py | 199 +++++++++--------- .../GafferUITest/StandardNodeToolbarTest.py | 12 +- .../GafferUITest/StringPlugValueWidgetTest.py | 141 +++++++------ 7 files changed, 347 insertions(+), 309 deletions(-) diff --git a/Changes.md b/Changes.md index 26b7b9605a..771a0b0bd4 100644 --- a/Changes.md +++ b/Changes.md @@ -28,6 +28,11 @@ Fixes [^1]: Improvement to a feature introduced in `1.7.0.0a1`, so should be omitted from final `1.7.0.0` release notes. [^2]: Included in `1.6.x.x`, so should be omitted from final `1.7.0.0` release notes. +Breaking Changes +---------------- + +- PlugValueWidgetTest : Removed `waitForUpdate()` method. Use `WidgetUpdateHandler` instead. + 1.7.0.0a9 (relative to 1.7.0.0a8) ========= diff --git a/python/GafferUITest/BoolPlugValueWidgetTest.py b/python/GafferUITest/BoolPlugValueWidgetTest.py index ce7fe8c5ff..05c77ddc8b 100644 --- a/python/GafferUITest/BoolPlugValueWidgetTest.py +++ b/python/GafferUITest/BoolPlugValueWidgetTest.py @@ -49,26 +49,28 @@ def test( self ) : n["user"]["p1"] = Gaffer.BoolPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) n["user"]["p2"] = Gaffer.BoolPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) - w = GafferUI.BoolPlugValueWidget( n["user"]["p1"] ) - self.assertEqual( w.getPlug(), n["user"]["p1"] ) - self.assertEqual( w.getPlugs(), { n["user"]["p1"] } ) - self.assertEqual( w.boolWidget().getState(), False ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : - n["user"]["p1"].setValue( True ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.boolWidget().getState(), True ) + w = GafferUI.BoolPlugValueWidget( n["user"]["p1"] ) + self.assertEqual( w.getPlug(), n["user"]["p1"] ) + self.assertEqual( w.getPlugs(), { n["user"]["p1"] } ) + self.assertEqual( w.boolWidget().getState(), False ) - w.setPlugs( n["user"].children() ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.boolWidget().getState(), w.boolWidget().State.Indeterminate ) + n["user"]["p1"].setValue( True ) + handler.waitForUpdate( w ) + self.assertEqual( w.boolWidget().getState(), True ) - n["user"]["p2"].setValue( True ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.boolWidget().getState(), True ) + w.setPlugs( n["user"].children() ) + handler.waitForUpdate( w ) + self.assertEqual( w.boolWidget().getState(), w.boolWidget().State.Indeterminate ) - w.setPlugs( [] ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.boolWidget().getState(), w.boolWidget().State.Indeterminate ) + n["user"]["p2"].setValue( True ) + handler.waitForUpdate( w ) + self.assertEqual( w.boolWidget().getState(), True ) + + w.setPlugs( [] ) + handler.waitForUpdate( w ) + self.assertEqual( w.boolWidget().getState(), w.boolWidget().State.Indeterminate ) def testInitialValue( self ) : @@ -76,10 +78,11 @@ def testInitialValue( self ) : n["user"]["p"] = Gaffer.BoolPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) for v in ( True, False ) : - n["user"]["p"].setValue( v ) - w = GafferUI.BoolPlugValueWidget( n["user"]["p"] ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.boolWidget().getState(), v ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + n["user"]["p"].setValue( v ) + w = GafferUI.BoolPlugValueWidget( n["user"]["p"] ) + handler.waitForUpdate( w ) + self.assertEqual( w.boolWidget().getState(), v ) def testErrorHandling( self ) : @@ -88,14 +91,16 @@ def testErrorHandling( self ) : script["n"] = Gaffer.Node() script["n"]["user"]["p"] = Gaffer.BoolPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) - w = GafferUI.BoolPlugValueWidget( script["n"]["user"]["p"] ) - self.assertFalse( w.boolWidget().getErrored() ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + + w = GafferUI.BoolPlugValueWidget( script["n"]["user"]["p"] ) + self.assertFalse( w.boolWidget().getErrored() ) - script["b"] = GafferTest.BadNode() - script["n"]["user"]["p"].setInput( script["b"]["out3"] ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertTrue( w.boolWidget().getErrored() ) + script["b"] = GafferTest.BadNode() + script["n"]["user"]["p"].setInput( script["b"]["out3"] ) + handler.waitForUpdate( w ) + self.assertTrue( w.boolWidget().getErrored() ) - script["n"]["user"]["p"].setInput( None ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertFalse( w.boolWidget().getErrored() ) + script["n"]["user"]["p"].setInput( None ) + handler.waitForUpdate( w ) + self.assertFalse( w.boolWidget().getErrored() ) diff --git a/python/GafferUITest/ColorChooserTest.py b/python/GafferUITest/ColorChooserTest.py index 1650d27ddd..fe95f7057c 100644 --- a/python/GafferUITest/ColorChooserTest.py +++ b/python/GafferUITest/ColorChooserTest.py @@ -246,59 +246,61 @@ def testSaveDefaultOptions( self ) : script["node"]["rgbaPlug"] = Gaffer.Color4fPlug() script["node"]["rgbaPlug"].setValue( imath.Color4f( 0.1 ) ) - rgbWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbPlug"] ) - rgbWidget.setColorChooserVisible( True ) - rgbaWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbaPlug"] ) - rgbaWidget.setColorChooserVisible( True ) - - GafferUITest.PlugValueWidgetTest.waitForUpdate( rgbWidget._ColorPlugValueWidget__colorChooser ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( rgbaWidget._ColorPlugValueWidget__colorChooser ) - - # Default state - for c in "rgbhsvtmi" : - self.assertTrue( self.__sliderFromWidget( rgbWidget, c ).getVisible() ) - self.assertTrue( self.__sliderFromWidget( rgbaWidget, c ).getVisible() ) - self.assertTrue( self.__sliderFromWidget( rgbaWidget, "a" ).getVisible() ) - self.assertEqual( self.__getStaticComponent( rgbWidget ), "v" ) - self.assertEqual( self.__getStaticComponent( rgbaWidget ), "v" ) - self.assertTrue( self.__getColorFieldVisibility( rgbWidget ) ) - self.assertTrue( self.__getColorFieldVisibility( rgbaWidget ) ) - self.assertTrue( self.__getDynamicSliderBackgrounds( rgbWidget ) ) - self.assertTrue( self.__getDynamicSliderBackgrounds( rgbaWidget ) ) - - # Modify `rgbWidget` - - self.__setVisibleComponents( rgbWidget, "rgbhsv" ) - self.__setStaticComponent( rgbWidget, "g" ) - self.__setColorFieldVisibility( rgbWidget, False ) - self.__setDynamicSliderBackgrounds( rgbWidget, False ) - - # Save defaults - colorChooser = self.__colorChooserFromWidget( rgbWidget ) - saveDefaultOptions( colorChooser, "colorChooser:inline:" ) - - del rgbWidget - del rgbaWidget - - # Both color types get the same value - rgbWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbPlug"] ) - rgbWidget.setColorChooserVisible( True ) - rgbaWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbaPlug"] ) - rgbaWidget.setColorChooserVisible( True ) - - GafferUITest.PlugValueWidgetTest.waitForUpdate( rgbWidget._ColorPlugValueWidget__colorChooser ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( rgbaWidget._ColorPlugValueWidget__colorChooser ) - - for c in "rgbhsv" : - self.assertTrue( self.__sliderFromWidget( rgbWidget, c ).getVisible() ) - self.assertTrue( self.__sliderFromWidget( rgbaWidget, c ).getVisible() ) - for c in "tmi" : - self.assertFalse( self.__sliderFromWidget( rgbWidget, c ).getVisible() ) - self.assertFalse( self.__sliderFromWidget( rgbaWidget, c ).getVisible() ) - self.assertTrue( self.__sliderFromWidget( rgbaWidget, "a" ).getVisible() ) - self.assertEqual( self.__getStaticComponent( rgbWidget ), "g" ) - self.assertEqual( self.__getStaticComponent( rgbaWidget ), "g" ) - self.assertFalse( self.__getColorFieldVisibility( rgbWidget ) ) - self.assertFalse( self.__getColorFieldVisibility( rgbaWidget ) ) - self.assertFalse( self.__getDynamicSliderBackgrounds( rgbWidget ) ) - self.assertFalse( self.__getDynamicSliderBackgrounds( rgbaWidget ) ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + + rgbWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbPlug"] ) + rgbWidget.setColorChooserVisible( True ) + rgbaWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbaPlug"] ) + rgbaWidget.setColorChooserVisible( True ) + + handler.waitForUpdate( rgbWidget._ColorPlugValueWidget__colorChooser ) + handler.waitForUpdate( rgbaWidget._ColorPlugValueWidget__colorChooser ) + + # Default state + for c in "rgbhsvtmi" : + self.assertTrue( self.__sliderFromWidget( rgbWidget, c ).getVisible() ) + self.assertTrue( self.__sliderFromWidget( rgbaWidget, c ).getVisible() ) + self.assertTrue( self.__sliderFromWidget( rgbaWidget, "a" ).getVisible() ) + self.assertEqual( self.__getStaticComponent( rgbWidget ), "v" ) + self.assertEqual( self.__getStaticComponent( rgbaWidget ), "v" ) + self.assertTrue( self.__getColorFieldVisibility( rgbWidget ) ) + self.assertTrue( self.__getColorFieldVisibility( rgbaWidget ) ) + self.assertTrue( self.__getDynamicSliderBackgrounds( rgbWidget ) ) + self.assertTrue( self.__getDynamicSliderBackgrounds( rgbaWidget ) ) + + # Modify `rgbWidget` + + self.__setVisibleComponents( rgbWidget, "rgbhsv" ) + self.__setStaticComponent( rgbWidget, "g" ) + self.__setColorFieldVisibility( rgbWidget, False ) + self.__setDynamicSliderBackgrounds( rgbWidget, False ) + + # Save defaults + colorChooser = self.__colorChooserFromWidget( rgbWidget ) + saveDefaultOptions( colorChooser, "colorChooser:inline:" ) + + del rgbWidget + del rgbaWidget + + # Both color types get the same value + rgbWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbPlug"] ) + rgbWidget.setColorChooserVisible( True ) + rgbaWidget = GafferUI.ColorPlugValueWidget( script["node"]["rgbaPlug"] ) + rgbaWidget.setColorChooserVisible( True ) + + handler.waitForUpdate( rgbWidget._ColorPlugValueWidget__colorChooser ) + handler.waitForUpdate( rgbaWidget._ColorPlugValueWidget__colorChooser ) + + for c in "rgbhsv" : + self.assertTrue( self.__sliderFromWidget( rgbWidget, c ).getVisible() ) + self.assertTrue( self.__sliderFromWidget( rgbaWidget, c ).getVisible() ) + for c in "tmi" : + self.assertFalse( self.__sliderFromWidget( rgbWidget, c ).getVisible() ) + self.assertFalse( self.__sliderFromWidget( rgbaWidget, c ).getVisible() ) + self.assertTrue( self.__sliderFromWidget( rgbaWidget, "a" ).getVisible() ) + self.assertEqual( self.__getStaticComponent( rgbWidget ), "g" ) + self.assertEqual( self.__getStaticComponent( rgbaWidget ), "g" ) + self.assertFalse( self.__getColorFieldVisibility( rgbWidget ) ) + self.assertFalse( self.__getColorFieldVisibility( rgbaWidget ) ) + self.assertFalse( self.__getDynamicSliderBackgrounds( rgbWidget ) ) + self.assertFalse( self.__getDynamicSliderBackgrounds( rgbaWidget ) ) diff --git a/python/GafferUITest/NumericPlugValueWidgetTest.py b/python/GafferUITest/NumericPlugValueWidgetTest.py index 8d58a42aba..352abba402 100644 --- a/python/GafferUITest/NumericPlugValueWidgetTest.py +++ b/python/GafferUITest/NumericPlugValueWidgetTest.py @@ -49,25 +49,27 @@ def test( self ) : n["i"]= Gaffer.IntPlug() n["f"] = Gaffer.FloatPlug() - w = GafferUI.NumericPlugValueWidget( n["i"] ) - self.assertTrue( w.getPlug().isSame( n["i"] ) ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertTrue( isinstance( w.numericWidget().getValue(), int ) ) - - w.setPlug( n["f"] ) - self.assertTrue( w.getPlug().isSame( n["f"] ) ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertTrue( isinstance( w.numericWidget().getValue(), float ) ) - - w = GafferUI.NumericPlugValueWidget( plugs = [] ) - self.assertEqual( w.getPlug(), None ) - self.assertEqual( w.numericWidget().getEditable(), False ) - - w.setPlug( n["f"] ) - self.assertTrue( w.getPlug().isSame( n["f"] ) ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertTrue( isinstance( w.numericWidget().getValue(), float ) ) - self.assertEqual( w.numericWidget().getEditable(), True ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + + w = GafferUI.NumericPlugValueWidget( n["i"] ) + self.assertTrue( w.getPlug().isSame( n["i"] ) ) + handler.waitForUpdate( w ) + self.assertTrue( isinstance( w.numericWidget().getValue(), int ) ) + + w.setPlug( n["f"] ) + self.assertTrue( w.getPlug().isSame( n["f"] ) ) + handler.waitForUpdate( w ) + self.assertTrue( isinstance( w.numericWidget().getValue(), float ) ) + + w = GafferUI.NumericPlugValueWidget( plugs = [] ) + self.assertEqual( w.getPlug(), None ) + self.assertEqual( w.numericWidget().getEditable(), False ) + + w.setPlug( n["f"] ) + self.assertTrue( w.getPlug().isSame( n["f"] ) ) + handler.waitForUpdate( w ) + self.assertTrue( isinstance( w.numericWidget().getValue(), float ) ) + self.assertEqual( w.numericWidget().getEditable(), True ) def testEditMultiplePlugs( self ) : @@ -75,25 +77,27 @@ def testEditMultiplePlugs( self ) : n["user"]["i1"] = Gaffer.IntPlug() n["user"]["i2"] = Gaffer.IntPlug() - w = GafferUI.NumericPlugValueWidget( n["user"].children() ) - self.assertEqual( w.getPlugs(), { n["user"]["i1"], n["user"]["i2"] } ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : - n["user"]["i1"].setValue( 2 ) - n["user"]["i2"].setValue( 2 ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.numericWidget().getText(), "2" ) + w = GafferUI.NumericPlugValueWidget( n["user"].children() ) + self.assertEqual( w.getPlugs(), { n["user"]["i1"], n["user"]["i2"] } ) - n["user"]["i1"].setValue( 1 ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.numericWidget().getText(), "" ) - self.assertEqual( w.numericWidget()._qtWidget().placeholderText(), "---" ) + n["user"]["i1"].setValue( 2 ) + n["user"]["i2"].setValue( 2 ) + handler.waitForUpdate( w ) + self.assertEqual( w.numericWidget().getText(), "2" ) - w.numericWidget().setValue( 10 ) - self.assertEqual( n["user"]["i1"].getValue(), 10 ) - self.assertEqual( n["user"]["i2"].getValue(), 10 ) + n["user"]["i1"].setValue( 1 ) + handler.waitForUpdate( w ) + self.assertEqual( w.numericWidget().getText(), "" ) + self.assertEqual( w.numericWidget()._qtWidget().placeholderText(), "---" ) - Gaffer.MetadataAlgo.setReadOnly( n["user"]["i1"], True ) - self.assertFalse( w.numericWidget().getEditable() ) + w.numericWidget().setValue( 10 ) + self.assertEqual( n["user"]["i1"].getValue(), 10 ) + self.assertEqual( n["user"]["i2"].getValue(), 10 ) + + Gaffer.MetadataAlgo.setReadOnly( n["user"]["i1"], True ) + self.assertFalse( w.numericWidget().getEditable() ) def testChangeToMixedPlugsDoesntOverwriteExistingPlugValues( self ) : @@ -103,19 +107,21 @@ def testChangeToMixedPlugsDoesntOverwriteExistingPlugValues( self ) : n["user"]["i1"].setValue( 1 ) n["user"]["i2"].setValue( 2 ) - w = GafferUI.NumericPlugValueWidget( n["user"]["i1"] ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.numericWidget().getText(), "1" ) - self.assertEqual( w.numericWidget()._qtWidget().placeholderText(), "" ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : - w.setPlugs( n["user"].children() ) - self.assertEqual( w.getPlugs(), { n["user"]["i1"], n["user"]["i2"] } ) + w = GafferUI.NumericPlugValueWidget( n["user"]["i1"] ) + handler.waitForUpdate( w ) + self.assertEqual( w.numericWidget().getText(), "1" ) + self.assertEqual( w.numericWidget()._qtWidget().placeholderText(), "" ) - self.assertEqual( n["user"]["i1"].getValue(), 1 ) - self.assertEqual( n["user"]["i2"].getValue(), 2 ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.numericWidget().getText(), "" ) - self.assertEqual( w.numericWidget()._qtWidget().placeholderText(), "---" ) + w.setPlugs( n["user"].children() ) + self.assertEqual( w.getPlugs(), { n["user"]["i1"], n["user"]["i2"] } ) + + self.assertEqual( n["user"]["i1"].getValue(), 1 ) + self.assertEqual( n["user"]["i2"].getValue(), 2 ) + handler.waitForUpdate( w ) + self.assertEqual( w.numericWidget().getText(), "" ) + self.assertEqual( w.numericWidget()._qtWidget().placeholderText(), "---" ) def testMixedOrInvalidValuesPreservesExisting( self ) : @@ -125,23 +131,25 @@ def testMixedOrInvalidValuesPreservesExisting( self ) : n["user"]["i1"].setValue( 1 ) n["user"]["i2"].setValue( 2 ) - w = GafferUI.NumericPlugValueWidget( n["user"]["i1"] ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.numericWidget().getValue(), 1 ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + + w = GafferUI.NumericPlugValueWidget( n["user"]["i1"] ) + handler.waitForUpdate( w ) + self.assertEqual( w.numericWidget().getValue(), 1 ) - w.numericWidget().setText( "" ) - w.numericWidget()._qtWidget().editingFinished.emit() + w.numericWidget().setText( "" ) + w.numericWidget()._qtWidget().editingFinished.emit() - self.assertEqual( n["user"]["i1"].getValue(), 1 ) + self.assertEqual( n["user"]["i1"].getValue(), 1 ) - w = GafferUI.NumericPlugValueWidget( n["user"].children() ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.numericWidget().getText(), "" ) + w = GafferUI.NumericPlugValueWidget( n["user"].children() ) + handler.waitForUpdate( w ) + self.assertEqual( w.numericWidget().getText(), "" ) - w.numericWidget()._qtWidget().editingFinished.emit() + w.numericWidget()._qtWidget().editingFinished.emit() - self.assertEqual( n["user"]["i1"].getValue(), 1 ) - self.assertEqual( n["user"]["i2"].getValue(), 2 ) + self.assertEqual( n["user"]["i1"].getValue(), 1 ) + self.assertEqual( n["user"]["i2"].getValue(), 2 ) def testFixedCharacterWidth( self ) : diff --git a/python/GafferUITest/PlugValueWidgetTest.py b/python/GafferUITest/PlugValueWidgetTest.py index 9df688d665..45e839c10c 100644 --- a/python/GafferUITest/PlugValueWidgetTest.py +++ b/python/GafferUITest/PlugValueWidgetTest.py @@ -35,7 +35,6 @@ # ########################################################################## -import unittest import warnings import weakref @@ -48,10 +47,9 @@ class PlugValueWidgetTest( GafferUITest.TestCase ) : - @staticmethod - def waitForUpdate( widget ) : + class WidgetUpdateHandler( GafferTest.ParallelAlgoTest.UIThreadCallHandler ) : - with GafferTest.ParallelAlgoTest.UIThreadCallHandler() as handler : + def waitForUpdate( self, widget ) : # Updates are done lazily, so we need to flush any pending updates. widget._PlugValueWidget__callUpdateFromValues.flush( widget ) @@ -59,7 +57,7 @@ def waitForUpdate( widget ) : # And updates for computed values are done in the background, so we # need to wait until they're done. if any( isinstance( p, Gaffer.ValuePlug ) and Gaffer.PlugAlgo.dependsOnCompute( p ) for p in widget.getPlugs() ) : - handler.assertCalled() + self.assertCalled() def testContext( self ) : @@ -68,13 +66,16 @@ def testContext( self ) : s["e"] = Gaffer.Expression() s["e"].setExpression( "parent[\"m\"][\"op1\"] = int( context[\"frame\"] )" ) - w = GafferUI.NumericPlugValueWidget( s["m"]["op1"] ) - self.assertEqual( w.context(), s.context() ) + with self.WidgetUpdateHandler() as handler : - s.context().setFrame( 10 ) - self.waitForUpdate( w ) - self.assertEqual( w.numericWidget().getValue(), 10 ) - self.assertEqual( w.context(), s.context() ) + w = GafferUI.NumericPlugValueWidget( s["m"]["op1"] ) + self.assertEqual( w.context(), s.context() ) + + s.context().setFrame( 10 ) + handler.waitForUpdate( w ) + + self.assertEqual( w.numericWidget().getValue(), 10 ) + self.assertEqual( w.context(), s.context() ) def testDisableCreationForSpecificTypes( self ) : @@ -276,41 +277,42 @@ def testUpdates( self ) : self.assertEqual( widget.updateCount, 1 ) self.assertEqual( widget.updateContexts[0], script.context() ) - # Changing the context shouldn't trigger an update, because the - # plug value isn't computed. - script.context().setFrame( 2 ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 1 ) + with self.WidgetUpdateHandler() as handler : + + # Changing the context shouldn't trigger an update, because the + # plug value isn't computed. + script.context().setFrame( 2 ) + self.assertEqual( widget.updateCount, 1 ) + + # Changing the plug should trigger an update. + widget.setPlug( script["add"]["op2"] ) + handler.waitForUpdate( widget ) + self.assertEqual( widget.updateCount, 2 ) + self.assertEqual( widget.updateContexts[1], script.context() ) - # Changing the plug should trigger an update. - widget.setPlug( script["add"]["op2"] ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 2 ) - self.assertEqual( widget.updateContexts[1], script.context() ) - - # Changing the context still shouldn't trigger an update, because the - # plug value isn't computed. - script.context().setFrame( 3 ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 2 ) - - # Changing the plug again should trigger an update again. This time we - # see two updates - one to denote the start of the background task, and - # one when it completes. This is because the plug's value is computed - # and we don't want to block the UI thread with computes. - widget.setPlug( script["add"]["sum"] ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 4 ) - self.assertEqual( widget.updateContexts[2], script.context() ) - self.assertEqual( widget.updateContexts[3], script.context() ) - - # And now changing the context should trigger an update, since computed - # values may be context-sensitive. - script.context().setFrame( 4 ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 6 ) - self.assertEqual( widget.updateContexts[4], script.context() ) - self.assertEqual( widget.updateContexts[5], script.context() ) + # Changing the context still shouldn't trigger an update, because the + # plug value isn't computed. + script.context().setFrame( 3 ) + handler.waitForUpdate( widget ) + self.assertEqual( widget.updateCount, 2 ) + + # Changing the plug again should trigger an update again. This time we + # see two updates - one to denote the start of the background task, and + # one when it completes. This is because the plug's value is computed + # and we don't want to block the UI thread with computes. + widget.setPlug( script["add"]["sum"] ) + handler.waitForUpdate( widget ) + self.assertEqual( widget.updateCount, 4 ) + self.assertEqual( widget.updateContexts[2], script.context() ) + self.assertEqual( widget.updateContexts[3], script.context() ) + + # And now changing the context should trigger an update, since computed + # values may be context-sensitive. + script.context().setFrame( 4 ) + handler.waitForUpdate( widget ) + self.assertEqual( widget.updateCount, 6 ) + self.assertEqual( widget.updateContexts[4], script.context() ) + self.assertEqual( widget.updateContexts[5], script.context() ) class LegacyUpdateCountPlugValueWidget( GafferUI.PlugValueWidget ) : @@ -394,32 +396,36 @@ def testContextForEditorSettings( self ) : editor.settings()["__contextQuery"].addQuery( Gaffer.StringPlug(), "testVariable" ) editor.settings()["testPlug"].setInput( editor.settings()["__contextQuery"]["out"][0]["value"] ) - widget = self.UpdateCountPlugValueWidget( editor.settings()["testPlug"] ) + with self.WidgetUpdateHandler() as handler : + + widget = self.UpdateCountPlugValueWidget( editor.settings()["testPlug"] ) + handler.waitForUpdate( widget ) - # Editor not viewing anything yet, so we just use the default - # script context. + # Editor not viewing anything yet, so we just use the default + # script context. - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 2 ) # One at the start of the background update, and one on completion - self.assertEqual( widget.updateContexts[1], script.context() ) + self.assertEqual( widget.updateCount, 2 ) # One at the start of the background update, and one on completion + self.assertEqual( widget.updateContexts[1], script.context() ) - # Editor viewing `node`, so we should use the context that has been - # tracked for it. + # Editor viewing `node`, so we should use the context that has been + # tracked for it. - editor.settings()["in"].setInput( script["node"]["sum"] ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 4 ) - self.assertEqual( widget.updateContexts[3], contextTracker.context( script["node"] ) ) - self.assertIn( "testVariable", widget.updateContexts[3] ) + editor.settings()["in"].setInput( script["node"]["sum"] ) + handler.waitForUpdate( widget ) + + self.assertEqual( widget.updateCount, 4 ) + self.assertEqual( widget.updateContexts[3], contextTracker.context( script["node"] ) ) + self.assertIn( "testVariable", widget.updateContexts[3] ) - # Editor viewing `contextVariables`, so we should use the context that - # has been tracked for that. + # Editor viewing `contextVariables`, so we should use the context that + # has been tracked for that. - editor.settings()["in"].setInput( script["contextVariables"]["out"] ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 6 ) - self.assertEqual( widget.updateContexts[5], contextTracker.context( script["contextVariables"] ) ) - self.assertNotIn( "testVariable", widget.updateContexts[5] ) + editor.settings()["in"].setInput( script["contextVariables"]["out"] ) + handler.waitForUpdate( widget ) + + self.assertEqual( widget.updateCount, 6 ) + self.assertEqual( widget.updateContexts[5], contextTracker.context( script["contextVariables"] ) ) + self.assertNotIn( "testVariable", widget.updateContexts[5] ) def testContextTrackerUpdates( self ) : @@ -445,43 +451,48 @@ def testContextTrackerUpdates( self ) : self.assertEqual( widget.updateCount, 0 ) self.assertEqual( len( widget.updateContexts ), 0 ) - # First update should occur when we make the widget visible. - # Since we're showing a computed plug, we get two updates - one - # to indicate the start of the background update and one when - # it finishes. - window.setVisible( True ) - self.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 2 ) - self.assertEqual( widget.updateContexts[-1], script.context() ) + with self.WidgetUpdateHandler() as handler : - # Changing focus should cause an update when the ContextTracker - # comes up with a new tracked context. + # First update should occur when we make the widget visible. + # Since we're showing a computed plug, we get two updates - one + # to indicate the start of the background update and one when + # it finishes. - contextTracker = GafferUI.ContextTracker.acquireForFocus( script ) - with GafferUITest.ContextTrackerTest.UpdateHandler() as h : - script.setFocus( script["contextVariables0"] ) - self.waitForUpdate( widget ) + window.setVisible( True ) + handler.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 4 ) - self.assertEqual( widget.updateContexts[-1], contextTracker.context( script["add"]["sum"] ) ) + self.assertEqual( widget.updateCount, 2 ) + self.assertEqual( widget.updateContexts[-1], script.context() ) - # Changing focus to an equivalent node should not cause an update, - # because the same context will be found. + # Changing focus should cause an update when the ContextTracker + # comes up with a new tracked context. - with GafferUITest.ContextTrackerTest.UpdateHandler() as h : - script.setFocus( script["contextVariables1"] ) - self.waitForIdle() - self.assertEqual( widget.updateCount, 4 ) + contextTracker = GafferUI.ContextTracker.acquireForFocus( script ) + with GafferUITest.ContextTrackerTest.UpdateHandler() : + script.setFocus( script["contextVariables0"] ) + + handler.waitForUpdate( widget ) + self.assertEqual( widget.updateCount, 4 ) + self.assertEqual( widget.updateContexts[-1], contextTracker.context( script["add"]["sum"] ) ) + + # Changing focus to an equivalent node should not cause an update, + # because the same context will be found. + + with GafferUITest.ContextTrackerTest.UpdateHandler() as h : + script.setFocus( script["contextVariables1"] ) + + self.waitForIdle() + self.assertEqual( widget.updateCount, 4 ) - # But changing focus to a node that yields a different context should - # trigger an update. + # But changing focus to a node that yields a different context should + # trigger an update. - with GafferUITest.ContextTrackerTest.UpdateHandler() as h : - script.setFocus( script["contextVariables2"] ) - self.waitForUpdate( widget ) + with GafferUITest.ContextTrackerTest.UpdateHandler() as h : + script.setFocus( script["contextVariables2"] ) - self.assertEqual( widget.updateCount, 6 ) - self.assertEqual( widget.updateContexts[-1], contextTracker.context( script["add"]["sum"] ) ) + handler.waitForUpdate( widget ) + self.assertEqual( widget.updateCount, 6 ) + self.assertEqual( widget.updateContexts[-1], contextTracker.context( script["add"]["sum"] ) ) def tearDown( self ) : diff --git a/python/GafferUITest/StandardNodeToolbarTest.py b/python/GafferUITest/StandardNodeToolbarTest.py index a9a0a419dd..bd651a1da1 100644 --- a/python/GafferUITest/StandardNodeToolbarTest.py +++ b/python/GafferUITest/StandardNodeToolbarTest.py @@ -67,8 +67,10 @@ def testNoUnnecessaryUpdates( self ) : ( view, view["testPlug" ] ), ] : - toolbar = GafferUI.StandardNodeToolbar( node ) - widget = toolbar._StandardNodeToolbar__layout.plugValueWidget( plug ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( widget ) - self.assertEqual( widget.updateCount, 1 ) - self.assertEqual( widget.updateContexts[0], script.context() ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + + toolbar = GafferUI.StandardNodeToolbar( node ) + widget = toolbar._StandardNodeToolbar__layout.plugValueWidget( plug ) + handler.waitForUpdate( widget ) + self.assertEqual( widget.updateCount, 1 ) + self.assertEqual( widget.updateContexts[0], script.context() ) diff --git a/python/GafferUITest/StringPlugValueWidgetTest.py b/python/GafferUITest/StringPlugValueWidgetTest.py index 95036d1505..41b5e27c1a 100644 --- a/python/GafferUITest/StringPlugValueWidgetTest.py +++ b/python/GafferUITest/StringPlugValueWidgetTest.py @@ -53,52 +53,54 @@ def test( self ) : n["user"]["p1"].setValue( "p1" ) n["user"]["p2"].setValue( "p2" ) - w = GafferUI.StringPlugValueWidget( n["user"]["p1"] ) - self.assertEqual( w.getPlug(), n["user"]["p1"] ) - self.assertEqual( w.getPlugs(), { n["user"]["p1"] } ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "p1" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) - - n["user"]["p1"].setValue( "x" ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "x" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) - - w.setPlugs( n["user"].children() ) - - self.assertEqual( n["user"]["p1"].getValue(), "x" ) - self.assertEqual( n["user"]["p2"].getValue(), "p2" ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) - - w = GafferUI.StringPlugValueWidget( n["user"].children() ) - self.assertEqual( w.getPlugs(), { n["user"]["p1"], n["user"]["p2"] } ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) - - n["user"]["p2"].setValue( "x" ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "x" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) - - n["user"]["p1"].setValue( "" ) - n["user"]["p2"].setValue( "" ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) - - Gaffer.Metadata.registerValue( n["user"]["p1"], "stringPlugValueWidget:placeholderText", "test" ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) - - Gaffer.Metadata.registerValue( n["user"]["p2"], "stringPlugValueWidget:placeholderText", "test" ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "test" ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + + w = GafferUI.StringPlugValueWidget( n["user"]["p1"] ) + self.assertEqual( w.getPlug(), n["user"]["p1"] ) + self.assertEqual( w.getPlugs(), { n["user"]["p1"] } ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "p1" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) + + n["user"]["p1"].setValue( "x" ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "x" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) + + w.setPlugs( n["user"].children() ) + + self.assertEqual( n["user"]["p1"].getValue(), "x" ) + self.assertEqual( n["user"]["p2"].getValue(), "p2" ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) + + w = GafferUI.StringPlugValueWidget( n["user"].children() ) + self.assertEqual( w.getPlugs(), { n["user"]["p1"], n["user"]["p2"] } ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) + + n["user"]["p2"].setValue( "x" ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "x" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) + + n["user"]["p1"].setValue( "" ) + n["user"]["p2"].setValue( "" ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) + + Gaffer.Metadata.registerValue( n["user"]["p1"], "stringPlugValueWidget:placeholderText", "test" ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "" ) + + Gaffer.Metadata.registerValue( n["user"]["p2"], "stringPlugValueWidget:placeholderText", "test" ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "test" ) def testMixedValuesPreserved( self ) : @@ -112,32 +114,34 @@ def testMixedValuesPreserved( self ) : Gaffer.Metadata.registerValue( n["user"]["p1"], "stringPlugValueWidget:placeholderText", "test" ) Gaffer.Metadata.registerValue( n["user"]["p2"], "stringPlugValueWidget:placeholderText", "test" ) - w = GafferUI.StringPlugValueWidget( { n["user"]["p1"], n["user"]["p2"] } ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + + w = GafferUI.StringPlugValueWidget( { n["user"]["p1"], n["user"]["p2"] } ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) - w.textWidget()._qtWidget().editingFinished.emit() + w.textWidget()._qtWidget().editingFinished.emit() - self.assertEqual( n["user"]["p1"].getValue(), "p1" ) - self.assertEqual( n["user"]["p2"].getValue(), "p2" ) + self.assertEqual( n["user"]["p1"].getValue(), "p1" ) + self.assertEqual( n["user"]["p2"].getValue(), "p2" ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "---" ) - # Simulate the user editing, even if it results in an empty string - self.assertEqual( w.textWidget()._qtWidget().text(), "" ) - w.textWidget()._qtWidget().textChanged.emit( w.textWidget()._qtWidget().text() ) + # Simulate the user editing, even if it results in an empty string + self.assertEqual( w.textWidget()._qtWidget().text(), "" ) + w.textWidget()._qtWidget().textChanged.emit( w.textWidget()._qtWidget().text() ) - self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "test" ) + self.assertEqual( w.textWidget()._qtWidget().placeholderText(), "test" ) - self.assertEqual( n["user"]["p1"].getValue(), "p1" ) - self.assertEqual( n["user"]["p2"].getValue(), "p2" ) + self.assertEqual( n["user"]["p1"].getValue(), "p1" ) + self.assertEqual( n["user"]["p2"].getValue(), "p2" ) - w.textWidget()._qtWidget().editingFinished.emit() + w.textWidget()._qtWidget().editingFinished.emit() - self.assertEqual( n["user"]["p1"].getValue(), "" ) - self.assertEqual( n["user"]["p2"].getValue(), "" ) + self.assertEqual( n["user"]["p1"].getValue(), "" ) + self.assertEqual( n["user"]["p2"].getValue(), "" ) def testExceptionHandling( self ) : @@ -151,10 +155,11 @@ def testExceptionHandling( self ) : # We want that to be reflected in the UI. - w = GafferUI.StringPlugValueWidget( script["n"]["p"] ) - GafferUITest.PlugValueWidgetTest.waitForUpdate( w ) - self.assertEqual( w.textWidget().getText(), "" ) - self.assertTrue( w.textWidget().getErrored() ) + with GafferUITest.PlugValueWidgetTest.WidgetUpdateHandler() as handler : + w = GafferUI.StringPlugValueWidget( script["n"]["p"] ) + handler.waitForUpdate( w ) + self.assertEqual( w.textWidget().getText(), "" ) + self.assertTrue( w.textWidget().getErrored() ) # And we don't want the widget to live beyond its natural life # due to reference cycles introduced by exception handling.