def test(self):

        # Make a few input scenes

        script = Gaffer.ScriptNode()

        script["Cube"] = GafferScene.Cube("Cube")
        script["Group"] = GafferScene.Group("Group")
        script["Group"]["in"][0].setInput(script["Cube"]["out"])
        script["Group"]["transform"]["translate"].setValue(imath.V3f(30))
        script["PathFilter"] = GafferScene.PathFilter("PathFilter")
        script["PathFilter"]["paths"].setValue(
            IECore.StringVectorData(['/group/cube']))
        script["Transform"] = GafferScene.Transform("Transform")
        script["Transform"]["in"].setInput(script["Group"]["out"])
        script["Transform"]["filter"].setInput(script["PathFilter"]["out"])
        script["CustomAttributes"] = GafferScene.CustomAttributes(
            "CustomAttributes")
        script["CustomAttributes"]["in"].setInput(script["Transform"]["out"])
        script["CustomAttributes"]["filter"].setInput(
            script["PathFilter"]["out"])
        script['CustomAttributes']['attributes'].addChild(
            Gaffer.NameValuePlug("existingAttr", IECore.StringData("test")))
        script["CollectTransforms"] = GafferScene.CollectTransforms(
            "CollectTransforms")
        script["CollectTransforms"]["in"].setInput(
            script["CustomAttributes"]["out"])
        script["CollectTransforms"]["filter"].setInput(
            script["PathFilter"]["out"])
        script["Expression1"] = Gaffer.Expression("Expression1")
        script["Expression1"].setExpression(
            """
s = context.get( "collect:transformName", "attr0" )
i = int( s[4] )
parent["Transform"]["transform"]["translate"] = imath.V3f( i )
""", "python")

        ref = IECore.CompoundObject()
        ref["existingAttr"] = IECore.StringData("test")

        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        script["CollectTransforms"]["attributes"].setValue(
            IECore.StringVectorData(['attr1', 'attr2']))

        ref["attr1"] = IECore.M44fData(imath.M44f().translate(imath.V3f(1)))
        ref["attr2"] = IECore.M44fData(imath.M44f().translate(imath.V3f(2)))
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        # Switch to writing custom variable, which expression doesn't use, so we don't get
        # special transforms
        script["CollectTransforms"]["attributeContextVariable"].setValue(
            "collect:customVar")

        ref["attr1"] = IECore.M44fData(imath.M44f().translate(imath.V3f(0)))
        ref["attr2"] = IECore.M44fData(imath.M44f().translate(imath.V3f(0)))
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        # Test requireVariation
        script["CollectTransforms"]["requireVariation"].setValue(True)
        del ref["attr1"]
        del ref["attr2"]
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        # Test reading custom variable
        script["Expression1"].setExpression(
            """
s =  context.get( "collect:customVar", "attr0" )
i = int( s[4] )
parent["Transform"]["transform"]["translate"] = imath.V3f( i )
""", "python")

        ref["attr1"] = IECore.M44fData(imath.M44f().translate(imath.V3f(1)))
        ref["attr2"] = IECore.M44fData(imath.M44f().translate(imath.V3f(2)))
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        # Test space
        script["CollectTransforms"]["space"].setValue(
            GafferScene.Transform.Space.World)
        ref["attr1"] = IECore.M44fData(imath.M44f().translate(imath.V3f(31)))
        ref["attr2"] = IECore.M44fData(imath.M44f().translate(imath.V3f(32)))
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        script["CollectTransforms"]["attributeContextVariable"].setValue(
            "collect:bogus")
        del ref["attr1"]
        del ref["attr2"]
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        script["CollectTransforms"]["requireVariation"].setValue(False)
        ref["attr1"] = IECore.M44fData(imath.M44f().translate(imath.V3f(30)))
        ref["attr2"] = IECore.M44fData(imath.M44f().translate(imath.V3f(30)))
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        script["CollectTransforms"]["space"].setValue(
            GafferScene.Transform.Space.Local)

        # Test overwriting existing attribute
        script["Expression1"].setExpression("", "python")
        script["CollectTransforms"]["requireVariation"].setValue(False)
        script["CollectTransforms"]["attributes"].setValue(
            IECore.StringVectorData(['existingAttr']))
        ref = IECore.CompoundObject()
        ref["existingAttr"] = IECore.M44fData(imath.M44f().translate(
            imath.V3f(0)))
        self.assertEqual(
            script["CollectTransforms"]["out"].attributes("/group/cube"), ref)

        # Test naughtily pulling directly on the "transforms" plug
        self.assertEqual(script["CollectTransforms"]["transforms"].getValue(),
                         IECore.CompoundObject())

        context = Gaffer.Context()
        context.set("scene:path",
                    IECore.InternedStringVectorData(["group", "cube"]))
        with context:
            self.assertEqual(
                script["CollectTransforms"]["transforms"].getValue(), ref)
示例#2
0
    def testDynamicPlugsAndGIL(self):

        script = Gaffer.ScriptNode()

        script["plane"] = GafferScene.Plane()
        script["plane"]["divisions"].setValue(IECore.V2i(20))

        script["sphere"] = GafferScene.Sphere()

        script["expression"] = Gaffer.Expression()
        script["expression"].setExpression(
            "parent['sphere']['radius'] = context.getFrame() + float( context['instancer:id'] )"
        )

        script["instancer"] = GafferScene.Instancer()
        script["instancer"]["in"].setInput(script["plane"]["out"])
        script["instancer"]["instance"].setInput(script["sphere"]["out"])
        script["instancer"]["parent"].setValue("/plane")

        script["attributes"] = GafferScene.CustomAttributes()
        script["attributes"]["in"].setInput(script["instancer"]["out"])

        script["outputs"] = GafferScene.Outputs()
        script["outputs"]["in"].setInput(script["attributes"]["out"])

        # Simulate an InteractiveRender or Viewer traversal of the scene
        # every time it is dirtied. If the GIL isn't released when dirtiness
        # is signalled, we'll end up with a deadlock as the traversal enters
        # python on another thread to evaluate the expression. We increment the frame
        # between each test to ensure the expression result is not cached and
        # we do truly enter python.
        traverseConnection = Gaffer.ScopedConnection(
            GafferSceneTest.connectTraverseSceneToPlugDirtiedSignal(
                script["outputs"]["out"]))
        with Gaffer.Context() as c:

            c.setFrame(1)
            script["attributes"]["attributes"].addMember(
                "test1", IECore.IntData(10))

            c.setFrame(2)
            script["attributes"]["attributes"].addOptionalMember(
                "test2", IECore.IntData(20))

            c.setFrame(3)
            script["attributes"]["attributes"].addMembers(
                IECore.CompoundData({
                    "test3": 30,
                    "test4": 40,
                }))

            c.setFrame(4)
            p = script["attributes"]["attributes"][0]
            del script["attributes"]["attributes"][p.getName()]

            c.setFrame(5)
            script["attributes"]["attributes"].addChild(p)

            c.setFrame(6)
            script["attributes"]["attributes"].removeChild(p)

            c.setFrame(7)
            script["attributes"]["attributes"].setChild(p.getName(), p)

            c.setFrame(8)
            script["attributes"]["attributes"].removeChild(p)

            c.setFrame(9)
            script["attributes"]["attributes"][p.getName()] = p

            c.setFrame(10)
            script["outputs"].addOutput(
                "test", IECoreScene.Display("beauty.exr", "exr", "rgba"))
示例#3
0
	def testNullPaths( self ) :

		f = GafferScene.PathFilter()
		with Gaffer.Context() as c :
			c["scene:path"] = IECore.InternedStringVectorData( [ "a" ] )
			self.assertEqual( f["out"].getValue(), int( IECore.PathMatcher.Result.NoMatch ) )
示例#4
0
	def __testExtension( self, ext, formatName, options = {}, metadataToIgnore = [] ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.__rgbFilePath+".exr" )

		expectedFile = self.__rgbFilePath+"."+ext

		tests = [ {
			'name': "default",
			'plugs': {},
			'metadata': options.get( "metadata", {} ),
			'maxError': options.get( "maxError", 0.0 ) } ]

		for optPlugName in options['plugs'] :
			for optPlugVal in options['plugs'][optPlugName] :
				name = "{}_{}".format(optPlugName, optPlugVal['value'])
				optMetadata = dict(options.get( "metadata", {} ))
				optMetadata.update( optPlugVal.get( "metadata", {} ) )
				tests.append( {
					'name': name,
					'plugs': { optPlugName: optPlugVal['value'] },
					'metadata': optMetadata,
					'maxError': optPlugVal.get( "maxError", options['maxError'] ) } )

		for test in tests:
			name = test['name']
			maxError = test['maxError']
			overrideMetadata = test['metadata']
			testFile = self.__testFile( name, "RGBA", ext )

			self.failIf( os.path.exists( testFile ), "Temporary file already exists : {}".format( testFile ) )

			# Setup the writer.
			w = GafferImage.ImageWriter()
			w["in"].setInput( r["out"] )
			w["fileName"].setValue( testFile )
			w["channels"].setValue( IECore.StringVectorData( r["out"]["channelNames"].getValue() ) )

			for opt in test['plugs']:
				w[formatName][opt].setValue( test['plugs'][opt] )

			# Execute
			with Gaffer.Context() :
				w["task"].execute()
			self.failUnless( os.path.exists( testFile ), "Failed to create file : {} ({}) : {}".format( ext, name, testFile ) )

			# Check the output.
			expectedOutput = GafferImage.ImageReader()
			expectedOutput["fileName"].setValue( expectedFile )

			writerOutput = GafferImage.ImageReader()
			writerOutput["fileName"].setValue( testFile )

			expectedMetadata = expectedOutput["out"]["metadata"].getValue()
			writerMetadata = writerOutput["out"]["metadata"].getValue()
			# they were written at different times so
			# we can't expect those values to match
			if "DateTime" in writerMetadata :
				expectedMetadata["DateTime"] = writerMetadata["DateTime"]

			# the writer adds several standard attributes that aren't in the original file
			expectedMetadata["Software"] = IECore.StringData( "Gaffer " + Gaffer.About.versionString() )
			expectedMetadata["HostComputer"] = IECore.StringData( platform.node() )
			expectedMetadata["Artist"] = IECore.StringData( os.getlogin() )
			expectedMetadata["DocumentName"] = IECore.StringData( "untitled" )

			for key in overrideMetadata :
				expectedMetadata[key] = overrideMetadata[key]

			# some formats support IPTC standards, and some of the standard metadata
			# is translated automatically by OpenImageIO.
			for key in writerMetadata.keys() :
				if key.startswith( "IPTC:" ) :
					expectedMetadata["IPTC:OriginatingProgram"] = expectedMetadata["Software"]
					expectedMetadata["IPTC:Creator"] = expectedMetadata["Artist"]
					break

			# some input files don't contain all the metadata that the ImageWriter
			# will create, and some output files don't support all the metadata
			# that the ImageWriter attempt to create.
			for metaName in metadataToIgnore :
				if metaName in writerMetadata :
					del writerMetadata[metaName]
				if metaName in expectedMetadata :
					del expectedMetadata[metaName]

			for metaName in expectedMetadata.keys() :
				self.assertTrue( metaName in writerMetadata.keys(), "Writer Metadata missing expected key \"{}\" set to \"{}\" : {} ({})".format(metaName, str(expectedMetadata[metaName]), ext, name) )
				self.assertEqual( expectedMetadata[metaName], writerMetadata[metaName], "Metadata does not match for key \"{}\" : {} ({})".format(metaName, ext, name) )

			op = IECore.ImageDiffOp()
			op["maxError"].setValue( maxError )
			res = op(
				imageA = expectedOutput["out"].image(),
				imageB = writerOutput["out"].image()
			)

			if res.value :
				matchingError = 0.0
				for i in range( 10 ) :
					maxError += 0.1
					op["maxError"].setValue( maxError )
					res = op(
						imageA = expectedOutput["out"].image(),
						imageB = writerOutput["out"].image()
					)

					if not res.value :
						matchingError = maxError
						break

				if matchingError > 0.0 :
					self.assertFalse( True, "Image data does not match : {} ({}). Matches with max error of {}".format( ext, name, matchingError ) )
				else:
					self.assertFalse( True, "Image data does not match : {} ({}).".format( ext, name ) )
示例#5
0
	def __testExtension( self, ext, formatName, options = {}, metadataToIgnore = [] ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.__rgbFilePath+".exr" )
		expectedFile = "{base}.{ext}".format( base=self.__rgbFilePath, ext=ext )

		tests = [ {
			'name': "default",
			'plugs': {},
			'metadata': options.get( "metadata", {} ),
			'maxError': options.get( "maxError", 0.0 ) } ]

		for optPlugName in options['plugs'] :
			for optPlugVal in options['plugs'][optPlugName] :
				name = "{}_{}".format(optPlugName, optPlugVal['value'])
				optMetadata = dict(options.get( "metadata", {} ))
				optMetadata.update( optPlugVal.get( "metadata", {} ) )
				tests.append( {
					'name': name,
					'plugs': { optPlugName: optPlugVal['value'] },
					'metadata': optMetadata,
					'maxError': optPlugVal.get( "maxError", options['maxError'] ) } )

		for test in tests:

			name = test['name']
			maxError = test['maxError']
			overrideMetadata = test['metadata']
			testFile = self.__testFile( name, "RGBA", ext )

			self.failIf( os.path.exists( testFile ), "Temporary file already exists : {}".format( testFile ) )

			# Setup the writer.
			w = GafferImage.ImageWriter()
			w["in"].setInput( r["out"] )
			w["fileName"].setValue( testFile )
			w["channels"].setValue( "*" )

			for opt in test['plugs']:
				w[formatName][opt].setValue( test['plugs'][opt] )

			# Execute
			with Gaffer.Context() :
				w["task"].execute()
			self.failUnless( os.path.exists( testFile ), "Failed to create file : {} ({}) : {}".format( ext, name, testFile ) )

			# Check the output.
			expectedOutput = GafferImage.ImageReader()
			expectedOutput["fileName"].setValue( expectedFile )

			writerOutput = GafferImage.ImageReader()
			writerOutput["fileName"].setValue( testFile )

			expectedMetadata = expectedOutput["out"]["metadata"].getValue()
			writerMetadata = writerOutput["out"]["metadata"].getValue()
			# they were written at different times so
			# we can't expect those values to match
			if "DateTime" in writerMetadata :
				expectedMetadata["DateTime"] = writerMetadata["DateTime"]

			# the writer adds several standard attributes that aren't in the original file
			expectedMetadata["Software"] = IECore.StringData( "Gaffer " + Gaffer.About.versionString() )
			expectedMetadata["HostComputer"] = IECore.StringData( platform.node() )
			expectedMetadata["Artist"] = IECore.StringData( os.environ["USER"] )
			expectedMetadata["DocumentName"] = IECore.StringData( "untitled" )

			for key in overrideMetadata :
				expectedMetadata[key] = overrideMetadata[key]

			self.__addExpectedIPTCMetadata( writerMetadata, expectedMetadata )

			for metaName in expectedMetadata.keys() :
				if metaName in metadataToIgnore :
					continue
				if metaName in ( "fileFormat", "dataType" ) :
					# These are added on automatically by the ImageReader, and
					# we can't expect them to be the same when converting between
					# image formats.
					continue
				self.assertTrue( metaName in writerMetadata.keys(), "Writer Metadata missing expected key \"{}\" set to \"{}\" : {} ({})".format(metaName, str(expectedMetadata[metaName]), ext, name) )
				self.assertEqual( expectedMetadata[metaName], writerMetadata[metaName], "Metadata does not match for key \"{}\" : {} ({})".format(metaName, ext, name) )

			self.assertImagesEqual( expectedOutput["out"], writerOutput["out"], maxDifference = maxError, ignoreMetadata = True )
