Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiline exec node #1056

Closed
Kosvor2 opened this issue Dec 24, 2016 · 26 comments
Closed

Multiline exec node #1056

Kosvor2 opened this issue Dec 24, 2016 · 26 comments

Comments

@Kosvor2
Copy link
Collaborator

Kosvor2 commented Dec 24, 2016

exec_node

    str1 = StringProperty(name='str1', default='for i, i2 in zip(V1, V2):', update=updateNode)
    str2 = StringProperty(name='str2', default='    out.append(i + i2)', update=updateNode)
    str3 = StringProperty(name='str3', default='', update=updateNode)

    def draw_buttons(self, context, layout):
        layout.prop(self,  "str1", text="")
        layout.prop(self,  "str2", text="")
        layout.prop(self,  "str3", text="")

    def sv_init(self, context):
        self.inputs.new('StringsSocket', 'V1')
        self.inputs.new('StringsSocket', 'V2')
        self.inputs.new('StringsSocket', 'V3')
        self.outputs.new('StringsSocket', 'out')

    def process(self):
        v1,v2,v3 = self.inputs
        V1, V2, V3 = v1.sv_get(0), v2.sv_get(0), v3.sv_get(0)
        out = []
        
        exec(self.str1+"\n"+self.str2+"\n"+self.str3)
        
        self.outputs[0].sv_set(out)
        

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

image

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

