leon-thomm/Ryven

set_output_val does nothing/ends def update_event

Opened this issue · 5 comments

nodes.py:

from typing import Tuple
from ryven.node_env import *

from random import random

from ryvencore.Flow import Flow
from ryvencore.Session import Session
#from dialog_system import *
#from ... import lambda_function

from random import random
from ryven import *


class Datenbank(Data):
    def __init__(self, value=None, load_from=None):
        super().__init__(value, load_from)
    def serialize(self):
        # Convert self.value to a format that can be easily saved/transferred
        return {"data": self.value}

    def deserialize(self, data):
        # Convert the transferred data back to the original format
        self.value = data["data"]



class RandNode(Node):
    """Generates scaled random float values"""

    title = 'Rand'
    tags = ['random', 'numbers']
    init_inputs = [NodeInputType()]
    init_outputs = [NodeOutputType()]

    def update_event(self, inp=-1):
        self.set_output_val(0,random() * self.input(0).payload)       

def placeholdprint(text):
    print(text)

class PrintNode(Node):
    title = 'Print'
    init_inputs = [NodeInputType()]

    def update_event(self, inp=-1):
        print(self.input(0))
        print('received!')

class Textbox(Node):
    title='Textbox'
    init_inputs = [NodeInputType(type_= 'data', label= 'Textblock:'),
                   NodeInputType(type_= 'data', label= 'Inhalt:')]
    init_outputs = [NodeOutputType(type_= 'data', label='anDa')]
    
    def __init__(self, params: Tuple[Flow | Session]):
        super().__init__(params)
        
    def update_event(self, inp=-1):
        print('ok')
        self.set_output_val(0,'lol')
        print('lol')
        self.set_output_val('anDa','anDa')
        

class Datenbank(Node):
    title='Datenbank'
    init_inputs = [NodeInputType(type_='data', label='Datenbank')]
    init_outputs = [NodeOutputType(type_='data', label='DatenOutput')]
    
    def __init__(self, params: Tuple[Flow | Session]):
        super().__init__(params)
        
        self.data = {}

    def update_event(self, inp=-1):
        self.data.update(self.input(0))
        print(self.data)
        self.set_output_val(0,self.data)
    


export_nodes([
	PrintNode,
    Textbox,
    Datenbank,
    RandNode,
])

Session.register_data_type

@on_gui_load
def load_gui():
    # import gui sources here only
    from . import gui

#if __name__ == '__main__':
 #   letsgo = Session()
  #  Session.register_data_type(Datenbank)
   # run_ryven(letsgo)

gui.py:

from qtpy.QtWidgets import *
from qtpy.QtCore import *

from ryven.gui_env import *

from . import nodes


class ButtonNode_MainWidget(NodeMainWidget, QPushButton):

    def __init__(self, params):
        NodeMainWidget.__init__(self, params)
        QPushButton.__init__(self,'Textblock hinzufügen')
        
        self.clicked.connect(self.update_node)



 
@node_gui(nodes.Textbox)
class TextboxGui(NodeGUI):
    main_widget_class = ButtonNode_MainWidget
    main_widget_pos = 'between ports'
    color = '#99dd55'
    
    
    input_widget_classes = {
        'textboxname': inp_widgets.Builder.str_line_edit(),
        'textboxbox': inp_widgets.Builder.str_line_edit(),
    }
    
    style = 'normal'
    color = '#c69a15'

    def __init__(self, params):
        super().__init__(params)

        self.input_widgets[self.node.inputs[-2]]= {'name': 'textboxbox', 'pos': 'besides'}
        self.input_widgets[self.node.inputs[-1]]= {'name': 'textboxname', 'pos': 'below'}


        

#@node_gui(nodes.woinfoalter)
class woinfoalterGUI(NodeGUI):
    input_widget_classes = {
        'varname': inp_widgets.Builder.str_line_edit(),
    #    'val': inp_widgets.Builder.str_line_edit(),
    }
    # init_input_widgets = {
    #     1: {'name': 'varname', 'pos': 'besides'},
    #     2: {'name': 'val', 'pos': 'besides'}
    # }
    style = 'normal'
    color = '#c69a15'

    def __init__(self, params):
        super().__init__(params)

        self.input_widgets[self.node.inputs[-2]] = {'name': 'varname', 'pos': 'below'}
        #self.input_widgets[self.node.inputs[-1]] = {'name': 'val', 'pos': 'besides'}


