Icon file path for Command

Fallowning example from Chris Fullmer Extended Views I added .png files to command for toolbar icons…

    cmd1.small_icon = "./images/iso_small.png"

and it works… but I don’t really know why
but being curious I checked this:

file_loaded('oo--' + File.expand_path('./images/iso_small.png'))

and it shows something in ‘My Documents’ home directory where deffiniatelly I don’t have those icons…
Digging in more I had a look into S4U Select and there he even only wrote files names…

cmd.small_icon = "edgelength_small.png"

which accidently helped me guess (maybe correctly) that -probably- cmd.small_icon = … is searching for given file name in all? directories suplied by ‘require’ statements in $LOADED_FEATURS (or $")… ?? Though these are not exactly directories… rather paths… but consist directories…
Is that the reason why people #require ‘sketchup.rb’ several times in samemodule but different files ? maybe not since it doesn’t load the directory of the file from which it is called to load…

still there is a question why the ‘.’ in “./resources/images/image.png” doesn’t mean this File.expand_path(".") which is home directory…

It seems that #small_icon itself can identify a path as relative and resolve it relative to the location of the caller.

Ye it looks like #small_icon has some extra spetial…


for #play_sound it doesn’t work even if its just line below

I just define a constant to the plugin’s folder already in the registrar and keep referencing that throughout the code base. This also allows the workaround for the string encoding on Windows to be performed only once.

1 Like

This does not show where a file exists, but it resolves relative parts (., .., ~) and creates a putative absolute path. This does not mean that a file exists at this path.

I’m not sure what you would use this for… This is a SketchUp-defined function that adds whatever given string to a list, so you could later check with file_loaded? whether this part of your code was already run. It allows code to run “only once”. It makes sense to pass a string unique to your extension, like your reverse domain or the extension’s directory path. Adding every image path would rather make it inefficient.

No, . is a file system entry which means “this directory”. Usually it is resolved as the “current working directory”. When you launch a program like SketchUp, your operating system should ensure that it is launched with your home directory as working directory, unless you override it.

It seems SketchUp does not change the current working directory but resolves it as the current Ruby file’s directory (__dir__).

1 Like

Please be aware that the File::expand_path method is stupid. It does not “divine” correct paths given “half a path”. Instead it just stupidly prepends the current working directory, ie, the result of Dir::pwd, to the given relative path.

What your test shows is that the current working directory as set by SketchUp is beneath the user’s HOME directory. In Ruby the home path will be …


But your code can never rely upon the set working directory staying the same. Any extension could change it at any time.

If your "images" subdirectory is beneath the current file’s directory location, then use the global __dir__ method, thus:

File.join( __dir__, '/images/iso_small.png')

I don’t think so. It will accept a given absolute path, or either … automatically uses the calling files location and appends the given relative path, or it appends the given relative path to the “Plugins” path.
If it cannot find the icon file the icon will be a green silly face icon. There may also be an error output to the console.

No people do this because they do not really understand Ruby’s dependency issues or how SketchUp loads things.

In truth, no one needs to do this (require “sketchup.rb”) anymore since SketchUp 2014 was released with Ruby 2.0.0. Since then SketchUp has preloaded all 3 files in it’s "Tools" folder before loading any extensions.

Normally, in Ruby, a file only requires another file if it uses or calls something that the other file defines.

In SketchUp Ruby, it will be acceptable to require "sketchup.rb" if your code is going to use any of the 3 non-deprecated methods that "sketchup.rb" has added to the Top Level Objectspace.
However a set of files for an extension only needs to do it once. The loader file (as specified in the 2nd argument to the SketchupExtension class constructor call, would be the best place to list all require calls for dependencies.

It will not help to mangle the use of file_loaded() with icon files. We also usually suggest that you use internal module variables to hold the loaded state of your module’s resources, rather than slow file_loaded?() method and the global shared $loaded_files array (where name clashes can occur.) IE …

# Within your extension submodule:
if !@ui_loaded
  @ui_loaded = true
  # Define commands, menu items, and toolbars here

I do not believe this is true. The 2015 API release notes have an entry where they specifically changed what the startup cycle was setting the current working directory to be. (Ie, at one time it was not the user’s home path but was variously other places. I was one of those who politicized for the user’s documents path.) At the console in SU2016 …

#=> C:/Users/Dan/Documents

But as said above, any extension could change it so don’t rely upon it. (Use the block form of the Dir::chdir method.)

The Ruby global __dir__ method is independent of what SketchUp has done and is set by Ruby’s implementation of the global require and load methods.

1 Like

I still think there is something extra about #small_icon method…

cmd1.small_icon = "./images/iso_small.png"
cmd1.small_icon = "/images/iso_small.png"
cmd1.small_icon = "images/iso_small.png"

they all work well… and from my not big experience I can say usually methods are not so flexible about giving some extra or less slashes and dots…
for instance UI.play_sound … It needs exact full path. Non of above relative paths would work.

I’m not sure what you would use this for…

file_loaded(‘oo–’ + File.expand_path(’./images/iso_small.png’))
That was just to see what ruby think the “.” is when ‘inside’ of the file loading it and if it is different from being typed in ruby console… File.expand_path("") shows a different results…

still if a “.” is a current working directory and not a home… “./images/image.png” looks strange to work…


File.join( __dir__, '/images/iso_small.png')

I wouldn’t guess that one since I was mainly trying in ruby console (2017) and there basicly __dir__ == nil , cousing error in your expression… but it works in code…

This is comparing apples and oranges. (Ie, a UI module method with a UI::Command class instance method.) You are expecting the API to be consistent, when it is not. It has been implemented over about 20 years, bit by bit, by various programmers.

Both #small_icon= and #large_icon= should work the same, they both having been implemented and updated at the same times.

The UI.play_sound method has special handling of relative paths as described in it’s documentation. This comes from way back when extensions were still installed beneath SketchUp’s %ProgramFiles% path. (Plugins were moved to a user path on Mac with SU2013 and on WIndows with SU2014.)

Please rely upon the documentation and do not make these incorrect assumptions. When the documentation does not help you check the official API issue tracker or ask for clarification here in the forums. (There are many errors and omissions in the documentation.)

Paths (especially relative paths) can be goofy with the SketchUp API because the plugin locations have changed over the history of SketchUp. This is also why is is better to use absolute paths whenever a method allows it (which should be most methods.)

Also, the $LOADED_FEATURES and $LOAD_PATH arrays are only used by the Ruby file loading methods. Do not assume other API methods use these Ruby globals unless their documentation states so.

Yes this is true. The __dir__ method and the __FILE__ operator are designed to work from code loaded from a file.

You might be able to you use eval and pass a filename argument for use in the console, but this would be “clunky”.

Better to just save the file, and reload it from the console using load() after making changes.

1 Like