(sadly editing isn't obvious with the box/non emboss version..) you have to leftclick and drag on the text to be able to select it for editing.. (or ctrl + left double click)

    def draw_buttons(self, context, layout):
        b = layout.box()
        col = b.column(align=True)
        col.prop(self,  "str1", text="", emboss=False)
        col.prop(self,  "str2", text="", emboss=False)
        col.prop(self,  "str3", text="", emboss=False)

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

@Kosvor2 maybe make

    def process(self):
        v1,v2,v3 = self.inputs
        V1, V2, V3 = v1.sv_get(0), v2.sv_get(0), v3.sv_get(0)
        out = []
        extend = out.extend    # make these available (and makes the code faster )
        append = out.append     
        exec(self.str1+"\n"+self.str2+"\n"+self.str3)
        
        self.outputs[0].sv_set(out)

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

active_inactive_gif

    def draw_buttons(self, context, layout):
        if not context.active_node == self:
            b = layout.box()
            col = b.column(align=True)
            col.prop(self,  "str1", text="", emboss=False)
            col.prop(self,  "str2", text="", emboss=False)
            col.prop(self,  "str3", text="", emboss=False)
        else:
            col = layout.column(align=True)
            col.prop(self,  "str1", text="")
            col.prop(self,  "str2", text="")
            col.prop(self,  "str3", text="")

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

also i'm using a slightly older version of blender for windows, because of a bug in the windows ID / objects code.. that's why the X isn't there i think?

@Kosvor2
Copy link
Collaborator Author

Kosvor2 commented Dec 24, 2016

Yeah, this X is only waste of space in a text box... and no way to turn it off as far as i know.

exec_node_02

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

Would be neat to make the string variables dynamically created, starting out with 3 -- and if user needs more then press + . i'm not sure if it's possible to make a collection / group for Strings .. i have feeling it should be possible..

but that's low priority..

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

ijatgif

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

import bpy
from bpy.props import StringProperty

import sverchok
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode


lines = """\
for i, i2 in zip(V1, V2):
    out.append(i + i2)
""".strip().split('\n')


def update_wrapper(self, context):
    try:
        updateNode(context.node, context)
    except:
        ...


# Assign a collection
class SvExecNodeDynaStringItem(bpy.types.PropertyGroup):
    line = bpy.props.StringProperty(name="line to eval", default="", update=update_wrapper)


class SvExecNodeModCallback(bpy.types.Operator):

    bl_idname = "node.callback_execnodemod"
    bl_label = "generic callback"

    cmd = bpy.props.StringProperty(default='')

    def execute(self, context):
        getattr(context.node, self.cmd)(self)
        return {'FINISHED'}


class SvExecNodeMod(bpy.types.Node, SverchCustomTreeNode):
    ''' Objects Input Lite'''
    bl_idname = 'SvExecNodeMod'
    bl_label = 'Exec Node Mod'
    bl_icon = 'OUTLINER_OB_EMPTY'

    dynamic_strings = bpy.props.CollectionProperty(type=SvExecNodeDynaStringItem)

    def draw_buttons(self, context, layout):
        if len(self.dynamic_strings) == 0:
            return

        if not context.active_node == self:
            b = layout.box()
            col = b.column(align=True)
            for idx, line in enumerate(self.dynamic_strings):
                col.prop(self.dynamic_strings[idx], "line", text="", emboss=False)
        else:
            for idx, line in enumerate(self.dynamic_strings):
                layout.prop(self.dynamic_strings[idx], "line", text="")

        row = layout.row(align=True)
        # add() remove() clear() move()
        row.operator('node.callback_execnodemod', text='', icon='ZOOMIN').cmd = 'add_new_line'
        row.operator('node.callback_execnodemod', text='', icon='ZOOMOUT').cmd = 'remove_last_line'

    def add_new_line(self, context):
        self.dynamic_strings.add().line = ""

    def remove_last_line(self, context):
        if len(self.dynamic_strings) > 1:
            self.dynamic_strings.remove(len(self.dynamic_strings)-1)


    def sv_init(self, context):
        self.inputs.new('StringsSocket', 'V1')
        self.inputs.new('StringsSocket', 'V2')
        self.inputs.new('StringsSocket', 'V3')
        self.outputs.new('StringsSocket', 'out')

        # add default strings
        self.dynamic_strings.add().line = lines[0]
        self.dynamic_strings.add().line = lines[1]
        self.dynamic_strings.add().line = ""


    def process(self):
        v1,v2,v3 = self.inputs
        V1, V2, V3 = v1.sv_get(0), v2.sv_get(0), v3.sv_get(0)
        out = []
        extend = out.extend
        append = out.append
        exec('\n'.join([j.line for j in self.dynamic_strings]))
        
        self.outputs[0].sv_set(out)
        

def register():
    bpy.utils.register_class(SvExecNodeDynaStringItem)
    bpy.utils.register_class(SvExecNodeMod)
    bpy.utils.register_class(SvExecNodeModCallback)


def unregister():
    bpy.utils.unregister_class(SvExecNodeModCallback)
    bpy.utils.unregister_class(SvExecNodeMod)
    bpy.utils.unregister_class(SvExecNodeDynaStringItem)

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

not sure the nesting is working right there.. but that's all for now.

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

fixed update wrapper, issue with nesting remains..

@Kosvor2
Copy link
Collaborator Author

Kosvor2 commented Dec 24, 2016

You mean like this:


# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

import bpy
from bpy.props import StringProperty

import sverchok
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode


lines = """\
for i, i2 in zip(V1, V2):
    append([v1 + v2 for v1, v2 in zip(i, i2)])
""".strip().split('\n')


def update_wrapper(self, context):
    try:
        updateNode(context.node, context)
    except:
        ...


# Assign a collection
class SvExecNodeDynaStringItem(bpy.types.PropertyGroup):
    line = bpy.props.StringProperty(name="line to eval", default="", update=update_wrapper)


class SvExecNodeModCallback(bpy.types.Operator):

    bl_idname = "node.callback_execnodemod"
    bl_label = "generic callback"

    cmd = bpy.props.StringProperty(default='')

    def execute(self, context):
        getattr(context.node, self.cmd)(self)
        return {'FINISHED'}


class SvExecNodeMod(bpy.types.Node, SverchCustomTreeNode):
    ''' Objects Input Lite'''
    bl_idname = 'SvExecNodeMod'
    bl_label = 'Exec Node Mod'
    bl_icon = 'OUTLINER_OB_EMPTY'

    dynamic_strings = bpy.props.CollectionProperty(type=SvExecNodeDynaStringItem)

    def draw_buttons(self, context, layout):
        if len(self.dynamic_strings) == 0:
            return

        if not context.active_node == self:
            b = layout.box()
            col = b.column(align=True)
            for idx, line in enumerate(self.dynamic_strings):
                col.prop(self.dynamic_strings[idx], "line", text="", emboss=False)
        else:
            col = layout.column(align=True)
            for idx, line in enumerate(self.dynamic_strings):
                col.prop(self.dynamic_strings[idx], "line", text="")

        row = layout.row(align=True)
        # add() remove() clear() move()
        row.operator('node.callback_execnodemod', text='', icon='ZOOMIN').cmd = 'add_new_line'
        row.operator('node.callback_execnodemod', text='', icon='ZOOMOUT').cmd = 'remove_last_line'

    def add_new_line(self, context):
        self.dynamic_strings.add().line = ""

    def remove_last_line(self, context):
        if len(self.dynamic_strings) > 1:
            self.dynamic_strings.remove(len(self.dynamic_strings)-1)


    def sv_init(self, context):
        self.inputs.new('StringsSocket', 'V1')
        self.inputs.new('StringsSocket', 'V2')
        self.inputs.new('StringsSocket', 'V3')
        self.outputs.new('StringsSocket', 'out')

        # add default strings
        self.dynamic_strings.add().line = lines[0]
        self.dynamic_strings.add().line = lines[1]
        self.dynamic_strings.add().line = ""


    def process(self):
        v1,v2,v3 = self.inputs
        V1, V2, V3 = v1.sv_get(0), v2.sv_get(0), v3.sv_get(0)
        out = []
        extend = out.extend
        append = out.append
        exec('\n'.join([j.line for j in self.dynamic_strings]))
        
        self.outputs[0].sv_set(out)
        

def register():
    bpy.utils.register_class(SvExecNodeDynaStringItem)
    bpy.utils.register_class(SvExecNodeMod)
    bpy.utils.register_class(SvExecNodeModCallback)


def unregister():
    bpy.utils.unregister_class(SvExecNodeModCallback)
    bpy.utils.unregister_class(SvExecNodeMod)
    bpy.utils.unregister_class(SvExecNodeDynaStringItem)

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

yeah :)

