Save an entityID to a temp file?

Hello! New to ruby and trying to figure some stuff out. I have this bit of code that returns entityID


  def getid(selection)
    model = Sketchup.active_model
    sel = model.selection
    ss = sel.grep(Sketchup::ComponentInstance)[0]
    id = ss.entityID
    if ss
      puts id
    end
  end

getid(Sketchup.active_model.selection)

when I try to write the entityID to a file it will not place the text in the file.


  def getid(selection)
    model = Sketchup.active_model
    sel = model.selection
    ss = sel.grep(Sketchup::ComponentInstance)[0]
    id = ss.persistent_id
    if ss
      puts id
    end
  end

  content = getid(Sketchup.active_model.selection)
  fpath = File.join("/Users",ENV["USER"],"Desktop/myfile.txt")
  IO.write(fpath, content)

If I place quotes around content = “” it will print to file as text.


  content = "desk"
  fpath = File.join("/Users",ENV["USER"],"Desktop/myfile.txt")
  IO.write(fpath, content)

Any insights appreciated!

Maybe more to the point I am trying to “remember” a selection so I can return to it after running a script. Is there another better way to do this?

(1) The if ss validity test should come before any attempt to call a method (such as #persistent_id) which could raise a NoMethodError if ss happens to point at nil, (which can happen if the grepped array of instances is empty. Ie #[0] called upon an empty array will return nil.)

(2) The #persistent_id method returns an Integer, not a String which File.write (aka IO.write) expects for the 2nd argument. (But a quick test shows it being converted to a String at least on Windows platform.) You can always be safe and call content.to_s which does not hurt if it is already a String.

(3) fpath = File.join("/Users",ENV["USER"],"Desktop/myfile.txt") will only work on Mac.
You can also just do: "~/Desktop/myfile.txt" as Ruby should resolve “~” to the value of ENV["HOME"].

On Windows ENV["USER"] is ENV["USERNAME"]. Also Windows does not define ENV["HOME"], instead calling it ENV["USERPROFILE"]. SketchUp has since around v2014 defined an ENV["HOME"] alias to ENV["USERPROFILE"] in it’s copy of the environment on Windows so that Ruby’s resolving of “~” in paths will work for certain core Ruby methods. (See File::expand_path class method.)

P.S. - Users normally do not like file and such saved to the desktop. (It can cause a reordering of icons in some versions depending upon desktop settings.) Better to use Sketchup.temp_dir for temporary files. (And good etiquette is to clean up temp files when done.)


Remembering a selection is a different problem that deserves it’s own topic. (Might already have a topic and there at least 1 “selection memory” plugins available.)

1 Like

Just for quick answer. You can take a “snapshot” of any API collection by using it’s #to_a method and assigning it to a reference. In the case of restoring a selection, clear it and then pass the old “snapshot” array to it’s #add method.

1 Like

Hey Dan, thanks for the great info as always.

I will switch to a temp file in side my extensions own folder, using desktop for testing/trying stuff currently.

I am now getting the script to write to file. Switched to entityID which you recommended in another thread. However, I get the desired list of id numbers (4 in this case) in the console, but the script returns [nil, nil, nil, nil] and writes that to file. How do I get the script to return the values instead of the nils?


  def getsid(selection)
    Sketchup.active_model.selection.grep(Sketchup::ComponentInstance) { |entity| 
      puts entity.entityID 
    }
  end
  
  content = getsid(Sketchup.active_model.selection)
  fpath = "~/Desktop/myfile.txt"
  
  IO.write(fpath, content)

Also when I try the ~ i get this error. The file is definitely there… Mac issue maybe?
Screen Shot 2022-02-10 at 2.35.51 PM

I will look at the snapshot method too.

*also using content.to_s results in the same behaviour, no change.

Success! This works…


  def getid
    Sketchup.active_model.selection.grep(Sketchup::ComponentInstance) { |entity| 
      (entity.entityID).to_s 
    }
  end

  fpath = File.join("/Users",ENV["USER"],"Desktop/myfile.txt")
  IO.write(fpath, getid)
  

For the no directory entry error, likely …

fpath = File.expand_path("~/Desktop/myfile.txt")

FYI: Using special folders is fragile at least on Windows as these folders (ie, “Desktop”, “Documents”, “Favorites”, “Videos”, etc.) can be language localized and a Windows system API call may be needed to get the folder name.

What thread ? How old ? Be aware that #entityID is not persistent between sessions. It predates the #persistent_id method.

Also, geometric operations can cause saved IDs to become invalid and new IDs created. (Such as when a face is broken into smaller faces, etc.)

If this is within the same session, it does not make much sense to save an array of integers to a text file. (You’d have to convert them back to integers again.)

Just save the array to a @instance_var reference in your extension submodule.

  def get_ids(ary)
    ary.map(&:persistent_id)
  end

  def some_other_task
    model = Sketchup.active_model
    insts = model.selection.grep(Sketchup::ComponentInstance)
    @ids  = get_ids(insts)
    # ... later on use @ids
  end

I would like to discourage you from passing the model’s selection object around between methods as it’s functionality is limited. In the above example, the result of #grep is a plane ol’ Array object. So insts is more versatile, and the get_ids method becomes more usable by multiple places in the code.

So, in code, when you want to hold onto multiple references, you use an array or a hash. You do not use SketchUp’s selection object. That is meant for the user’s manual use, or if your code must show (highlight) elements for the user to see (and possible use afterward.)

My point is that new coders often misuse the selection object as a temporary collection to hold entities that they want their code to work on. This is not necessary. It also limits what you can do with the members of the selection, as said, it does not have as many nifty methods as a normal array.
And you can easily get a plain array from the selection using #to_a or any of the many filter methods of Enumerable which is mixed into the Selection class.

2 Likes

Hey Dan, thanks for all of this, learning lots. This is the thread about enyityID:

I will PM you what I have so far, maybe I am going about this the wrong way.

Yep this works great.

And it can also work on Windows using English:

fpath = File.expand_path("~/Desktop/myfile.txt")
=> "C:/Users/Dan/Desktop/myfile.txt"

But, FYI, there is no "myfile.txt" file in my "Desktop" folder.
So this method does not actually test for file existence.

Further more, given …

fpath = File.expand_path("~/BogusFolder/myfile.txt")
=> "C:/Users/Dan/BogusFolder/myfile.txt"

… you can see that it stupidly just expands the "~" into the user’s ENV["HOME"] path and does not check that the following path … folder nor file are valid.

So, a call to File.exist?(fpath) will be a smart thing to test after building any path.

1 Like