sketchucation logo sketchucation
    • Login
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info

    Instructor content

    Scheduled Pinned Locked Moved Developers' Forum
    84 Posts 8 Posters 7.9k Views 8 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Dan RathbunD Offline
      Dan Rathbun
      last edited by

      @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.

      I'm not here much anymore.

      1 Reply Last reply Reply Quote 0
      • Dan RathbunD Offline
        Dan Rathbun
        last edited by

        @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 or WIN, etc.

        Note that using the old deprecated PLATFORM constant directly may cause problems in future Ruby versions, when they stop defining it. Use RUBY_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.)

        I'm not here much anymore.

        1 Reply Last reply Reply Quote 0
        • Dan RathbunD Offline
          Dan Rathbun
          last edited by


          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"
          

          💭

          I'm not here much anymore.

          1 Reply Last reply Reply Quote 0
          • Dan RathbunD Offline
            Dan Rathbun
            last edited by

            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 the require_all() method from the sketchup.rb file to autoload scripts from the appdata path. The require_all() method will also push the appdata path onto the $LOAD_PATH array, so that subsequently, normal require() or load() 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.

            I'm not here much anymore.

            1 Reply Last reply Reply Quote 0
            • P Offline
              Pout
              last edited by

              tried this to before
              after some testing i just created a tutorial in a webdialog

              1 Reply Last reply Reply Quote 0
              • B Offline
                bentleykfrog
                last edited by

                @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. 😕

                1 Reply Last reply Reply Quote 0
                • B Offline
                  bentleykfrog
                  last edited by

                  @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"

                  1 Reply Last reply Reply Quote 0
                  • B Offline
                    bentleykfrog
                    last edited by

                    @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.

                    1 Reply Last reply Reply Quote 0
                    • B Offline
                      bentleykfrog
                      last edited by

                      I've updated Dan's edit of the load_instructor script to address some issues:

                      • script now contains getInstructorContentDirectory method as per Dan's suggestion
                      • script methods converted from camelCase
                      • added prefix 'lins_' to each method to reduce chance of method name conflicts
                      • removed returns when not needed
                      • made the temp directory creation more consistent
                      • modified the precedence of mac over temp file setting
                      • added private method calls to relative_instructor_path & temp_instructor_folder methods
                      • added plugin folder name attribute to lins_setup() to resolve FILE issue when load_instructor.rb installed into [any $LOAD_PATH]/mixin/load_instructor.rb and default values of instructor folder and temp folder used
                      • added $LOAD_PATH check for plugins instructor folder if no instructor_folder attribute is set to lins_setup
                      • since we're specifying a folder name even on default, unique temp folder name creation was removed

                      Read the definition of use in the code to get a better understanding of how to use Load Instructor.

                      Issues I'm unsure of:

                      • I removed the @@tool_filepath setting as I'm not sure what to do with it. If the scripts are intended to be truly portable, then how does an .rbs file require() other files? ThomThom's vertex tools method suggests that their code will already have a variable showing the scripts location, with which you could set the instructor folder location from, and use the location folder attribute of lins_setup().
                      • Sketchup loaded from a server location? (ie. portable versions of Sketchup?)
                      • Attaching an AppObserver for onQuit to delete the temp file? (I wouldn't have a clue about this: I'm concerned that it might overwrite other plugins' onQuit method, and I'm guessing that onQuit won't be called when there's a bugsplat, so maybe its better to have consistent temp folder directories as in this update? and rely on the system/users preference for temp folder clearing?)

                      Thanks to Dan for all the tips/advice/&code, you've been more than helpful.


                      Load Instructor [Updated 13-03-2011:7.12am]

                      1 Reply Last reply Reply Quote 0
                      • B Offline
                        bentleykfrog
                        last edited by

                        @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, then use a relative path to the /temp/instructor/ directory in getInstructorContentDirectory. This would avoid the volume traversal problem but I think it would be a bit of a headache.

                        Script Updated on 2011-03-13: See http://forums.sketchucation.com/viewtopic.php?f=180&t=31060&p=315364#p315364

                        Here's a very long post and a very-rough draft of the Instructor-copy-to-temp-folder script. I've tested this on Windows 7 with Sketchup 8 and I used [url=http://forums.sketchucation.com/viewtopic.php?t=17660:2t71i41l]Alex's Plugin Loader[/url:2t71i41l] for testing on other drives. 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.

                        I'm not sure about alot of things, to say the least;

                        • file chmod permissions on close? (ie. Security, should we go for a File.chmod(0644, temp_abs_path+entry) before close?)
                        • file encoding copy? I'm using the texture writer to supplement the simplicity of my File.write code but I'm thinking that if encoding is preserved the script might not have the issue of corrupt images on copy (can we use require "fileutils", I've got no experience with ruby extensions)
                        • block devices and character devices? 😲 (this flies way over my head)
                        • And add more to that 'fifo', 'link', 'socket'
                        • File.executable? checks? (probably not needed as sketchup loads the plugin script unless the plugin and instructor content are in completely foreign folders (thomthom?))
                        • Temp directory clearing? is this necessary on tool close or would it be better to store a default with the script and maintain the files on exit, and check if the files exist and are consistent on next load? (probably better to clear and load for each sketchup session?)
                        • Not sure about flash/movie/audio/etc.. files, (my target.write code doesn't like anything more complex than a text file, there's probably some encoding settings that I'm missing out on as in my first point that is corrupting the file on write. If fileutils is not possible then maybe encoding settings might solve this)
                        • How about server locations on windows like "\someserver" instead of "C:\" 😕

                        Also, please forgive the amateur coding, its my first try at something that is cut-and-paste portable. But I'm open for critique/suggestions for improvement (though its a little off topic, best to PM)

                        # ##-------------------------##
                        # ##----DEFINITION OF USE----##
                        # ##-------------------------##
                        # #first add the load instructor to your module
                        # #or whatever module you'd like to add it to
                        # #in this example, I've added it to MyModule
                        
                        # #calling the load instructor
                        # #specifying no attributes should return the base temp and 
                        # #construct a instructor path by; [ruby_script_name]/instructor
                        # instructor_copy = MyModule;;LoadInstructor.new
                        # #after calling the load instructor, you can set the
                        # #instructor_folder & temp_folder if you need to adjust
                        # #the default values
                        # instructor_copy.instructor_folder = [custom instructor folder]
                        # instructor_copy.temp_folder = [custom temp folder]
                        # #or you can call the load instructor with custom directories like this
                        # temp_folder = ([PLATFORM].grep(/mswin/)==[PLATFORM]) ? ENV["TEMP"] ; ENV["TMPDIR"]
                        # instructor_folder = File.join(File.join(File.basename( __FILE__ ),"floating_camera"), "instructor")
                        # instructor_copy = MyModule;;LoadInstructor.new( instructor_folder , temp_folder )
                        # #we need to copy the files to the temp directory
                        # #if needed by this method
                        # instructor_copy.copyInstructor
                        # #any errors in the copy will be reported to the ruby console
                        
                        # #after this has completed, you can return the instructor temp 
                        # #folder path (relative to the temp folder) by;
                        # instructor_temp_folder = instructor_copy.returnTempInstructorFolderName
                        # #this will be helpful if you're storing the instructor temp 
                        # #folder location in a class variable to check if you need 
                        # #to run the script per sketchup session (returns false if 
                        # #no temp folder created)
                        
                        # #finally, you can get the relative path from the sketchup 
                        # #tools folder to the instructor folder (whether it was 
                        # #copied to the temp folder or not) by calling
                        # @@relative_instructor_folder = instructor_copy.returnRelativePath
                        
                        # #as an example, you can put these calls inside your tools 
                        # #initialize method, like the example below. Initialize 
                        # #will be called before getInstructorContentDirectory
                        # def initialize
                        	# instructor = CControl;;LoadInstructor.new
                        	# instructor.copyInstructor
                        	# @@relative_instructor_path = instructor.returnRelativePath
                        # end #def
                        
                        # #and then add this def to your tool to give sketchup the
                        # #relative path
                        # def getInstructorContentDirectory
                        	# return @@relative_instructor_path
                        # end #def
                        
                        # #now you should have a consistent Instructor Directory
                        # #across drives
                        
                        	#need to check if paths are relative or absolute
                        	class LoadInstructor
                        		attr_accessor ;instructor_folder, ;temp_folder
                        		def initialize(instructor_folder=false, temp_folder=false)
                        			if instructor_folder
                        				@instructor_folder = instructor_folder
                        			else
                        				@instructor_folder = self.getDefaultInstructorFolder
                        			end
                        			if temp_folder
                        				@temp_folder = temp_folder
                        			elsif [PLATFORM].grep(/mswin/)!=[PLATFORM]
                        				@temp_folder = @instructor_folder
                        			else
                        				@temp_folder = self.getDefaultTempFolder
                        			end
                        		end #def
                        		
                        		def getDefaultInstructorFolder
                        			dir = File.expand_path(File.dirname( __FILE__ ))
                        			file_ext = File.extname( __FILE__)
                        			file_basename = File.basename( __FILE__ , file_ext)
                        			dir = dir + "/" + file_basename + "/instructor"
                        			return dir
                        		end #def
                        		
                        		def getDefaultTempFolder
                        			temp_folder = File.expand_path(ENV["TEMP"])
                        			return temp_folder
                        		end #def
                        		
                        		# adapted from http://boonedocks.net/mike/archives/162-Determining-Image-File-Types-in-Ruby.html
                        		# checks for jpg,png,tif,tga & bmp and variations in their data formats
                        		def getImageType(file)
                        		
                        			filestart = IO.read(file,30)
                        			case filestart
                        				when /^BM/
                        					#puts "BMP; "+file
                        					return 'bmp'
                        				when /^GIF8/
                        					#puts "GIF; "+file
                        					return 'gif'
                        				when /^MM\x00\*(\x00){3}/
                        					#when /^MM.\*.{3}/
                        					#puts "TIF MAC; "+file
                        					return 'tif'
                        				when /^II\*\x00\x08(\x00){3}\x15/
                        					#when /^II\*./
                        					#puts "TIF PC; "+file
                        					return 'tif'
                        				when /^\x89PNG/
                        					#puts "PNG; "+file
                        					return 'png'
                        				when /^\xff\xd8\xff\xe0\x00\x10JFIF/
                        					#puts "JPG; "+file
                        					return 'jpg'
                        				when /^\xff\xd8\xff\xe0/
                        					#puts "JPG; "+file
                        					return 'jpg'
                        				when /^\xff\xd8\xff\xe1(.*){2}Exif/
                        					#puts "JPG; "+file
                        					return 'jpg'
                        				when /^\x00\x00\x0A\x00/
                        					#puts "TGA Compressed; "+file
                        					return 'tga'
                        				when /^\x00\x00\x02\x00/
                        					#puts "TGA; "+file
                        					return 'tga'
                        				else
                        					#check for tga files by extension
                        					fileext = File.extname(file)
                        					case fileext
                        						when /^\.(?i)(tga|vda|icb|vst)/
                        							#puts "TGA by extension; "+file
                        							return 'tga'
                        						when /^\.(?i)(tif|tiff)/
                        							#puts "TIF by extension; "+file
                        							return 'tif'
                        					end #case
                        					puts "unknown; "+IO.read(file,20).dump
                        					return 'unknown'
                        			end
                        		end
                        		
                        		def checkValidity(filename)
                        			filetype = File.ftype(filename) #grabs the file type; 'file','directory','characterSpecial','blockSpecial',
                        																			#'fifo','link','socket','unknown'
                        			case filetype
                        				when "file"
                        					if (!File.readable?(filename))
                        						puts "file is not readable; "+filename
                        						return "unreadable"
                        					end #if
                        					if (!File.size?(filename))
                        						puts "file exists but has 0 size; "+filename
                        						return "invalidsize"
                        					end #if
                        					image_type = self.getImageType(filename)
                        					if(image_type == 'unknown')
                        						puts "file exists but is unknown (assume text file); "+filename
                        						return "unknowntype"
                        					end #if
                        					#puts "found image file; "+filename
                        					return "imgfile"
                        				when "directory"
                        					#puts "found directory; "+filename
                        					return "directory"
                        			end #case
                        		end #def
                        		
                        		#call like self.copyInstructorFiles([base_dir_of_instructor_folder],[base_dir_of_instructor_folder])
                        		#dir changes on recursion through the child directories and basedir stays constant to
                        		#maintain the folder/file structure in the temp folder
                        		def copyInstructorFiles(temp_folder, dir, basedir)
                        			entries = Dir.entries(dir)
                        			if (dir != basedir)
                        				temp_path = dir.dup
                        				temp_path.slice!(basedir)
                        				temp_folder_abs_path = temp_folder+temp_path
                        				Dir.delete(temp_folder_abs_path) if(File.exists?(temp_folder_abs_path))
                        				Dir.mkdir(temp_folder_abs_path, 0700)
                        			else
                        				temp_folder_abs_path = temp_folder
                        			end #if
                        			entries.delete(".")
                        			entries.delete("..")
                        			
                        			#create a group and a texture writer object for copying imgfiles
                        			entries.each {|entry|
                        				entry_absolute_path = File.join(dir, entry)
                        				file_validity = self.checkValidity(entry_absolute_path)
                        				case file_validity
                        					when "directory"
                        						#recurse through the child directories and copy the files
                        						self.copyInstructorFiles(temp_folder,entry_absolute_path,basedir)
                        					when "imgfile"
                        						grp = Sketchup.active_model.entities.add_group
                        						tw  = Sketchup.create_texture_writer
                        						#so create a material name
                        						filename = File.basename(entry_absolute_path)
                        						fileext  = File.extname(filename)
                        						filebase = File.basename(entry_absolute_path, fileext)
                        						matname = "instructor_"+filebase + rand(1000).to_s
                        						
                        						#load the image into a material
                        						mat = Sketchup.active_model.materials.add(matname)
                        						mat.texture = entry_absolute_path
                        						mat.texture.size = [mat.texture.image_height,mat.texture.image_width]
                        						
                        						#apply the material to a group and export to temp directory
                        						grp.material = mat
                        						tw.load(grp)
                        						result = tw.write(grp, File.join(temp_folder_abs_path,entry))
                        						
                        						case result
                        							when 0
                        								#puts "file written; "+temp_folder_abs_path+"/"+filename
                        							when 1; puts "file failed (invalid tiff); "+temp_folder_abs_path+"/"+filename
                        							when 2; puts "file failed (unknown); "+temp_folder_abs_path+"/"+filename
                        						end #case
                        						
                        						#delete the material
                        						grp.material = nil
                        						Sketchup.active_model.materials.remove mat
                        						grp.erase! if grp.valid?
                        					when "unknowntype"
                        						source = File.open(entry_absolute_path)
                        						target = File.open(File.join(temp_folder_abs_path,entry), "w")
                        						
                        						target.write( source.read(64) ) while not source.eof?
                        						
                        						source.close
                        						target.close
                        				end #case
                        			}
                        		end #def
                        		
                        		def copyInstructor
                        			@instructor_folder = File.expand_path(@instructor_folder)
                        			@temp_folder       = File.expand_path(@temp_folder)		
                        			#on mac there's no need to copy, just to generate a correct relative path
                        			#as other volumes can be accessed through the /volumes/ folder
                        			#so @instructor_folder == @temp_folder only on mac
                        			if @instructor_folder != @temp_folder
                        				#check if paths are on the same drive in windows, if they are
                        				#there's no need to copy as they can be accessed through relative paths
                        				instructor_path_array = @instructor_folder.split("/")
                        				temp_path_array = @temp_folder.split("/")
                        				if instructor_path_array[0] != temp_path_array[0]
                        					#check if the temp path is equal to ENV["TEMP"]
                        					#if so we need to generate a unique instructor folder id
                        					if @temp_folder == File.expand_path( ENV["TEMP"] )
                        						temp_instructor_folder = File.join(@temp_folder, "instructor-"+Time.now.to_f.round.to_s)
                        					elsif (temp_path_array[temp_path_length-1] != "instructor")
                        						temp_instructor_folder = File.join(@temp_folder, "instructor")
                        					else
                        						temp_instructor_folder = @temp_folder
                        					end #if
                        					Dir.delete(temp_instructor_folder) if(File.exists?(temp_instructor_folder))
                        					Dir.mkdir(temp_instructor_folder, 0700)
                        					self.copyInstructorFiles(temp_instructor_folder,@instructor_folder,@instructor_folder)
                        					@temp_folder = temp_instructor_folder
                        				else
                        					puts "on same drive"
                        				end
                        			else
                        				puts "on a mac"
                        			end #if
                        		end #def
                        		
                        		def returnTempInstructorFolderName
                        			#return the relative path from ENV["TEMP"] so user can store this
                        			#in a default and use it for consistency between plugins and sessions
                        			instructor_path_array = @instructor_folder.split("/")
                        			temp_path_array = @temp_folder.split("/")
                        			if (@instructor_folder != @temp_folder) && (instructor_path_array[0] != temp_path_array[0])
                        				temp_instructor_folder_name = @temp_folder.dup
                        				temp_instructor_folder_name.slice!(File.expand_path( ENV["TEMP"] ))
                        				return temp_instructor_folder_name[1,temp_instructor_folder_name.length]
                        			else
                        				return false
                        			end
                        		end #def
                        		
                        		def returnRelativePath
                        			tool_path = Sketchup.get_resource_path( 'helpcontent/tool' )
                        			temp_path = @temp_folder
                        			#need to reverse loop through tool path by the separator until
                        			#we get a match starting at the first line of the temp path
                        			relativity_index = 0
                        			common_directory_path = temp_path
                        			tool_path_array = File.expand_path(tool_path).split('/')
                        			0.upto(tool_path_array.length-1) {|i|
                        				common_directory = tool_path_array[0, tool_path_array.length - i]
                        				if [PLATFORM].grep(/mswin/)==[PLATFORM]
                        					common_directory_path = common_directory.join('/')
                        				else
                        					common_directory_path = '/'+common_directory.join('/')
                        				end
                        				if common_directory_path == temp_path[0,common_directory_path.length]
                        					break
                        				end
                        				relativity_index = relativity_index.next
                        			}
                        			
                        			temp_path = temp_path[common_directory_path.length,temp_path.length]
                        			path_to_common = "..#{File;;SEPARATOR}" * relativity_index
                        			relative_path = File.join( path_to_common, temp_path)
                        			return relative_path
                        		end #def
                        	
                        	end #class
                        

                        Script Updated on 2011-03-13: See http://forums.sketchucation.com/viewtopic.php?f=180&t=31060&p=315364#p315364


                        Load Instructor [Updated 10-03-2011:3.20am]

                        1 Reply Last reply Reply Quote 0
                        • Dan RathbunD Offline
                          Dan Rathbun
                          last edited by

                          @bentleykfrog said:

                          Issues I'm unsure of:

                          • I removed the @@tool_filepath setting as I'm not sure what to do with it. If the scripts are intended to be truly portable, then how does an .rbs file require() other files? ThomThom's vertex tools method suggests that their code will already have a variable showing the scripts location, with which you could set the instructor folder location from, and use the location folder attribute of lins_setup().

                          Well, he actually says constant. And this is true, because the SketchupExtension class load() (which uses Sketchup::require(),) cannot for some reason directly load rbs files. So we must use a non-scrambled loader script to in turn load the rbs. I do the same thing ThomThom does.. however, there is no way to know what the author will choose to name his file path constant. Nor.. should a library (or mixin,) module force an author to use any specific name. Therefor.. an internal variable is needed, and I chose to make it a class variable, and named it @@tool_filepath. ThomThom and any other author, simply assigns @@tool_filepath to point to the constant name they defined in their loader script. Perhaps because my example showed a literal string assignment, you assumed that was necessary? It's not, for example ThomThom could declare in his Tool class:
                          @@tool_filepath = VertexToolsPath

                          The way I had it.. it would use __FILE__ as a default for non-scrambled files, and insist on a explicit assignment of the @@tool_filepath variable for rbs files.
                          By instead, adding the required argument script_folder to the lins_setup() method, you have also defeated the default initialize() method that is mixed in (it has no arguments in the lins_setup() call.)
                          I think it will work better for newbie's the previous way.

                          @bentleykfrog said:

                          • Sketchup loaded from a server location? (ie. portable versions of Sketchup?)

                          In normal circumstances, this is a violation of the Sketchup "Terms of Use." After all cloud computing is one of Google's main business models. One website that did this was told to remove Sketchup from their servers.
                          Now if you were in a educational environment, and had permission from Google it may be different. But it's not that hard to keep client application installs up to date. The 'bear' is keeping all the Plugins for all those client workstations up to date. So really using a shared Plugins folder on a network drive is what most admins will want to do. And as I showed (above) it can easily be done by pussing a network path into the $LOAD_PATH array and using the require_all() method.
                          The tricky part comes in with TIG's case, where each student has their own personal "User Plugins" folder on a network drive. These personal folders cannot be shared with write access (because it would defeat the purpose of having secure user accounts.)
                          So everything should work fine for the student whilst they are working on a plugin... because they will have access to their own instructor content folders. When they finish and 'publish' the plugin for others to use, the plugin would get installed (by the teacher, or an IT person,) into the 'Network Shared Plugins' path, where all the students could have access to it and it's help content.

                          @bentleykfrog said:

                          • Attaching an AppObserver for onQuit to delete the temp file? (I wouldn't have a clue about this: I'm concerned that it might overwrite other plugins' onQuit method, and I'm guessing that onQuit won't be called when there's a bugsplat, so maybe its better to have consistent temp folder directories as in this update? and rely on the system/users preference for temp folder clearing?)

                          True about the BugSplat! If you've done away with the temp names that have timecodes embedded, then the erase on first access method will work fine (in case the help content was updated after the last use.)
                          The onQuit() would not interfere as a special observer instance would have been used, BUT... because of their quirky nature, if you can avoid an observer, do so. ALSO I have been trying to use an AppObserver and it's onQuit() callback, (in another project,) only to discover that Sketchup does not wait for the callback to return. I'm trying to save settings and window locations.. but Sketchup is closing all it's owned and child windows, and it's own application window before any of the code in my callback can do it's job. Annoying!

                          @bentleykfrog said:

                          Thanks to Dan for all the tips/advice/&code, you've been more than helpful.

                          no problem.. glad I could help.

                          I'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • Dan RathbunD Offline
                            Dan Rathbun
                            last edited by

                            A few more observations / issues:

                            File.exists? is deprecated.
                            Use the call File.exist? (singular) for future compatability.

                            The $LOAD_PATH members can be manipulated. Making an assumption about where the the first member points could cause problems. (Refering to the lins_get_default_instructor_folder() method.)

                            The lins_return_relative_path() method still references the deprecated PLATFORM constant, and assumes all PCs will running a MSVS compiled Ruby. The mingW compiled Ruby is becoming more popular and the Windows Ruby One-Click Installer uses only the mingW compiler now.
                            To test for Windows, Line 553 should be, either:
                            if( not RUBY_PLATFORM =~ /darwin/i )
                            or
                            if RUBY_PLATFORM =~ /(mswin|mingw)/i
                            The second has the advantage that, if in the future say a Sketchup Linux edition is released, you code will not break.

                            Instructions:
                            The inline instructions have grown to be very confusing for newbies.
                            I would suggest reverting to the simple default instructions, and add a note for advanced use see load_instructor_advanced.txt, and move all the advanced instructions into the separate text file.

                            And.. when this is all done (seeing as how you still have a beta version number,) and you decide on the first release, it would be best to create a new topic with a [ code ] prefix for version 1.0 (so Jim's indexer script can find it.) Then we will remove our beta version(s) this topic, and insert a link to the code release topic.

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • B Offline
                              bentleykfrog
                              last edited by

                              Dan, your advice is invaluable. There's still a couple of things I'm concerned about:

                              @dan rathbun said:

                              The way I had it.. it would use __FILE__ as a default for non-scrambled files, and insist on a explicit assignment of the @@tool_filepath variable for rbs files.
                              By instead, adding the required argument script_folder to the lins_setup() method, you have also defeated the default initialize() method that is mixed in (it has no arguments in the lins_setup() call.)
                              I think it will work better for newbie's the previous way.

                              The issue I had with the previous version was the way that load_instructor.rb constructed the default path to the /instructor/ folder. The previous version used __FILE__ from /mixin/load_instructor.rb and then navigated back to the folder that contained the /mixin/ folder. From that path it used /instructor/, so it meant that you would have issues if you had more than one ruby script using Load Instructor. So there needs to be a string attribute that identifies the name of the folder that contains /instructor/ to avoid this conflict between plugins. If the LoadInstructor mixin is going to be stored in a file that is separate from your script then calling __FILE__ will only be able to identify /load_instructor.rb and wont be able to identify the filename of the script that the LoadInstructor mixin was loaded into (unless there's a method for getting around this).

                              Regarding the @@tool_filepath issue, you should still be able to declare these paths in the second and third attributes of lins_setup()? like:

                              @@tool_filepath = VertexToolsPath
                              
                              def initialize
                                instructor_folder = File.join( @@tool_filepath ,'your_scripts_folder/instructor')
                                temp_folder       = (RUBY_PLATFORM =~ /darwin/i) ? '' ; File.join(ENV["TEMP"], 'your_scripts_folder/instructor')
                                lins_setup('',instructor_folder,temp_folder)
                              end
                              

                              And if you're storing all the files beneath any of the load path's, you shouldn't need to worry about the second and third attributes, or @@tool_filepath for that matter, like:

                              def initialize
                                lins_setup('your_scripts_folder')
                              end
                              

                              @dan rathbun said:

                              The $LOAD_PATH members can be manipulated. Making an assumption about where the the first member points could cause problems. (Refering to the lins_get_default_instructor_folder() method.)

                              Hmmm, it should be best then if I threw an error if the /instructor/ folder can't be found, like (using your @@tool_filepath check):

                              def lins_get_default_instructor_folder(script_folder)
                                instructor_folder = ''
                                $LOAD_PATH.each {|load_path|
                                  check_path = File.join(load_path, script_folder, 'instructor')
                                  if check_path != '(eval)' && File.exist?(check_path)
                                    instructor_folder = File.join(load_path, script_folder, 'instructor')
                                    break
                                  end
                                }
                                if instructor_folder.empty?
                                  file_path = File.join(script_folder, 'instructor')
                                  raise(ScriptError,"#{self.class.name}; Instructor folder error. Could not find #{file_path} beneath any of the $LOAD_PATH's.")
                                else
                                  return instructor_folder
                                end #if
                              end #def
                              

                              I've adopted all your other suggestions to the script, I just have to sort out the former issues before I'll produce the first release.

                              1 Reply Last reply Reply Quote 0
                              • Dan RathbunD Offline
                                Dan Rathbun
                                last edited by

                                Niall... I am sorry. I've got my head wrapped around another project, and I was not seeing the obvious here.

                                YOU cannot use __FILE__ at all, in your mixin code. Because it's a special Ruby keyword that gets evaluated when the mixin is parsed.

                                I was thinking wrongly that it's evaluation would be defered if it was used inside a method (the "setup" method.)

                                The tool author CAN use __FILE__ if his script is unscrambled, otherwise if he scrambles his tool code, he must use a rbs loader where he should set a constant (like ThomThom's example.)

                                The tool author only cares where YOUR code is when he uses the require() statement which should find it via the $LOAD_PATH array. Otherwise where the mixin file resides (it's path,) has no bearing on anything else. (Once it's loaded.. it is refered to as Mixin::LoadInstructor and it's path is irrelevant.)

                                So really yout mixin NEEDS to use the @@tool_filepath class variable in both cases (an rb or rbs.)

                                And the tool author needs to always set it to the path of his tool script file, either:
                                @@tool_filepath = __FILE__ # for unscrambled
                                or
                                @@tool_filepath = SOME_CONSTANT # (or a literal,) for scrambled rbs.
                                If the author is smart, they will wrap their loader and declare their path constant, in the same tool class namespace their rbs code in wrapped in.

                                I'm not here much anymore.

                                1 Reply Last reply Reply Quote 0
                                • Dan RathbunD Offline
                                  Dan Rathbun
                                  last edited by

                                  @bentleykfrog said:

                                  Dan, your advice is invaluable. There's still a couple of things I'm concerned about:

                                  Regarding the @@tool_filepath issue, you should still be able to declare these paths in the second and third attributes of lins_setup()? like:

                                  @@tool_filepath would contain the "your_scripts_folder" so to get just the path part use:
                                  File.dirname(@@tool_filepath)
                                  which removes the actual tool script filename, leaving the relative path from one of the $LOAD_PATH members. You can then expand it to get the tool script's absolute folder path.

                                  So your thinking.. what's the difference in making the author use a variable or making them use a method argument ??
                                  Well IMHO the variable is cleaner, set once and forget. And that variable can be referenced by other methods written by the tool author.

                                  Here's an example of a rbs loader script that loads a scrambled tool script named testtool.rbs:

                                  module Niall
                                    class TestTool
                                      require('mixin/load_instructor.rb')
                                      include( Mixin;;LoadInstructor )
                                      @@tool_filepath = File.dirname(__FILE__)+'/testtool.rbs'
                                      Sketchup.require( @@tool_filepath )
                                    end # class
                                  end # module
                                  

                                  In this example, the author does the 'mixing' in the loader, sets @@tool_filepath and uses it in his call to load the rbs.

                                  I'm not here much anymore.

                                  1 Reply Last reply Reply Quote 0
                                  • B Offline
                                    bentleykfrog
                                    last edited by

                                    @dan rathbun said:

                                    Here's an example of a rbs loader script that loads a scrambled tool script named testtool.rbs:

                                    module Niall
                                    >   class TestTool
                                    >     require('mixin/load_instructor.rb')
                                    >     include( Mixin;;LoadInstructor )
                                    >     @@tool_filepath = File.dirname(__FILE__)+'/testtool.rbs'
                                    >     Sketchup.require( @@tool_filepath )
                                    >   end # class
                                    > end # module
                                    

                                    In this example, the author does the 'mixing' in the loader, sets @@tool_filepath and uses it in his call to load the rbs.

                                    Thats cool then, I don't really see an issue passing @@tool_filepath as an argument to lins_setup(). With default values and your script in the $LOAD_PATH's, lins_setup('my_scripts_files') won't require @@tool_filepath. You'll just need it for requiring the mixin possibly? With custom values you'll need to pass absolute values as the second and third arguments so you'll need to use File.join( @@tool_filepath, 'my_scripts_files/instructor-boogaloo'), so that's quite easy to do, and I've documented it under Advanced Use.

                                    Looks like I'm ready to release this code, I'll start a new topic.

                                    1 Reply Last reply Reply Quote 0
                                    • Dan RathbunD Offline
                                      Dan Rathbun
                                      last edited by

                                      @bentleykfrog said:

                                      Thats cool then, I don't really see an issue passing @@tool_filepath as an argument to lins_setup(). With default values and your script in the $LOAD_PATH's, lins_setup('my_scripts_files') won't require @@tool_filepath.

                                      You're missing the point of the @@tool_filepath variable.
                                      In the simple default mode, YOU set the script_folder argument of lins_setup() to have a default value so the method can be called with no arguments, like so:
                                      def lins_setup( instructor_folder=false, temp_folder=false, script_folder=File.dirname(@@tool_filepath) )

                                      The absolute path to help content would be:
                                      instructor_folder = 'instructor' if instructor_folder==false File.expand_path("#{script_folder}/#{instructor_folder}")

                                      I show the script_folder argument on the end, as it's least likely authors will override that argument's default, and most likely wish to specify a custom help content folder name. (Specifying a custom Temp dir would fall in the middle.)

                                      @bentleykfrog said:

                                      You'll just need it [ @@tool_filepath] for requiring the mixin possibly?

                                      No.. the plugin script's path has nothing to do with what path the user decides to use for their mixin library. The mixin lbrary folder should be in a subfolder of one of the $LOAD_PATH members, so Ruby will have no problem finding it.

                                      @bentleykfrog said:

                                      With custom values you'll need to pass absolute values as the second and third arguments so you'll need to use File.join( @@tool_filepath, 'my_scripts_files/instructor-boogaloo'), so that's quite easy to do, and I've documented it under Advanced Use.

                                      Actually as I said (above,) it's:
                                      File.dirname(@@tool_filepath)
                                      as @@tool_filepath will be a relative path that contains the tool script filename on the end.

                                      I'm not here much anymore.

                                      1 Reply Last reply Reply Quote 0
                                      • B Offline
                                        bentleykfrog
                                        last edited by

                                        @dan rathbun said:

                                        @bentleykfrog said:

                                        Thats cool then, I don't really see an issue passing @@tool_filepath as an argument to lins_setup(). With default values and your script in the $LOAD_PATH's, lins_setup('my_scripts_files') won't require @@tool_filepath.

                                        You're missing the point of the @@tool_filepath variable.
                                        In the simple default mode, YOU set the script_folder argument of lins_setup() to have a default value so the method can be called with no arguments, like so:
                                        def lins_setup( instructor_folder=false, temp_folder=false, script_folder=File.dirname(@@tool_filepath) )

                                        I'm still trying to wrap my head around this. In the code's current state, using the default mode, the script_folder argument just specifies a relative path from any of the $LOAD_PATH's to the 'instructor' folder's parent. So absolute paths from different volumes won't work with this, unfortunately. The default mode assumes that you'll have access to the $LOAD_PATH's, or use a script that adds accessible folder to the $LOAD_PATH. Some plugin loader's don't do this 😞

                                        @dan rathbun said:

                                        @bentleykfrog said:

                                        You'll just need it [ @@tool_filepath] for requiring the mixin possibly?

                                        No.. the plugin script's path has nothing to do with what path the user decides to use for their mixin library. The mixin lbrary folder should be in a subfolder of one of the $LOAD_PATH members, so Ruby will have no problem finding it.

                                        Ahh, I see. In my case, using Alex's Plugin Loader, when a new script is loaded $LOAD_PATH's aren't updated to show the file's folder location. So I'm thinking of instances where a Sketchup user doesn't have access to the $LOAD_PATH's but does have a plugin loader script installed. So in this case, you would need to bundle up load_instructor.rb with your plugin's files. Then, we need to pass an absolute path to require() representing the location of load_instructor.rb. I was thinking that you would use File.dirname( @@tool_filepath for this, or in my Advanced Use example for .rb files I use File.dirname( __FILE__ ).

                                        @dan rathbun said:

                                        @bentleykfrog said:

                                        With custom values you'll need to pass absolute values as the second and third arguments so you'll need to use File.join( @@tool_filepath, 'my_scripts_files/instructor-boogaloo'), so that's quite easy to do, and I've documented it under Advanced Use.

                                        Actually as I said (above,) it's:
                                        File.dirname(@@tool_filepath)
                                        as @@tool_filepath will be a relative path that contains the tool script filename on the end.

                                        You're right, I forgot about that 😕

                                        1 Reply Last reply Reply Quote 0
                                        • Dan RathbunD Offline
                                          Dan Rathbun
                                          last edited by

                                          @bentleykfrog said:

                                          I'm still trying to wrap my head around this. In the code's current state, using the default mode, the script_folder argument just specifies a relative path from any of the $LOAD_PATH's to the 'instructor' folder's parent.

                                          Exactly as it should be, and what File.dirname(@@tool_filepath) would do, which is why I suggest it as the default value for your script_folder argument.

                                          @bentleykfrog said:

                                          So absolute paths from different volumes won't work with this, unfortunately. The default mode assumes that you'll have access to the $LOAD_PATH's, or use a script that adds accessible folder to the $LOAD_PATH. Some plugin loader's don't do this 😞

                                          They are not supposed to if they are not needed. Kernel.require() can use absolute paths, but it's better, memory wise, if you will be loading multiple scripts from a custom plugins folder (or library folder, using the standard Ruby library folders as an example,) to push that path onto $LOAD_PATH, so that when scripts are loaded, only the relative part, gets pushed into the $" (aka $LOADED_FEATURES) array. Pay no mind to the erroroneous way that Sketchup.require() pushes absolute paths onto $". It's a bug.. and I have entered a report on this.

                                          @bentleykfrog said:

                                          Ahh, I see. In my case, using Alex's Plugin Loader, when a new script is loaded $LOAD_PATH's aren't updated to show the file's folder location.

                                          They are not supposed to. If the script can be found by using one of the members in the $LOAD_PATH array, then there is already an appropriate load path in the array.

                                          It is not customary to push every specific plugin folder into $LOAD_PATH if there is already a path in the array, for one of the plugin folder's ancestor directories. (You would run the risk of confusing the require() method, resulting in scripts or library modules getting loaded more than once. That can reset class and module variables causing problems.)

                                          @bentleykfrog said:

                                          So I'm thinking of instances where a Sketchup user doesn't have access to the $LOAD_PATH's but does have a plugin loader script installed.

                                          I don't follow you. The $LOAD_PATH array is a Ruby global. It cannot be garbage collected. A user will always have access to it. However, a script might make a mistake and wipe the members out, resulting in an empty $LOAD_PATH array. There is also the possibility that a user may feel, sometime after startup, that they have loaded all that needs to be loaded from say, the Tools path, and could "pop" that path off the array. But it's more likely they would insert other custom paths at the beginning of the array (if say, they are using a common plugins folder some where else, which can have a drive letter at the beginning.) The require() method does not mind absolute paths on other drives.

                                          @bentleykfrog said:

                                          So in this case, you would need to bundle up load_instructor.rb with your plugin's files. Then, we need to pass an absolute path to require() representing the location of load_instructor.rb.

                                          Again.. No. You are confounding the issues of finding and loading a library file, with the issues of finding and loading a script and it's help content. Apples and Oranges.

                                          @bentleykfrog said:

                                          I was thinking that you would use File.dirname( @@tool_filepath ) for this, or in my Advanced Use example for .rb files I use File.dirname( __FILE__ ).

                                          Oh.. you are refering to the example in Section 3.4
                                          That should not be necessary. Unless the author needs to customize the mixin module for his/her own use. In which case they would need to change the module name, or the methods would overwrite a previously loaded edition from another place (required by another tool plugin.)
                                          But.. this defeats the general idea of a library. I cannot think of any good reason why that example (3.4) would ever be needed, and it just confuses the issue.
                                          ALL users and authors, should just simply follow the instructions in 2.1 and 2.2, and they will then never have any problems requiring or including the load instructor mixin module.


                                          So really,.. remove the example in Section 3.4 and consider the issue of finding and loading 'mixin/instructor.rb' done. It's not a problem.


                                          I'm not here much anymore.

                                          1 Reply Last reply Reply Quote 0
                                          • B Offline
                                            bentleykfrog
                                            last edited by

                                            Just a quick reply before I have to run some errands.

                                            @bentleykfrog said:

                                            Ahh, I see. In my case, using Alex's Plugin Loader, when a new script is loaded $LOAD_PATH's aren't updated to show the file's folder location.

                                            I should have elaborated on what I meant by $LOAD_PATH. In this case, I meant the folders that are represented in the $LOAD_PATH array. As a case example, take my University. There's only read access granted to anything below C:/Program Files & C:/Program Files (x86). They don't provide a script that will load some read/writeable directories to the $LOAD_PATH array. I'm asserting that if there was a similar setup, but had Alex's Plugin Loader underneath one of the $LOAD_PATH's in C:/Program Files & C:/Program Files (x86), you would need to bundle load_instructor.rb with your script, and provide an absolute path to it using require(). This is probably quite rare though, but its one of the reasons I provide the Advanced Use of the Load Instructor.

                                            got to go, will respond to your post in more detail when I get some more free time.

                                            -niall

                                            1 Reply Last reply Reply Quote 0
                                            • 1
                                            • 2
                                            • 3
                                            • 4
                                            • 5
                                            • 2 / 5
                                            • First post
                                              Last post
                                            Buy SketchPlus
                                            Buy SUbD
                                            Buy WrapR
                                            Buy eBook
                                            Buy Modelur
                                            Buy Vertex Tools
                                            Buy SketchCuisine
                                            Buy FormFonts

                                            Advertisement