示例#6
0
    def testAllRenderedSignal(self):
        class AllRenderedTest(object):
            allRenderedSignalCalled = False

            def allRendered(self):
                self.allRenderedSignalCalled = True

        script = Gaffer.ScriptNode()
        script["plane"] = GafferScene.Plane()

        # test creating/deleting a single procedural:
        t = AllRenderedTest()
        allRenderedConnection = GafferScene.SceneProcedural.allRenderedSignal(
        ).connect(t.allRendered)

        procedural = GafferScene.SceneProcedural(script["plane"]["out"],
                                                 Gaffer.Context(), "/")
        self.assertEqual(t.allRenderedSignalCalled, False)
        del procedural
        self.assertEqual(t.allRenderedSignalCalled, True)

        # create/delete two of 'em:
        t = AllRenderedTest()
        allRenderedConnection = GafferScene.SceneProcedural.allRenderedSignal(
        ).connect(t.allRendered)
        procedural1 = GafferScene.SceneProcedural(script["plane"]["out"],
                                                  Gaffer.Context(), "/")
        procedural2 = GafferScene.SceneProcedural(script["plane"]["out"],
                                                  Gaffer.Context(), "/")

        self.assertEqual(t.allRenderedSignalCalled, False)
        del procedural1
        self.assertEqual(t.allRenderedSignalCalled, False)
        del procedural2
        self.assertEqual(t.allRenderedSignalCalled, True)

        # now actually render them:
        renderer = IECore.CapturingRenderer()
        t = AllRenderedTest()
        allRenderedConnection = GafferScene.SceneProcedural.allRenderedSignal(
        ).connect(t.allRendered)
        procedural1 = GafferScene.SceneProcedural(script["plane"]["out"],
                                                  Gaffer.Context(), "/")
        procedural2 = GafferScene.SceneProcedural(script["plane"]["out"],
                                                  Gaffer.Context(), "/")

        self.assertEqual(t.allRenderedSignalCalled, False)
        with IECore.WorldBlock(renderer):
            renderer.procedural(procedural1)

        self.assertEqual(t.allRenderedSignalCalled, False)
        with IECore.WorldBlock(renderer):
            renderer.procedural(procedural2)

        self.assertEqual(t.allRenderedSignalCalled, True)

        # now render one and delete one:
        renderer = IECore.CapturingRenderer()
        t = AllRenderedTest()
        allRenderedConnection = GafferScene.SceneProcedural.allRenderedSignal(
        ).connect(t.allRendered)
        procedural1 = GafferScene.SceneProcedural(script["plane"]["out"],
                                                  Gaffer.Context(), "/")
        procedural2 = GafferScene.SceneProcedural(script["plane"]["out"],
                                                  Gaffer.Context(), "/")

        self.assertEqual(t.allRenderedSignalCalled, False)
        del procedural1
        self.assertEqual(t.allRenderedSignalCalled, False)
        with IECore.WorldBlock(renderer):
            renderer.procedural(procedural2)

        self.assertEqual(t.allRenderedSignalCalled, True)
示例#7
0
class NodeToolbar( GafferUI.Widget ) :

	__fallbackContext = Gaffer.Context()

	def __init__( self, node, topLevelWidget, **kw ) :

		GafferUI.Widget.__init__( self, topLevelWidget, **kw )

		self.__node = node

		scriptNode = self.__node.scriptNode()
		self.__context = scriptNode.context() if scriptNode is not None else self.__fallbackContext

	## Returns the node the toolbar represents.
	def node( self ) :

		return self.__node

	def getContext( self ) :

		return self.__context

	def setContext( self, context ) :

		self.__context = context

	## Creates a NodeToolbar instance for the specified node and edge.
	# Note that not all nodes have toolbars, so None may be returned.
	@classmethod
	def create( cls, node, edge = GafferUI.Edge.Top ) :

		# Try to create a toolbar using metadata.
		toolbarType = Gaffer.Metadata.value( node, "nodeToolbar:%s:type" % str( edge ).lower() )
		if toolbarType is not None :
			if toolbarType == "" :
				return None
			path = toolbarType.split( "." )
			toolbarClass = __import__( path[0] )
			for n in path[1:] :
				toolbarClass = getattr( toolbarClass, n )
			return toolbarClass( node )

		# Fall back to deprecated registry.
		if edge == GafferUI.Edge.Top :
			nodeHierarchy = IECore.RunTimeTyped.baseTypeIds( node.typeId() )
			for typeId in [ node.typeId() ] + nodeHierarchy :
				creator = cls.__creators.get( typeId, None )
				if creator is not None :
					return creator( node )

		return None

	__creators = {}
	## Registers a subclass of NodeToolbar to be used with a specific node type.
	## \deprecated. Use "nodeToolbar:top|bottom|left|right:type" metadata instead.
	@classmethod
	def registerCreator( cls, nodeClassOrTypeId, toolbarCreator ) :

		assert( callable( toolbarCreator ) )

		if isinstance( nodeClassOrTypeId, IECore.TypeId ) :
			nodeTypeId = nodeClassOrTypeId
		else :
			nodeTypeId = nodeClassOrTypeId.staticTypeId()

		cls.__creators[nodeTypeId] = toolbarCreator
示例#8
0
    def testHash(self):

        c1 = Gaffer.Context()
        c1.setFrame(1)
        c2 = Gaffer.Context()
        c2.setFrame(2)
        c3 = Gaffer.Context()
        c3.setFrame(3.0)

        # Hashes that don't use the context are equivalent
        n = GafferDispatchTest.LoggingTaskNode()
        with c1:
            c1h = n["task"].hash()
        with c2:
            c2h = n["task"].hash()
        with c3:
            c3h = n["task"].hash()

        self.assertEqual(c1h, c2h)
        self.assertEqual(c1h, c3h)

        # Hashes that do use the context differ
        n2 = GafferDispatchTest.LoggingTaskNode()
        n2["frameSensitivePlug"] = Gaffer.StringPlug(defaultValue="####")
        with c1:
            c1h = n2["task"].hash()
        with c2:
            c2h = n2["task"].hash()
        with c3:
            c3h = n2["task"].hash()

        self.assertNotEqual(c1h, c2h)
        self.assertNotEqual(c1h, c3h)

        # Hashes match across the same node type
        n3 = GafferDispatchTest.LoggingTaskNode()
        n3["frameSensitivePlug"] = Gaffer.StringPlug(defaultValue="####")
        with c1:
            c1h2 = n3["task"].hash()
        with c2:
            c2h2 = n3["task"].hash()
        with c3:
            c3h2 = n3["task"].hash()

        self.assertEqual(c1h, c1h2)
        self.assertEqual(c2h, c2h2)
        self.assertEqual(c3h, c3h2)

        # Hashes differ across different node types
        class MyNode(GafferDispatchTest.LoggingTaskNode):
            def __init__(self):
                GafferDispatchTest.LoggingTaskNode.__init__(self)

        IECore.registerRunTimeTyped(MyNode)

        n4 = MyNode()
        n4["frameSensitivePlug"] = Gaffer.StringPlug(defaultValue="####")
        with c1:
            c1h3 = n4["task"].hash()
        with c2:
            c2h3 = n4["task"].hash()
        with c3:
            c3h3 = n4["task"].hash()

        self.assertNotEqual(c1h3, c1h2)
        self.assertNotEqual(c2h3, c2h2)
        self.assertNotEqual(c3h3, c3h2)
示例#9
0
	def testRequirementsOverride( self ) :

		class SelfRequiringNode( Gaffer.ExecutableNode ) :

			def __init__( self ) :

				Gaffer.ExecutableNode.__init__( self )
				
				self.addChild( Gaffer.IntPlug( "multiplier", defaultValue = 1 ) )
				
				self.preExecutionCount = 0
				self.mainExecutionCount = 0

			def requirements( self, context ) :
				
				if context.get( "selfExecutingNode:preExecute", None ) is None :
					
					customContext = Gaffer.Context( context )
					customContext["selfExecutingNode:preExecute"] = True
					requirements = [ Gaffer.ExecutableNode.Task( self, customContext ) ]
				
				else :
					
					# We need to evaluate our external requirements as well,
					# and they need to be requirements of our preExecute task
					# only, since that is the topmost branch of our internal
					# requirement graph. We also need to use a Context which
					# does not contain our internal preExecute entry, incase
					# that has meaning for any of our external requirements.
					customContext = Gaffer.Context( context )
					del customContext["selfExecutingNode:preExecute"]
					requirements = Gaffer.ExecutableNode.requirements( self, customContext )
				
				return requirements

			def hash( self, context ) :

				h = Gaffer.ExecutableNode.hash( self, context )
				h.append( context.get( "selfExecutingNode:preExecute", False ) )
				h.append( self["multiplier"].hash() )
				return h

			def execute( self ) :
				
				if Gaffer.Context.current().get( "selfExecutingNode:preExecute", False ) :
					self.preExecutionCount += self["multiplier"].getValue()
				else :
					self.mainExecutionCount += self["multiplier"].getValue()
		
		IECore.registerRunTimeTyped( SelfRequiringNode )

		s = Gaffer.ScriptNode()
		s["e1"] = SelfRequiringNode()
		s["e2"] = SelfRequiringNode()
		s["e2"]["requirements"][0].setInput( s["e1"]['requirement'] )

		c1 = s.context()
		c2 = Gaffer.Context( s.context() )
		c2["selfExecutingNode:preExecute"] = True

		# e2 requires itself with a different context
		self.assertEqual( s["e2"].requirements( c1 ), [ Gaffer.ExecutableNode.Task( s["e2"], c2 ) ] )
		# e2 in the other context requires e1 with the original context
		self.assertEqual( s["e2"].requirements( c2 ), [ Gaffer.ExecutableNode.Task( s["e1"], c1 ) ] )
		# e1 requires itself with a different context
		self.assertEqual( s["e1"].requirements( c1 ), [ Gaffer.ExecutableNode.Task( s["e1"], c2 ) ] )
		# e1 in the other context has no requirements
		self.assertEqual( s["e1"].requirements( c2 ), [] )
		
		self.assertEqual( s["e1"].preExecutionCount, 0 )
		self.assertEqual( s["e1"].mainExecutionCount, 0 )
		self.assertEqual( s["e2"].preExecutionCount, 0 )
		self.assertEqual( s["e2"].mainExecutionCount, 0 )
		
		dispatcher = Gaffer.Dispatcher.create( "testDispatcher" )

		dispatcher.dispatch( [ s["e1"] ] )
		self.assertEqual( s["e1"].preExecutionCount, 1 )
		self.assertEqual( s["e1"].mainExecutionCount, 1 )
		self.assertEqual( s["e2"].preExecutionCount, 0 )
		self.assertEqual( s["e2"].mainExecutionCount, 0 )
		
		dispatcher.dispatch( [ s["e2"] ] )
		self.assertEqual( s["e1"].preExecutionCount, 2 )
		self.assertEqual( s["e1"].mainExecutionCount, 2 )
		self.assertEqual( s["e2"].preExecutionCount, 1 )
		self.assertEqual( s["e2"].mainExecutionCount, 1 )
示例#10
0
        def __init__(self, **kw):

            GafferUI.TextWidget.__init__(self, **kw)

            self.__context = Gaffer.Context()
示例#11
0
	def testDefaultFormat( self ) :

		text = GafferImage.Text()
		with Gaffer.Context() as c :
			GafferImage.FormatPlug().setDefaultFormat( c, GafferImage.Format( 100, 200, 2 ) )
			self.assertEqual( text["out"]["format"].getValue(), GafferImage.Format( 100, 200, 2 ) )
