Python components def RunScript empty outputs

Hi,

I am running a RunScript method in ironpython.

The codes seems to work correctly, but I cannot output anything.
What could be a reason for it?

It’s quite hard to tell what’s going on there. When you’re running a GHPython component in SDK mode you output by calling return at the end of the RunScript method. See Giulio’s old example here:

When you have several outputs you simply return as many variables (e.g. return ham, spam, eggs). On a side note I’d recommend running in procedural mode unless there’s a good reason not to.

1 Like

Why don’t you do something like this?

class Foo:
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = self.bar(a, b)

    def bar(self, a, b):
        return a + b
    
if __name__ == "__main__":
    f = Foo(12, 14)

    # Outputs
    a = f.c

@AndersDeleuran and @diff-arch thank you both.

I finally used return ham, spam, eggs option.

I went in this complicated way because I am using compas componentize workflow and am following the same template: compas-dev/compas-actions.ghpython_components: Trying to make Grasshopper development version-control friendlier since 1337. (github.com)

Seems to be a simple mistake. Thanks

1 Like

I hadn’t seen this before, looks cool but also quite convoluted. Depending on what your requirements are, two components I developed back at CITA might be enough. I don’t think I ever did publish the code outside the Smart Geometry/Advances in Architectural Geometry workshops in 2016, but here it is:

"""
Iterates the GHPython components in the document and makes user objects of them
if the their Category property matches the input Category parameter. Also grabs
all the code and writes this to .py files.
    Input:
        Toggle: Activate the component using a boolean toggle {item,bool}
        Folder: The folder/directory to save the user objects and source code to {item,str}
        Category: The name of the category for which to make user objects {item,str}
    Output:
        TLOC: Lines of code in user object {item,int}
    Remarks:
        Author: Anders Holden Deleuran
        License: Apache License 2.0
        Version: 160812
"""

ghenv.Component.Name = "GHPythonToUserObject"
ghenv.Component.NickName = "PTUO"
ghenv.Component.Category = "CM_FAHS"
ghenv.Component.SubCategory = "0 Development"

import os
import Grasshopper as gh

def ghPythonToUserObject(ghPyComp,writeFolder):
    
    """ Automates the creation of a GHPython user object. Based on this thread:
    http://www.grasshopper3d.com/forum/topics/change-the-default-values-for-userobject-popup-menu """
    
    # Make a user object
    uo = gh.Kernel.GH_UserObject()
    
    # Set its properties based on the GHPython component properties
    uo.Icon = ghPyComp.Icon_24x24    
    uo.BaseGuid = ghPyComp.ComponentGuid
    uo.Exposure = ghenv.Component.Exposure.primary
    uo.Description.Name = ghPyComp.Name
    uo.Description.Description = ghPyComp.Description
    uo.Description.Category = ghPyComp.Category
    uo.Description.SubCategory = ghPyComp.SubCategory
    
    # Set the user object data and save to file
    uo.SetDataFromObject(ghPyComp)
    uo.Path = os.path.join(writeFolder,ghPyComp.Name+".ghuser")
    uo.SaveToFile()
    
    # Update the grasshopper ribbon UI (doesn't seem to work)
    #gh.Kernel.GH_ComponentServer.UpdateRibbonUI()

def exportGHPythonSource(ghPyComp,writeFolder):
    
    """ Export the source code of a GHPython component """
    
    # Get code and lines of code
    code = ghPyComp.Code
    code = code.replace("\r","")
    lines = code.splitlines()
    loc = len(lines)
    
    # Check/make source file folder
    srcFolder = os.path.join(writeFolder,"src")
    if not os.path.isdir(srcFolder):
        os.makedirs(srcFolder)
        
    # Write code to .py file
    srcFile = os.path.join(srcFolder,ghPyComp.Name + ".py")
    f = open(srcFile,"w")
    f.write(code)
    f.close()
    
    return loc

if Toggle and Folder and Category:
    
    # Make GH component warning handler
    wh = gh.Kernel.GH_RuntimeMessageLevel.Warning
    
    # Iterate the canvas and get to the GHPython components
    TLOC = 0
    for obj in ghenv.Component.OnPingDocument().Objects:
        if type(obj) is type(ghenv.Component):
            
            # Check that category matches and export to files
            if obj.Category == Category:
                ghPythonToUserObject(obj,Folder)
                loc = exportGHPythonSource(obj,Folder)
                TLOC += loc
                obj.AddRuntimeMessage(wh,"I was just saved as a user object, hooray!")

And this one:

