How to update sticky inside Cluster

I’m testing if I could utilise sticky as a “global variable” in a script, where the same value is needed in several locations and inside clusters. Trying to make it updatable.

I can get the “receiver” to update, but once it is inside a cluster it’s just off the radar.
How to, either:

  1. update the cluster, or
  2. find whether the receiver is inside the cluster and update that

Sticky in cluster.gh (6.1 KB)

if you are ok with a dummy parameter
here it is
clustersticky_forum.gh (6.7 KB)

sticky

Hi @Will_Wang ,
thank you for this. It’s interesting, but IMO the need for a dummy parameter kind of negates the purpose of the sticky in this case - as I could just as easily send the values through a hidden wire.

But as a solution I’m trying to comprehend what’s happening in your script. It seems that you did not change any code (including my typo in the sender), just added an input and hooked it up to the receiver. What’s the mechanics here - what now gets recognised and thus, updated?

And why does it stop working, if I unhook the input from the receiver OR if I add some data to it? So, the dummy input really needs to be empty.

Ok, I actually got it working. @Will_Wang

I needed to expire the cluster component, as well as the “Receiver” component inside the cluster.
The components that need to be expired are still recognised by their NickName, which is set to “RECEIVE”. In this way, I don’t need to expire the entire document.

from scriptcontext import sticky
import Grasshopper as gh

if not sticky.has_key("x"): sticky["x"] = x
if not sticky.has_key("y"): sticky["y"] = y


if sticky["x"] != x or sticky["y"] != y:
    sticky["x"] = x
    sticky["y"] = y
    
    for obj in ghenv.Component.OnPingDocument().Objects:
        if obj.NickName == "RECEIVE":
            obj.ExpireSolution(True)
        elif type(obj) == gh.Kernel.Special.GH_Cluster:
            obj.ExpireSolution(True)
            for comp in obj.Document("").Objects:
                if comp.NickName == "RECEIVE":
                    comp.ExpireSolution(True)

Now this works inside and outside of clusters, as is.
clustersticky_forum.gh (11.0 KB)

1 Like

Hi Toni, this is more of an alternative solution, which may fit your needs better:

I’ve recently switched from using the Rhino runtime sticky when requiring global variables across one Grasshopper definition. To instead writing and reading persistent data directly to the Grasshopper file ValueTable. This has the added benefit of actually “sticking” between sessions. And can thus be used to e.g. remember baked objects, element tracking across Grasshopper/Revit. Here’s a quick GHPython example, using the standard json module to pack/unpack the data (i.e. you can have a JSON database hidden in there):

220202_SaveDataToGrasshopperFile_00.gh (3.1 KB)

1 Like

Hi, apart from that. A great way for this use-case is to implement the observer pattern. Create an “observable” (or “subject” or “notifier”) and store it in the sticky. Then any observer(=your “Foo” component) must subscribe once. Whenever the observable changes its states, all subscribers will get notified and update. The observable can also persist itself (e.g. into a .json)!

@AndersDeleuran Thank you for this input. I remembered seeing this sometime, somewhere here. Seems a bit more intelligent version, as the values are actually persistent. Is the ValueTable reachable from inside a cluster, as this is more document dependant (afaik)?

However, this still needs to be updated via the method I used with sticky, or the method @TomTom is outlining here. @TomTom could you clarify this with an example? How do you subscribe and get notified independently?

Something like this might work, although the cluster document nesting might become quite brittle (a cluster is essentially a Grasshopper document within a Grasshopper document):

220202_SaveDataToGrasshopperFile_01.gh (4.6 KB)

@AndersDeleuran If the sticky values are visible through all layers, including inside nested clusters, would it actually make sense to combine the ValueTable and sticky methods? I.e. use Valuetable to store the values, and sticky to distribute them. So, every time document opens, sticky variables are populated by ValueTable values.

Uh that’s a good question. To be honest, I’d avoid clusters if at all possible when it comes to this type of metaprogramming. As they’ve historically demonstrated “odd” expiration behaviour and general “wonkiness”. If you can implement your business code in GHPython on the top-level document, that’d likely be simpler/more predictable.

Edit: Hang on, this (selecting the last document in the Grasshopper document server list) appears to work with deeper cluster nestings as well:

220202_SaveDataToGrasshopperFile_02.gh (7.2 KB)

@AndersDeleuran That seems promising… I’ll try implementing this for my needs…

For some background, I’m actually trying to package small helping snippets for robot toolpath creation (using plugin) for more visually appealing and hassle-free components. Hense the need for clusters. And as many of these need info on the robot, tool, and base - I thought about having that as a floating sticky variable, so there would not be a need to input that info to every “target” component, for instance. But, the info needs to update inside the cluster if the user changes any of these.
Currently, I have packaged this and send through a single hidden wire. I want to get rid of that input on the cluster components.

1 Like

Ah yes, in that case it might be hard to avoid clusters. That said, depending on the plug-ins you’re using, you might be able to implement them in GHPython using node-in-code.

Hi @AndersDeleuran
I’m not sure yet, how relevant this is in this case, but I created this small script that retrieves the Top document from inside a cluster, even inside nested clusters (getting to the Top recursively).

This could be used to update the Parent Top document from inside the cluster. For instance, when changing sticky values from inside cluster.

This might work more reliably than the ds.DocumentCount[-1]

Ok @AndersDeleuran @Will_Wang
I think I came up with the “ultimate” solution using sticky. This could be easily adjusted for ValueTables as well.

Now the Sender works from inside a cluster as well, even if it is inside a nested cluster. And the Receiver works even if it is inside a nested cluster. The update process starts from the Top parent document, and Clusters are searched and updated recursively as well.

from scriptcontext import sticky
import Grasshopper as gh

def updateReceivers(name, doc):
    for obj in doc.Objects:
        #if receiver is present in the document
        if obj.NickName == name:
            obj.ExpireSolution(True)
            
        #if receiver is inside a cluster
        elif type(obj) == gh.Kernel.Special.GH_Cluster:
            obj.ExpireSolution(True)
            updateReceivers(name, obj.Document(""))


def getTopDoc(d):
    owner = None
    topDoc = d
    try:
        owner = d.Owner
        topDoc = getTopDoc(owner.OnPingDocument())
    except:
        pass
    #type GH_Document
    return topDoc
                 

    

#initialise keys for the first use
if not sticky.has_key("x"): sticky["x"] = x
if not sticky.has_key("y"): sticky["y"] = y

#if values have been updated, 
#update receivers
if sticky["x"] != x or sticky["y"] != y:
    sticky["x"] = x
    sticky["y"] = y
    #update all receivers recursively
    #starting from the TOP parent document
    mainDoc =  getTopDoc(ghenv.Component.OnPingDocument())
    updateReceivers("RECEIVE", mainDoc)

clustersticky_clustered.gh (10.8 KB)

2 Likes