Library Plugin Viewer/Editor

I am thinking about creating a custom library that stores a bunch of data for a given truss or foundation assembly, which I can then later pull from when a user chooses to edit one of these assemblies.

To my knowledge SketchUp does not come with a built in library editor or viewer, can someone suggest the best third party plugin for this task.

have you looked into attribute_dictionaries?

you can store data using ruby’s :to_json or binary data using :marshall…

and retrieve them for use in your html

storing as shorthand is always best…

john

What kind (types) of data ?

To do what with ?

How will the user choose to edit them ?

As John mentioned, there is the standard library JSON, which I think SketchUp loads in most cases before any plugins are processed. But you always should show a dependency if using it at the top of your extension module:

module Author::SomePlugin
  require "json"
  # ...
end

However, neither the Sketchup::AttributeDictionary class, nor any of the other API entity classes, are implemented to properly interface with the JSON or Marshalling libraries.

Sketchup::AttributeDictionary.json_creatable?
#=> false

… meaning the class does not define a .json_create() class method.

The Sketchup::AttributeDictionary class gets the .to_json instance method from inheritance via Object (which gets it from the json mixin library.)

But the Sketchup::AttributeDictionary class has (so far) failed to properly define .inspect and .to_json method overrides, so that Sketchup::AttributeDictionary#to_json() will work.

# With a DC selected, and "cdef" set to reference it's definition object:
ad = cdef.attribute_dictionaries["dynamic_attributes"]
#=> #<Sketchup::AttributeDictionary:0x0000000ea1c378>
j = ad.to_json
#=> "#<Sketchup::AttributeDictionary:0x0000000ea1c378>"

Normally you can use the JSON::generate(hash_object) module function workaround.

But be aware, as of SketchUp 2016, the Sketchup::AttributeDictionary class has not provided .to_h() or .to_hash() conversion methods, and neither has the Ruby 2.0 core.

The Ruby 2.2 core (SketchUp 2017+) added an inheritable .to_h() conversion method that can be inherited from the Enumerable mixin module, (which is already mixed into Sketchup::AttributeDictionary and many other API collection type classes.)
But, I haven’t yet done much SketchUp 2017 testing so test if Ruby 2.2.4’s .to_h() mixin works for the dictionary class. If not you’ll need to go the singleton route, below, but omit the response test in favor of a SketchUp version test (or some other way to determine if this method is working correctly.)

You can add singleton .to_h() methods to your specific plugin dictionaries, “on the fly” like:

if !dict.respond_to(:to_h)
  def dict.to_h
    Hash[self.entries] # Hash class alternate constructor.
    # Use Enumerable#entries(), which creates an array argument,
    # of nested arrays, each with a key / value pair.
  end
end

Then, you can use:

json_string = dict.to_h.to_json

because Hash’s to_json will work, in Ruby 2.0 and higher if the JSON library is loaded.


In the same way, SketchUp entity objects have not been made compatible with the core Marshal module. None of the SketchUp API entity classes implement the proper marshalling conversion methods requisite to be compatible with this library, which are marshall_dump() and marshall_load().

In addition marshalling complicates matters with byte stream versioning compatibility issues that are probably best avoided. (See the second paragraph of the Marshal module documentation.)

If this data will only be used in a choice control that the user will pick from (ie, a list in a dialog box,) then it maks no sense to bloat the model filesize by attaching an entire library to a model entity.

A simple Hash of data records, accessed by a (String or Symbol) key, will suffice (ie, tabular data.)

The kind of object used for the data records is debatable:

  • If you only wish to access each data field by numerical index, then each record can be an Array.
  • If you wish to access each data field by a key name, then each record can be an Hash.
  • If you wish to access each data field by a field name instance method (ie, dot notation,) then each record can be an OpenStruct.

So I’d recommend using a Hash of record OpenStructs (or Hashes.)
And then just save them as JSON strings using Ruby core File class.

See JSON lib’s additions to OpenStruct class:

# USE (assumses inside a module that extends itself):
dir = File.dirname(__FILE__) # your plugin subdirectory
PATH ||= {} # Hash of paths used in plugin
PATH[:truss_data]= File.join( dir, "data", "truss_data.json" )

@trussjson = get_datajson(PATH[:truss_data])
# You may need to pass this json to a WebDialog or HTMLDialog

@trussdata = get_datahash(@trussjson)
# For internal Ruby building geometry etc.
# This can be set immediately after getting from JSON file,
# or later from the WebDIalog if changes have been made.

# After making changes to @trussdata hash:
save_datajson(PATH[:truss_data],@trussdata)

def save_datajson(filepath,datahash)
  # Always wrap IO ops within a rescue block:
  File.open(filepath, "w") {|f| f.write(datahash.to_json) }
rescue
  # handle IO errors here
else
  # set a success flag ?
end


def get_datajson(filepath)
  # Always wrap IO ops within a rescue block:
  File.read(filepath, "r")
rescue
  # handle IO errors here
else
  # set a success flag ?
end

def get_datahash(json_str)
  JSON.parse(json_str)
rescue
  # handle parse errors here
else
  # set a success flag ?
end

This depends upon the types of data. SketchUp since v2014 has come with Ruby Standard library, which most libraries work well.

Ruby has a CSV library.
http://ruby-doc.org/stdlib-2.0.0/libdoc/csv/rdoc/index.html

You could create your data in a spreadsheet then export as comma separated values text file. Then convert that to either a Hash and than a JSON string using a little Ruby utility.

Read the file as an array of arrays, assume first field is the hash key.

require "csv"
csv = CSV.read(PATH[:truss_csv])
@datahash = {}

# Each record in hash is an array of data fields
csv.each {|record|
  key = record.shift.to_sym
  @datahash[key]= record # assignment
}

# Each record in hash is an hash of data fields
fields = [...] # assume an array of field names exists
csv.each {|record|
  key = record.shift.to_sym
  record_hash = {}
  record.each_with_index do |val,i|
     record_hash[fields[i].to_sym]= val
  end
  @datahash[key]= record_hash # assignment
}

# For an OpenStruct record, load the library:
require 'ostruct'
# Then the assignment line in 
# the above hash of hashes example, would be:
  @datahash[key]= OpenStruct.new(record_hash) # assignment 
# Cleanup when done with csv object
csv.clear
csv = nil
GC.start # garbage collect

Another alternative is the standard library file type PStore:
http://ruby-doc.org/stdlib-2.0.0/libdoc/pstore/rdoc/index.html

… which is very much like a cross between JSON and OpenStruct. However, it uses Marshal behind the scenes, which has it’s limitations and quirks (mentioned above.)

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.