From 79ca53db83d884bea958a8e70eec0b5d30fc19dc Mon Sep 17 00:00:00 2001 From: ilgarlunin Date: Thu, 28 Mar 2019 15:43:08 +0300 Subject: [PATCH] subgraph test wip --- PyFlow/Core/Common.py | 4 +- PyFlow/Core/GraphTree.py | 4 ++ PyFlow/Core/PinBase.py | 6 ++- .../Packages/PyflowBase/Nodes/graphNodes.py | 15 +++--- PyFlow/Packages/PyflowBase/Nodes/subgraph.py | 34 ++++++++++---- PyFlow/Packages/PyflowBase/Pins/AnyPin.py | 6 ++- PyFlow/Packages/PyflowBase/Pins/FloatPin.py | 2 +- PyFlow/Packages/PyflowBase/Pins/IntPin.py | 2 +- PyFlow/Tests/Test_General.py | 46 ++++++++++++++++++- run_tests.bat | 3 +- 10 files changed, 96 insertions(+), 26 deletions(-) diff --git a/PyFlow/Core/Common.py b/PyFlow/Core/Common.py index 2a36633aa..e745024d7 100644 --- a/PyFlow/Core/Common.py +++ b/PyFlow/Core/Common.py @@ -154,8 +154,8 @@ def canConnectPins(src, dst): if src.owningNode().graph() is None or dst.owningNode().graph() is None: return False - if src.owningNode().graph() != dst.owningNode().graph(): - return False + # if src.owningNode().graph() != dst.owningNode().graph(): + # return False if cycle_check(src, dst): print('cycles are not allowed') diff --git a/PyFlow/Core/GraphTree.py b/PyFlow/Core/GraphTree.py index 8c411bad0..54cbb7dd4 100644 --- a/PyFlow/Core/GraphTree.py +++ b/PyFlow/Core/GraphTree.py @@ -57,10 +57,14 @@ def Tick(self, deltaTime): node.data.Tick(deltaTime) def switchGraph(self, newGraphName): + if newGraphName not in self.getTree(): + return False + old = self.activeGraph() new = self.getTree()[newGraphName].data if old == new: return False + self.__activeGraph = new self.onGraphSwitched.send(old=old, new=new) return True diff --git a/PyFlow/Core/PinBase.py b/PyFlow/Core/PinBase.py index 9da569986..c436ca594 100644 --- a/PyFlow/Core/PinBase.py +++ b/PyFlow/Core/PinBase.py @@ -126,8 +126,9 @@ def getName(self): def pinDataTypeHint(): return None - def supportedDataTypes(self): - return (self.dataType,) + @staticmethod + def supportedDataTypes(): + return () def defaultValue(self): return self._defaultValue @@ -162,6 +163,7 @@ def setData(self, data): if self.direction == PinDirection.Output: for i in self.affects: i._data = self.currentData() + # i.setData(self.currentData()) i.setClean() if self.direction == PinDirection.Input or self._alwaysPushDirty: push(self) diff --git a/PyFlow/Packages/PyflowBase/Nodes/graphNodes.py b/PyFlow/Packages/PyflowBase/Nodes/graphNodes.py index 075ffe949..0f142b1c1 100644 --- a/PyFlow/Packages/PyflowBase/Nodes/graphNodes.py +++ b/PyFlow/Packages/PyflowBase/Nodes/graphNodes.py @@ -30,11 +30,16 @@ def description(): def addOutPin(self): name = str(len(self.outputs)) p = self.addOutputPin(name, 'AnyPin') - p.setAlwaysPushDirty(True) + # p.setAlwaysPushDirty(True) p.actLikeDirection = PinDirection.Input self.onPinCreated.send(p) return p + def compute(self): + for i in self.outputs.values(): + for j in i.affected_by: + i.setData(j.getData()) + def postCreate(self, jsonTemplate=None): super(graphInputs, self).postCreate(jsonTemplate=jsonTemplate) # recreate dynamically created pins @@ -43,14 +48,6 @@ def postCreate(self, jsonTemplate=None): # add outputs pass - def compute(self): - # This node is special. Output pins of this node are actually inputs pins in terms of execution and data gathering - # We get data from input pin on subgraphNode and put it to corresponding output pin on graphInputs node - for valuePin in [p for p in self.outputs.values() if p.IsValuePin()]: - # this can be changed to support multiple connections later - # affected_by is list of connected pins - valuePin.setData(list(valuePin.affected_by)[0].getData()) - class graphOutputs(NodeBase): """Represents a group of output pins on subgraph node diff --git a/PyFlow/Packages/PyflowBase/Nodes/subgraph.py b/PyFlow/Packages/PyflowBase/Nodes/subgraph.py index 85b0d1ef9..56590d61d 100644 --- a/PyFlow/Packages/PyflowBase/Nodes/subgraph.py +++ b/PyFlow/Packages/PyflowBase/Nodes/subgraph.py @@ -1,5 +1,6 @@ import os import json +import weakref from PyFlow.Core import NodeBase from PyFlow.Core import GraphBase @@ -15,8 +16,8 @@ class subgraph(NodeBase): def __init__(self, name): super(subgraph, self).__init__(name) self.rawGraph = None - self.__inputsMap = {} # { self.[inputPin].uid: innerPinUid } - self.__outputsMap = {} # { self.[outputPin].uid: innerPinUid } + self.__inputsMap = {} # { self.[inputPin]: innerOutPin } + self.__outputsMap = {} # { self.[outputPin]: innerInPin } @staticmethod def pinTypeHints(): @@ -46,9 +47,10 @@ def onGraphInputPinCreated(self, outPin): outPin.dataType, outPin.defaultValue(), outPin.call, - outPin.constraint, - outPin.supportedDataTypes()) - self.__inputsMap[subgraphInputPin.uid] = outPin.uid + outPin.constraint) + subgraphInputPin.supportedDataTypes = outPin.supportedDataTypes + self.__inputsMap[subgraphInputPin] = outPin + pinAffects(subgraphInputPin, outPin) # connect outPin.nameChanged.connect(subgraphInputPin.setName) outPin.killed.connect(subgraphInputPin.kill) @@ -58,14 +60,21 @@ def onGraphInputPinDeleted(self, inPin): print("onGraphInputPinDeleted", inPin.getName()) def onGraphOutputPinCreated(self, inPin): + """Reaction when pin added to graphOutputs node + + Arguments: + inPin {PinBase} -- input pin on graphOutputs node + """ + # add companion pin for graphOutputs node's input pin subgraphOutputPin = self.addOutputPin(inPin.name, inPin.dataType, inPin.defaultValue(), inPin.call, - inPin.constraint, - inPin.supportedDataTypes()) - self.__outputsMap[subgraphOutputPin.uid] = inPin.uid + inPin.constraint) + subgraphOutputPin.supportedDataTypes = inPin.supportedDataTypes + self.__outputsMap[subgraphOutputPin] = inPin + pinAffects(inPin, subgraphOutputPin) # connect inPin.nameChanged.connect(subgraphOutputPin.setName) inPin.killed.connect(subgraphOutputPin.kill) @@ -83,3 +92,12 @@ def postCreate(self, jsonTemplate=None): self.rawGraph.onInputPinDeleted.connect(self.onGraphInputPinDeleted) self.rawGraph.onOutputPinCreated.connect(self.onGraphOutputPinCreated) self.rawGraph.onOutputPinDeleted.connect(self.onGraphOutputPinDeleted) + + def compute(self): + # get data from subgraph node input pins and put it to inner companions + # for inputPin, innerOutPin in self.__inputsMap.items(): + # innerOutPin.setData(inputPin.getData()) + + # put data from inner graph pins to outer subgraph node output companions + for outputPin, innerPin in self.__outputsMap.items(): + outputPin.setData(innerPin.getData()) diff --git a/PyFlow/Packages/PyflowBase/Pins/AnyPin.py b/PyFlow/Packages/PyflowBase/Pins/AnyPin.py index 47f42e072..9f493552b 100644 --- a/PyFlow/Packages/PyflowBase/Pins/AnyPin.py +++ b/PyFlow/Packages/PyflowBase/Pins/AnyPin.py @@ -68,6 +68,10 @@ def serialize(self): dt['value'] = encodedValue return dt + def pinConnected(self, other): + self.updateOnConnection(other) + super(AnyPin, self).pinConnected(other) + def updateOnConnection(self, other): if self.constraint is None: self.setType(other) @@ -149,10 +153,10 @@ def setType(self, other): self.super = other.__class__ self.dataType = other.dataType self.color = other.color - self._wrapper().setType(other.color()) self.setData(other.defaultValue()) self.setDefaultValue(other.defaultValue()) self.dirty = other.dirty self.isPrimitiveType = other.isPrimitiveType self.jsonEncoderClass = other.jsonEncoderClass self.jsonDecoderClass = other.jsonDecoderClass + # self._wrapper().setType(other.color()) diff --git a/PyFlow/Packages/PyflowBase/Pins/FloatPin.py b/PyFlow/Packages/PyflowBase/Pins/FloatPin.py index ddcf04067..18c14d3f5 100644 --- a/PyFlow/Packages/PyflowBase/Pins/FloatPin.py +++ b/PyFlow/Packages/PyflowBase/Pins/FloatPin.py @@ -30,7 +30,7 @@ def color(): @staticmethod def supportedDataTypes(): - return ('FloatPin', 'IntPin') + return ('FloatPin', 'IntPin',) @staticmethod def processData(data): diff --git a/PyFlow/Packages/PyflowBase/Pins/IntPin.py b/PyFlow/Packages/PyflowBase/Pins/IntPin.py index ef8c29683..2711ce8a7 100644 --- a/PyFlow/Packages/PyflowBase/Pins/IntPin.py +++ b/PyFlow/Packages/PyflowBase/Pins/IntPin.py @@ -26,7 +26,7 @@ def color(): @staticmethod def supportedDataTypes(): - return ('IntPin', 'FloatPin') + return ('IntPin', 'FloatPin',) @staticmethod def processData(data): diff --git a/PyFlow/Tests/Test_General.py b/PyFlow/Tests/Test_General.py index 8b8ee428d..bf4a8c160 100644 --- a/PyFlow/Tests/Test_General.py +++ b/PyFlow/Tests/Test_General.py @@ -348,17 +348,61 @@ def test_subgraph(self): self.assertEqual(list(subgraphNodeInstance.outputs.values())[0].name, inPin.name, "name is not synchronized") # add simple calculation + foos = packages['PyflowBase'].GetFunctionLibraries()["IntLib"].getFunctions() + + addNode1 = NodeBase.initializeFromFunction(foos["add"]) + addNode2 = NodeBase.initializeFromFunction(foos["add"]) + GT.activeGraph().addNode(addNode1) + GT.activeGraph().addNode(addNode2) + addNode1.setData("b", 1) + addNode2.setData("b", 1) + connection = connectPins(addNode1.getPinByName('out', PinSelectionGroup.Outputs), addNode2.getPinByName('a', PinSelectionGroup.Inputs)) + self.assertEqual(connection, True) + + # connect add nodes with graph inputs/outputs + connected = connectPins(inputs1.getPinByName('first'), addNode1.getPinByName('a')) + self.assertEqual(connected, True) + connected = connectPins(outputs1.getPinByName('first'), addNode2.getPinByName('out')) + self.assertEqual(connected, True) # go back to root graph GT.switchGraph("testGraph") self.assertEqual(GT.activeGraph().name, "testGraph", "failed to return back to root from subgraph node") # check exposed pins added + self.assertEqual(len(subgraphNodeInstance.inputs), 1) + self.assertEqual(len(subgraphNodeInstance.outputs), 1) # connect getter to subgraph output pin + defaultLibFoos = packages['PyflowBase'].GetFunctionLibraries()["DefaultLib"].getFunctions() + printNode = NodeBase.initializeFromFunction(defaultLibFoos["pyprint"]) + GT.activeGraph().addNode(printNode) + + subgraphOutPin = subgraphNodeInstance.getPinByName('first', PinSelectionGroup.Outputs) + self.assertIsNotNone(subgraphOutPin, "failed to find subgraph out pin") + + connected = connectPins(printNode.getPinByName('entity'), subgraphOutPin) + self.assertEqual(connected, True) # check value - pass + printNode.getPinByName('inExec').call() + self.assertEqual(printNode.getPinByName('entity').currentData(), 2) + + # connect another add node to exposed subgraph input + addNode3 = NodeBase.initializeFromFunction(foos["add"]) + GT.activeGraph().addNode(addNode3) + addNode3.setData('a', 1) + + subgraphInPin = subgraphNodeInstance.getPinByName('first', PinSelectionGroup.Inputs) + self.assertIsNotNone(subgraphInPin, "failed to find subgraph out pin") + + GT.activeGraph().plot() + + connected = connectPins(addNode3.getPinByName('out'), subgraphInPin) + self.assertEqual(connected, True) + + printNode.getPinByName('inExec').call() + self.assertEqual(printNode.getPinByName('entity').currentData(), 3) if __name__ == '__main__': diff --git a/run_tests.bat b/run_tests.bat index df2500eab..d624d2fa4 100644 --- a/run_tests.bat +++ b/run_tests.bat @@ -1,2 +1,3 @@ @echo off -python -m unittest discover "PyFlow/Tests" \ No newline at end of file +python -m unittest discover "PyFlow/Tests" +pause \ No newline at end of file