InstanceDefinition UserDictionary

Hi,

I’m trying to do this:

  1. User clicks a button in a panel.

  2. Get the selected Block.

  3. Set a key on the BlockDefinition:

if (myInstanceObject.InstanceDefinition.UserDictionary.SetKey("mykey", 42)) {
    // tried both with, and without myInstanceObject.CommitChanges(); here.
}

This succeeds. I get true back, and if I try to read the value immediately after, it’s correct (42).

  1. In a DisplayConduit I read the value back with:
if (myInstanceObject.InstanceDefinition.UserDictionary.TryGetInteger("mykey", out int itemId)) { 
  // Draw textdot here with `itemId`
}

This fails. There is nothing there.

Am I at all allowed to set UserDict on InstanceDefinitions? If so, how do I “commit” the changes, so they are available everywhere?

Shamelessly tagging you @dale as you are usually really good at answering my silly questions.

Hi @aske,

This script seems to work:

import Rhino
import scriptcontext as sc
import System

__rnd__ = System.Random(System.Environment.TickCount)

def test_idef_setvalue(iref_obj, key, value):
    if isinstance(iref_obj, Rhino.DocObjects.InstanceObject):
        rc = iref_obj.InstanceDefinition.UserDictionary.Set(key, value)
        if rc:
            print("Dictionary set, value = {0}".format(value))

def test_idef_getvalue(iref_obj, key):
    if isinstance(iref_obj, Rhino.DocObjects.InstanceObject):
        rc, value = iref_obj.InstanceDefinition.UserDictionary.TryGetInteger(key)
        if rc and value:
            print("Dictionary get, value = {0}".format(value))

def testme():
    filter = Rhino.DocObjects.ObjectType.InstanceReference
    rc, objref = Rhino.Input.RhinoGet.GetOneObject("Select block", False, filter)
    if not objref or rc != Rhino.Commands.Result.Success: 
        return rc
    
    obj = objref.Object()
    key = "test"
    value = __rnd__.Next(1000)
    
    test_idef_setvalue(obj, key, value)
    test_idef_getvalue(obj, key)
    
if __name__ == "__main__":
    testme()

I see your key had different cases (“mykey” vs “myKey”). Perhaps this is the issue?

– Dale

Sorry, I should have been more clear: It’s RhinoCommon.

edit: and the key is just a placeholder. It’s the same in the real code.

The button is in a Eto panel.

But overall it should work, is what I’m getting from you?
Do I need to commit, or something else?

I spent some time creating a minimal repro case from the Rhino Eto Samples.
However it works fine in the sample I have created, and I literally just copied code around :man_shrugging:

I did narrow down the issue to something really weird in our project though: The key is put into a centralized constants file as public const string. This works in the sample project. But in the full project it doesn’t work.

Here is a video of the behavior:
https://stykka-public.s3.eu-west-1.amazonaws.com/tmp/weird_behavior.mov

The video shows Visual Studio, compilation to Rhino, and demonstration of working and none-working code:
00:14 For all blocks, all blockdefs keys are displayed in a display conduit in a TextDot.
00:18 In a Panel (Stykka → Templates) there is a button that triggers the code.
00:21-00:27 When clicking the “Set for current button” button the TextDot text does not update.
00:34 Code is displayed that uses the constant string.

		        // This fails!
				reloadedInstanceObject.InstanceDefinition.UserDictionary.Set(Constants.USER_KEY_ITEM_TEMPLATE_ID, item.id);

00:36 Code is changed to using the same string, but typed in place.

		        // This works!
				reloadedInstanceObject.InstanceDefinition.UserDictionary.Set("com.stykka.part.item_template.id", item.id);

00:43 Compile and run.
01:33 Same button as before now works.
01:52 Constant is shown.

I’m not a C# expert. Am I declaring the constants wrong? It works in the smaller sample project.

namespace Stykka.Common.Utils
{
    public static class Constants
    {
        public const string USER_KEY_ITEM_TEMPLATE_ID = "com.stykka.item_template.id";
    }
}

I don’t really know what to do from here, other than hardcoding the strings :sob:

Hi @aske,

The video isn’t helpful. And without code I can run here, I can only speculate.

But if your conduit is holding onto an instance of InstanceDefinition and somewhere in your plug-in you add user text to it, then your conduit is going to need to re-get the instance definition as it is now holding onto an old version.

My wild guess…

– Dale

I put up the modified Eto sample project here: Set and Display int on Block defs via EtoPanel and DisplayConduit. · Doerge/rhino-developer-samples@beb34e5 · GitHub

To be clear: This works’ish. There is sometimes some issues with the TextDot not displaying immediately. You either have to click the “Set” button a few times, or move the Blocks around a bit.

I think I’ve been careful to always use the Guids and always call into Rhino to get a reference to the instancedef’s, but maybe you can spot my mistake.

Thanks for taking a look!

@dale did you have time to look at my sample? Could you spot any mistakes?

Not really. Can you post the project?

– Dale

I’m not sure I understand your question. The diff I linked to previously is in a forked McNeel project:

Or do you mean my “real” project? I attempted to split it out of there, to make it easier to see what’s going on.