UserText vs UserString vs UserDictionary

Hello all,

I am trying to work with ‘Attribute User Text’ via python in grasshopper.
I can get and set these Key and Value pairs with rhinoscriptsyntax as below,
My first question is, is there a way to do the same with rhinocommon?
I have managed to read the Values for existing Keys via Rhino.Runtime namespace, but this only seems to allow me to read values for existing Keys, rather than get and set Keys and Values.

Which leads me to my second question, I have looked at alternatives in rhinocommon and understand that I can get and set Keys/Values for UserString or UserDictionary in the code, but it seems these have nothing to do with the UserText values shown in the properties panel in Rhino, is that right?
Eg. working with the UserDictionary, can have no effect on the Attributes that are displayed in the Rhino properties panel?

All of this means I am wondering what the difference is between these three things (UserText, UserString and UserDictionary). And when and why to try and use which one. Im purposely not mentioning UserData, which appears even more complex!

I am quite confused by all of this but hopefully the questions come across in a clear enough way for someone to offer some advice, I would greatly appreciate it!

rhinoscriptsyntax / UserText:

import Rhino
import rhinoscriptsyntax as rs

scriptcontext.doc = Rhino.RhinoDoc.ActiveDoc

k = rs.GetUserText(iGeometry) 
v = []
for i in k:

scriptcontext.doc = ghdoc

Runtime / UserText:

import Rhino

v = Rhino.Runtime.TextFields.UserText(str(iGeometry),"TestKey1")

UserDictionary - setting Key/Value, not affecting Rhino object:

import Rhino
import Rhino.Geometry as rg

k = iGeometry.UserDictionary.Keys
v = iGeometry.UserDictionary.Values

Hi @Tom_D,

Something to keep in mind, when working with Grasshopper, is that only Rhino objects have attributes. You can make a lot of geometry in Grasshopper, and none of it will have attributes until baked into the document.

rhinoscriptsyntax is just a bunch of easy-to-use functions, written in Python, that call into RhinoCommon.

For example, this is the source for rs.GetUserText:



UserText and UserString is the same thing.

UserDictionary is the ArchivableDictionary object attached to every object that inherits from CommonObject.

– Dale

Hi @dale

Thank you very much for the reply, really appreciated.

There is one thing that is still confusing me…

Thank you for the link to the source for rs.GetUserText , that was really useful. I can see how it refers to the rhinocommon method GetUserString, so this makes sense:

but im still confused why the rs Get/Set methods affect the UserText in the attributes panel in the model, but the rhinocommon Get/Set methods dont.
I guess it could be something to do with how the rhino geometry is referenced or what context the script is running in, but so far I cant work it out!
Ideally I would like to use the rhinocmmon methods to affect the values shown in the model though.

rs affecting attributes panel:

rhinocommon changing string values stored somewhere else ?!

I did try to reference the object in various ways and check the type() , to make sure im looking at the right namespaces in the API but it still hasnt unlocked it for me!

user text tests.3dm (69.1 KB)
user text (14.0 KB)

In case anyone ends up looking at these posts, thought I would try to tie it up…

I did figure out a couple of ways of doing what I wanted in the end, thanks to @dale 's post, looking at other posts here, going through the API documentation and firing a couple of queries to chatGPT.

one way involves making a copy of the object attributes, updating them and then modifying them in the ObjectTable (via Objects property of RhinoDoc):

import Rhino

def set_user_text(object_id, key, value):

    # find object
    active_doc = Rhino.RhinoDoc.ActiveDoc
    doc_object = active_doc.Objects.FindId(object_id)

    # make copy of attributes
    new_attributes = doc_object.Attributes.Duplicate()
    # set new user text
    new_attributes.SetUserString(key, value)

    # modify attributes
    active_doc.Objects.ModifyAttributes(doc_object, new_attributes, True)

object_id = i_geometry_id
key = "TestKey2028"
value = "TestValue2028"
set_user_text(object_id, key, value)

The other way is to use the ObjectAttributes methods, commit changes and update the view:

import Rhino

def set_user_text(object_id, key, value):

    # find object
    active_doc = Rhino.RhinoDoc.ActiveDoc
    doc_object = active_doc.Objects.FindId(object_id)

    # set new user text
    doc_object.Attributes.SetUserString(key, value)
    # commit

    # redraw
object_id = i_geometry_id
key = "TestKey2080"
value = "TestValue2085"
set_user_text(object_id, key, value)

I havent switched the scriptcontext to rhino and back to gh again but i understand that could be useful/important depending on what else is going on.

My understanding is that the first of these two approaches (via the table) might be a bit ‘safer’ , eg. if something went wrong halfway through, there wouldnt be just partially updated data.

Anyway, thanks again for the help, any other comments/criticism gretaly appreciated !