Run/Solve a Grasshopper file without starting Rhino CPython Rhino.Inside

Hello, how can I enable the solution of my grasshopper document?

Example from https://github.com/mcneel/rhino.inside-cpython.

The attributes of the document already state that the document is enabled, but it does not work as expected, even with ExpireSolution.

import rhinoinside
rhinoinside.load()

filePath =r"simple_def (1).gh"

Rhino = rhinoinside.Rhino
#Start grasshopper in "headless" mode
pluginObject = Rhino.RhinoApp.GetPlugInObject("Grasshopper")
if pluginObject:
    pluginObject.RunHeadless()

import Grasshopper
io = Grasshopper.Kernel.GH_DocumentIO()

if not io.Open(filePath):
    print("File loading failed.")
else:
    doc = io.Document
    doc.Enabled = True
    doc.ExpireSolution()
    for obj in doc.Objects:
        try:
            param = Grasshopper.Kernel.IGH_Param(obj)
        except:
            continue
            
        #look for param with nickname "CollectMe"
        if param.NickName == "CollectMe":
            param.CollectData()
            param.ComputeData()

            for item in param.VolatileData.AllData(True):
                try:
                    success, line = item.CastTo(Rhino.Geometry.Line())
                    if success: print("Got a Line: {}".format(line))
                except:
                    pass

Got a Line: -5.5574407422041,12.7579864904777,0,-3.39439210003612,-2.61104333545273,0

doc.Enabled, doc.EnableSolutions

(True, True)

The solution itself does not get executed, how can I do it?

I can only trigger the parameters by param.CollectData() and param.ComputeData(), but not the whole solution.

To load a GH definition with CPython Rhino.Inside you can do the following, without having to get hold of a plugin object like that. This is closer to how Grasshopper is loaded in Rhino Compute.

import rhinoinside
from pathlib import Path
import clr

sysdir = Path(r"C:\Program Files\Rhino 7 WIP\System")
plugdir = Path(sysdir, "..", "Plug-ins").resolve()
rhinoinside.load(f"{sysdir}")

GrasshopperDll = f'{Path(plugdir, "Grasshopper", "Grasshopper.dll").resolve()}'
GH_IODll= f'{Path(plugdir, "Grasshopper", "GH_IO.dll")}'
GH_UtilDll= f'{Path(plugdir, "Grasshopper", "GH_Util.dll")}'

clr.AddReference(GrasshopperDll)
clr.AddReference(GH_IODll)
clr.AddReference(GH_UtilDll)

# Set up ready, now do the actual Rhino usage

import System
import Rhino

import Grasshopper
from Grasshopper.Kernel import GH_Document
from GH_IO.Serialization import GH_Archive

definition = GH_Document()
archive = GH_Archive()
archive.ReadFromFile(r"N:\gh\times_viz.gh")

archive.ExtractObject(definition, "Definition")

for ob in definition.Objects:
    print(ob)

CollectData() and ComputeData() is also how Compute uses GH definitions:

1 Like

Nathan, thanks for your answer.

I think I still don’t understand how I could run a solution. For example it seems like python components are not collected and computed, because Grasshopper.Kernel.IGH_Param throws an error with these.
What I want / am looking for is executing the GH file as if it were open.

Thanks for your help and have a nice weekend!

Computing the data for parameters should result in solution being run AFAIK. @fraguada has been recently looking at that part of the code in compute, so maybe he can share some of his wisdom here.

1 Like

This is an example file with a python component that saves a file containing the current date and time to the temp directory.
You could use it for testing the behaviour. With GUI it shows up, without it does not.

example_file.gh (3.5 KB)

Used code

print("0")
import rhinoinside
from pathlib import Path
import clr
print("1")
sysdir = Path(r"C:\Program Files\Rhino 7 WIP\System")
plugdir = Path(sysdir, "..", "Plug-ins").resolve()
rhinoinside.load(f"{sysdir}")