@zeffii
Copy link
Collaborator

zeffii commented Dec 24, 2016

i quite like this form of exec node, the multi line solution is something that might replace the simpler node-scripts..

@Kosvor2
Copy link
Collaborator Author

Kosvor2 commented Dec 25, 2016

https://github.com/nortikin/sverchok/tree/exec_node

@Kosvor2 Kosvor2 closed this as completed Dec 26, 2016
@zeffii
Copy link
Collaborator

zeffii commented Dec 26, 2016

might have to include a variety of imports too , some of the math module perhaps

@zeffii
Copy link
Collaborator

zeffii commented Dec 26, 2016

with the dynamic strings I think the iojson code probably needs a dedicated encode/decode part..

iojson doesn't know about propertygroups../ collectionproperty

@zeffii zeffii reopened this Dec 27, 2016
@zeffii
Copy link
Collaborator

zeffii commented Dec 27, 2016

iojson support added now #1064

@zeffii zeffii closed this as completed Dec 27, 2016
@Kosvor2
Copy link
Collaborator Author

Kosvor2 commented Dec 28, 2016

exec_text

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

import bpy
from bpy.props import StringProperty
import numpy as np
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode


class SvExecNodeMod(bpy.types.Node, SverchCustomTreeNode):
    ''' Exec Node Mod'''
    bl_idname = 'SvExecNodeMod'
    bl_label = 'Exec Node Mod'
    bl_icon = 'OUTLINER_OB_EMPTY'

    text = StringProperty(default='', update=updateNode)

    def draw_buttons(self, context, layout):
        layout.prop_search(self, 'text', bpy.data, "texts", text="")

    def sv_init(self, context):
        self.inputs.new('StringsSocket', 'V1')
        self.inputs.new('StringsSocket', 'V2')
        self.inputs.new('StringsSocket', 'V3')
        self.outputs.new('StringsSocket', 'out')

    def process(self):
        v1, v2, v3 = self.inputs
        V1, V2, V3 = v1.sv_get(0), v2.sv_get(0), v3.sv_get(0)
        out = []
        extend = out.extend
        append = out.append
        exec(bpy.data.texts[self.text].as_string())

        self.outputs[0].sv_set(out)