示例#12
0
	def testFrameRangeMask( self ) :

		testSequence = IECore.FileSequence( self.temporaryDirectory() + "/incompleteSequence.####.exr" )
		shutil.copyfile( self.fileName, testSequence.fileNameForFrame( 1 ) )
		shutil.copyfile( self.fileName, testSequence.fileNameForFrame( 3 ) )
		shutil.copyfile( self.offsetDataWindowFileName, testSequence.fileNameForFrame( 5 ) )
		shutil.copyfile( self.offsetDataWindowFileName, testSequence.fileNameForFrame( 7 ) )

		reader = GafferImage.ImageReader()
		reader["fileName"].setValue( testSequence.fileName )
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Hold )

		oiio = GafferImage.OpenImageIOReader()
		oiio["fileName"].setValue( testSequence.fileName )
		oiio["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Hold )

		context = Gaffer.Context()

		# make sure the tile we're comparing isn't black
		# so we can tell if BlackOutside is working.
		blackTile = IECore.FloatVectorData( [ 0 ] * GafferImage.ImagePlug.tileSize() * GafferImage.ImagePlug.tileSize() )
		with context :
			for i in range( 1, 11 ) :
				context.setFrame( i )
				self.assertNotEqual( reader["out"].channelData( "R", imath.V2i( 0 ) ), blackTile )

		def assertBlack() :

			# format and data window still match
			self.assertEqual( reader["out"]["format"].getValue(), oiio["out"]["format"].getValue() )
			self.assertEqual( reader["out"]["dataWindow"].getValue(), oiio["out"]["dataWindow"].getValue() )
			self.assertNotEqual( GafferImage.ImageAlgo.image( reader["out"] ), GafferImage.ImageAlgo.image( oiio["out"] ) )
			# the metadata and channel names are at the defaults
			self.assertEqual( reader["out"]["metadata"].getValue(), reader["out"]["metadata"].defaultValue() )
			self.assertEqual( reader["out"]["channelNames"].getValue(), reader["out"]["channelNames"].defaultValue() )
			# channel data is black
			self.assertEqual( reader["out"].channelData( "R", imath.V2i( 0 ) ), blackTile )

		def assertMatch() :

			self.assertEqual( reader["out"]["format"].getValue(), oiio["out"]["format"].getValue() )
			self.assertEqual( reader["out"]["dataWindow"].getValue(), oiio["out"]["dataWindow"].getValue() )
			self.assertEqual( reader["out"]["metadata"].getValue(), oiio["out"]["metadata"].getValue() )
			self.assertEqual( reader["out"]["channelNames"].getValue(), oiio["out"]["channelNames"].getValue() )
			self.assertEqual( reader["out"].channelData( "R", imath.V2i( 0 ) ), oiio["out"].channelData( "R", imath.V2i( 0 ) ) )
			self.assertImagesEqual( reader["out"], oiio["out"] )

		def assertHold( holdFrame ) :

			context = Gaffer.Context()
			context.setFrame( holdFrame )
			with context :
				holdImage = GafferImage.ImageAlgo.image( reader["out"] )
				holdFormat = reader["out"]["format"].getValue()
				holdDataWindow = reader["out"]["dataWindow"].getValue()
				holdMetadata = reader["out"]["metadata"].getValue()
				holdChannelNames = reader["out"]["channelNames"].getValue()
				holdTile = reader["out"].channelData( "R", imath.V2i( 0 ) )

			self.assertEqual( reader["out"]["format"].getValue(), holdFormat )
			self.assertEqual( reader["out"]["dataWindow"].getValue(), holdDataWindow )
			self.assertEqual( reader["out"]["metadata"].getValue(), holdMetadata )
			self.assertEqual( reader["out"]["channelNames"].getValue(), holdChannelNames )
			self.assertEqual( reader["out"].channelData( "R", imath.V2i( 0 ) ), holdTile )
			self.assertEqual( GafferImage.ImageAlgo.image( reader["out"] ), holdImage )

		reader["start"]["frame"].setValue( 4 )
		reader["end"]["frame"].setValue( 7 )

		# frame 0 errors, match from 1-10
		reader["start"]["mode"].setValue( GafferImage.ImageReader.FrameMaskMode.None )
		reader["end"]["mode"].setValue( GafferImage.ImageReader.FrameMaskMode.None )
		with context :

			for i in range( 0, 11 ) :
				context.setFrame( i )
				assertMatch()

		# black from 0-3, match from 4-10
		reader["start"]["mode"].setValue( GafferImage.ImageReader.FrameMaskMode.BlackOutside )
		with context :

			for i in range( 0, 4 ) :
				context.setFrame( i )
				assertBlack()

			for i in range( 4, 11 ) :
				context.setFrame( i )
				assertMatch()

		# black from 0-3, match from 4-7, black from 8-10
		reader["end"]["mode"].setValue( GafferImage.ImageReader.FrameMaskMode.BlackOutside )
		with context :

			for i in range( 0, 4 ) :
				context.setFrame( i )
				assertBlack()

			for i in range( 4, 8 ) :
				context.setFrame( i )
				assertMatch()

			for i in range( 8, 11 ) :
				context.setFrame( i )
				assertBlack()

		# hold frame 4 from 0-3, match from 4-7, black from 8-10
		reader["start"]["mode"].setValue( GafferImage.ImageReader.FrameMaskMode.ClampToFrame )
		with context :

			for i in range( 0, 4 ) :
				context.setFrame( i )
				assertHold( 4 )

			for i in range( 4, 8 ) :
				context.setFrame( i )
				assertMatch()

			for i in range( 8, 11 ) :
				context.setFrame( i )
				assertBlack()

		# hold frame 4 from 0-3, match from 4-7, hold frame 7 from 8-10
		reader["end"]["mode"].setValue( GafferImage.ImageReader.FrameMaskMode.ClampToFrame )
		with context :

			for i in range( 0, 4 ) :
				context.setFrame( i )
				assertHold( 4 )

			for i in range( 4, 8 ) :
				context.setFrame( i )
				assertMatch()

			for i in range( 8, 11 ) :
				context.setFrame( i )
				assertHold( 7 )
示例#13
0
	def testMissingFrameMode( self ) :

		testSequence = IECore.FileSequence( self.temporaryDirectory() + "/incompleteSequence.####.exr" )
		shutil.copyfile( self.fileName, testSequence.fileNameForFrame( 1 ) )
		shutil.copyfile( self.offsetDataWindowFileName, testSequence.fileNameForFrame( 3 ) )

		reader = GafferImage.ImageReader()
		reader["fileName"].setValue( testSequence.fileName )

		oiio = GafferImage.OpenImageIOReader()
		oiio["fileName"].setValue( testSequence.fileName )

		def assertMatch() :

			self.assertEqual( reader["out"]["format"].getValue(), oiio["out"]["format"].getValue() )
			self.assertEqual( reader["out"]["dataWindow"].getValue(), oiio["out"]["dataWindow"].getValue() )
			self.assertEqual( reader["out"]["metadata"].getValue(), oiio["out"]["metadata"].getValue() )
			self.assertEqual( reader["out"]["channelNames"].getValue(), oiio["out"]["channelNames"].getValue() )

			# It is only valid to query the data inside the data window
			if not GafferImage.BufferAlgo.empty( reader["out"]["dataWindow"].getValue() ):
				self.assertEqual( reader["out"].channelData( "R", imath.V2i( 0 ) ), oiio["out"].channelData( "R", imath.V2i( 0 ) ) )
			self.assertImagesEqual( reader["out"], oiio["out"] )

		context = Gaffer.Context()

		# set to a missing frame
		context.setFrame( 2 )

		# everything throws
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Error )
		with context :
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["format"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["dataWindow"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["metadata"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["channelNames"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"].channelData, "R", imath.V2i( 0 ) )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", GafferImage.ImageAlgo.image, reader["out"] )

		# Hold mode matches OpenImageIOReader
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Hold )
		oiio["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Hold )
		with context :
			assertMatch()

		# Black mode matches OpenImageIOReader
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Black )
		oiio["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Black )
		with context :
			assertMatch()

		# set to a different missing frame
		context.setFrame( 4 )

		# Hold mode matches OpenImageIOReader
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Hold )
		oiio["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Hold )
		with context :
			assertMatch()

		# Black mode matches OpenImageIOReader
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Black )
		oiio["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Black )
		with context :
			assertMatch()

		# set to a missing frame before the start of the sequence
		context.setFrame( 0 )

		# Hold mode matches OpenImageIOReader
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Hold )
		oiio["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Hold )
		with context :
			assertMatch()

		# Black mode matches OpenImageIOReader
		reader["missingFrameMode"].setValue( GafferImage.ImageReader.MissingFrameMode.Black )
		oiio["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Black )
		with context :
			assertMatch()

		# explicit fileNames do not support MissingFrameMode
		reader["fileName"].setValue( testSequence.fileNameForFrame( 0 ) )
		reader["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Hold )
		with context :
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", GafferImage.ImageAlgo.image, reader["out"] )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["format"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["dataWindow"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["metadata"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["channelNames"].getValue )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"].channelData, "R", imath.V2i( 0 ) )

		reader["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Black )
		oiio["missingFrameMode"].setValue( GafferImage.OpenImageIOReader.MissingFrameMode.Black )
		with context :
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", GafferImage.ImageAlgo.image, reader["out"] )
			self.assertRaisesRegexp( RuntimeError, ".*incompleteSequence.*.exr.*", reader["out"]["format"].getValue )
			self.assertEqual( reader["out"]["dataWindow"].getValue(), oiio["out"]["dataWindow"].getValue() )
			self.assertEqual( reader["out"]["metadata"].getValue(), oiio["out"]["metadata"].getValue() )
			self.assertEqual( reader["out"]["channelNames"].getValue(), oiio["out"]["channelNames"].getValue() )
			self.assertTrue( GafferImage.BufferAlgo.empty( reader["out"]['dataWindow'].getValue() ) )
			self.assertTrue( GafferImage.BufferAlgo.empty( oiio["out"]['dataWindow'].getValue() ) )
示例#14
0
class PlugLayout(GafferUI.Widget):

    # We use this when we can't find a ScriptNode to provide the context.
    __fallbackContext = Gaffer.Context()

    def __init__(self,
                 parent,
                 orientation=GafferUI.ListContainer.Orientation.Vertical,
                 layoutName="layout",
                 rootSection="",
                 **kw):

        assert (isinstance(parent, (Gaffer.Node, Gaffer.Plug)))

        self.__layout = _TabLayout(orientation) if isinstance(
            parent, Gaffer.Node) else _CollapsibleLayout(orientation)

        GafferUI.Widget.__init__(self, self.__layout, **kw)

        self.__parent = parent
        self.__readOnly = False
        self.__layoutName = layoutName
        # not to be confused with __rootSection, which holds an actual _Section object
        self.__rootSectionName = rootSection

        # we need to connect to the childAdded/childRemoved signals on
        # the parent so we can update the ui when plugs are added and removed.
        self.__childAddedConnection = parent.childAddedSignal().connect(
            Gaffer.WeakMethod(self.__childAddedOrRemoved))
        self.__childRemovedConnection = parent.childRemovedSignal().connect(
            Gaffer.WeakMethod(self.__childAddedOrRemoved))

        # since our layout is driven by metadata, we must respond dynamically
        # to changes in that metadata.
        self.__metadataChangedConnection = Gaffer.Metadata.plugValueChangedSignal(
        ).connect(Gaffer.WeakMethod(self.__plugMetadataChanged))

        # and since our activations are driven by plug values, we must respond
        # when the plugs are dirtied.
        self.__plugDirtiedConnection = self.__node().plugDirtiedSignal(
        ).connect(Gaffer.WeakMethod(self.__plugDirtied))

        # frequently events that trigger a ui update come in batches, so we
        # perform the update lazily using a LazyMethod. the dirty variables
        # keep track of the work we'll need to do in the update.
        self.__layoutDirty = True
        self.__activationsDirty = True
        self.__summariesDirty = True

        # mapping from layout item to widget, where the key is either a plug or
        # the name of a custom widget (as returned by layoutOrder()).
        self.__widgets = {}
        self.__rootSection = _Section(self.__parent)

        # set up an appropriate default context in which to view the plugs.
        scriptNode = self.__node() if isinstance(
            self.__node(), Gaffer.ScriptNode) else self.__node().scriptNode()
        self.setContext(scriptNode.context(
        ) if scriptNode is not None else self.__fallbackContext)

        # schedule our first update, which will take place when we become
        # visible for the first time.
        self.__updateLazily()

    def getReadOnly(self):

        return self.__readOnly

    def setReadOnly(self, readOnly):

        if readOnly == self.getReadOnly():
            return

        self.__readOnly = readOnly
        for widget in self.__widgets.values():
            self.__applyReadOnly(widget, self.__readOnly)

    def getContext(self):

        return self.__context

    def setContext(self, context):

        self.__context = context
        self.__contextChangedConnection = self.__context.changedSignal(
        ).connect(Gaffer.WeakMethod(self.__contextChanged))

        for widget in self.__widgets.values():
            self.__applyContext(widget, context)

    ## Returns a PlugValueWidget representing the specified child plug.
    # Because the layout is built lazily on demand, this might return None due
    # to the user not having opened up the ui - in this case lazy=False may
    # be passed to force the creation of the ui.
    def plugValueWidget(self, childPlug, lazy=True):

        if not lazy:
            self.__updateLazily.flush(self)

        w = self.__widgets.get(childPlug, None)
        if w is None:
            return w
        elif isinstance(w, GafferUI.PlugValueWidget):
            return w
        else:
            return w.plugValueWidget()

    ## Returns the custom widget registered with the specified name.
    # Because the layout is built lazily on demand, this might return None due
    # to the user not having opened up the ui - in this case lazy=False may
    # be passed to force the creation of the ui.
    def customWidget(self, name, lazy=True):

        if not lazy:
            self.__updateLazily.flush(self)

        return self.__widgets.get(name)

    ## Returns the list of section names that will be used when laying
    # out the plugs of the specified parent. The sections are returned
    # in the order in which they will be created.
    @classmethod
    def layoutSections(cls,
                       parent,
                       includeCustomWidgets=False,
                       layoutName="layout"):

        d = collections.OrderedDict()
        for item in cls.layoutOrder(parent,
                                    includeCustomWidgets,
                                    layoutName=layoutName):
            sectionPath = cls.__staticSectionPath(item, parent, layoutName)
            sectionName = ".".join(sectionPath)
            d[sectionName] = 1

        return d.keys()

    ## Returns the child plugs of the parent in the order in which they
    # will be laid out, based on "<layoutName>:index" Metadata entries. If
    # includeCustomWidgets is True, then the positions of custom widgets
    # are represented by the appearance of the names of the widgets as
    # strings within the list. If a section name is specified, then the
    # result will be filtered to include only items in that section.
    @classmethod
    def layoutOrder(cls,
                    parent,
                    includeCustomWidgets=False,
                    section=None,
                    layoutName="layout",
                    rootSection=""):

        items = parent.children(Gaffer.Plug)
        items = [plug for plug in items if not plug.getName().startswith("__")]

        if includeCustomWidgets:
            for name in Gaffer.Metadata.registeredValues(parent):
                m = re.match(layoutName + ":customWidget:(.+):widgetType",
                             name)
                if m and cls.__metadataValue(parent, name):
                    items.append(m.group(1))

        itemsAndIndices = [list(x) for x in enumerate(items)]
        for itemAndIndex in itemsAndIndices:
            index = cls.__staticItemMetadataValue(itemAndIndex[1], "index",
                                                  parent, layoutName)
            if index is not None:
                index = index if index >= 0 else sys.maxint + index
                itemAndIndex[0] = index

        itemsAndIndices.sort(key=lambda x: x[0])

        if section is not None:
            sectionPath = section.split(".") if section else []
            itemsAndIndices = [
                x for x in itemsAndIndices if cls.__staticSectionPath(
                    x[1], parent, layoutName) == sectionPath
            ]

        if rootSection:
            rootSectionPath = rootSection.split("." if rootSection else [])
            itemsAndIndices = [
                x for x in itemsAndIndices
                if cls.__staticSectionPath(x[1], parent, layoutName)
                [:len(rootSectionPath)] == rootSectionPath
            ]

        return [x[1] for x in itemsAndIndices]

    @GafferUI.LazyMethod()
    def __updateLazily(self):

        self.__update()

    def __update(self):

        if self.__layoutDirty:
            self.__updateLayout()
            self.__layoutDirty = False

        if self.__activationsDirty:
            self.__updateActivations()
            self.__activationsDirty = False

        if self.__summariesDirty:
            self.__updateSummariesWalk(self.__rootSection)
            self.__summariesDirty = False

        # delegate to our layout class to create a concrete
        # layout from the section definitions.

        self.__layout.update(self.__rootSection)

    def __updateLayout(self):

        # get the items to lay out - these are a combination
        # of plugs and strings representing custom widgets.
        items = self.layoutOrder(self.__parent,
                                 includeCustomWidgets=True,
                                 layoutName=self.__layoutName,
                                 rootSection=self.__rootSectionName)

        # ditch widgets we don't need any more

        itemsSet = set(items)
        self.__widgets = {
            k: v
            for k, v in self.__widgets.items() if k in itemsSet
        }

        # ditch widgets whose metadata type has changed - we must recreate these.
        self.__widgets = {
            k: v
            for k, v in self.__widgets.items()
            if isinstance(k, str) or v is not None and Gaffer.Metadata.value(
                k, "plugValueWidget:type") == v.__plugValueWidgetType
        }

        # make (or reuse existing) widgets for each item, and sort them into
        # sections.
        rootSectionDepth = self.__rootSectionName.count(
            ".") + 1 if self.__rootSectionName else 0
        self.__rootSection.clear()
        for item in items:

            if item not in self.__widgets:
                if isinstance(item, Gaffer.Plug):
                    widget = self.__createPlugWidget(item)
                else:
                    widget = self.__createCustomWidget(item)
                self.__widgets[item] = widget
            else:
                widget = self.__widgets[item]

            if widget is None:
                continue

            section = self.__rootSection
            for sectionName in self.__sectionPath(item)[rootSectionDepth:]:
                section = section.subsection(sectionName)

            if len(section.widgets) and self.__itemMetadataValue(
                    item, "accessory"):
                row = GafferUI.ListContainer(
                    GafferUI.ListContainer.Orientation.Horizontal, spacing=4)
                row.append(section.widgets[-1])
                row.append(widget)
                section.widgets[-1] = row
            else:
                section.widgets.append(widget)

            if self.__itemMetadataValue(item, "divider"):
                section.widgets.append(
                    GafferUI.Divider(GafferUI.Divider.Orientation.Horizontal if
                                     self.__layout.orientation() == GafferUI.
                                     ListContainer.Orientation.Vertical else
                                     GafferUI.Divider.Orientation.Vertical))

    def __updateActivations(self):

        with self.getContext():
            # Must scope the context when getting activators, because they are typically
            # computed from the plug values, and may therefore trigger a compute.
            activators = self.__metadataValue(
                self.__parent, self.__layoutName + ":activators") or {}

        activators = {k: v.value
                      for k, v in activators.items()
                      }  # convert CompoundData of BoolData to dict of booleans

        def active(activatorName):

            result = True
            if activatorName:
                result = activators.get(activatorName)
                if result is None:
                    with self.getContext():
                        result = self.__metadataValue(
                            self.__parent,
                            self.__layoutName + ":activator:" + activatorName)
                    result = result if result is not None else False
                    activators[activatorName] = result

            return result

        for item, widget in self.__widgets.items():
            if widget is not None:
                widget.setEnabled(
                    active(self.__itemMetadataValue(item, "activator")))
                widget.setVisible(
                    active(
                        self.__itemMetadataValue(item, "visibilityActivator")))

    def __updateSummariesWalk(self, section):

        with self.getContext():
            # Must scope the context because summaries are typically
            # generated from plug values, and may therefore trigger
            # a compute.
            section.summary = self.__metadataValue(
                self.__parent, self.__layoutName + ":section:" +
                section.fullName + ":summary") or ""

        for subsection in section.subsections.values():
            self.__updateSummariesWalk(subsection)

    def __import(self, path):

        path = path.split(".")
        result = __import__(path[0])
        for n in path[1:]:
            result = getattr(result, n)

        return result

    def __createPlugWidget(self, plug):

        result = GafferUI.PlugValueWidget.create(plug)
        if result is None:
            return result

        if isinstance(result,
                      GafferUI.PlugValueWidget) and not result.hasLabel(
                      ) and self.__itemMetadataValue(plug, "label") != "":
            result = GafferUI.PlugWidget(result)
            if self.__layout.orientation(
            ) == GafferUI.ListContainer.Orientation.Horizontal:
                # undo the annoying fixed size the PlugWidget has applied
                # to the label.
                ## \todo Shift all the label size fixing out of PlugWidget and just fix the
                # widget here if we're in a vertical orientation.
                QWIDGETSIZE_MAX = 16777215  # qt #define not exposed by PyQt or PySide
                result.labelPlugValueWidget().label()._qtWidget(
                ).setFixedWidth(QWIDGETSIZE_MAX)

        self.__applyReadOnly(result, self.getReadOnly())
        self.__applyContext(result, self.getContext())

        # Store the metadata value that controlled the type created, so we can compare to it
        # in the future to determine if we can reuse the widget.
        result.__plugValueWidgetType = Gaffer.Metadata.value(
            plug, "plugValueWidget:type")

        return result

    def __createCustomWidget(self, name):

        widgetType = self.__itemMetadataValue(name, "widgetType")
        widgetClass = self.__import(widgetType)

        return widgetClass(self.__parent)

    def __node(self):

        return self.__parent if isinstance(
            self.__parent, Gaffer.Node) else self.__parent.node()

    @classmethod
    def __metadataValue(cls, plugOrNode, name):

        if isinstance(plugOrNode, Gaffer.Node):
            return Gaffer.Metadata.value(plugOrNode, name)
        else:
            return Gaffer.Metadata.value(plugOrNode, name)

    @classmethod
    def __staticItemMetadataValue(cls, item, name, parent, layoutName):

        if isinstance(item, Gaffer.Plug):
            v = Gaffer.Metadata.value(item, layoutName + ":" + name)
            if v is None and name in ("divider", "label"):
                # Backwards compatibility with old unprefixed metadata names.
                v = Gaffer.Metadata.value(item, name)
            return v
        else:
            return cls.__metadataValue(
                parent, layoutName + ":customWidget:" + item + ":" + name)

    def __itemMetadataValue(self, item, name):

        return self.__staticItemMetadataValue(item,
                                              name,
                                              parent=self.__parent,
                                              layoutName=self.__layoutName)

    @classmethod
    def __staticSectionPath(cls, item, parent, layoutName):

        m = None
        if isinstance(parent, Gaffer.Node):
            # Backwards compatibility with old metadata entry
            ## \todo Remove
            m = cls.__staticItemMetadataValue(item, "nodeUI:section", parent,
                                              layoutName)
            if m == "header":
                m = ""

        if m is None:
            m = cls.__staticItemMetadataValue(item, "section", parent,
                                              layoutName)

        return m.split(".") if m else []

    def __sectionPath(self, item):

        return self.__staticSectionPath(item,
                                        parent=self.__parent,
                                        layoutName=self.__layoutName)

    def __childAddedOrRemoved(self, *unusedArgs):

        # typically many children are added and removed at once, so
        # we do a lazy update so we can batch up several changes into one.
        # upheaval is over.
        self.__layoutDirty = True
        self.__updateLazily()

    def __applyReadOnly(self, widget, readOnly):

        if widget is None:
            return

        if hasattr(widget, "setReadOnly"):
            widget.setReadOnly(readOnly)
        elif isinstance(widget, GafferUI.PlugWidget):
            widget.labelPlugValueWidget().setReadOnly(readOnly)
            widget.plugValueWidget().setReadOnly(readOnly)
        elif hasattr(widget, "plugValueWidget"):
            widget.plugValueWidget().setReadOnly(readOnly)

    def __applyContext(self, widget, context):

        if hasattr(widget, "setContext"):
            widget.setContext(context)
        elif isinstance(widget, GafferUI.PlugWidget):
            widget.labelPlugValueWidget().setContext(context)
            widget.plugValueWidget().setContext(context)
        elif hasattr(widget, "plugValueWidget"):
            widget.plugValueWidget().setContext(context)

    def __plugMetadataChanged(self, nodeTypeId, plugPath, key, plug):

        parentAffected = isinstance(
            self.__parent,
            Gaffer.Plug) and Gaffer.MetadataAlgo.affectedByChange(
                self.__parent, nodeTypeId, plugPath, plug)
        childAffected = Gaffer.MetadataAlgo.childAffectedByChange(
            self.__parent, nodeTypeId, plugPath, plug)
        if not parentAffected and not childAffected:
            return

        if key in ("divider", self.__layoutName + ":divider",
                   self.__layoutName + ":index",
                   self.__layoutName + ":section",
                   self.__layoutName + ":accessory", "plugValueWidget:type"):
            # we often see sequences of several metadata changes - so
            # we schedule a lazy update to batch them into one ui update.
            self.__layoutDirty = True
            self.__updateLazily()
        elif re.match(self.__layoutName + ":section:.*:summary", key):
            self.__summariesDirty = True
            self.__updateLazily()

    def __plugDirtied(self, plug):

        if not self.visible() or plug.direction() != plug.Direction.In:
            return

        self.__activationsDirty = True
        self.__summariesDirty = True
        self.__updateLazily()

    def __contextChanged(self, context, name):

        self.__activationsDirty = True
        self.__summariesDirty = True
        self.__updateLazily()
示例#15
0
	def _run( self, args ) :

		self.__timers = collections.OrderedDict()
		self.__memory = collections.OrderedDict()

		self.__memory["Application"] = _Memory.maxRSS()

		script = Gaffer.ScriptNode()
		script["fileName"].setValue( os.path.abspath( args["script"].value ) )

		with _Timer() as loadingTimer :
			script.load( continueOnError = True )
		self.__timers["Loading"] = loadingTimer

		self.__memory["Script"] = _Memory.maxRSS() - self.__memory["Application"]

		if args["performanceMonitor"].value :
			self.__performanceMonitor = Gaffer.PerformanceMonitor()
		else :
			self.__performanceMonitor = None

		if args["contextMonitor"].value :
			contextMonitorRoot = None
			if args["contextMonitorRoot"].value :
				contextMonitorRoot = script.descendant( args["contextMonitorRoot"].value )
				if contextMonitorRoot is None :
					IECore.msg( IECore.Msg.Level.Error, "stats", "Context monitor root \"%s\" does not exist" % args["contextMonitorRoot"].value )
					return 1
			self.__contextMonitor = Gaffer.ContextMonitor( contextMonitorRoot )
		else :
			self.__contextMonitor = None

		self.__output = file( args["outputFile"].value, "w" ) if args["outputFile"].value else sys.stdout

		with Gaffer.Context( script.context() ) as context :

			context.setFrame( args["frame"].value )

			self.__writeVersion( script )

			self.__output.write( "\n" )

			self.__writeArgs( args )

			self.__output.write( "\n" )

			self.__writeSettings( script )

			self.__output.write( "\n" )

			self.__writeVariables( script )

			self.__output.write( "\n" )

			if args["nodeSummary"].value :

				self.__writeNodes( script )

			if args["scene"].value :

				self.__writeScene( script, args )

			if args["image"].value :

				self.__writeImage( script, args )

			if args["task"].value :

				self.__writeTask( script, args )

		self.__output.write( "\n" )

		self.__writeMemory()

		self.__output.write( "\n" )

		self.__writePerformance( script, args )

		self.__output.write( "\n" )

		self.__writeContext( script, args )

		self.__output.write( "\n" )

		self.__output.close()

		return 0
示例#16
0
        def traverser():

            try:
                GafferSceneTest.traverseScene(g["out"], Gaffer.Context())
            except Exception, e:
                exceptions.append(e)
示例#17
0
    def testHistory(self):

        plane = GafferScene.Plane()

        attributesFilter = GafferScene.PathFilter()
        attributesFilter["paths"].setValue(IECore.StringVectorData(["/plane"]))

        attributes = GafferScene.StandardAttributes()
        attributes["in"].setInput(plane["out"])
        attributes["filter"].setInput(attributesFilter["out"])
        attributes["attributes"].addMember("test", 10)

        group = GafferScene.Group()
        group["in"][0].setInput(attributes["out"])

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

        transform = GafferScene.Transform()
        transform["in"].setInput(group["out"])
        transform["filter"].setInput(transformFilter["out"])

        # Transform history

        with Gaffer.Context() as c:
            c.setFrame(10)
            history = GafferScene.SceneAlgo.history(
                transform["out"]["transform"], "/group/plane")

        self.assertEqual(history.scene, transform["out"])
        self.assertEqual(history.context.getFrame(), 10)
        self.assertEqual(history.context["scene:path"],
                         IECore.InternedStringVectorData(["group", "plane"]))
        self.assertEqual(len(history.predecessors), 1)

        history = history.predecessors[0]
        self.assertEqual(history.scene, group["out"])
        self.assertEqual(history.context.getFrame(), 10)
        self.assertEqual(history.context["scene:path"],
                         IECore.InternedStringVectorData(["group", "plane"]))
        self.assertEqual(len(history.predecessors), 1)

        history = history.predecessors[0]
        self.assertEqual(history.scene, plane["out"])
        self.assertEqual(history.context.getFrame(), 10)
        self.assertEqual(history.context["scene:path"],
                         IECore.InternedStringVectorData(["plane"]))
        self.assertEqual(len(history.predecessors), 0)

        # Attributes history

        with Gaffer.Context() as c:
            c.setFrame(20)
            history = GafferScene.SceneAlgo.history(
                transform["out"]["attributes"], "/group/plane")

        self.assertEqual(history.scene, group["out"])
        self.assertEqual(history.context.getFrame(), 20)
        self.assertEqual(history.context["scene:path"],
                         IECore.InternedStringVectorData(["group", "plane"]))
        self.assertEqual(len(history.predecessors), 1)

        history = history.predecessors[0]
        self.assertEqual(history.scene, attributes["out"])
        self.assertEqual(history.context.getFrame(), 20)
        self.assertEqual(history.context["scene:path"],
                         IECore.InternedStringVectorData(["plane"]))
        self.assertEqual(len(history.predecessors), 1)

        history = history.predecessors[0]
        self.assertEqual(history.scene, plane["out"])
        self.assertEqual(history.context.getFrame(), 20)
        self.assertEqual(history.context["scene:path"],
                         IECore.InternedStringVectorData(["plane"]))
        self.assertEqual(len(history.predecessors), 0)
示例#18
0
    def test(self):

        f1 = GafferScene.PathFilter()
        f1["paths"].setValue(IECore.StringVectorData([
            "/a/b/c",
        ]))

        f2 = GafferScene.PathFilter()
        f2["paths"].setValue(IECore.StringVectorData([
            "/a/b/c/e/f/g",
        ]))

        u = GafferScene.UnionFilter()
        self.assertEqual(u["out"].getValue(),
                         IECore.PathMatcher.Result.NoMatch)

        h1 = u["out"].hash()

        u["in"][0].setInput(f1["out"])
        h2 = u["out"].hash()
        self.assertNotEqual(h1, h2)

        for path in [
                "/a",
                "/b",
                "/a/b",
                "/a/b/c",
                "/a/b/c/d",
        ]:
            with Gaffer.Context() as c:
                c["scene:path"] = IECore.InternedStringVectorData(
                    path[1:].split("/"))
                self.assertEqual(u["out"].getValue(), f1["out"].getValue())

        u["in"][1].setInput(f2["out"])
        h3 = u["out"].hash()
        self.assertNotEqual(h2, h3)

        for path, result in [
            ("/a", IECore.PathMatcher.Result.DescendantMatch),
            ("/b", IECore.PathMatcher.Result.NoMatch),
            ("/a/b", IECore.PathMatcher.Result.DescendantMatch),
            ("/a/b/c", IECore.PathMatcher.Result.DescendantMatch
             | IECore.PathMatcher.Result.ExactMatch),
            ("/a/b/c/d", IECore.PathMatcher.Result.AncestorMatch),
            ("/a/b/c/e", IECore.PathMatcher.Result.AncestorMatch
             | IECore.PathMatcher.Result.DescendantMatch),
            ("/a/b/c/e/f/g", IECore.PathMatcher.Result.AncestorMatch
             | IECore.PathMatcher.Result.ExactMatch),
            ("/a/b/c/e/f/g/h", IECore.PathMatcher.Result.AncestorMatch),
        ]:
            with Gaffer.Context() as c:
                c["scene:path"] = IECore.InternedStringVectorData(
                    path[1:].split("/"))
                self.assertEqual(u["out"].getValue(), int(result))

        f2["paths"].setValue(IECore.StringVectorData([
            "/a/b",
        ]))

        h4 = u["out"].hash()
        self.assertNotEqual(h3, h4)
示例#19
0
class PlugValueWidget(GafferUI.Widget):
    class MultiplePlugsError(ValueError):
        pass

    def __init__(self, topLevelWidget, plugs, **kw):

        GafferUI.Widget.__init__(self, topLevelWidget, **kw)
        self._qtWidget().setProperty("gafferPlugValueWidget", True)

        if isinstance(plugs, Gaffer.Plug):
            plugs = {plugs}
        elif plugs is None:
            plugs = set()
        elif not isinstance(plugs, set):
            plugs = set(plugs)

        # We don't want to call `_updateFromPlugs()` yet because the derived
        # classes haven't constructed. They can call it themselves
        # upon completing construction.
        self.__setPlugsInternal(plugs, callUpdateFromPlugs=False)

        self.__readOnly = False

        self.dragEnterSignal().connect(Gaffer.WeakMethod(self.__dragEnter),
                                       scoped=False)
        self.dragLeaveSignal().connect(Gaffer.WeakMethod(self.__dragLeave),
                                       scoped=False)
        self.dropSignal().connect(Gaffer.WeakMethod(self.__drop), scoped=False)

        Gaffer.Metadata.nodeValueChangedSignal().connect(Gaffer.WeakMethod(
            self.__nodeMetadataChanged),
                                                         scoped=False)

    ## Changes the plugs displayed by this widget. May be overridden by derived classes,
    # but all implementations must call the base class version first. Note that it is
    # acceptable for `plugs` to be empty, so derived classes should be implemented with
    # this in mind.
    def setPlugs(self, plugs):

        if not isinstance(plugs, set):
            plugs = set(plugs)

        if self.setPlug.__code__ is not PlugValueWidget.setPlug.__code__:
            # Legacy subclass has overridden `setPlug()`. Implement via
            # that so that it can do whatever it needs to do.
            if len(plugs) <= 1:
                self.setPlug(next(iter(plugs), None))
            else:
                raise Exception("{} does not support multiple plugs".format(
                    self.__class__.__name__))
        else:
            self.__setPlugsInternal(plugs, callUpdateFromPlugs=True)

    def getPlugs(self):

        return self.__plugs

    ## Convenience function that calls `setPlugs()`. Note that
    # `plug` may be `None`.
    def setPlug(self, plug):

        if self.setPlug.__code__ is not PlugValueWidget.setPlug.__code__:
            # Legacy subclass has overridden `setPlug()`. Do work internally
            # to avoid recursion.
            self.__setPlugsInternal({plug} if plug is not None else set(),
                                    callUpdateFromPlugs=True)
        else:
            # Implement via `setPlugs()` so that new classes may
            # override it.
            self.setPlugs({plug} if plug is not None else set())

    ## Convenience function. Raises MultiplePlugsError if more than one plug is
    # being displayed.
    def getPlug(self):

        if len(self.__plugs) > 1:
            raise self.MultiplePlugsError()

        return next(iter(self.__plugs), None)

    ## By default, PlugValueWidgets operate in the main context held by the script node
    # for the script the plug belongs to. This function allows an alternative context
    # to be provided, making it possible to view a plug at a custom frame (or with any
    # other context modification).
    def setContext(self, context):

        assert (isinstance(context, Gaffer.Context))
        if context is self.__context:
            return

        self.__context = context
        self.__updateContextConnection()
        self._updateFromPlugs()

    def getContext(self):

        return self.__context

    ## \deprecated
    def setReadOnly(self, readOnly):

        assert (isinstance(readOnly, bool))
        if readOnly == self.__readOnly:
            return

        self.__readOnly = readOnly
        self._updateFromPlugs()

    ## \deprecated
    def getReadOnly(self):

        return self.__readOnly

    ## Should be reimplemented to return True if this widget includes
    # some sort of labelling for the plug. This is used to prevent
    # extra labels being created in the NodeUI when they're not necessary.
    def hasLabel(self):

        return False

    ## Implemented to return a tooltip containing the plug name and description.
    def getToolTip(self):

        result = GafferUI.Widget.getToolTip(self)
        if result:
            return result

        if not self.getPlugs():
            return ""

        # Name

        if len(self.getPlugs()) == 1:
            result = "# " + self.getPlug().relativeName(self.getPlug().node())
        else:
            result = "# {} plugs".format(len(self.getPlugs()))

        # Input

        if len(self.getPlugs()) == 1:
            input = self.getPlug().getInput()
            if input is not None:
                result += "\n\nInput : {}".format(
                    input.relativeName(input.commonAncestor(self.getPlug())))

        # Description

        description = sole(
            Gaffer.Metadata.value(p, "description") for p in self.getPlugs())
        if description:
            result += "\n\n" + description

        return result

    ## Because Plugs may have child Plugs, so too PlugValueWidgets may
    # have child PlugValueWidgets to represent the children of their plug.
    # This method should be reimplemented to return such children, or `None`
    # if no appropriate child exists.
    def childPlugValueWidget(self, childPlug):

        return None

    __popupMenuSignal = None
    ## This signal is emitted whenever a popup menu for a plug is about
    # to be shown. This provides an opportunity to customise the menu from
    # external code. The signature for slots is ( menuDefinition, plugValueWidget ),
    # and slots should just modify the menu definition in place.
    @classmethod
    def popupMenuSignal(cls):

        if cls.__popupMenuSignal is None:
            cls.__popupMenuSignal = _PopupMenuSignal()

        return cls.__popupMenuSignal

    ## Returns a PlugValueWidget suitable for representing the specified plugs.
    # The type of widget returned may be customised on a per-plug basis by a
    # "plugValueWidget:type" metadata value, specifying the fully qualified
    # python type name for a widget class. To suppress the creation of a widget,
    # a value of "" may be registered - in this case None will be returned from
    # create(). If useTypeOnly is True, then the metadata will be ignored and
    # only the plug type will be taken into account in creating a PlugValueWidget.
    @classmethod
    def create(cls, plugs, useTypeOnly=False):

        if isinstance(plugs, Gaffer.Plug):
            creators = {cls.__creator(plugs, useTypeOnly)}
        else:
            creators = {cls.__creator(p, useTypeOnly) for p in plugs}

        if len(creators) > 1:
            raise Exception("Multiple widget creators")

        creator = next(iter(creators))
        if creator is not None:
            return creator(plugs)

        return None

    ## Registers a PlugValueWidget type for a specific Plug type.
    @classmethod
    def registerType(cls, plugClassOrTypeId, creator):

        if isinstance(plugClassOrTypeId, IECore.TypeId):
            plugTypeId = plugClassOrTypeId
        else:
            plugTypeId = plugClassOrTypeId.staticTypeId()

        cls.__plugTypesToCreators[plugTypeId] = creator

    ## Ensures that the specified plug has a visible PlugValueWidget,
    # creating one if necessary.
    @classmethod
    def acquire(cls, plug):

        editor = GafferUI.NodeEditor.acquire(plug.node())

        plugValueWidget = editor.nodeUI().plugValueWidget(plug)
        if not plugValueWidget:
            return None

        plugValueWidget.reveal()

        return plugValueWidget

    ## Must be implemented by subclasses so that the widget reflects the current
    # status of the plugs. To temporarily suspend calls to this function, use
    # `Gaffer.BlockedConnection( self._plugConnections() )`.
    def _updateFromPlugs(self):

        # Default implementation falls back to legacy update for a single plug.
        updateFromPlug = getattr(self, "_updateFromPlug", None)
        if updateFromPlug is not None:
            updateFromPlug()

    def _plugConnections(self):

        return (self.__plugDirtiedConnections +
                self.__plugInputChangedConnections +
                [self.__plugMetadataChangedConnection])

    ## Returns True if the plug's values are editable as far as this UI is concerned
    # - that `plug.settable()` is True for all plugs and `self.getReadOnly()` is
    # False. By default, an animated plug is considered to be non-editable because
    # it has an input connection. Subclasses which support animation editing may pass
    # `canEditAnimation = True` to have animated plugs considered as editable.
    def _editable(self, canEditAnimation=False):

        if self.__readOnly or not self.getPlugs():
            return False

        for plug in self.getPlugs():

            if hasattr(plug, "settable") and not plug.settable():
                if not canEditAnimation or not Gaffer.Animation.isAnimated(
                        plug):
                    return False

            if Gaffer.MetadataAlgo.readOnly(plug):
                return False

        return True

    ## Called to convert the specified value into something
    # suitable for passing to a plug.setValue() call. Returns
    # None if no such conversion is necessary. May be reimplemented
    # by derived classes to provide more complex conversions than
    # the standard. The base class uses this method to accept drag/drop
    # and copy/paste data.
    def _convertValue(self, value):

        plugValueType = sole(
            type(p.defaultValue()) if hasattr(p, "defaultValue") else None
            for p in self.getPlugs())
        if plugValueType is None:
            return None

        if isinstance(value, plugValueType):
            return value
        elif isinstance(value, IECore.Data):

            dataValue = None
            if hasattr(value, "value"):
                dataValue = value.value
            else:
                with IECore.IgnoredExceptions(Exception):
                    if len(value) == 1:
                        dataValue = value[0]

            if dataValue is None:
                return None
            elif isinstance(dataValue, plugValueType):
                return dataValue
            else:
                with IECore.IgnoredExceptions(Exception):
                    return plugValueType(dataValue)

        return None

    ## Adds a useful popup menu to the specified widget, providing useful functions that
    # operate on the plug. The menu is populated with the result of _popupMenuDefinition(),
    # and may also be customised by external code using the popupMenuSignal().
    def _addPopupMenu(self,
                      widget=None,
                      buttons=GafferUI.ButtonEvent.Buttons.Right):

        if widget is None:
            widget = self

        # it's unclear under what circumstances we get given a right-click vs a context menu event,
        # but we try to cover all our bases by connecting to both.

        widget.buttonPressSignal().connect(functools.partial(
            Gaffer.WeakMethod(self.__buttonPress), buttonMask=buttons),
                                           scoped=False)

        if buttons & GafferUI.ButtonEvent.Buttons.Right:
            widget.contextMenuSignal().connect(functools.partial(
                Gaffer.WeakMethod(self.__contextMenu)),
                                               scoped=False)

    ## Returns a definition for the popup menu - this is called each time the menu is displayed
    # to allow for dynamic menus. Subclasses may override this method to customise the menu, but
    # should call the base class implementation first.
    def _popupMenuDefinition(self):

        menuDefinition = IECore.MenuDefinition()

        if all(hasattr(p, "getValue") for p in self.getPlugs()):

            applicationRoot = sole(
                p.ancestor(Gaffer.ApplicationRoot) for p in self.getPlugs())
            menuDefinition.append(
                "/Copy Value", {
                    "command":
                    Gaffer.WeakMethod(self.__copyValue),
                    "active":
                    len(self.getPlugs()) == 1 and applicationRoot is not None
                })

            pasteValue = None
            if applicationRoot is not None:
                pasteValue = self._convertValue(
                    applicationRoot.getClipboardContents())

            menuDefinition.append(
                "/Paste Value", {
                    "command":
                    functools.partial(Gaffer.WeakMethod(self.__setValues),
                                      pasteValue),
                    "active":
                    self._editable() and pasteValue is not None
                })

            menuDefinition.append("/CopyPasteDivider", {"divider": True})

        if any(p.getInput() is not None for p in self.getPlugs()):
            menuDefinition.append(
                "/Edit input...",
                {"command": Gaffer.WeakMethod(self.__editInputs)})
            menuDefinition.append("/EditInputDivider", {"divider": True})
            menuDefinition.append(
                "/Remove input", {
                    "command":
                    Gaffer.WeakMethod(self.__removeInputs),
                    "active":
                    not self.getReadOnly() and all(
                        p.acceptsInput(None)
                        and not Gaffer.MetadataAlgo.readOnly(p)
                        for p in self.getPlugs()),
                })
        if all(
                hasattr(p, "defaultValue")
                and p.direction() == Gaffer.Plug.Direction.In
                for p in self.getPlugs()):
            menuDefinition.append(
                "/Default", {
                    "command":
                    functools.partial(
                        Gaffer.WeakMethod(self.__setValues),
                        [p.defaultValue() for p in self.getPlugs()]),
                    "active":
                    self._editable()
                })

        if all(
                Gaffer.NodeAlgo.hasUserDefault(p)
                and p.direction() == Gaffer.Plug.Direction.In
                for p in self.getPlugs()):
            menuDefinition.append(
                "/User Default", {
                    "command": Gaffer.WeakMethod(self.__applyUserDefaults),
                    "active": self._editable()
                })

        with self.getContext():
            if any(Gaffer.NodeAlgo.presets(p) for p in self.getPlugs()):
                menuDefinition.append(
                    "/Preset", {
                        "subMenu": Gaffer.WeakMethod(self.__presetsSubMenu),
                        "active": self._editable()
                    })

        if len(menuDefinition.items()):
            menuDefinition.append("/LockDivider", {"divider": True})

        readOnly = any(
            Gaffer.MetadataAlgo.getReadOnly(p) for p in self.getPlugs())
        menuDefinition.append(
            "/Unlock" if readOnly else "/Lock", {
                "command":
                functools.partial(Gaffer.WeakMethod(self.__applyReadOnly),
                                  not readOnly),
                "active":
                not self.getReadOnly() and not any(
                    Gaffer.MetadataAlgo.readOnly(p.parent())
                    for p in self.getPlugs()),
            })

        self.popupMenuSignal()(menuDefinition, self)

        return menuDefinition

    def __plugDirtied(self, plug):

        if plug in self.__plugs:
            self._updateFromPlugs()

    def __plugInputChanged(self, plug):

        if plug in self.__plugs:
            self.__updateContextConnection()
            self._updateFromPlugs()

    def __plugMetadataChanged(self, nodeTypeId, plugPath, key, plug):

        for p in self.__plugs:
            if (Gaffer.MetadataAlgo.affectedByChange(p, nodeTypeId, plugPath,
                                                     plug)
                    or Gaffer.MetadataAlgo.readOnlyAffectedByChange(
                        p, nodeTypeId, plugPath, key, plug)):
                self._updateFromPlugs()
                return

    def __nodeMetadataChanged(self, nodeTypeId, key, node):

        for p in self.__plugs:
            if Gaffer.MetadataAlgo.readOnlyAffectedByChange(
                    p, nodeTypeId, key, node):
                self._updateFromPlugs()
                return

    def __contextChanged(self, context, key):

        self._updateFromPlugs()

    def __setPlugsInternal(self, plugs, callUpdateFromPlugs):

        assert (isinstance(plugs, set))
        if len(plugs) and sole(p.__class__ for p in plugs) is None:
            raise ValueError("Plugs have different types")

        nodes = set()
        scriptNodes = set()
        for plug in plugs:
            nodes.add(plug.node())
            scriptNodes.add(plug.ancestor(Gaffer.ScriptNode))

        assert (len(scriptNodes) <= 1)

        self.__plugs = plugs

        self.__plugDirtiedConnections = [
            node.plugDirtiedSignal().connect(
                Gaffer.WeakMethod(self.__plugDirtied)) for node in nodes
        ]
        self.__plugInputChangedConnections = [
            node.plugInputChangedSignal().connect(
                Gaffer.WeakMethod(self.__plugInputChanged)) for node in nodes
        ]

        if self.__plugs:
            self.__plugMetadataChangedConnection = Gaffer.Metadata.plugValueChangedSignal(
            ).connect(Gaffer.WeakMethod(self.__plugMetadataChanged))
        else:
            self.__plugMetadataChangedConnection = None

        scriptNode = next(iter(scriptNodes), None)
        self.__context = scriptNode.context(
        ) if scriptNode is not None else self.__fallbackContext
        self.__updateContextConnection()

        if callUpdateFromPlugs:
            self._updateFromPlugs()

    def __updateContextConnection(self):

        # We only want to be notified of context changes for plugs whose values are
        # computed.

        context = self.__context
        if all(p.source().direction() == Gaffer.Plug.Direction.In
               for p in self.getPlugs()):
            context = None

        if context is not None:
            self.__contextChangedConnection = context.changedSignal().connect(
                Gaffer.WeakMethod(self.__contextChanged))
        else:
            self.__contextChangedConnection = None

    # we use this when the plugs being viewed doesn't have a ScriptNode ancestor
    # to provide a context.
    __fallbackContext = Gaffer.Context()

    def __buttonPress(self, widget, event, buttonMask):

        if event.buttons & buttonMask:
            return self.__contextMenu()

        return False

    def __contextMenu(self, *unused):

        if not self.getPlugs():
            return False

        menuDefinition = self._popupMenuDefinition()
        if not len(menuDefinition.items()):
            return False

        if len(self.getPlugs()) == 1:
            title = self.getPlug().relativeName(self.getPlug().node())
            title = ".".join([
                IECore.CamelCase.join(IECore.CamelCase.split(x))
                for x in title.split(".")
            ])
        else:
            title = "{} plugs".format(len(self.getPlugs()))

        self.__popupMenu = GafferUI.Menu(menuDefinition, title=title)
        self.__popupMenu.popup(parent=self)

        return True

    def __copyValue(self):

        with self.getContext():
            value = self.getPlug().getValue()

        if not isinstance(value, IECore.Object):
            # Trick to get Data from a simple type - put
            # it in a CompoundData (which will convert to
            # Data automatically) and then get it back out.
            value = IECore.CompoundData({"v": value})["v"]

        self.getPlug().ancestor(
            Gaffer.ApplicationRoot).setClipboardContents(value)

    def __setValues(self, values):

        if not isinstance(values, list):
            values = itertools.repeat(values, len(self.getPlugs()))

        with Gaffer.UndoScope(
                next(iter(self.getPlugs())).ancestor(Gaffer.ScriptNode)):
            for plug, value in zip(self.getPlugs(), values):
                plug.setValue(value)

    def __editInputs(self):

        # We may have multiple inputs from the same node.
        # Choose one input plug per node to reveal.
        nodesToPlugs = {}
        for p in self.getPlugs():
            i = p.getInput()
            if i is not None:
                nodesToPlugs[i.node()] = i

        # Acquire a NodeEditor for each node, and reveal the
        # chosen plug.
        for node, plug in nodesToPlugs.items():

            nodeEditor = GafferUI.NodeEditor.acquire(node)
            if nodeEditor is None:
                continue

            plugValueWidget = nodeEditor.nodeUI().plugValueWidget(plug)
            if plugValueWidget is not None:
                plugValueWidget.reveal()

    def __removeInputs(self):

        with Gaffer.UndoScope(
                next(iter(self.getPlugs())).ancestor(Gaffer.ScriptNode)):
            for p in self.getPlugs():
                p.setInput(None)

    def __applyUserDefaults(self):

        with Gaffer.UndoScope(
                next(iter(self.getPlugs())).ancestor(Gaffer.ScriptNode)):
            for p in self.getPlugs():
                Gaffer.NodeAlgo.applyUserDefault(p)

    def __presetsSubMenu(self):

        with self.getContext():

            currentPreset = sole((Gaffer.NodeAlgo.currentPreset(p) or ""
                                  for p in self.getPlugs()))

            # Find the union of the presets across all plugs,
            # and count how many times they occur.
            presets = []
            presetCounts = collections.Counter()
            for plug in self.getPlugs():
                for preset in Gaffer.NodeAlgo.presets(plug):
                    if not presetCounts[preset]:
                        presets.append(preset)
                    presetCounts[preset] += 1

        # Build menu. We'll list every preset we found, but disable
        # any which aren't available for all plugs.
        result = IECore.MenuDefinition()
        for presetName in presets:
            menuPath = presetName if presetName.startswith(
                "/") else "/" + presetName
            result.append(
                menuPath, {
                    "command":
                    functools.partial(Gaffer.WeakMethod(self.__applyPreset),
                                      presetName),
                    "active":
                    self._editable()
                    and presetCounts[preset] == len(self.getPlugs()),
                    "checkBox":
                    presetName == currentPreset,
                })

        return result

    def __applyPreset(self, presetName, *unused):

        with self.getContext():
            with Gaffer.UndoScope(
                    next(iter(self.getPlugs())).ancestor(Gaffer.ScriptNode)):
                for p in self.getPlugs():
                    Gaffer.NodeAlgo.applyPreset(p, presetName)

    def __applyReadOnly(self, readOnly):

        with Gaffer.UndoScope(
                next(iter(self.getPlugs())).ancestor(Gaffer.ScriptNode)):
            for p in self.getPlugs():
                Gaffer.MetadataAlgo.setReadOnly(p, readOnly)

    # drag and drop stuff

    def __dragEnter(self, widget, event):

        if self.getReadOnly() or any(
                Gaffer.MetadataAlgo.readOnly(p) for p in self.getPlugs()):
            return False

        if isinstance(event.sourceWidget, GafferUI.PlugValueWidget):
            sourcePlugValueWidget = event.sourceWidget
        else:
            sourcePlugValueWidget = event.sourceWidget.ancestor(
                GafferUI.PlugValueWidget)

        if sourcePlugValueWidget is not None and sourcePlugValueWidget.getPlugs(
        ) & self.getPlugs():
            return False

        if isinstance(event.data, Gaffer.Plug):
            if all(p.direction() == Gaffer.Plug.Direction.In
                   and p.acceptsInput(event.data) for p in self.getPlugs()):
                self.setHighlighted(True)
                return True
        elif all(hasattr(p, "setValue")
                 for p in self.getPlugs()) and self._convertValue(
                     event.data) is not None:
            if all(p.settable() for p in self.getPlugs()):
                self.setHighlighted(True)
                return True

        return False

    def __dragLeave(self, widget, event):

        self.setHighlighted(False)

    def __drop(self, widget, event):

        self.setHighlighted(False)

        with Gaffer.UndoScope(next(iter(self.getPlugs())).node().scriptNode()):
            if isinstance(event.data, Gaffer.Plug):
                for p in self.getPlugs():
                    p.setInput(event.data)
            else:
                v = self._convertValue(event.data)
                for p in self.getPlugs():
                    p.setValue(v)

        return True

    # Type registry internals

    @classmethod
    def __creator(cls, plug, useTypeOnly):

        # First try to create one using a creator registered for the specific plug.
        if not useTypeOnly:

            widgetType = Gaffer.Metadata.value(plug, "plugValueWidget:type")
            if widgetType is None:
                widgetType = Gaffer.Metadata.value(plug, "layout:widgetType")
                if widgetType is not None:
                    warnings.warn(
                        "The \"layout:widgetType\" metadata entry is deprecated, use \"plugValueWidget:type\" instead.",
                        DeprecationWarning)
                    if widgetType == "None":
                        return None

            if widgetType is not None:
                if widgetType == "":
                    return None
                path = widgetType.split(".")
                widgetClass = __import__(path[0])
                for n in path[1:]:
                    widgetClass = getattr(widgetClass, n)
                return widgetClass

        # If that failed, then just create something based on the type of the plug.
        typeId = plug.typeId()
        for plugTypeId in [plug.typeId()] + IECore.RunTimeTyped.baseTypeIds(
                plug.typeId()):
            if plugTypeId in cls.__plugTypesToCreators:
                creator = cls.__plugTypesToCreators[plugTypeId]
                if creator is not None:
                    return creator
                else:
                    return None

        return None

    __plugTypesToCreators = {}
示例#20
0
    def _updateFromPlug(self):

        with Gaffer.Context():

            self.__button.setImage("soloChannel{0}.png".format(
                self.getPlug().getValue()))
示例#21
0
class PlugValueWidget(GafferUI.Widget):
    def __init__(self, topLevelWidget, plug, **kw):

        GafferUI.Widget.__init__(self, topLevelWidget, **kw)

        # we don't want to call _updateFromPlug yet because the derived
        # classes haven't constructed yet. they can call it themselves
        # upon completing construction.
        self.__setPlugInternal(plug, callUpdateFromPlug=False)

        self.__readOnly = False

        self.dragEnterSignal().connect(Gaffer.WeakMethod(self.__dragEnter),
                                       scoped=False)
        self.dragLeaveSignal().connect(Gaffer.WeakMethod(self.__dragLeave),
                                       scoped=False)
        self.dropSignal().connect(Gaffer.WeakMethod(self.__drop), scoped=False)

        Gaffer.Metadata.nodeValueChangedSignal().connect(Gaffer.WeakMethod(
            self.__nodeMetadataChanged),
                                                         scoped=False)

    ## Note that it is acceptable to pass None to setPlug() (and to the constructor)
    # and that derived classes should be implemented to cope with this eventuality.
    def setPlug(self, plug):

        self.__setPlugInternal(plug, callUpdateFromPlug=True)

    def getPlug(self):

        return self.__plug

    ## By default, PlugValueWidgets operate in the main context held by the script node
    # for the script the plug belongs to. This function allows an alternative context
    # to be provided, making it possible to view a plug at a custom frame (or with any
    # other context modification).
    def setContext(self, context):

        assert (isinstance(context, Gaffer.Context))
        if context is self.__context:
            return

        self.__context = context
        self.__updateContextConnection()
        self._updateFromPlug()

    def getContext(self):

        return self.__context

    ## This method allows editing of the plug value
    # to be disabled for this ui. Note that even when getReadOnly()
    # is False, the ui may not allow editing due to the plug
    # itself being read only for other reasons.
    def setReadOnly(self, readOnly):

        assert (isinstance(readOnly, bool))
        if readOnly == self.__readOnly:
            return

        self.__readOnly = readOnly
        self._updateFromPlug()

    def getReadOnly(self):

        return self.__readOnly

    ## Should be reimplemented to return True if this widget includes
    # some sort of labelling for the plug. This is used to prevent
    # extra labels being created in the NodeUI when they're not necessary.
    def hasLabel(self):

        return False

    ## Implemented to return a tooltip containing the plug name and description.
    def getToolTip(self):

        result = GafferUI.Widget.getToolTip(self)
        if result:
            return result

        plug = self.getPlug()
        if plug is None:
            return ""

        input = plug.getInput()

        inputText = ""
        if input is not None:
            inputText = " <- " + input.relativeName(
                input.commonAncestor(plug, Gaffer.GraphComponent))

        result = "# " + plug.relativeName(plug.node()) + inputText
        description = Gaffer.Metadata.value(plug, "description")
        if description:
            result += "\n\n" + description

        return result

    ## Because Plugs may have child Plugs, so too PlugValueWidgets may
    # have child PlugValueWidgets to represent the children of their plug.
    # This method should be reimplemented to return such children. Because
    # UIs may be built lazily on demand, the lazy flag is provided to
    # determine whether or not the query should force a build in the case
    # that one has not been performed yet.
    def childPlugValueWidget(self, childPlug, lazy=True):

        return None

    ## Ensures that the specified plug has a visible PlugValueWidget,
    # creating one if necessary.
    @classmethod
    def acquire(cls, plug):

        editor = GafferUI.NodeEditor.acquire(plug.node())

        plugValueWidget = editor.nodeUI().plugValueWidget(plug, lazy=False)
        if not plugValueWidget:
            return None

        plugValueWidget.reveal()

        return plugValueWidget

    ## Must be implemented by subclasses so that the widget reflects the current
    # status of the plug. To temporarily suspend calls to this function, use
    # Gaffer.BlockedConnection( self._plugConnections() ).
    def _updateFromPlug(self):

        raise NotImplementedError

    def _plugConnections(self):

        return [
            self.__plugDirtiedConnection,
            self.__plugInputChangedConnection,
            self.__plugFlagsChangedConnection,
            self.__plugMetadataChangedConnection,
        ]

    ## Returns True if the plug value is editable as far as this ui is concerned
    # - that plug.settable() is True and self.getReadOnly() is False. By default,
    # an animated plug is considered to be non-editable because it has an input
    # connection. Subclasses which support animation editing may pass
    # `canEditAnimation = True` to have animated plugs considered as editable.
    def _editable(self, canEditAnimation=False):

        plug = self.getPlug()

        if plug is None:
            return False

        if self.__readOnly:
            return False

        if hasattr(plug, "settable") and not plug.settable():
            if not canEditAnimation or not Gaffer.Animation.isAnimated(plug):
                return False

        if Gaffer.MetadataAlgo.readOnly(plug):
            return False

        return True

    ## Called to convert the specified value into something
    # suitable for passing to a plug.setValue() call. Returns
    # None if no such conversion is necessary. May be reimplemented
    # by derived classes to provide more complex conversions than
    # the standard. The base class uses this method to accept drag/drop
    # and copy/paste data.
    def _convertValue(self, value):

        if not hasattr(self.getPlug(), "defaultValue"):
            return None

        plugValueType = type(self.getPlug().defaultValue())
        if isinstance(value, plugValueType):
            return value
        elif isinstance(value, IECore.Data):

            dataValue = None
            if hasattr(value, "value"):
                dataValue = value.value
            else:
                with IECore.IgnoredExceptions(Exception):
                    if len(value) == 1:
                        dataValue = value[0]

            if dataValue is None:
                return None
            elif isinstance(dataValue, plugValueType):
                return dataValue
            else:
                with IECore.IgnoredExceptions(Exception):
                    return plugValueType(dataValue)

        return None

    ## Adds a useful popup menu to the specified widget, providing useful functions that
    # operate on the plug. The menu is populated with the result of _popupMenuDefinition(),
    # and may also be customised by external code using the popupMenuSignal().
    def _addPopupMenu(self,
                      widget=None,
                      buttons=GafferUI.ButtonEvent.Buttons.Right):

        if widget is None:
            widget = self

        # it's unclear under what circumstances we get given a right-click vs a context menu event,
        # but we try to cover all our bases by connecting to both.

        widget.buttonPressSignal().connect(functools.partial(
            Gaffer.WeakMethod(self.__buttonPress), buttonMask=buttons),
                                           scoped=False)

        if buttons & GafferUI.ButtonEvent.Buttons.Right:
            widget.contextMenuSignal().connect(functools.partial(
                Gaffer.WeakMethod(self.__contextMenu)),
                                               scoped=False)

    ## Returns a definition for the popup menu - this is called each time the menu is displayed
    # to allow for dynamic menus. Subclasses may override this method to customise the menu, but
    # should call the base class implementation first.
    def _popupMenuDefinition(self):

        menuDefinition = IECore.MenuDefinition()

        if hasattr(self.getPlug(), "getValue"):

            applicationRoot = self.getPlug().ancestor(Gaffer.ApplicationRoot)
            menuDefinition.append(
                "/Copy Value", {
                    "command": Gaffer.WeakMethod(self.__copyValue),
                    "active": applicationRoot is not None
                })

            pasteValue = None
            if applicationRoot is not None:
                pasteValue = self._convertValue(
                    applicationRoot.getClipboardContents())

            menuDefinition.append(
                "/Paste Value", {
                    "command":
                    functools.partial(Gaffer.WeakMethod(self.__setValue),
                                      pasteValue),
                    "active":
                    self._editable() and pasteValue is not None
                })

            menuDefinition.append("/CopyPasteDivider", {"divider": True})

        if self.getPlug().getInput() is not None:
            menuDefinition.append(
                "/Edit input...",
                {"command": Gaffer.WeakMethod(self.__editInput)})
            menuDefinition.append("/EditInputDivider", {"divider": True})
            menuDefinition.append(
                "/Remove input", {
                    "command":
                    Gaffer.WeakMethod(self.__removeInput),
                    "active":
                    self.getPlug().acceptsInput(None)
                    and not self.getReadOnly()
                    and not Gaffer.MetadataAlgo.readOnly(self.getPlug()),
                })
        if hasattr(self.getPlug(), "defaultValue") and self.getPlug(
        ).direction() == Gaffer.Plug.Direction.In:
            menuDefinition.append(
                "/Default", {
                    "command":
                    functools.partial(Gaffer.WeakMethod(self.__setValue),
                                      self.getPlug().defaultValue()),
                    "active":
                    self._editable()
                })

        if Gaffer.NodeAlgo.hasUserDefault(self.getPlug()) and self.getPlug(
        ).direction() == Gaffer.Plug.Direction.In:
            menuDefinition.append(
                "/User Default", {
                    "command": Gaffer.WeakMethod(self.__applyUserDefault),
                    "active": self._editable()
                })

        if Gaffer.NodeAlgo.presets(self.getPlug()):
            menuDefinition.append(
                "/Preset", {
                    "subMenu": Gaffer.WeakMethod(self.__presetsSubMenu),
                    "active": self._editable()
                })

        if len(menuDefinition.items()):
            menuDefinition.append("/LockDivider", {"divider": True})

        readOnly = Gaffer.MetadataAlgo.getReadOnly(self.getPlug())
        menuDefinition.append(
            "/Unlock" if readOnly else "/Lock", {
                "command":
                functools.partial(Gaffer.WeakMethod(self.__applyReadOnly),
                                  not readOnly),
                "active":
                not self.getReadOnly()
                and not Gaffer.MetadataAlgo.readOnly(self.getPlug().parent()),
            })

        self.popupMenuSignal()(menuDefinition, self)

        return menuDefinition

    __popupMenuSignal = Gaffer.Signal2()
    ## This signal is emitted whenever a popup menu for a plug is about
    # to be shown. This provides an opportunity to customise the menu from
    # external code. The signature for slots is ( menuDefinition, plugValueWidget ),
    # and slots should just modify the menu definition in place.
    @classmethod
    def popupMenuSignal(cls):

        return cls.__popupMenuSignal

    ## Returns a PlugValueWidget suitable for representing the specified plug.
    # The type of plug returned may be customised on a per-widget basis by a
    # "plugValueWidget:type" metadata value, specifying the fully qualified
    # python type name for a widget class. To suppress the creation of a widget,
    # a value of "" may be registered - in this case None will be returned from
    # create(). If useTypeOnly is True, then the metadata will be ignored and
    # only the plug type will be taken into account in creating a PlugValueWidget.
    @classmethod
    def create(cls, plug, useTypeOnly=False):

        # first try to create one using a creator registered for the specific plug
        if not useTypeOnly:

            widgetType = Gaffer.Metadata.value(plug, "plugValueWidget:type")
            if widgetType is None:
                widgetType = Gaffer.Metadata.value(plug, "layout:widgetType")
                if widgetType is not None:
                    warnings.warn(
                        "The \"layout:widgetType\" metadata entry is deprecated, use \"plugValueWidget:type\" instead.",
                        DeprecationWarning)
                    if widgetType == "None":
                        return None

            if widgetType is not None:
                if widgetType == "":
                    return None
                path = widgetType.split(".")
                widgetClass = __import__(path[0])
                for n in path[1:]:
                    widgetClass = getattr(widgetClass, n)
                return widgetClass(plug)

        # if that failed, then just create something based on the type of the plug
        typeId = plug.typeId()
        for plugTypeId in [plug.typeId()] + IECore.RunTimeTyped.baseTypeIds(
                plug.typeId()):
            if plugTypeId in cls.__plugTypesToCreators:
                creator = cls.__plugTypesToCreators[plugTypeId]
                if creator is not None:
                    return creator(plug)
                else:
                    return None

        return None

    ## Registers a PlugValueWidget type for a specific Plug type.
    @classmethod
    def registerType(cls, plugClassOrTypeId, creator):

        if isinstance(plugClassOrTypeId, IECore.TypeId):
            plugTypeId = plugClassOrTypeId
        else:
            plugTypeId = plugClassOrTypeId.staticTypeId()

        cls.__plugTypesToCreators[plugTypeId] = creator

    __plugTypesToCreators = {}

    def __plugDirtied(self, plug):

        if plug.isSame(self.__plug):

            self._updateFromPlug()

    def __plugInputChanged(self, plug):

        if plug.isSame(self.__plug):
            self.__updateContextConnection()
            self._updateFromPlug()

    def __plugFlagsChanged(self, plug):

        if plug.isSame(self.__plug):
            self._updateFromPlug()

    def __plugMetadataChanged(self, nodeTypeId, plugPath, key, plug):

        if self.__plug is None:
            return

        if (Gaffer.MetadataAlgo.affectedByChange(self.__plug, nodeTypeId,
                                                 plugPath, plug)
                or Gaffer.MetadataAlgo.readOnlyAffectedByChange(
                    self.__plug, nodeTypeId, plugPath, key, plug)):
            self._updateFromPlug()

    def __nodeMetadataChanged(self, nodeTypeId, key, node):

        if self.__plug is None:
            return

        if Gaffer.MetadataAlgo.readOnlyAffectedByChange(
                self.__plug, nodeTypeId, key, node):
            self._updateFromPlug()

    def __contextChanged(self, context, key):

        self._updateFromPlug()

    def __setPlugInternal(self, plug, callUpdateFromPlug):

        self.__plug = plug

        context = self.__fallbackContext

        if self.__plug is not None:
            self.__plugDirtiedConnection = plug.node().plugDirtiedSignal(
            ).connect(Gaffer.WeakMethod(self.__plugDirtied))
            self.__plugInputChangedConnection = plug.node(
            ).plugInputChangedSignal().connect(
                Gaffer.WeakMethod(self.__plugInputChanged))
            self.__plugFlagsChangedConnection = plug.node(
            ).plugFlagsChangedSignal().connect(
                Gaffer.WeakMethod(self.__plugFlagsChanged))
            self.__plugMetadataChangedConnection = Gaffer.Metadata.plugValueChangedSignal(
            ).connect(Gaffer.WeakMethod(self.__plugMetadataChanged))
            scriptNode = self.__plug.ancestor(Gaffer.ScriptNode)
            if scriptNode is not None:
                context = scriptNode.context()
        else:
            self.__plugDirtiedConnection = None
            self.__plugInputChangedConnection = None
            self.__plugFlagsChangedConnection = None
            self.__plugMetadataChangedConnection = None

        self.__context = context
        self.__updateContextConnection()

        if callUpdateFromPlug:
            self._updateFromPlug()

    def __updateContextConnection(self):

        # we only want to be notified of context changes if we have a plug and that
        # plug has an incoming connection. otherwise context changes are irrelevant
        # and we'd just be slowing things down by asking for notifications.

        context = self.__context
        plug = self.getPlug()
        if plug is None or (plug.getInput() is None
                            and plug.direction() == Gaffer.Plug.Direction.In):
            context = None

        if context is not None:
            self.__contextChangedConnection = context.changedSignal().connect(
                Gaffer.WeakMethod(self.__contextChanged))
        else:
            self.__contextChangedConnection = None

    # we use this when the plug being viewed doesn't have a ScriptNode ancestor
    # to provide a context.
    __fallbackContext = Gaffer.Context()

    def __buttonPress(self, widget, event, buttonMask):

        if event.buttons & buttonMask:
            return self.__contextMenu()

        return False

    def __contextMenu(self, *unused):

        if self.getPlug() is None:
            return False

        menuDefinition = self._popupMenuDefinition()
        if not len(menuDefinition.items()):
            return False

        title = self.getPlug().relativeName(self.getPlug().node())
        title = ".".join([
            IECore.CamelCase.join(IECore.CamelCase.split(x))
            for x in title.split(".")
        ])

        self.__popupMenu = GafferUI.Menu(menuDefinition, title=title)
        self.__popupMenu.popup(parent=self)

        return True

    def __copyValue(self):

        with self.getContext():
            value = self.getPlug().getValue()

        if not isinstance(value, IECore.Object):
            # Trick to get Data from a simple type - put
            # it in a CompoundData (which will convert to
            # Data automatically) and then get it back out.
            value = IECore.CompoundData({"v": value})["v"]

        self.getPlug().ancestor(
            Gaffer.ApplicationRoot).setClipboardContents(value)

    def __setValue(self, value):

        with Gaffer.UndoScope(self.getPlug().ancestor(Gaffer.ScriptNode)):
            self.getPlug().setValue(value)

    def __editInput(self):

        nodeEditor = GafferUI.NodeEditor.acquire(
            self.getPlug().getInput().node())
        if nodeEditor is None:
            return

        plugValueWidget = nodeEditor.nodeUI().plugValueWidget(
            self.getPlug().getInput())
        if plugValueWidget is None:
            return

        plugValueWidget.reveal()

    def __removeInput(self):

        with Gaffer.UndoScope(self.getPlug().ancestor(Gaffer.ScriptNode)):
            self.getPlug().setInput(None)

    def __applyUserDefault(self):

        with Gaffer.UndoScope(self.getPlug().ancestor(Gaffer.ScriptNode)):
            Gaffer.NodeAlgo.applyUserDefault(self.getPlug())

    def __presetsSubMenu(self):

        with self.getContext():
            currentPreset = Gaffer.NodeAlgo.currentPreset(self.getPlug())

        result = IECore.MenuDefinition()
        for presetName in Gaffer.NodeAlgo.presets(self.getPlug()):
            menuPath = presetName if presetName.startswith(
                "/") else "/" + presetName
            result.append(
                menuPath, {
                    "command":
                    functools.partial(Gaffer.WeakMethod(self.__applyPreset),
                                      presetName),
                    "active":
                    self._editable(),
                    "checkBox":
                    presetName == currentPreset,
                })

        return result

    def __applyPreset(self, presetName, *unused):

        with Gaffer.UndoScope(self.getPlug().ancestor(Gaffer.ScriptNode)):
            Gaffer.NodeAlgo.applyPreset(self.getPlug(), presetName)

    def __applyReadOnly(self, readOnly):

        with Gaffer.UndoScope(self.getPlug().ancestor(Gaffer.ScriptNode)):
            Gaffer.MetadataAlgo.setReadOnly(self.getPlug(), readOnly)

    # drag and drop stuff

    def __dragEnter(self, widget, event):

        if self.getReadOnly() or Gaffer.MetadataAlgo.readOnly(self.getPlug()):
            return False

        if isinstance(event.sourceWidget, GafferUI.PlugValueWidget):
            sourcePlugValueWidget = event.sourceWidget
        else:
            sourcePlugValueWidget = event.sourceWidget.ancestor(
                GafferUI.PlugValueWidget)

        if sourcePlugValueWidget is not None and sourcePlugValueWidget.getPlug(
        ).isSame(self.getPlug()):
            return False

        if isinstance(event.data, Gaffer.Plug):
            if self.getPlug().acceptsInput(event.data):
                self.setHighlighted(True)
                return True
        elif hasattr(self.getPlug(), "setValue") and self._convertValue(
                event.data) is not None:
            if self.getPlug().settable():
                self.setHighlighted(True)
                return True

        return False

    def __dragLeave(self, widget, event):

        self.setHighlighted(False)

    def __drop(self, widget, event):

        self.setHighlighted(False)

        with Gaffer.UndoScope(self.getPlug().node().scriptNode()):
            if isinstance(event.data, Gaffer.Plug):
                self.getPlug().setInput(event.data)
            else:
                self.getPlug().setValue(self._convertValue(event.data))

        return True
示例#22
0
    def testContextAffectsHash(self):

        # Hash should be constant if context not
        # accessed.
        n = GafferDispatch.PythonCommand()
        n["command"].setValue("a = 10")

        with Gaffer.Context() as c:

            h = n["task"].hash()

            c.setTime(2)
            self.assertEqual(n["task"].hash(), h)
            c.setTime(3)
            self.assertEqual(n["task"].hash(), h)

            c["testInt"] = 10
            self.assertEqual(n["task"].hash(), h)
            c["testInt"] = 20
            self.assertEqual(n["task"].hash(), h)

        # If we access the frame, then we should
        # be sensitive to the time, but not anything else

        n["command"].setValue("a = context.getFrame()")

        with Gaffer.Context() as c:

            c.setTime(1)
            h1 = n["task"].hash()

            c.setTime(2)
            h2 = n["task"].hash()

            c.setTime(3)
            h3 = n["task"].hash()

            self.assertNotEqual(h1, h)
            self.assertNotEqual(h2, h1)
            self.assertNotEqual(h3, h2)
            self.assertNotEqual(h3, h1)

            c["testInt"] = 10
            self.assertEqual(n["task"].hash(), h3)
            c["testInt"] = 20
            self.assertEqual(n["task"].hash(), h3)

        # The same should apply if we access the frame
        # via subscripting rather than the method.

        n["command"].setValue("a = context['frame']")

        with Gaffer.Context() as c:

            c.setTime(1)
            h1 = n["task"].hash()

            c.setTime(2)
            h2 = n["task"].hash()

            c.setTime(3)
            h3 = n["task"].hash()

            self.assertNotEqual(h1, h)
            self.assertNotEqual(h2, h1)
            self.assertNotEqual(h3, h2)
            self.assertNotEqual(h3, h1)

            c["testInt"] = 10
            self.assertEqual(n["task"].hash(), h3)
            c["testInt"] = 20
            self.assertEqual(n["task"].hash(), h3)

        # Likewise, accessing other variables should
        # affect the hash.

        n["command"].setValue("a = context['testInt']")

        with Gaffer.Context() as c:

            c["testInt"] = 1
            h1 = n["task"].hash()

            c["testInt"] = 2
            h2 = n["task"].hash()

            c["testInt"] = 3
            h3 = n["task"].hash()

            self.assertNotEqual(h2, h1)
            self.assertNotEqual(h3, h2)
            self.assertNotEqual(h3, h1)

            c.setFrame(2)
            self.assertEqual(n["task"].hash(), h3)
            c.setFrame(3)
            self.assertEqual(n["task"].hash(), h3)
示例#23
0
	def __testMetadataDoesNotAffectPixels( self, ext, overrideMetadata = {}, metadataToIgnore = [] ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.__rgbFilePath+"."+ext )
		d = GafferImage.DeleteImageMetadata()
		d["in"].setInput( r["out"] )
		m = GafferImage.ImageMetadata()
		m["in"].setInput( d["out"] )
		# lets tell a few lies
		# IPTC:Creator will have the current username appended to the end of
		# the existing one, creating a list of creators. Blank it out for
		# this test
		d["names"].setValue( "IPTC:Creator" )
		m["metadata"].addMember( "PixelAspectRatio", IECore.FloatData( 2 ) )
		m["metadata"].addMember( "oiio:ColorSpace", IECore.StringData( "Rec709" ) )
		m["metadata"].addMember( "oiio:BitsPerSample", IECore.IntData( 8 ) )
		m["metadata"].addMember( "oiio:UnassociatedAlpha", IECore.IntData( 1 ) )
		m["metadata"].addMember( "oiio:Gamma", IECore.FloatData( 0.25 ) )

		testFile = self.__testFile( "metadataHasNoAffect", "RGBA", ext )
		self.failIf( os.path.exists( testFile ) )

		w = GafferImage.ImageWriter()
		w["in"].setInput( m["out"] )
		w["fileName"].setValue( testFile )
		w["channels"].setValue( IECore.StringVectorData( m["out"]["channelNames"].getValue() ) )

		testFile2 = self.__testFile( "noNewMetadata", "RGBA", ext )
		self.failIf( os.path.exists( testFile2 ) )

		w2 = GafferImage.ImageWriter()
		w2["in"].setInput( d["out"] )
		w2["fileName"].setValue( testFile2 )
		w2["channels"].setValue( IECore.StringVectorData( r["out"]["channelNames"].getValue() ) )

		inMetadata = w["in"]["metadata"].getValue()
		self.assertEqual( inMetadata["PixelAspectRatio"], IECore.FloatData( 2 ) )
		self.assertEqual( inMetadata["oiio:ColorSpace"], IECore.StringData( "Rec709" ) )
		self.assertEqual( inMetadata["oiio:BitsPerSample"], IECore.IntData( 8 ) )
		self.assertEqual( inMetadata["oiio:UnassociatedAlpha"], IECore.IntData( 1 ) )
		self.assertEqual( inMetadata["oiio:Gamma"], IECore.FloatData( 0.25 ) )

		with Gaffer.Context() :
			w["task"].execute()
			w2["task"].execute()
		self.failUnless( os.path.exists( testFile ) )
		self.failUnless( os.path.exists( testFile2 ) )

		after = GafferImage.ImageReader()
		after["fileName"].setValue( testFile )

		before = GafferImage.ImageReader()
		before["fileName"].setValue( testFile2 )

		inImage = w["in"].image()
		afterImage = after["out"].image()
		beforeImage = before["out"].image()

		inImage.blindData().clear()
		afterImage.blindData().clear()
		beforeImage.blindData().clear()

		self.assertEqual( afterImage, inImage )
		self.assertEqual( afterImage, beforeImage )

		self.assertEqual( after["out"]["format"].getValue(), r["out"]["format"].getValue() )
		self.assertEqual( after["out"]["format"].getValue(), before["out"]["format"].getValue() )

		self.assertEqual( after["out"]["dataWindow"].getValue(), r["out"]["dataWindow"].getValue() )
		self.assertEqual( after["out"]["dataWindow"].getValue(), before["out"]["dataWindow"].getValue() )

		afterMetadata = after["out"]["metadata"].getValue()
		beforeMetadata = before["out"]["metadata"].getValue()
		expectedMetadata = r["out"]["metadata"].getValue()
		# they were written at different times so we can't expect those values to match
		beforeMetadata["DateTime"] = afterMetadata["DateTime"]
		expectedMetadata["DateTime"] = afterMetadata["DateTime"]
		# the writer adds several standard attributes that aren't in the original file
		expectedMetadata["Software"] = IECore.StringData( "Gaffer " + Gaffer.About.versionString() )
		expectedMetadata["HostComputer"] = IECore.StringData( platform.node() )
		expectedMetadata["Artist"] = IECore.StringData( os.getlogin() )
		expectedMetadata["DocumentName"] = IECore.StringData( "untitled" )
		# some formats support IPTC standards, and some of the standard metadata
		# is translated automatically by OpenImageIO.
		for key in afterMetadata.keys() :
			if key.startswith( "IPTC:" ) :
				expectedMetadata["IPTC:OriginatingProgram"] = expectedMetadata["Software"]
				expectedMetadata["IPTC:Creator"] = expectedMetadata["Artist"]
				break

		for key in overrideMetadata :
			expectedMetadata[key] = overrideMetadata[key]
			beforeMetadata[key] = overrideMetadata[key]

		for key in metadataToIgnore :
			if key in expectedMetadata :
				del expectedMetadata[key]
			if key in beforeMetadata :
				del beforeMetadata[key]
			if key in afterMetadata :
				del afterMetadata[key]

		for metaName in expectedMetadata.keys() :
			self.assertTrue( metaName in afterMetadata.keys(), "Writer Metadata missing expected key \"{}\" set to \"{}\" : {}".format(metaName, str(expectedMetadata[metaName]), ext) )
			self.assertEqual( expectedMetadata[metaName], afterMetadata[metaName], "Metadata does not match for key \"{}\" : {}".format(metaName, ext) )

		for metaName in beforeMetadata.keys() :
			self.assertTrue( metaName in afterMetadata.keys(), "Writer Metadata missing expected key \"{}\" set to \"{}\" : {}".format(metaName, str(beforeMetadata[metaName]), ext) )
			self.assertEqual( beforeMetadata[metaName], afterMetadata[metaName], "Metadata does not match for key \"{}\" : {}".format(metaName, ext) )
示例#24
0
        def f():

            self.failIf(c.isSame(Gaffer.Context.current()))
            with Gaffer.Context():
                pass
示例#25
0
    def testPythonExpressionAndGIL(self):

        script = Gaffer.ScriptNode()

        script["plane"] = GafferScene.Plane()
        script["plane"]["divisions"].setValue(IECore.V2i(20))

        script["sphere"] = GafferScene.Sphere()

        script["expression"] = Gaffer.Expression()
        script["expression"].setExpression(
            "parent['sphere']['radius'] = context.getFrame() + float( context['instancer:id'] )"
        )

        script["instancer"] = GafferScene.Instancer()
        script["instancer"]["in"].setInput(script["plane"]["out"])
        script["instancer"]["instance"].setInput(script["sphere"]["out"])
        script["instancer"]["parent"].setValue("/plane")

        # The Instancer spawns its own threads, so if we don't release the GIL
        # when invoking it, and an upstream node enters Python, we'll end up
        # with a deadlock. Test that isn't the case. We increment the frame
        # between each test to ensure the expression result is not cached and
        # we do truly enter python.
        with Gaffer.Context() as c:

            c["scene:path"] = IECore.InternedStringVectorData(["plane"])

            c.setFrame(1)
            script["instancer"]["out"]["globals"].getValue()
            c.setFrame(2)
            script["instancer"]["out"]["bound"].getValue()
            c.setFrame(3)
            script["instancer"]["out"]["transform"].getValue()
            c.setFrame(4)
            script["instancer"]["out"]["object"].getValue()
            c.setFrame(5)
            script["instancer"]["out"]["attributes"].getValue()
            c.setFrame(6)
            script["instancer"]["out"]["childNames"].getValue()
            c.setFrame(7)

            c.setFrame(101)
            script["instancer"]["out"]["globals"].hash()
            c.setFrame(102)
            script["instancer"]["out"]["bound"].hash()
            c.setFrame(103)
            script["instancer"]["out"]["transform"].hash()
            c.setFrame(104)
            script["instancer"]["out"]["object"].hash()
            c.setFrame(105)
            script["instancer"]["out"]["attributes"].hash()
            c.setFrame(106)
            script["instancer"]["out"]["childNames"].hash()
            c.setFrame(107)

            # The same applies for the higher level helper functions on ScenePlug

            c.setFrame(200)
            script["instancer"]["out"].bound("/plane")
            c.setFrame(201)
            script["instancer"]["out"].transform("/plane")
            c.setFrame(202)
            script["instancer"]["out"].fullTransform("/plane")
            c.setFrame(203)
            script["instancer"]["out"].attributes("/plane")
            c.setFrame(204)
            script["instancer"]["out"].fullAttributes("/plane")
            c.setFrame(205)
            script["instancer"]["out"].object("/plane")
            c.setFrame(206)
            script["instancer"]["out"].childNames("/plane")
            c.setFrame(207)

            c.setFrame(300)
            script["instancer"]["out"].boundHash("/plane")
            c.setFrame(301)
            script["instancer"]["out"].transformHash("/plane")
            c.setFrame(302)
            script["instancer"]["out"].fullTransformHash("/plane")
            c.setFrame(303)
            script["instancer"]["out"].attributesHash("/plane")
            c.setFrame(304)
            script["instancer"]["out"].fullAttributesHash("/plane")
            c.setFrame(305)
            script["instancer"]["out"].objectHash("/plane")
            c.setFrame(306)
            script["instancer"]["out"].childNamesHash("/plane")
            c.setFrame(307)
示例#26
0
    def testWithBlockReturnValue(self):

        with Gaffer.Context() as c:
            self.failUnless(isinstance(c, Gaffer.Context))
            self.failUnless(c.isSame(Gaffer.Context.current()))
示例#27
0
    def testContextChangedAndGIL(self):

        script = Gaffer.ScriptNode()

        script["plane"] = GafferScene.Plane()
        script["plane"]["divisions"].setValue(IECore.V2i(20))

        script["sphere"] = GafferScene.Sphere()

        script["expression"] = Gaffer.Expression()
        script["expression"].setExpression(
            "parent['sphere']['radius'] = context.get( 'minRadius', 0.1 ) + context.getFrame() + float( context['instancer:id'] )"
        )

        script["instancer"] = GafferScene.Instancer()
        script["instancer"]["in"].setInput(script["plane"]["out"])
        script["instancer"]["instance"].setInput(script["sphere"]["out"])
        script["instancer"]["parent"].setValue("/plane")

        context = Gaffer.Context()
        traverseConnection = Gaffer.ScopedConnection(
            GafferSceneTest.connectTraverseSceneToContextChangedSignal(
                script["instancer"]["out"], context))
        with context:

            context.setFrame(10)
            context.setFramesPerSecond(50)
            context.setTime(1)

            context.set("a", 1)
            context.set("a", 2.0)
            context.set("a", "a")
            context.set("a", IECore.V2i())
            context.set("a", IECore.V3i())
            context.set("a", IECore.V2f())
            context.set("a", IECore.V3f())
            context.set("a", IECore.Color3f())
            context.set("a", IECore.BoolData(True))

            context["b"] = 1
            context["b"] = 2.0
            context["b"] = "b"
            context["b"] = IECore.V2i()
            context["b"] = IECore.V3i()
            context["b"] = IECore.V2f()
            context["b"] = IECore.V3f()
            context["b"] = IECore.Color3f()
            context["b"] = IECore.BoolData(True)

            with Gaffer.BlockedConnection(traverseConnection):
                # Must add it with the connection disabled, otherwise
                # the addition causes a traversal, and then remove() gets
                # all its results from the cache.
                context["minRadius"] = 0.2

            context.remove("minRadius")

            with Gaffer.BlockedConnection(traverseConnection):
                context["minRadius"] = 0.3

            del context["minRadius"]
示例#28
0
    def testSubstituteTildeInMiddle(self):

        c = Gaffer.Context()
        self.assertEqual(c.substitute("a~b"), "a~b")
示例#29
0
    def testMissingFrameMode(self):

        testSequence = IECore.FileSequence(self.temporaryDirectory() +
                                           "/incompleteSequence.####.exr")
        shutil.copyfile(self.fileName, testSequence.fileNameForFrame(1))
        shutil.copyfile(self.offsetDataWindowFileName,
                        testSequence.fileNameForFrame(3))

        reader = GafferImage.OpenImageIOReader()
        reader["fileName"].setValue(testSequence.fileName)

        context = Gaffer.Context()

        # get frame 1 data for comparison
        context.setFrame(1)
        with context:
            f1Image = reader["out"].image()
            f1Format = reader["out"]["format"].getValue()
            f1DataWindow = reader["out"]["dataWindow"].getValue()
            f1Metadata = reader["out"]["metadata"].getValue()
            f1ChannelNames = reader["out"]["channelNames"].getValue()
            f1Tile = reader["out"].channelData("R", imath.V2i(0))

        # make sure the tile we're comparing isn't black
        # so we can tell if MissingFrameMode::Black is working.
        blackTile = IECore.FloatVectorData([0] *
                                           GafferImage.ImagePlug.tileSize() *
                                           GafferImage.ImagePlug.tileSize())
        self.assertNotEqual(f1Tile, blackTile)

        # set to a missing frame
        context.setFrame(2)

        # everything throws
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Error)
        with context:
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"].image)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["format"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["dataWindow"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["metadata"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["channelNames"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"].channelData, "R",
                                    imath.V2i(0))

        # everything matches frame 1
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Hold)
        with context:
            self.assertEqual(reader["out"].image(), f1Image)
            self.assertEqual(reader["out"]["format"].getValue(), f1Format)
            self.assertEqual(reader["out"]["dataWindow"].getValue(),
                             f1DataWindow)
            self.assertEqual(reader["out"]["metadata"].getValue(), f1Metadata)
            self.assertEqual(reader["out"]["channelNames"].getValue(),
                             f1ChannelNames)
            self.assertEqual(reader["out"].channelData("R", imath.V2i(0)),
                             f1Tile)

        # the windows match frame 1, but everything else is default
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Black)
        with context:
            self.assertNotEqual(reader["out"].image(), f1Image)
            self.assertEqual(reader["out"]["format"].getValue(), f1Format)
            self.assertEqual(reader["out"]["dataWindow"].getValue(),
                             reader["out"]["dataWindow"].defaultValue())
            self.assertEqual(reader["out"]["metadata"].getValue(),
                             reader["out"]["metadata"].defaultValue())
            self.assertEqual(reader["out"]["channelNames"].getValue(),
                             reader["out"]["channelNames"].defaultValue())
            self.assertEqual(reader["out"].channelData("R", imath.V2i(0)),
                             blackTile)

        # get frame 3 data for comparison
        context.setFrame(3)
        with context:
            f3Image = reader["out"].image()
            f3Format = reader["out"]["format"].getValue()
            f3DataWindow = reader["out"]["dataWindow"].getValue()
            f3Metadata = reader["out"]["metadata"].getValue()
            f3ChannelNames = reader["out"]["channelNames"].getValue()
            f3Tile = reader["out"].channelData("R", imath.V2i(0))

        # set to a different missing frame
        context.setFrame(4)

        # everything matches frame 3
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Hold)
        with context:
            self.assertNotEqual(reader["out"].image(), f1Image)
            self.assertNotEqual(reader["out"]["format"].getValue(), f1Format)
            self.assertNotEqual(reader["out"]["dataWindow"].getValue(),
                                f1DataWindow)
            self.assertNotEqual(reader["out"]["metadata"].getValue(),
                                f1Metadata)
            # same channel names is fine
            self.assertEqual(reader["out"]["channelNames"].getValue(),
                             f1ChannelNames)
            self.assertNotEqual(reader["out"].channelData("R", imath.V2i(0)),
                                f1Tile)
            self.assertEqual(reader["out"].image(), f3Image)
            self.assertEqual(reader["out"]["format"].getValue(), f3Format)
            self.assertEqual(reader["out"]["dataWindow"].getValue(),
                             f3DataWindow)
            self.assertEqual(reader["out"]["metadata"].getValue(), f3Metadata)
            self.assertEqual(reader["out"]["channelNames"].getValue(),
                             f3ChannelNames)
            self.assertEqual(reader["out"].channelData("R", imath.V2i(0)),
                             f3Tile)

        # the windows match frame 3, but everything else is default
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Black)
        with context:
            self.assertNotEqual(reader["out"]["format"].getValue(), f1Format)
            self.assertEqual(reader["out"]["format"].getValue(), f3Format)
            self.assertEqual(reader["out"]["dataWindow"].getValue(),
                             reader["out"]["dataWindow"].defaultValue())
            self.assertEqual(reader["out"]["metadata"].getValue(),
                             reader["out"]["metadata"].defaultValue())
            self.assertEqual(reader["out"]["channelNames"].getValue(),
                             reader["out"]["channelNames"].defaultValue())
            self.assertEqual(reader["out"].channelData("R", imath.V2i(0)),
                             blackTile)

        # set to a missing frame before the start of the sequence
        context.setFrame(0)

        # everything matches frame 1
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Hold)
        with context:
            self.assertEqual(reader["out"].image(), f1Image)
            self.assertEqual(reader["out"]["format"].getValue(), f1Format)
            self.assertEqual(reader["out"]["dataWindow"].getValue(),
                             f1DataWindow)
            self.assertEqual(reader["out"]["metadata"].getValue(), f1Metadata)
            self.assertEqual(reader["out"].channelData("R", imath.V2i(0)),
                             f1Tile)

        # the windows match frame 1, but everything else is default
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Black)
        with context:
            self.assertEqual(reader["out"]["format"].getValue(), f1Format)
            self.assertEqual(reader["out"]["dataWindow"].getValue(),
                             reader["out"]["dataWindow"].defaultValue())
            self.assertEqual(reader["out"]["metadata"].getValue(),
                             reader["out"]["metadata"].defaultValue())
            self.assertEqual(reader["out"]["channelNames"].getValue(),
                             reader["out"]["channelNames"].defaultValue())
            self.assertEqual(reader["out"].channelData("R", imath.V2i(0)),
                             blackTile)

        # explicit fileNames do not support MissingFrameMode
        reader["fileName"].setValue(testSequence.fileNameForFrame(0))
        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Hold)
        with context:
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"].image)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["format"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["dataWindow"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["metadata"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["channelNames"].getValue)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"].channelData, "R",
                                    imath.V2i(0))

        reader["missingFrameMode"].setValue(
            GafferImage.OpenImageIOReader.MissingFrameMode.Black)
        with context:
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"].image)
            self.assertRaisesRegexp(RuntimeError,
                                    ".*incompleteSequence.*.exr.*",
                                    reader["out"]["format"].getValue)
            self.assertEqual(reader["out"]["dataWindow"].getValue(),
                             reader["out"]["dataWindow"].defaultValue())
            self.assertEqual(reader["out"]["metadata"].getValue(),
                             reader["out"]["metadata"].defaultValue())
            self.assertEqual(reader["out"]["channelNames"].getValue(),
                             reader["out"]["channelNames"].defaultValue())
            self.assertEqual(reader["out"].channelData("R", imath.V2i(0)),
                             blackTile)
