GH sc.sticky change event / call python code components from another python component

Hello,

I would like to load and save a configuration from and to a file in Grasshopper using python, which works, apart from the right save function trigger.

Once loaded, the configuration is saved in a dict in the sc.sticky. The config file should automatically (without for example pressing a button) reflect changes made to the configuration, but I don’t know how to properly trigger the save function.

Right now, I am using a Timer-component to periodically trigger a save config python component.

I checked if there is an event that I could use for this (https://developer.rhino3d.com/wip/api/grasshopper/html/N_Grasshopper_Kernel.htm), but I didn’t find a suitable one. Additionally, it doesn’t seem like one can even use these events from Python, only from VB and C#. Is it possible in GH to call python from a VB or C# script?

Another solution would be calling some python component from another python component. I did not find anything on this here either.

Of course, I could trigger the update directly by connecting all of the components, but I’d rather not do this.

Thanks

from scriptcontext import sticky
id = ghenv.Component.InstanceGuid
if sticky.has_key('i'):
   a = sticky['i']
from scriptcontext import sticky
comp = ghenv.Component.OnPingDocument().FindObject(id, False)
sticky['i'] = v
comp.ExpireSolution(True)

UpdateById.gh (9.7 KB)

3 Likes

Hello Mahdiyar, thanks for your answer! :smile:
How fragile is this solution? When copying these elements from one file to another, the ids change, if you copy them as a cluster they ids stay the same. Is this generally true?

I think a version which is using named groups (that’s the way Rhino computed identified elements too as I learned today) would be more robust.
Unfortunately I haven’t found a way to find out which Grasshopper groups a component is part of yet.
I checked the forum for solutions (Is there a way to accesss groups-information in an object? this one is talking about Rhino groups, not about GH groups), but coudn’t find anything on it.

Thanks again!

#ID
from scriptcontext import sticky
comp = ghenv.Component.OnPingDocument().FindObject(id, False)
sticky['i'] = v
comp.ExpireSolution(True)
#Name
from scriptcontext import sticky
sticky['i'] = v
for obj in ghenv.Component.OnPingDocument().Objects:
    if obj.NickName in names:
        obj.ExpireSolution(True)
#Group
from scriptcontext import sticky
import Grasshopper as gh

def FindPythonObjectInCurrentGroup():
    pythonObjects = []
    ghObjects = ghenv.Component.OnPingDocument().Objects
    for obj in ghObjects:
        if type(obj) is gh.Kernel.Special.GH_Group:
            if obj.ObjectIDs.Contains(ghenv.Component.InstanceGuid):
                group = obj
                for obj in ghObjects:
                    if ghenv.Component.ComponentGuid == obj.ComponentGuid:
                        if ghenv.Component.InstanceGuid != obj.InstanceGuid:
                            if group.ObjectIDs.Contains(obj.InstanceGuid):
                                pythonObjects.append(obj)
    return pythonObjects

sticky['i'] = v
pythonObjects = FindPythonObjectInCurrentGroup()
for obj in pythonObjects: 
    obj.ExpireSolution(True)

Update.gh (9.1 KB)

4 Likes

Thank you very much! :+1:

Hello! I know that this is a python post, but I was trying to create something similar in C# with a sender component to a clustered receiver, can anyone help me plaese?

Sender:

using System;
using Grasshopper.Kernel;

public class SenderComponent : GH_Component
{
    public SenderComponent() : base("Sender", "S", "Send numeric data to receiver", "Test", "Data") { }

    protected override void RegisterInputParams(GH_InputParamManager pManager)
    {
        pManager.AddNumberParameter("Data", "D", "Numeric data to send", GH_ParamAccess.item);
    }

    protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }

    protected override void SolveInstance(IGH_DataAccess DA)
    {
        double data = 0;

        if (!DA.GetData(0, ref data)) return;

        // Get the nickname of the target receiver
        string targetNickname = "ReceiverNickname";

        // Find the remote component with the given nickname within the cluster
        GH_Component targetComponent = null;
        foreach (IGH_DocumentObject obj in OnPingDocument().Objects)
        {
            if (obj is GH_Cluster)
            {
                GH_Cluster cluster = obj as GH_Cluster;
                foreach (IGH_Component clusterComponent in cluster.ClusterObjects(true))
                {
                    if (clusterComponent.NickName == targetNickname && clusterComponent is GH_Component)
                    {
                        targetComponent = clusterComponent as GH_Component;
                        break;
                    }
                }
            }
        }

        // Check if the target component was found
        if (targetComponent != null)
        {
            // Send data to the target component
            targetComponent.Params.Input[0].AddVolatileData(new GH_Path(0), 0, data);
        }

        // Expire solution to update the changes
        ExpireSolution(true);
    }

    public override Guid ComponentGuid
    {
        get { return new Guid("GUID_HERE"); }
    }
}

Sender:

using System;
using Grasshopper.Kernel;

public class ReceiverComponent : GH_Component
{
    public ReceiverComponent() : base("Receiver", "R", "Receive numeric data from sender", "Test", "Data") { }

    protected override void RegisterInputParams(GH_InputParamManager pManager) { }

    protected override void RegisterOutputParams(GH_OutputParamManager pManager)
    {
        pManager.AddNumberParameter("Data", "D", "Received numeric data", GH_ParamAccess.item);
    }

    protected override void SolveInstance(IGH_DataAccess DA)
    {
        double data = 0;

        // Receive data from the sender component
        DA.GetData(0, ref data);

        // Do something with the received data...

        // Output the received data
        DA.SetData(0, data);
    }

    public override bool Read(GH_IReader reader)
    {
        // Perform any additional reading here if needed
        return base.Read(reader);
    }

    public override bool Write(GH_IWriter writer)
    {
        // Perform any additional writing here if needed
        return base.Write(writer);
    }

    protected override void ExpireDownStreamObjects()
    {
        // Expire downstream objects to trigger a re-computation when data is received
        OnPingDocument().NewSolution(true);
        base.ExpireDownStreamObjects();
    }

    public override Guid ComponentGuid
    {
        get { return new Guid("GUID_HERE"); }
    }
}

Thanks!