def register():
    bpy.utils.register_class(SvExecNodeMod)


def unregister():
    bpy.utils.unregister_class(SvExecNodeMod)

@zeffii do you find it's worth to add this as mode?

@Kosvor2 Kosvor2 reopened this Dec 28, 2016
@Kosvor2
Copy link
Collaborator Author

Kosvor2 commented Dec 28, 2016

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

import json
import bpy
from bpy.props import StringProperty
import numpy as np
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode


lines = """\
for i, i2 in zip(V1, V2):
    append([x + y for x, y in zip(i, i2)])
""".strip().split('\n')


def update_wrapper(self, context):
    try:
        updateNode(context.node, context)
    except:
        ...


class SvExecNodeDynaStringItem(bpy.types.PropertyGroup):
    line = bpy.props.StringProperty(name="line to eval", default="", update=update_wrapper)


class SvExecNodeModCallback(bpy.types.Operator):

    bl_idname = "node.callback_execnodemod"
    bl_label = "generic callback"

    cmd = bpy.props.StringProperty(default='')

    def execute(self, context):
        getattr(context.node, self.cmd)(self)
        return {'FINISHED'}


class SvExecNodeMod(bpy.types.Node, SverchCustomTreeNode):
    ''' Exec Node Mod'''
    bl_idname = 'SvExecNodeMod'
    bl_label = 'Exec Node Mod'
    bl_icon = 'OUTLINER_OB_EMPTY'

    text = StringProperty(default='', update=updateNode)
    dynamic_strings = bpy.props.CollectionProperty(type=SvExecNodeDynaStringItem)

    def draw_buttons(self, context, layout):
        if len(self.dynamic_strings) == 0:
            return

        if not context.active_node == self:
            b = layout.box()
            col = b.column(align=True)
            for idx, line in enumerate(self.dynamic_strings):
                col.prop(self.dynamic_strings[idx], "line", text="", emboss=False)
        else:
            col = layout.column(align=True)
            for idx, line in enumerate(self.dynamic_strings):
                col.prop(self.dynamic_strings[idx], "line", text="")

        row = layout.row(align=True)
        # add() remove() clear() move()
        row.operator('node.callback_execnodemod', text='', icon='ZOOMIN').cmd = 'add_new_line'
        row.operator('node.callback_execnodemod', text='', icon='ZOOMOUT').cmd = 'remove_last_line'
        row.operator('node.callback_execnodemod', text='', icon='TRIA_UP').cmd = 'shift_up'
        row.operator('node.callback_execnodemod', text='', icon='TRIA_DOWN').cmd = 'shift_down'

    def draw_buttons_ext(self, context, layout):
        col = layout.column(align=True)
        col.operator('node.callback_execnodemod', text='copy to node').cmd = 'copy_from_text'
        col.prop_search(self, 'text', bpy.data, "texts", text="")

    def add_new_line(self, context):
        self.dynamic_strings.add().line = ""

    def remove_last_line(self, context):
        if len(self.dynamic_strings) > 1:
            self.dynamic_strings.remove(len(self.dynamic_strings)-1)

    def shift_up(self, context):
        sds = self.dynamic_strings
        for i in range(len(sds)):
            sds.move(i+1, i)

    def shift_down(self, context):
        sds = self.dynamic_strings
        L = len(sds)
        for i in range(L):
            sds.move(L-i, i-1)

    def copy_from_text(self, context):
        for i, i2 in zip(self.dynamic_strings, bpy.data.texts[self.text].lines):
            i.line = i2.body

    def sv_init(self, context):
        self.inputs.new('StringsSocket', 'V1')
        self.inputs.new('StringsSocket', 'V2')
        self.inputs.new('StringsSocket', 'V3')
        self.outputs.new('StringsSocket', 'out')

        # add default strings
        self.dynamic_strings.add().line = lines[0]
        self.dynamic_strings.add().line = lines[1]
        self.dynamic_strings.add().line = ""
        self.width = 289

    def process(self):
        v1, v2, v3 = self.inputs
        V1, V2, V3 = v1.sv_get(0), v2.sv_get(0), v3.sv_get(0)
        out = []
        extend = out.extend
        append = out.append
        exec('\n'.join([j.line for j in self.dynamic_strings]))
        self.outputs[0].sv_set(out)

    def storage_set_data(self, storage):
        strings_json = storage['string_storage']
        lines_list = json.loads(strings_json)['lines']
        self.id_data.freeze(hard=True)
        self.dynamic_strings.clear()
        for line in lines_list:
            self.dynamic_strings.add().line = line

        self.id_data.unfreeze(hard=True)

    def storage_get_data(self, node_dict):
        local_storage = {'lines': []}
        for item in self.dynamic_strings:
            local_storage['lines'].append(item.line)
        node_dict['string_storage'] = json.dumps(local_storage)