示例#30
0
    def testTaskSet(self):

        # A no-op TaskNode doesn't actually compute anything, so all tasks are the same
        c = Gaffer.Context()
        n = GafferDispatchTest.LoggingTaskNode()
        n["noOp"].setValue(True)
        t1 = GafferDispatch.TaskNode.Task(n, c)
        t2 = GafferDispatch.TaskNode.Task(n, c)
        self.assertEqual(t1, t2)
        c2 = Gaffer.Context()
        c2["a"] = 2
        t3 = GafferDispatch.TaskNode.Task(n, c2)
        self.assertEqual(t1, t3)
        n2 = GafferDispatchTest.LoggingTaskNode()
        n2["noOp"].setValue(True)
        t4 = GafferDispatch.TaskNode.Task(n2, c2)
        self.assertEqual(t1, t4)
        t5 = GafferDispatch.TaskNode.Task(n2, c)
        self.assertEqual(t1, t5)

        s = set([t1, t2, t3, t4, t4, t4, t1, t2, t4, t3, t2])
        # there should only be 1 task because they all have identical results
        self.assertEqual(len(s), 1)
        self.assertEqual(s, set([t1]))
        self.assertTrue(t1 in s)
        self.assertTrue(t2 in s)
        self.assertTrue(t3 in s)
        self.assertTrue(t4 in s)
        # even t5 is in there, because it's really the same task
        self.assertTrue(t5 in s)

        # MyNode.hash() depends on the context time, so tasks will vary
        my = GafferDispatchTest.LoggingTaskNode()
        my["frameSensitivePlug"] = Gaffer.StringPlug(defaultValue="####")
        c.setFrame(1)
        t1 = GafferDispatch.TaskNode.Task(my, c)
        t2 = GafferDispatch.TaskNode.Task(my, c)
        self.assertEqual(t1, t2)
        c2 = Gaffer.Context()
        c2.setFrame(2)
        t3 = GafferDispatch.TaskNode.Task(my, c2)
        self.assertNotEqual(t1, t3)
        my2 = GafferDispatchTest.LoggingTaskNode()
        my2["frameSensitivePlug"] = Gaffer.StringPlug(defaultValue="####")
        t4 = GafferDispatch.TaskNode.Task(my2, c2)
        self.assertNotEqual(t1, t4)
        self.assertEqual(t3, t4)
        t5 = GafferDispatch.TaskNode.Task(my2, c)
        self.assertEqual(t1, t5)
        self.assertNotEqual(t3, t5)

        s = set([t1, t2, t3, t4, t4, t4, t1, t2, t4, t3, t2])
        # t1 and t3 are the only distinct tasks
        self.assertEqual(len(s), 2)
        self.assertEqual(s, set([t1, t3]))
        # but they still all have equivalent tasks in the set
        self.assertTrue(t1 in s)
        self.assertTrue(t2 in s)
        self.assertTrue(t3 in s)
        self.assertTrue(t4 in s)
        self.assertTrue(t5 in s)