"""
Updates the source code of GHPython components on the canvas with the code
in the .py files in the Folder if the GHPython component Name property is 
the same as the name of one of the .py files.
    Inputs:
        Toggle: Activate the component {item,bool}
        Folder: The location of the .py source code files {item,str}
    Outputs:
        GUIDS: List of GHPython component that were updated {list,str}
    Remarks:
        Author: Anders Holden Deleuran
        License: Apache License 2.0
        Version: 160402
"""

ghenv.Component.Name = "UpdateGHPythonSourceCode"
ghenv.Component.NickName = "UPSC"
ghenv.Component.Category = "CM_FAHS"
ghenv.Component.SubCategory = "0 Development"

import os
import Grasshopper as gh

def getSrcCodeVersion(srcCode):
    
    """ Attempts to get the first instance of the word "version" (or, "Version")
    in a multi line string. Then attempts to extract an integer from this line 
    where the word "version" exists. So format version YYMMDD in the GHPython 
    docstring like so (or any other integer system): """
    
    # Get first line with version in it
    srcCodeLower = srcCode.lower()
    verStr = [l for l in srcCodeLower.split('\n') if "version" in l]
    if verStr:
        
        # Get the first substring integer and return it
        verInt = [int(s) for s in verStr[0].split() if s.isdigit()]
        if verInt:
            return int(verInt[0])
    else:
        return None

def updateGHPythonSrc(srcFolder,ghDocument):
    
    """ Update the source code of GHPython components in ghDocument with the code
    in the .py files in the srcFolder if the GHPython component Name property is 
    the same as the name of one of the .py files. """
    
    # Get python source files in folder
    pyFiles = [f for f in os.listdir(srcFolder) if f.endswith(".py")]
    
    # Make dictionary with script name as key and source code as value
    srcCode = {}
    for f in pyFiles:
        fOpen = open(os.path.join(srcFolder,f))
        fRead = fOpen.read()
        fName,fExt = os.path.splitext(f)
        srcCode[fName] = fRead
        
    # Make GH component warning handler
    wh = gh.Kernel.GH_RuntimeMessageLevel.Warning
    
    # Iterate the gh canvas and update ghpython source code
    guids = []
    for obj in ghDocument.Objects:
        if type(obj) is type(ghenv.Component):
            if obj.Name in srcCode:
                
                # Check that srcCode file has higher version number than obj source code
                srcCodeVer = getSrcCodeVersion(srcCode[obj.Name])
                objSrcVer = getSrcCodeVersion(obj.Code)
                if objSrcVer and srcCodeVer > objSrcVer:
                    
                    # Update the obj source
                    obj.Code = srcCode[obj.Name]
                    obj.AddRuntimeMessage(wh,"GHPython code was automatically updated, input/output parameters might have changed")
                    guids.append(str(obj.InstanceGuid))
                    
    return guids

if Toggle and Folder:
    GUIDS = updateGHPythonSrc(Folder,ghenv.Component.OnPingDocument())

Edit: This related “find and replace” for all GHPython components in a Grasshopper document is also quite useful for renaming variables and such:

"""
Find and replace substrings for all GHPython code in the grasshopper doc
    Inputs:
        Toggle: Activate the component using a boolean toggle {item,bool}
        Old: String to replace {item,str}
        New: String to replace with {item,str}
    Outputs:
        GUIDS: List of GHPython component that were updated {list,str}
    Remarks:
        Author: Anders Holden Deleuran
        License: Apache License 2.0
        Version: 160402
"""

ghenv.Component.Name = "ReplaceGHPythonString"
ghenv.Component.NickName = "RPS"
ghenv.Component.Category = "CM_FAHS"
ghenv.Component.SubCategory = "0 Development"

import os
import Grasshopper as gh

def replaceGHPythonString(oldStr,newStr,ghDocument):
    
    """ Find and replace substrings for all GHPython code in the grasshopper doc """
    
    # Make GH component warning handler
    wh = gh.Kernel.GH_RuntimeMessageLevel.Warning
    
    # Iterate the gh canvas and get to ghpython source code
    guids = []
    for obj in ghDocument.Objects:
        if type(obj) is type(ghenv.Component):
            if oldStr in obj.Code:
                obj.Code = obj.Code.replace(oldStr,newStr)
                obj.AddRuntimeMessage(wh,"GHPython code was updated, stuff might have changed!")
                guids.append(str(obj.InstanceGuid))
                    
    return guids

if Toggle and Old and New:
    GUIDS = replaceGHPythonString(Old,New,ghenv.Component.OnPingDocument())
else:
    GUIDS = []
2 Likes