def register():
    bpy.utils.register_class(SvExecNodeDynaStringItem)
    bpy.utils.register_class(SvExecNodeMod)
    bpy.utils.register_class(SvExecNodeModCallback)


def unregister():
    bpy.utils.unregister_class(SvExecNodeModCallback)
    bpy.utils.unregister_class(SvExecNodeMod)
    bpy.utils.unregister_class(SvExecNodeDynaStringItem)

exec_from_text

@Kosvor2
Copy link
Collaborator Author

Kosvor2 commented Dec 28, 2016

The second method avoids IF-ELSE statement within a Process that would make the node slower, so i will use it.

@Kosvor2 Kosvor2 closed this as completed Dec 28, 2016
@zeffii
Copy link
Collaborator

zeffii commented Dec 28, 2016

A single if-else statement wouldn't contribute to significant speed fluctuation inside the process function, I wouldn't worry about that.

can add a listener to text editors ctrl+enter operator, to update a current ExecNodeMod straight from text editor too..

@enzyme69
Copy link
Collaborator

enzyme69 commented Jan 4, 2017

Just wondering, with this Multi-Exec, what is the convenient or the special Sverchok functions in the node that allows for wrapping and putting list inside list inside list and stay sane?

We have Script Node already and this one also can be super useful, but not sure where to start? @Kosvor2 Maybe Video tutorial?

@enzyme69
Copy link
Collaborator

enzyme69 commented Jan 4, 2017

@zeffii I like the SN Lite for example and at some point you said it can do "vectorization" and although excited, but I was scared: is this going to be difficult?

@zeffii
Copy link
Collaborator

zeffii commented Jan 4, 2017

@enzyme69 snlite can already to vectorization, the snlite thread has examples.

but I was scared: is this going to be difficult?

jimmy, its difficulty is going to be directly proportional to your level of python-fu. But as it stands I think it's about as simple as it gets for snlite, maybe as it will ever get for code-based vectorization.

@enzyme69
Copy link
Collaborator

enzyme69 commented Jan 4, 2017

Ok, interesting, in that case I'll do. Feels motivated!

@zeffii
Copy link
Collaborator

zeffii commented Jan 4, 2017

before you go nuts on snlite, it's still beta. consider it broken ( but it works for my needs ) .

*reloading a .blend still doesn't pick up 'current' param values, * but this thread is not the place to talk about snlite.

@enzyme69 enzyme69 reopened this Sep 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants