NodeGraphQt icon indicating copy to clipboard operation
NodeGraphQt copied to clipboard

invoke computation on node

Open jf--- opened this issue 5 years ago • 4 comments

Hi,

I've been glancing the code base for a bit the other day, was a pleasure to navigate the code. I do have a few question, regarding invoking a computation in a node:

  • assuming the computation would be invoked by NodeModel, is it true that changes to the input are not emitting any events for now?
  • do i gather that currently, is there no designated method in NodeModel to override to invoke a computation?
  • it would be great that example.py would be able to execute a simple multiply operator
  • I'd love to help out on implementing such an example, hence the questions.

jf--- avatar Nov 29 '18 10:11 jf---

Hey @jf---

Thanks for checking out the code base 😸, the NodeModel shouldn't invoke any computation - it's just there to store the nodes data. However you can subclass the Node object and add your own function.

Here's an example snippet where we can invoke the execute() function on a node object. For the graph, I've wired up the port_connected signal to the foo function. It will be called when a port has been connected.

Thanks for wanting to help out 👍, feel free to ask if you have any more questions.

Cheers, J

class MyNodeGraph(NodeGraph):

    def __init__(self, parent=None):
        super(MyNodeGraph, self).__init__(parent)

        # wire up the foo function.
        self.port_connected.connect(self.foo)

    def foo(self, from_port, to_port):
        from_node = from_port.node()
        to_node = to_port.node()
        print('connected: "{}:{}" to "{}:{}"'.format(
            from_node.name(), from_port.name(), to_node.name(), to_port.name()
        ))

class MyNode(Node):
    """example test node."""

    # unique node identifier.
    __identifier__ = 'com.chantasticvfx'

    # initial default node name.
    NODE_NAME = 'Car Node'

    def __init__(self):
        super(MyNode, self).__init__()
        
        # create ports.
        self.add_input('foo')
        self.add_output('bar')
        
        # add custom node properties.
        self.create_property('car_brand', '')
        self.create_property('car_model', '')
        self.create_property('car_year', 0)
        self.create_property('car_is_electric', False)

def execute(self):
        message = (
            'CAR SPECS\nBrand:{}\nModel:{}\nYear:{}\nelectric:{}'
            .format(self.get_property('car_brand'),
                    self.get_property('car_model'),
                    self.get_property('car_year'),
                    self.get_property('is_electric')
                    )
        )
        print(message)

# create the node graph controller.
graph = MyNodeGraph()

# register node into the node graph.
graph.register_node(MyNode)

# create the nodes.
my_node1 = graph.create_node('com.chantasticvfx.MyNode', name='toyota')
my_node1.set_property('car_brand', 'Toyota')
my_node1.set_property('car_model', 'CHR')
my_node1.set_property('car_year', 2018)

my_node2 = graph.create_node('com.chantasticvfx.MyNode', name='bmw')
my_node2.set_property('car_brand', 'BMW')
my_node2.set_property('car_model', 'i3')
my_node2.set_property('car_year', 2012)

# connect my_node1 to my_node2.
my_node1.set_output(0, my_node2.input(0))

# run my_node1 execute function.
my_node1.execute()

# from my_node1 get the output port and run the connected "my_node2" execute function.
output_port = my_node1.output(0)
output_port.node().execute()

jchanvfx avatar Dec 05 '18 07:12 jchanvfx

This is really an excellent library and is almost exactly what I was looking for. I need to do computation inside a node and then pass the information along to another one, however.

From this example, I was able to figure out how to do it, but why isn't a default feature to pass a reference to the nodes that each node is attached to so that each node can access the data in those it is attached to? I'm sure that many people are looking to be passing data around the graph.

Veldrovive avatar Dec 08 '19 01:12 Veldrovive

I would like to vote for a math example or an example that shows how to find connected nodes.

Currently I am trying to have an upstream node update based on changing the value of a downstream node. Unfortunately the property_changed signal doesnt have have access to the next node so im not sure how to update it and execute

for instance. in the example you posted above, how might you edit that example so that the car properties were taken from text inputs on the first node and then each time its changed would update the text inputs on the second node

mikeporetti avatar Dec 17 '19 23:12 mikeporetti

My strategy was to simply pass a references to the nodes parents and children when a path was connected. Something like this:

class CompNode(BasicNode):
    def __init__(self):
        super(CompNode, self).__init__()
        self.parents = []
        self.children = []   

    def add_parent(self, node: CompNode):
        self.parents.append(node)

    def add_child(self, node: CompNode):
        self.children.append(node)

   def process(self):
       # Do some computation with data from previous nodes and update values accordingly
        pass

class MyNodeGraph(NodeGraph):
    def __init__(self, parent=None):
        super(MyNodeGraph, self).__init__(parent)

        # wire up the foo function.
        self.port_connected.connect(self.connect_nodes)

    def connect_nodes(self, from_port, to_port):
        from_node = from_port.node()
        to_node = to_port.node()

        from_node.add_child(to_node)
        to_node.add_parent(from_node)

And then a similar thing for disconnecting. It's not pretty, but it works just fine. I'll probably write classes that handle it more elegantly in the future and might make a pull request.

Veldrovive avatar Dec 17 '19 23:12 Veldrovive