Text File IO and Ruby I/O in the API

how do you do simple Open, Close, read file, write files that are standard ASCII files (.txt) via the API. Example of ruby reading/ writing into a text file in the Plugins directory

thx in advance

(I moved this from Meta to Developers > Ruby API)

The SketchUp API doesn’t provide methods to read or write files - this is one by Ruby itself. So you need to supplement The Sketch API docs with the Ruby language docs: Class: File (Ruby 2.0.0)

To read and write ASCII files can be as simple as this:

# Read a file.
content = File.read(filename)

# Write a file.
File.write(filename, content)

However, for this part of your question:

I would recommend against writing to files in the Plugins folder. A couple of reasons:

  1. If you support older version of SketchUp the location of the Plugins folder will be in locations where the user need admin access to write. Many users don’t. In recent versions the Plugins folder is in a location in the users own user folder so it should be ok.
  2. When updating an extension SketchUp might remove the old version before extracting the new one. Because of this you should follow standard OS conventions for where to store files. If it’s files the user should have access to then it’s usually in a location similar to their Documents folder. If it’s configuration then it’s something like AppData (Windows) or Library (OSX).

Further more - don’t assume your extension is always installed in the Plugins folder. Some users install them to custom locations - for instance to share among versions etc.

I recommend that if you need to read a file from your extension’s support folder then you use relative paths. Use __dir__ to get the folder for the current RB file. (Or if you need to support Ruby 1.8 - File.dirname(__FILE__).

If you really need to find the actual Plugins folder then you use Sketchup.find_support_folder('Plugins'). But due to the things I mentioned earlier here you really should not need this.

@sannerwind: What might not be apparent to newbies to Ruby, is that the File class is a subclass, of the IO class, and therefor inherits all the methods of it’s superclass(es.)
This is why you will not see read() and write() listed under the File class methods. So you need to refer to both classes.

“Basic Input and Output”, from the ol’ “Pick Axe” Ruby book:

Above gentlemen, thank you. Twas hard to believe SU did not add a few basic I/O or FILE classes/ methods, ruby IO references in their docs Hard to imagine a competent/ worthy system w/o some best and common I/O. Betcha they’re indexed/ hash access and virtual i/o extensions, methods which are in the bowels of their stickyness modules, which they should offer we peons. Re access to /plugins, usually, its not wise, but, depends on whatcha want to do, and why. there is in SU, importer/ classes and io methods.tantalizing to think there’s a centraliztion somewhere for console I/O, Puts, print… I want to see more SU users, so to have a better on-ramp makes good sense, and requires some pre-chewed I/O, file . thx again…

Hi guys, sorry for buggin here. Ive got this piece of code which i got from some one.
It check all open panels in SU and writes those to a txt file. The problem is it somehow doesnt write the list to the txt file

my_Toolbars.each do |tb|
# p SKETCHUP_CONSOLE = my_Toolbars.each
File.open(my_toolbar_list, ‘w’) { |f| f.write(tb) }
# File.write(my_toolbar_list, ‘w’) { |f| f.write(tb) }

i did got the list by printing it to the ruby console (# p SKETCHUP_CONSOLE = my_Toolbars.each)
Im working on mac and do see that the file is changed at a certain time but not is added.

hi, I finally saw you messages and PM’d you at SCF…


Well i got it fixed by changing a variable in the code… i replaced |tb| with my_toolsbars. I then wrights the correct code to the textfile need to get the tool panels back

my_Toolbars.each do |tb|
File.open(my_toolbar_list, ‘w’) { |f| f.write(my_Toolbars) }

The IO.write and Kernel.puts methods expect a String object as an argument.
When this is not true, many Ruby methods will attempt to coerce the argument into a String, by calling to_s() or inspect() upon the argument. If this is not successful, then Ruby will raise a TypeError exception.

ruby_tbs = []

require "objectspace" unless defined?(ObjectSpace)
ObjectSpace.each_object(UI::Toolbar) {|obj| ruby_tbs << obj.name }

native_tbs = UI.toolbar_names

all_tb_names = ruby_tbs + native_tbs


home_dir = ENV["HOME"] || ENV["USERPROFILE"]
my_toolbar_list = UI.savepanel("Save toolbar list",home_dir,"my_toolbar_list.txt")

if my_toolbar_list  # false if user cancelled dialog

  File.open(my_toolbar_list, 'w') {|file|
    all_tb_names.each {|name| file.puts(name) }


it was old v8 one of mine that I had quickly pulled out of something else…

it’s a helper for the fact you need to turn them on individually from the ‘hidden’ menu on a mac…

I fixed it a bit now and added verbose commenting…

# main module namespace
module JcB
  # plugins namespace
  module MacTB
    # we have the main menu in 'Help'
    main_tb_menu = UI.menu('Help').add_submenu('My Toolbar Organiser') unless file_loaded?(__FILE__)

    # then we have 4 sub menu items that we add after the required command

    # we find our list and turn those on using a multiline block
    my_tbs_on = UI::Command.new('MY TBs ON') do
      # we have choices of retriving the file depending on SU and Ruby versions
      # first is for older version
      ## my_toolbar_txt = File.join(File.dirname(__FILE__), 'my_toolbar.txt')
      # but this should work for v16
      my_toolbar_txt = File.join(__dir__, 'my_toolbars.txt')
      # we can check the path while testing
      puts " my_toolbar_txt #{my_toolbar_txt}"
      # then we need SU to understand our list as an array
      # there are many ways depending on how it's written
      # as we write an array format we can just read it using eval
      my_toolbar_list = eval(IO.read(my_toolbar_txt))
      # SU has two ways of storing toolbars
      # UI.toolbar_names.collect{ |native_toolbar| p native_toolbar}
      # ObjectSpace.each_object(UI::Toolbar){ |ruby_toolbar| p ruby_toolbar.name}
      # we can select and show these separately
      # for ruby we get the name and compare it
      my_toolbar_list.each do |ruby_toolbar_name|
        ObjectSpace.each_object(UI::Toolbar) do |named_toolbar|
          named_toolbar.show if named_toolbar.name === ruby_toolbar_name
        end # do |named_toolbar|
      end # do |ruby_toolbar_name|
      # for native it's much easier
      my_toolbar_list.select { |native_toolbar| UI.set_toolbar_visible(native_toolbar, true) }
    end # my_tbs_on
    # then we can add info for the menu, etc...
    my_tbs_on.status_bar_text = 'turn them all on'
    my_tbs_on.menu_text = 'MY TBs ON'
    # end of first item

    # we also need two ways of turning the two types off and it's really ANY toolbar...
    any_tbs_off = UI::Command.new('MY TBs OFF') do
      ObjectSpace.each_object(UI::Toolbar) { |ruby_toolbar| (ruby_toolbar.hide) }
      UI.toolbar_names.map { |native_toolbar| UI.set_toolbar_visible(native_toolbar, false) }
    # then we can add info for the menu, etc...
    any_tbs_off.status_bar_text = 'turn them all off'
    any_tbs_off.menu_text = 'MY TBs OFF'
    # end of second item

    # and turn them all on so we can position and record them
    all_tbs_on = UI::Command.new('Show All TBs') do
      ObjectSpace.each_object(UI::Toolbar) { |ruby_toolbar| (ruby_toolbar.show) }
      UI.toolbar_names.map { |native_toolbar| UI.set_toolbar_visible(native_toolbar, true) }
    # then we can add info for the menu, etc...
    all_tbs_on.status_bar_text = 'turn them all on and start from scratch'
    all_tbs_on.menu_text = 'Show All TBs'
    # end of All item

    # after we tun off any we don't want in our set, we record it to the txt file...
    record_my_choice = UI::Command.new('Record My TBs') do
      # we open the file inside the block so it only ever load when we use the tool...
      my_toolbar_list = File.join(__dir__, 'my_toolbars.txt')
      my_toolbar = []
      # need both types so we push them into an array using [] << inside a block
      ObjectSpace.each_object(UI::Toolbar) do |ruby_toolbar|
        my_toolbar << ruby_toolbar.name if ruby_toolbar.respond_to?(:name) && ruby_toolbar.visible? && ruby_toolbar != nil?
      UI.toolbar_names.map do |native_toolbar|
        my_toolbar << native_toolbar if UI.toolbar_visible?(native_toolbar) && native_toolbar != nil?
        File.open(my_toolbar_list, 'w') { |f| f.write(my_toolbar.each{|tb| tb}) }
    # then we can add info for the menu, etc...
    record_my_choice.status_bar_text = 'put in place'
    record_my_choice.menu_text = 'Record My TBs'
    # end of item
  end # MacTB
end # JcB

as you see it’s a little more complex than getting all the toolbars…
The idea is you turn them all on, position those you use, record the ones left on, and can toggle those off/on when rendering or writing code, etc…