Instructor content
-
That is also what I have Niall. What is that extra folder you have there Dan?
-
@chris fullmer said:
That is also what I have Niall. What is that extra folder you have there Dan?
This is the APPDATA path guys, not the Program Files path (or Program Files(x86) if your on Windows 6+.)
In the APPDATA path, below the "Sketchup #{VER}" folder, for PRO, there are 3 applet folders:
"Sketchup" (where the session.dat file is kept,) "Layout" and "StyleBuilder" folders. Each one of these folders holds user settings and preference files.On Win7, the path should be:
"C:\Users\#{ENV['USERNAME']\AppData\Roaming\Google\Google SketchUp #{VER}\SketchUp\Plugins
"As I said, on the Mac the User's APPDATA "Plugins" folder is created by Sketchup, but on PC you need to create it yourself, and push the path onto the
$LOAD_PATH
array to use it forrequire()
orload()
calls.If you wish (on PC,) to have user scripts load from the APPDATA path on Sketchup startup, you need to have a script in the "Shared" Plugins folder (the one in the Program Files path,) that has a call using the
require_all()
method from the sketchup.rb file.
Ex:<span class="syntaxdefault"></span><span class="syntaxcomment"># !appdata_load.rb<br /></span><span class="syntaxkeyword">require(</span><span class="syntaxstring">'sketchup.rb'</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">VER </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Sketchup</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">version</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">to_i<br />USER_PLUGINS </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">expand_path</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"#{ENV['APPDATA']}\Google\Google SketchUp #{VER}\SketchUp\Plugins"</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">require_all</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> USER_PLUGINS </span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span>
If you use it, the
require_all()
method will push the APPDATA plugins path onto the$LOAD_PATH
array for you.
I do have a Win7 machine in the other room, but have not yet installed Sketchup on it, so... I'm going by what "should be" according to published information. (May be that the Sketchup installer has a mind of it's own.)
Addendum: On Win7 (linix-like) you may see a symbolic link directly to AppData, in the My Computer tree that hides the "C:\Users#{ENV['USERNAME']}" prefix.
-
Sorry Dan, you lost me at "APPDATA". I never knew there was an alternate folder schema somewhere. I'm really not sure what appdata is.
-
@chris fullmer said:
Sorry Dan, you lost me at "APPDATA". I never knew there was an alternate folder schema somewhere. I'm really not sure what appdata is.
+1 on this, sorry for the confusion dan
Time to make a fool out of myself for a second time:
@thomthom said:
@chris fullmer said:
That worked for me, are you guys seeing differently?
The snippet I posted work for me when I added it to Bezier Tools as a working example. I generate a relative path from
Sketchup.get_resource_path( 'helpcontent' )
In the snippet you posted earlier, doesn't
parts = origin.split( File::SEPERATOR ).size
include the separator in the drive name (ie. C:/), so you get x+1 slashes to the root C:/ from helpcontent instead of x, which negates the/tool
folder?Also, just to double-check, is there meant to be an escaping character for w in
/^w:/
(ie./^\w:/
) or is something weird going on?sorry for the noob questions, I really appreciate the responses.
-
@bentleykfrog said:
In the snippet you posted earlier, doesn't parts = origin.split( File::SEPERATOR ).size include the separator in the drive name (ie. C:/), so you get x+1 slashes to the root C:/ from helpcontent instead of x, which negates the /tool folder?
That is a possibility... I might very well be wrong about where it base the path from. But the positive side-effect of my little bug is that it works.
@bentleykfrog said:
Also, just to double-check, is there meant to be an escaping character for w in /^w:/ (ie. /^\w:/) or is something weird going on?
You're right. And it's there in my original code - it seems that the forum ate the escape character!
Lemme try again:
Without PHP formatting:
# Get Instructor Path # # Tool.getInstructorContentDirectory expects a path relative to SketchUp's # Resource/<locale>/helpcontent/ folder, despite the documentations use an # absolute path. # # This method is a wrapper that generates a path to the actual help content # which SketchUp can use. # # The given path must be under the same drive as SketchUp's help content. # # This quick exist in all current SketchUp versions. # Current; SketchUp 8 M1 def self.get_instructor_path( path ) path = File.expand_path( path ) origin = Sketchup.get_resource_path( 'helpcontent' ) # Check if drive matches origin_drive = origin.match( /^(\w);/ ) if origin_drive origin_drive = origin_drive[1].downcase end path_drive = path.match( /^(\w);/ ) if path_drive path_drive = path_drive[1].downcase path = path[2...path.size] # Trim drive letter end if path_drive && origin_drive return nil unless origin_drive == path_drive end # Build relative path parts = origin.split( File;;SEPARATOR ).size path_to_root = "..#{File;;SEPARATOR}" * parts relative_path = File.join( path_to_root, path ) return relative_path end
With PHP formatting:
<span class="syntaxdefault"><br /> </span><span class="syntaxcomment"># Get Instructor Path<br /> #<br /> # Tool.getInstructorContentDirectory expects a path relative to SketchUp's<br /> # Resource/<locale>/helpcontent/ folder, despite the documentations use an<br /> # absolute path.<br /> #<br /> # This method is a wrapper that generates a path to the actual help content<br /> # which SketchUp can use.<br /> #<br /> # The given path must be under the same drive as SketchUp's help content.<br /> #<br /> # This quick exist in all current SketchUp versions.<br /> # Current; SketchUp 8 M1<br /> </span><span class="syntaxdefault">def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">get_instructor_path</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">path </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">path </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">expand_path</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">path </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">origin </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">Sketchup</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">get_resource_path</span><span class="syntaxkeyword">( </span><span class="syntaxstring">'helpcontent' </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxcomment"># Check if drive matches<br /> </span><span class="syntaxdefault">origin_drive </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">origin</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">match</span><span class="syntaxkeyword">( /^(</span><span class="syntaxdefault">w</span><span class="syntaxkeyword">);/ )<br /> if </span><span class="syntaxdefault">origin_drive<br /> origin_drive </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">origin_drive</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">].</span><span class="syntaxdefault">downcase<br /> end<br /> path_drive </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">match</span><span class="syntaxkeyword">( /^(</span><span class="syntaxdefault">w</span><span class="syntaxkeyword">);/ )<br /> if </span><span class="syntaxdefault">path_drive<br /> path_drive </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">path_drive</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">].</span><span class="syntaxdefault">downcase<br /> path </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">path</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">2.</span><span class="syntaxkeyword">..</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">size</span><span class="syntaxkeyword">] </span><span class="syntaxcomment"># Trim drive letter<br /> </span><span class="syntaxdefault">end<br /> </span><span class="syntaxkeyword">if </span><span class="syntaxdefault">path_drive </span><span class="syntaxkeyword">&& </span><span class="syntaxdefault">origin_drive<br /> </span><span class="syntaxkeyword">return </span><span class="syntaxdefault">nil unless origin_drive </span><span class="syntaxkeyword">== </span><span class="syntaxdefault">path_drive<br /> end<br /> </span><span class="syntaxcomment"># Build relative path<br /> </span><span class="syntaxdefault">parts </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">origin</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">split</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">File</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">SEPARATOR </span><span class="syntaxkeyword">).</span><span class="syntaxdefault">size<br /> path_to_root </span><span class="syntaxkeyword">= </span><span class="syntaxstring">"..#{File;;SEPARATOR}" </span><span class="syntaxkeyword">* </span><span class="syntaxdefault">parts<br /> relative_path </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">join</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">path_to_root</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">path </span><span class="syntaxkeyword">)<br /> return </span><span class="syntaxdefault">relative_path<br /> end<br /></span>
-
grrr.... the PHP formatter broke the code!
-
@thomthom said:
@bentleykfrog said:
In the snippet you posted earlier, doesn't parts = origin.split( File::SEPERATOR ).size include the separator in the drive name (ie. C:/), so you get x+1 slashes to the root C:/ from helpcontent instead of x, which negates the /tool folder?
That is a possibility... I might very well be wrong about where it base the path from. But the positive side-effect of my little bug is that it works.
Its definitely a positive side effect, and consistent across OS's as macs start with '/' instead of 'C:/'
@thomthom said:
With PHP formatting:
PHP & escape characters == no love (not even with double escaping), which sucks with Regex. From my experience there's no telling how many processes a php string has been through on a forum post (ie. as written in your own code or copy-and-paste from another website/forum) so I think the php code goes on the singular escape and doesn't bother to check for doubles, which is wrong even if you're formatting php code. I suppose since sketchucation is in php, they've at least got to make the code mute to prevent some pretty easy hacking, by formatting php code as a string. (I'll stop here since its way off topic)
-
Initial testing on the mac works too, though there's no need to copy the instructor files to the temp directory on a mac
-
@thomthom said:
grrr.... the PHP formatter broke the code!
Good catch!
We best be careful from now on using the [ code = php ] bbtag.
-
@chris fullmer said:
Sorry Dan, you lost me at "APPDATA". I never knew there was an alternate folder schema somewhere. I'm really not sure what appdata is.
It's a path where applications keep user specific data files.
And there are two (2) AppData paths (on PC at least.)
One is "Local" AppData, that is used only on that specific computer.
The other is "Roaming" AppData, which is used for a specific user, no matter what workstation they log onto within a given network.It looks like Sketchup does not use the Local AppData path at all (although GoogleEarth and GoogleUpdater do.)
Also.. if you have your system set to "hide system folders" you may not see either of the AppData folders at all. (They are defined as "hidden system" folders on XP.)
To see what the paths are, open a cmd shell, and type set to view all the Environment variables.
check the values for:
APPDATA
LOCALAPPDATATo make the situation a bit more confusing.. there is also a generic APPDATA path for All Users on a PC computer, kept under the "All Users" profile. This is the profile that is normally considered to be the "Shared" profile. It's where the "Shared Documents", "Shared Music", "Shared Pictures" etc. are kept. (And also where the SU Pro license file is kept.)
So normally we would think it better to keep "Shared Plugins" under the "All Users" profile, instead of beneath the Program Files application folder. (In the past, there was not much difference, but on Windows versions 6+, there is a security hiccup when trying to use a Plugins folder beneath the Program Files path. It may be easier, if Google changes the Sketchup PC edition, to use the "All Users" profile for the "Shared Plugins" and "Shared Tools".) -
@bentleykfrog said:
@bentleykfrog said:
Its a long way around but I'm thinking that it would be possible to write the required files and folders to the /temp/ folder on the C:/ drive before the tool loads, ...
.... So far, it works quite well. I'm just rolling over testing on the mac and will update this if there needs to be changes.
First of all. ALWAYS copying files to the TEMP folder seems like extra work that may never be needed, if the user does not have the instructor toolwindow open. (Note that the SUAPI is lacking a
UI.inspector_visible?()
method, which we could write for PC, IF we know the window names for all language builds.)Also.. there is no reason why the tool's
getInstructorContentDirectory()
callback could not be the 'trigger' that copies the files to TEMP, rather than doing it everytime. This is something to think about for the future.
(Issue) Does Sketchup always call thegetInstructorContentDirectory()
callback regardless of whether the Instructor window is open or closed?Playing 'devil's advocate', let's say we get Google to provide a cross-platform, locale independant query method to return visibilty state for inspector window, likely named:
UI.inspector_visible?('*caption*')
(where caption is one of the strings returned by the existingUI.inspector_names()
method.)
Then thegetInstructorContentDirectory()
callback would look like:class MyTool def getInstructorContentDirectory() # callback if UI.inspector_visible?('Instructor') temp =( MAC ? "#{ENV['TMPDIR']}" ; "#{ENV['TEMP']}" ) return copy_help_files( temp, @tool_help_subdir ) unless @help_files_copied else return "" end end def copy_help_files( temp, tool_help_subdir ) # call Niall's library method here # passing args temp and tool_help_subdir # Niall's method should return path to temp folder # with the "path to root" prepended and the # tool_help_subdir appeneded @help_files_copied = true end def deactivate() @help_files_copied = false # other code here end def suspend() @help_files_copied = false # other code here end end # class
This is just an idea, but actually Niall it would be better if your code was implemented as a Mixin Module (not a class,) and then people who need it actually "mix" it into their Tool class using
include()
(That likely means you need to rename the initialize method, as Tool coders often need to use that method to init their tool instance. how abouthelp_setup()
instead, and authors can insert a call tohelp_setup()
within theirinitialize()
method.)
This way all your methods become private methods of the Tool class instance.
Wrap your code inmodule Mixin::LoadInstructorContent
then to mix it in:class MyTool require('Mixin/load_instructor.rb') include( Mixin;;LoadInstructorContent ) # now all Niall's methods are instance methods # of THIS specific tool class 'MyTool' # a coder need only insert the help_setup() call; def initialize() help_setup() # other tool instance init code end end #class
SO.. and this is important your Mixin module, can actually define the
getInstructorContentDirectory()
callback method for the tool author, so he/she does not need to do it. -
@dan rathbun said:
(Issue) Does Sketchup always call the getInstructorContentDirectory() callback regardless of whether the Instructor window is open or closed?
I'm pretty sure it does not. If you put a UI.messagebox into the getInstructorContentDirectory() callback method, it does not pop up unless the instructor is actually open.
-
@chris fullmer said:
@dan rathbun said:
(Issue) Does Sketchup always call the getInstructorContentDirectory() callback regardless of whether the Instructor window is open or closed?
I'm pretty sure it does not. If you put a UI.messagebox into the getInstructorContentDirectory() callback method, it does not pop up unless the instructor is actually open.
Well cool! That in and of itself can be a test, and perhaps a means of defining a query method via an empty tool. More testing required.
-
@Niall
This is safer:
temp =( MAC ? "#{ENV['TMPDIR']}" : "#{ENV['TEMP']}" )
as there is more than one compiler for the PC, not just 'mswin' (there is also 'mingw')
So test for 'darwin' instead.
For the global constants:
<span class="syntaxdefault"> unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> <br /> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC </span><span class="syntaxkeyword">=(</span><span class="syntaxdefault"> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">RUBY_PLATFORM </span><span class="syntaxkeyword">=~</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">/</span><span class="syntaxdefault">darwin</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">i </span><span class="syntaxkeyword">?</span><span class="syntaxdefault"> true </span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> false </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> end<br /> unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">WIN</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> <br /> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">WIN </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> not Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> end<br /> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">OSX </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">OSX</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">PC </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">WIN unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">PC</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span>
Now they can be accessed by their simple names, ie
MAC
orWIN
, etc.Note that using the old deprecated
PLATFORM
constant directly may cause problems in future Ruby versions, when they stop defining it. UseRUBY_PLATFORM
instead.- Note there may still be a poor example on the Sketchup FAQ webpage, that suggests users set a constant, and this example uses the identifier
PLATFORM
, which may break your code.
see question: How can I detect if my plugin is running on the Mac vs. PC?
on: Google SketchUp Ruby API FAQs
(I entered a 'doc bug report', and suggested the code snippet above be added to the sketchup.rb file instead.)
- Note there may still be a poor example on the Sketchup FAQ webpage, that suggests users set a constant, and this example uses the identifier
-
Here's another way to get around the drive letters on PC
On my machine I have logical drives (XP SYSTEM), E:(DATA) and F:(TEMP)
My machine is named 'JEDI'Environment vars:
ENV['COMPUTERNAME'] = 'JEDI'
ENV['LOGONSERVER'] = '\JEDI'I went to drive F:, and made a dir 'Shared', went into that dir, and made a subdir 'Plugins'
Then I right-clicked that 'Plugins' subdir, and chose "Sharing and Security..." from the context menu.
Under "Network sharing and security" I checked the box "Share this folder on the network"
In the Sharename textbox I typed 'NetPlugins'...
.. below that I checked the box "Allow network users to change my files" *
(*If you don't at least temporarily, you can't copy files into that folder.)
and clicked the 'OK' button.The folder: 'F:\Shared\Plugins'
is now shared as: '\JEDI\NetPlugins'or generically as the Ruby string:
"#{File.expand_path(ENV['LOGONSERVER'])}/NetPlugins"
I copied a 'TestScript.rb' to the shared folder, and tested it at the console:
load "#{ENV['LOGONSERVER']}/NetPlugins/TestScript.rb"
.. no problem it loaded the file.I tested:
File.expand_path("#{ENV['LOGONSERVER']}/NetPlugins/TestScript.rb")
//JEDI/NetPlugins/TestScript.rb
.. so expanding the path will not break code by reverting to a drive letter prepended absolute path.Next I made a little file called 'zz.rb' (so it would load after all other plugins,) and put it in the normal Program Files Plugins folder. It has 2 lines:
server = ENV['LOGONSERVER'].gsub(/\\/,'/') require_all("#{server}/NetPlugins")
server = File.expand_path(ENV['LOGONSERVER'])
also works, if you dont like gsub... I then started Sketchup 8, and bingo! the TestScript.rb file loaded. Here's the test script:
puts "I am a test script loaded from #{File.expand_path(ENV['LOGONSERVER'])},\n and my path is; #{__FILE__}\n"
-
We need to think about two plugin install locations:
COMMOM_PLUGINS and USER_PLUGINS
Both platforms:
COMMOM_PLUGINS = Sketchup.find_support_file('Plugins')
Mac:
[normal case]
USER_PLUGINS = "/Users/#{ENV['USER']}" + COMMOM_PLUGINS
[if the user sets a custom HOME folder]
USER_PLUGINS = "#{ENV['HOME']}" + COMMOM_PLUGINS
PC:
VER = Sketchup.version.to_i USER_PLUGINS = File.expand_path(ENV['APPDATA']+'\Google\Google SketchUp '+VER+'\SketchUp\Plugins')
Note that on Mac, the loading of scripts in the user plugins (on Sketchup startup,) is automatic.
Not so on PC. A user must create the appdata dir, AND use therequire_all()
method from the sketchup.rb file to autoload scripts from the appdata path. Therequire_all()
method will also push the appdata path onto the$LOAD_PATH
array, so that subsequently, normalrequire()
orload()
can find rubies beneath that path.
%APPDATA%
On XP the appdata path is: "C:/Documents and Settings/#{ENV['USERNAME']/Application Data/" ...
On Windows 6.0+, it's "C:/Users/#{ENV['USERNAME']/AppData/Roaming" ...IN ADDITION, some people, myself included, wish to maintain a common cross version plugins folder that the Google installers will not touch. Some will wish to have this on another drive (one person here at SCF, said he uses a "thumbdrive", so he can take his plugins from machine to machine easily.)
Others may wish to create a folder under the appdata path, like:
USER_PLUGINS_ALL = File.expand_path(ENV['APPDATA']+'\Google\Google SketchUp All\Plugins')
And as TIG said, others may wish to use a network drive. -
tried this to before
after some testing i just created a tutorial in a webdialog -
@dan rathbun said:
...I tested:
File.expand_path("#{ENV['LOGONSERVER']}/NetPlugins/TestScript.rb")
//JEDI/NetPlugins/TestScript.rb
.. so expanding the path will not break code by reverting to a drive letter prepended absolute path...Nice find Dan, and this should (hopefully:untested:willcheck) work with the temp copy script, as it compares the array[0] of somepath.split("/") with the same of the temp directory to start the copy to the temp folder. So on pc this would be like comparing 'C:' with '' (before this there's a short-circuit for mac). Only question now is can sketchup run from a '\Server' path?? as this would ruin the comparison.
-
@bentleykfrog said:
Only question now is can sketchup run from a '\Server' path?? as this would ruin the comparison.
Actually that should read "can the default temp folder run from a '\Server' path?? as this would ruin the comparison"
-
@chris fullmer said:
@dan rathbun said:
(Issue) Does Sketchup always call the getInstructorContentDirectory() callback regardless of whether the Instructor window is open or closed?
I'm pretty sure it does not. If you put a UI.messagebox into the getInstructorContentDirectory() callback method, it does not pop up unless the instructor is actually open.
I get the same results on mac. Also, I notice that it calls getInstructorContentDirectory() onMouseOver and onMouseOut of the instructor dialog. Not sure if its a webkit quirk or if its intended, will check on pc.
Advertisement