class RandSliderWidget(NodeInputWidget, QSlider):
    """a standard Qt slider widget, which updates the node
    input it is attached to, every time the slider value changes"""
    
    def __init__(self, params):
        NodeInputWidget.__init__(self, params)
        QSlider.__init__(self)
        
        self.setOrientation(Qt.Horizontal)
        self.setMinimumWidth(100)
        self.setMinimum(0)
        self.setMaximum(100)
        self.setValue(50)
        self.valueChanged.connect(self.value_changed)
    
    def value_changed(self, val):
        # updates the node input this widget is attached to
        self.update_node_input(Data(val))
    
    def get_state(self) -> dict:
        # return the state of the widget
        return {'value': self.value()}
    
    def set_state(self, state: dict):
        # set the state of the widget
        self.setValue(state['value'])
    

@node_gui(nodes.RandNode)
class RandNodeGui(NodeGUI):
    color = '#fcba03'
    
    # register the input widget class
    input_widget_classes = { 'slider': RandSliderWidget }
    
    # attach the slider widget to the first node input
    # display it _below_ the input pin
    init_input_widgets = {
        0: {'name': 'slider', 'pos': 'below'}
    }

I can import the nodes just fine and I've checked for errors in the code but it's not that complicated, any use of set_output_val results in nothing.

I put in prints in the "Textbox" node, and they only ever confirm 'ok', which means as soon as self.set_output_val is called, it stops the rest of the update_event def

I'm sorry for messy code. :°

i have realized i need to subclass Data, but I don't understand where

It's a bit hard for me to help without precisely understanding the problem.

  • Please clean up the code, so it is nicely readable. why is there 2 Datenbank? what is the purpose?
  • Start the code block with ```python for syntax highlighting
  • Show a screenshot of the flow you are running, what you expect to happen, and what actually happens

Thank you for looking into it!
I've managed to understand my mistakes and got it working like I wanted since posting.

class Data_sub(Data):
    def __init__(self, value=None, load_from=None):
        super().__init__(value, load_from)
    def serialize(self, data):
        self.value ={"data": data}

    def deserialize(self):
        return self.value["data"]

After subclassing Data to serialize what I want to transfer to another node into a Data object I can then send via

self.set_output_val(index: int, data: Data)

To access the sent data via self.input(index: int) I needed to add .payload which isn't specified in the Readme. Without that it is a Data object.

Before being able to use the payload, it needs to be deserialized still:

class PrintNode(Node):
    title = 'Print'
    init_inputs = [NodeInputType()]
    def __init__(self, params: Tuple[Flow | Session]):
        self.dat= Data_sub()
        super().__init__(params)

    def update_event(self, inp=-1):
        
        print(self.input(0).payload) #prints my Data_sub object

        self.dat=self.input(0)
        k = self.dat.deserialize()
        print(k) #prints the data

With this I managed to transfer a dict between nodes.
It seems superfluous to have to subclass Data instead of (de)serialize being a def to call onto to send custom data between nodes.
There is also no error in console when using set_output_val() wrong, which would be helpful.

Unrelated, but before I struggle for another month I'd rather ask:
I plan to use Ryven as a Design GUI, so one can model "a conversation" via nodes with content and paths between them. Is there a quick way to export this, with names for the paths and the nodes (with content)?

I don't fully understand what you intend to use this serialize/deserialize for, there is no need to serialize data structure when passing them from one node to another. You can just pass the Data object to set_output_val and retrieve it in the receiving node with input().

I plan to use Ryven as a Design GUI, so one can model "a conversation" via nodes with content and paths between them. Is there a quick way to export this, with names for the paths and the nodes (with content)?

So Ryven saves projects in a JSON format that should be easy to work with for further processing. Otherwise, you can build it directly into the nodes. For example:

say we have a node "oo:plus:o" (2 in, 1 out) and "oo:minus:o" and they apply "+" and "-" on numeric input values. Now you want to add the ability to instead generate a parameterized expression, e.g."(inp1 + inp2) - (inp3 + inp4)". I would probably define a new data type class Expr(Data) (as opposed to Numeric(Data) or something) which you treat differently in your update_event() in plus/minus.1 This is just an example to get you started, there are many completely different approaches.

Footnotes

  1. The very Pythonic way would be to not change your plus/minus nodes at all but overload the __add__ and __sub__ methods in Expr to secretly generate the expression instead or performing the calculation, when the respective operations are applied to two Exprs, but this might make life more difficult than it needs to be.

#You can just pass the Data object to set_output_val and retrieve it in the receiving node with input().

My point was that I need to subclass Data in the first place to pass custom data, an info I only found thoroughly searching and reading the source code. For example arithmatic is a complex data structure:

def update_event(self):

self.arithmatic.serialize
self.set_output_val(0, self.arithmatic)

But I guess at that point it would be just as possible to implement this into set_output_val, so the Data subclass didnt have to exist in the first place.

I will probably use the JSON provided, but thats a great way to explain it :)
Thank you for the feedback, it's much appreciated.