Understanding Attribute DIctionaries(?)

I’m struggling to understand Attribute DIctionaries and how to best use them.
So here are some observations and perhaps misconceptions.

  1. A model can have multiple attribute dictionaries. Each has a name that must be unique across all extensions. A given extension might want to have more than one dictionary. I’ll discuss why below.

  2. An attribute dictionary has a list of unique attribute keys. Say I create a dictionary called “MyFurnitureApp” with 2 keys: “Manufacturer Name” and “Model Number”. I want the user to be able to enter values for each of these for each piece of furniture.

  3. If you are familiar with database concepts, you might think the API does error checking on valid dictionary names, valid keys and uniqueness of keys. NOT SO. There are 2 ways to create dictionary entries: Model.set_attribute and Entity.set_attribute. Both create dictionaries and keys by default if you mistype either in your code.

  4. Model.set_attribute, which you might think of as establishing the schema, even stores VALUES at the level of the model. I suppose you could use these a defaults when setting values for items. Another use of values at the level of the model would be for a separate reference dictionary to validate allowable values entries from a list. You might have a dictionary of valid manufacturers or valid model numbers.

  5. Component definitions have their own Attribute dictionaries. Say you load component definitions from external source. The definitions are loaded “in the Model”, but unlike other entities, their attribute dictionaries are not included in model.attribute_dictionaries. You have to reference the definitions themselves to see their attribute dictionaries. And, of course, when you add an instance of a component to the drawing window, the instance does not have the attributes But you can add different attributes to instances of the same definition without making them unique.

Please correct me if I’m wrong about any of this. Thanks