GrasshopperDll = f'{Path(plugdir, "Grasshopper", "Grasshopper.dll").resolve()}'
GH_IODll= f'{Path(plugdir, "Grasshopper", "GH_IO.dll")}'
GH_UtilDll= f'{Path(plugdir, "Grasshopper", "GH_Util.dll")}'
print("2")
clr.AddReference(GrasshopperDll)
clr.AddReference(GH_IODll)
clr.AddReference(GH_UtilDll)

# Set up ready, now do the actual Rhino usage
print("3")
import System
import Rhino

import Grasshopper
from Grasshopper.Kernel import GH_Document
from GH_IO.Serialization import GH_Archive

definition = GH_Document()
archive = GH_Archive()
archive.ReadFromFile(r"path\example_file.gh")

archive.ExtractObject(definition, "Definition")

for obj in definition.Objects:
    try:
        param = Grasshopper.Kernel.IGH_Param(obj)
    except:
        continue
    param.CollectData()
    param.ComputeData()
    print(param.Name)

You’ll be needing a `definition.NewSolution(True, GH_SolutionMode.Silent). Here is a simple addition example where each run sets different inputs, then gets solution:

inside_addition.gh (1.5 KB)

The GH file should live next to the .py file.

#tested with python3.7
import rhinoinside
from pathlib import Path
import clr

sysdir = Path(r"C:\Program Files\Rhino 7 WIP\System")
plugdir = Path(sysdir, "..", "Plug-ins").resolve()
rhinoinside.load(f"{sysdir}")

GrasshopperDll = f'{Path(plugdir, "Grasshopper", "Grasshopper.dll").resolve()}'
GH_IODll= f'{Path(plugdir, "Grasshopper", "GH_IO.dll")}'
GH_UtilDll= f'{Path(plugdir, "Grasshopper", "GH_Util.dll")}'

clr.AddReference(GrasshopperDll)
clr.AddReference(GH_IODll)
clr.AddReference(GH_UtilDll)

# Set up ready, now do the actual Rhino usage

import System
import Rhino

import Grasshopper
from Grasshopper.Kernel import GH_Document, GH_SolutionMode
from GH_IO.Serialization import GH_Archive
from Grasshopper.Kernel.Data import GH_Path
from Grasshopper.Kernel.Types import GH_Number

import random

definition = GH_Document()
archive = GH_Archive()
archive.ReadFromFile(r"N:\gh\inside_addition.gh")

archive.ExtractObject(definition, "Definition")


for ob in definition.Objects:
    print(f"{ob.NickName}:")
    for inp in ob.Params.Input:
        inp.VolatileData.Clear()
        n = float(random.randint(0, 100))
        ghn = GH_Number(n)
        inp.AddVolatileData(GH_Path(0), 0, GH_Number(n))
        print(f"\tSet input {inp.NickName} to {ghn}")

definition.NewSolution(True, GH_SolutionMode.Silent)

for ob in definition.Objects:
    for output in ob.Params.Output:
        output.CollectData()
        output.ComputeData()

        pathcount = output.VolatileData.PathCount
        idx = 0
        while idx < pathcount:
            b = output.VolatileData.get_Branch(idx)
            for item in b:
                print(f"\toutput {output.NickName} -> {item.Value}")
            idx = idx + 1

Output of a test run:

D:\Dev\Rhino\rhinoinside_cpython_test> python .\test.py
InsideAddition:
        Set input A to 34
        Set input B to 100
        output Result -> 134
1 Like

Thanks for your example, maybe you should add it to the CPython example on Github (https://github.com/mcneel/rhino.inside-cpython).
But even with the line

definition.NewSolution(True, GH_SolutionMode.Silent)

the example with the python component I posted does not work :thinking:.
What am I doing wrong / what is missing?

No, I don’t know why the script component does not want to work

1 Like

@nathanletwory do you know anything new related to this topic?

I have no new information regarding py script components in a definition when used like this.