Accessing GH canvas componens from Python (outside of GH)


#1

Hi All,

Is it possible to access GH components on canvas (for example, get the list of all sliders) from Python/RhinoCommon running from Rhino Script Editor, and make changes to them? ( like get min/max value of slider, slider type, name etc.)

I have very little experience with this but at this point wondering if this is even possible, and if so, how would a sample code look like to get started with this ?

thank you,

–jarek


#2

A simple approach would be to have a GHPython component sit on the canvas, harvest the information you need, and add this to the sticky dictionary, which you can then read from the Rhino Python Script editor.

Edit: missed the “and make changes to them” bit. That complicates things.


#4

Hi Anders,

Thanks for your response. Ideally I would like to avoid a GH extra component, just accessing the list of all sliders (if any), listing them by names + values, and thrn being able to change value from outside of GH.

If the only way to make it work is extra component in GH to communicate with “outside”, that could be a workaround, but I was hoping we could do without it…

–jarek


#5

Hi @Jarek,

there a several ways. The easiest, wich i used since Rhino 5 is to get hold of the plugin object and access the GH_RhinoScriptInterface documented here. Using this you can make a full roundtrip and set values, but you cannot easily get values from components. An example using the plugin object can be found here.

Since Rhino 6, i switched to using the Grasshopper SDK more often. After importing the whole Grasshopper namespace you have full access to almost everything. To get and set slider values you could do something like this:

  • ping the open grasshopper document
  • check if the DisplayName matches your target document name
  • find the gh objects (components by name) using FindObjects
  • Optionally, compare the search results obj.InstanceGuid with an id
  • Optionally, compare the search results obj.NickName with a name
  • Once you have the slider component obj(s) you can get values from it
import Grasshopper as gh

# TODO: get doc and slider object

# validate this is a slider
if not isinstance(slider, gh.Kernel.Special.GH_NumberSlider):
    return

# get slider values and range
print "CurrentValue:", slider.Slider.Value
print "Minimum:", slider.Slider.Minimum
print "Maximum:", slider.Slider.Maximum

# set a slider value
slider.Slider.RaiseEvents = False
slider.ExpireSolution(True)
slider.SetSliderValue(new_value)  # caution !
slider.ExpireSolution(False)
slider.Slider.RaiseEvents = True

# update solution (recalculates all objects)
mydoc.NewSolution(True) 

When setting a new slider value, it has to be noted that you’re using the proper format and range. Integers are easy but with decimals some notes have to be taken. Eg. cultural differences when a system uses comma instead of point as decimal sign. I appreciate if @piac will jump in here if above code snippet would break something. He wrote a whole post about setting slider values without breaking something. So far, above did work for me but i am just toying around with GH still.

_
c.


#6

Hi Clement, thanks!! lots of good stuff to poke around, but glad to see it may be possible (if you know what you are doing…:wink: )

best,

–jarek


#7

I’m pretty sure you can’t import the Grasshopper namespace from the EditPythonScript editor (at least not out of the box). So you’ll likely have to access the Grasshopper plugin, then get its open documents, then access their canvas objects. Which is probably possible I imagine, but I don’t remember seeing any precedence for it. Crawling the canvas objects from within a GHPython component on the canvas is conversely pretty trivial, by iterating over ghenv.Component.OnPingDocument().Objects and getting/setting their properties.

Can you maybe elaborate upon what you’re trying to do here (why do need to go through the EditPythonScript editor)? Perhaps there is a more straightforward solution.


#8

@AndersDeleuran, what happens if you try it in Rhino 6 while GH is not open ?

I have a handful of scripts here for “external” control which do open GH, load definition, populate various components with values, bake objects etc. So far i used the plugin object methods for it. Since V6 i switched to using the Grasshopper namespace which i have to import first of course.

_
c.


#9

Hi @clement,

The 2nd method (at least the start of it: import Grasshopper as gh works as long as GH has been initiated and is open, otherwise the module can’t be found, which makes sense.
As for the next steps, I have more learning to do…but looks like it’s out there. Got stuck on FindObjects :slight_smile:
Is there a way to tell if GH is even open/loaded ? (so the script doesn’t bother trying importing Grasshopper Namespace)

@AndersDeleuran - with this, I am looking into adding a functionality to one of the tools I am working on where user could pick a slider by name from a pop-up list, and then while working with the tool, use keyboard key shortcuts to manipulate slider values. Maybe also would include the Boolean switch once I figure out how to do these things.

thank you both for helping with this!

–jarek


#10

Yes, use the plugin object. eg like this:

import Rhino
gh_plugin = Rhino.RhinoApp.GetPlugInObject("Grasshopper")
if not gh_plugin: return False

if not gh_plugin.IsEditorLoaded: gh_plugin.LoadEditor()

then give it a litle bit of time to open. After this you can load a document like this:

rc = gh_plugin.OpenDocument(gh_file_path_name)

then import the namespace as described above. You can also check if this fails and stop eg use:

try:
    import Grasshopper as gh
    print "OK, imported Grasshopper"
except ImportError:
    print "Error, start Grasshopper first"

Once you have the grasshopper document object eg. mydoc use this:

import System

# create the searchlist
search_name = "MySliderName"
search_list = System.Collections.Generic.List[str]([search_name])

# search and limit the search to one object
found_items = mydoc.FindObjects(search_list, 1)
if not found_items:
   print "Component '{}' not found in document".format(search_name)
   return 

# return the single found object        
return found_items[0]

_
c.