The common method is:
something.set_ attribute("dictionary_name", "key_name", value)
The first two arguments are strings and need to be unique to your extension - unless it’s changing a dynamic_component etc…
The third ‘value’ can be several types:

  • string : “cat”, “a dog” etc
  • integer : 0, 1, -666 etc
  • float { 0.0, 1.0, -6.666
  • boolean : true, false ,nil…
  • array : [1,2,3], [“a”, 1.0, true] etc

something.get_ attribute("dictionary_name", "key_name", default_value)
The first two are as above, the third is optional, but if that key is not set it returns ‘nil’.

the ‘something’ can be most things - the model itself, any entity placed in the model [vertex, edge, face, component_instance, guide point or edge, image, group], also ‘stored’ component definition, material, layer, style already loaded into the model.
Using the same named dictionary for say the model and a definition has no direct connectivity between those dictionaries - unless you mimic it in your code.
I’d used a dictionary attached to the model itself to store model-specific key/values - like my user-chosen default extension settings - whereas saving those settings using Sketchup.write_defaults etc is global and affects all models.

For a given component definition you might want to have a specific dictionary, which is setting/getting its manufacturer and part_name key/values etc, but if you place instances of that, then you’ll need to inspect the definition to get those - however, you might want to give each instance other entries - for that same dictionary - setting instance specific key/value pairs for say color, id etc - these might vary by instance instead of by definition…

Yes, and so can have an AttributeDictionaries collection.

To be more precise, any Sketchup::Entity subclass instance object (and Sketchup::Model instance objects,) can have a collection of Sketchup::AttributeDictionary references.

This means you may also attach a dictionary to model collection objects like the layers collection, etc. (They need not only be a drawing element type of object.)

Yes, because the toplevel dictionary collections (at the model or entity levels) are shared objectspaces.

But not just unique across extensions, but also (most importantly) across all authors !

Therefore, each dictionary name should follow the same conventions extensions use for Ruby module namespacing.

IE: AuthorNamespace_SpecificPlugin_DictionaryName

:warning: Yes, as we know, the SketchUp Team has not been doing this. The “dynamic_attributes” dictionaries are kind of global, and are not qualified. There are also quite a few other plugins and examples that do not use qualified dictionary names. The Ocean Modeling extension would be another example.

Building upon the reply to 1(a) above, … Sketchup::AttributeDictionary objects, are a subclass of the Sketchup::Entity class.

This brings to light an obscure feature most do not know of. Because they are an entity, dictionaries can themselves have a collection of Sketchup::AttributeDictionary references.

Can you say “Nested dictionaries, boys & girls ?”

* At sub-nested dictionary levels, it will not be necessary to prefix qualify dictionary names, if the topmost dictionary (in a nested dictionary tree) is properly qualified to an author and extension.

True.

At one time if the dictionary name was created with a nil value, or the key was either nil or an empty string, it would corrupt the model and crash SketchUp. This was fixed, but the fix is not listed in the public online Release Notes.

So there are SketchUp versions, some ppl use, that still have this nasty bug.

Most coders create little wrapper validation methods that trap nil or an empty string (that results from any process, such as user input, etc.,) when creating or accessing dictionaries with regard to the name or keys. (Values can be an empty string.)

Do not use a global dictionary with the empty string name. The DC extension uses such a dictionary (or did at one time,) and doing so might interfer with Dynamic Components.

So use defensive programming techniques. Validate all user input, etc.

True you could use them as defaults for dictionaries attached elsewhere. (I often use them for model specific user plugin settings.)

But remember that components often have dictionaries. Components can be saved out as SKP component files. When these files are directly opened for editing reasons, their dictionaries will now be at the top model level.

This is one of the reason that model objects mimic entity objects, in many API respects.

Normally this belongs outside a model. In a plugin database file, or a lookup by some other means, say a datafile file (XML, CSV, etc.) on the plugin author’s website.

An exception would be a Source Control List specific to only this model (if this is a model of a component or something purchased as a single product.)

This is false the way you have written it.

Only the model object’s OWN attribute dictionaries are EVER included in it’s dictionaries collection.

Neither loading external component definitions, nor inserting component instances, will affect the model’s dictionaries, or their attributes.

So, the “unlike other entities” phrase makes no sense and causes the whole statement to be false.

True. Because, …

Every entity’s dictionary objects must be accessed via it’s own collection ([] / []=) or the instance wrapper methods (get_attribute / set_attribute) upon the object reference itself.

I meant “unlike other entities” in the following sense: to the casual user, Model.attribute_dictionaries would include ALL dictionaries “in the Model”.
Yet the component browser calls all components as “in Model” that have been loaded (whether “used” or not used). No other kind of drawing_element (edges, faces etc) could have dictionaries that don’t appear in model.attribute_dictionaries.

So its a question of SU UI clarilty of terminology. Correct reminology: Definitions are never “in Model”, only instances are.

Still does not make sense. There is no built-in interface for “the casual user” that can possibly even give them the idea that a Model.attribute_dictionaries collection even exists!

It is something only known to API programmers, (or was, until a few coders like Eneroth and Andreas created Attribute Inspector plugins.)

What does the Component Browser have to do with the model’s dictionaries collection ? Nothing.

They are apples and oranges. One is the model’s collection of definitions, the other is the model’s collection of dictionaries. Two separate things.

What !? Where do you get these ideas from ? This is exasperatingly wrong.

I’ll say it again,… once more in a different way:

EVERY entity that can have dictionary objects, and does have dictionary objects, ONLY has those dictionary objects, in THEIR OWN dictionaries collection; whose dictionary members ONLY contain THEIR OWN attributes.

Saying that an entity’s dictionary objects will appear in any other entity’s (including the model’s) dictionaries collection, is wrong and ludicrous !

Just because definitions can have dictionaries, does not make them both the same kind of fruit. Definitions are no different than any other entity subclass object, that can have a dictionary attached. Equating similar behavior to dictionaries that components have, just because components can have dictionaries is totally incorrect. There is no documentation that has ever loosely implied this.

Yes, this is one of your misconceptions, because you are confounding different object types and their interfaces, and assuming concepts that are incorrect.

And I do not want any Ruby newb coming in here and reading these misconceptions, and thinking they are true.

Once again incorrect. Definitions loaded into the model’s definitions collection always appear in the “In Model” collection listing in the Component Browser, because they are then a part of the internal model database. Instances used are a reference that is also added to the database, but into the model’s entities collection.

So you are again confounding the functionalities of two separate collections, … neither of which has anything to do with attribute dictionaries.

No it is not! Dictionaries do NOT appear in the UI. (And let’s not bring the DC dialogs into this and the weird way it uses dictionaries “under it’s hood.” That will just further cloud the concepts.)


Back to the struggle to understand dictionary objects.

They are most like a standard Ruby classes Hash and PStore, that is written into the internal model database, and associated with an entity or the model. (In this respect, the model acts no different than any other entity that can have a dictionary.)


At this time, I’ll remind you of your closing in the 1st post:

I am struggling to understand why when we give you what you asked for, and try and set you straight, that we get weird convoluted arguments in return.

I feel like the character in the cartoons that when he realizes he’s “been had” that he momentarily morphs into a sucker.

So, the last thing I will add to the convo is in regard to

There is no best way. There are IMO innumerable ways to use them. Parametric settings, plugin defaults, user preferences, classifications, identification, specifications, etc., etc.

1 Like

bare in mind that adding attributes can create file bloat…

recently 20k of attributes was found to be increasing file size ten fold on a 3mb model [i.e. it was 30mb with the 20kb of attributes added]…

in response the extension developer is now purging the attributes after use and/or on model closure…

john

20kb of attributes on every Entity, or a 20kb dictionary once?

Thanks Dan, for knocking some sense into my thick skull. I somehow just couldn’t imagine that an object class called a “dictionary” could exist and yet have no characteristics commonly understood to be essential to the meaning of that term.

A “dictionary” is a master list of uniquely named concepts, each defined by a set of attributes and attribute values that each potential instance must have to be considered a true instance of the concept. “Dictionaries” further usually define the kind and range of values allowed for each attribute of an instance.

Advanced programming tools have existed in various languages for at least 40 years to aid in the construction and maintenance of such “dictionaries” along with methods to create and label instances and design UI input dialogs that enforce dictionary constraints on users.

Dictionary/instance relationships at the user level have been common in CAD systems I’ve developed and used since the 1980’s. SketchUp’s component and materials libraries are fine examples.

What we have here and misleadingly called “attribute dictionaries” are the electronic equivalent of Postit-Notes. Each note is limited to 3 fields (2 keys and value) and any number of them can be stuck on any instance of object class as long as the combination of the 2 keys is unique to that object instance. That’s it.

Such an object is an essential primitive building block, but I kept looking for more functionality in the API and couldn’t find it.

An analogous situation would be Ruby with Array.new but without any of the other array methods. Take a look at those methods. I don’t think any SU extension developer (especially a newbie) should be spending their time creating similar methods each in their own way. Do you?

Because do they have some uses, I vote to call them “Data Stickers” to avoid further confusion.

Have you stopped to consider that YOU are only one confused here ? Or that YOU are purposely creating the confusion (via topic inveiglement,) because you do not like the use of the word “dictionary” ?

You can call (refer to) objects by whatever local identifiers makes you feel “warm and fuzzy”.

module BMDroid
  module SomePlugin

    # Refer to the class by my own "nickname":
    DataStickers = Sketchup::AttributeDictionary

  end
end

But, don’t expect the rest of the SketchUp coding world to follow your example. For a myriad of reasons:

  • We don’t actually refer to these classes by class identifier. (Ie, their constructors are wrapped within the functionality of accessor instance methods.) So, the point is moot.

  • Again and again, the SketchUp development team has tried to explain that their philosophy is a minimalist core that is extensible. (This also applies to the SketchUp APIs.) So,…

    • It matters not what gargantuan Microsoft (with their thousands of programmers) calls their attribute collection objects in their Visio VB API, …

    • nor what huge AutoDesk (with their hundreds of programmers) calls their attribute collection objects in their various AutoCAD APIs, … because the extension code is not transportable between these applications and SketchUp.

  • The API class names are not renamed when, for example, features are renamed in the user interface. (Ie, Sketchup::Page was renamed in the GUI to “Scene”, but remains in the API with the historical classname.) Arbitrary renaming would break thousands of plugins “out in the wild.”

  • There is like 12 years of online documentation and forum discussions using the current classname. Do you wish to track all that down and attach some notice of a name change ? I don’t think so.

This is your idea on what this class should be, (based upon your past experience with other product APIs, and what they called a “dictionary.”)

But if they were like this, they’d likely be much less powerful, and not as flexible. Some of the rendering extensions may not have been able to save the kinds of data that they do in SketchUp’s attribute collection objects, if they were more strongly typed.

Firstly, the basic definition of a dictionary, is simply a list (collection) of terms and their definitions. The printed form is for human consumption and is usually ordered alphabetically. But the computer form is made up of key-value pairs, and is accessed by the key.

The real underlying question for this whole topic thread (and what you should have asked in the opening post, instead of inveigling us into this worthless argument about what a “dictionary” should be,) is:

Why does the Sketchup::AttributeDictionary API class use the word “Dictionary” in it’s name?

Because SketchUp is programmed in C++ as an MFC application.

The SketchUp Ruby API is made up of many wrappers of C++ objects. In many cases the names of the wrapper classes or methods, just used the underlying C++ name (or something similar.)

It may not make sense to you, Barry, but it does to the C++ core programmers maintaining the code (both for the SketchUp engine and the API interfaces.)

Yes, because they were designed to be this way, by Microsoft (or the inventors of C++.)

If you look at the MSDN page, you’ll see that they were designed to be compatible with basic database tables.

They are simply lists of key / values pairs, and are one of the basic forms of data collections in all of programming. (They can be called by different names in different programming languages / environments. Ie, Struct, Hash, HashTable, Table, etc.)

It also likely makes them the most compatible that they can be when exported to various file formats.

So the name was not chosen to mimic AutoCAD APIs, nor to mimic Visio VB, nor to confuse anyone who makes intransigent assertions that anything using the word “dictionary” in it’s name, must conform to their idea of what a dictionary should be, or must conform to what some other applications call a “dictionary”.

The class comes with a basic set of accessor and iterator methods, and in addition, does inherit methods from it’s ancestors.

Sketchup::AttributeDictionary.ancestors

outputs:

 Enumerable,
 Sketchup::Entity,
 Object, 
 JSON::Ext::Generator::GeneratorMethods::Object,
 Kernel,
 BasicObject

So it has all the nice methods from the Enumerable mixin module, and all it’s other ancestor superclasses.

But the SketchUp API documentation does not document the Ruby core objects and methods. You need to refer to Ruby core documentation for this.


I agree that in SketchUp programming, GUI programming and input validation can be tedious and boring, but others have already been there, and done that. ThomThom wrote a GUI library that leverages UI::WebDialog “under it’s hood” but is used in a more Ruby-ish way. It may even have built-in validation as he wrote a good tutorial on this at his blogsite.

If he can do it, so can others, and so can you.

Nothing stops you from writing your own helper class to do input validation. Or from using ThomThom’s libs. Or someone else’s.

It would be more difficult for coders to create wrapper or helper classes if the dictionary class was not as basic as it is.

2 Likes