sketchucation logo sketchucation
    • Login
    1. Home
    2. Dan Rathbun
    3. Posts
    Oops, your profile's looking a bit empty! To help us tailor your experience, please fill in key details like your SketchUp version, skill level, operating system, and more. Update and save your info on your profile page today!
    🚨 Skimp | 25% Off until March 30 Buy Now
    Offline
    • Profile
    • Following 0
    • Followers 1
    • Topics 92
    • Posts 4,904
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: Get a list of File names on the input box

      Dave, make it easier on yourself and e1-else.
      Use [ code ] tags:

      [code]
        # Ruby program goes here ...
        Indent works
      [/code]
      
      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Coping with SU2017 entity handling changes

      @sdmitch said:

      I have tried using .clone and .dup but neither remedy the problem. Any guidance will be greatly appreciated.

      These two methods are Ruby Core functions that rely upon specific Ruby housekeeping methods to do the copying.

      The SketchUp API has not set up these "housekeeping" methods, so doing this will not work.

      They have only in a few places "mimicked" a clone method (mostly in the Geom classes,) and a partial cloning in Group#copy. (It does not copy over attributes and other parameters.)

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Get a list of File names on the input box

      (1) Ruby file and directory operations work best with paths using "/" which is defined as File::SEPARATOR.

      This statement will replace each occurrence of "\" OR "" OR "//" with "/":
      path.gsub(/\\\\|\\|\/\//,'/')

      Wrap it up in a utility method:

      def slashify(path)
        path.gsub(/\\\\|\\|\/\//,'/')
      end
      
      

      (2) If you wish only the filenames, without the path prepended to every filename, use:

      Dir.entries("C:/TestFolder/")[2..-1].join('|')

      The [2..-1] sub-range ignores the first two members ( 2 is the third member) which are "." and "..". The -1 means the last member.


      (3) The preferred means is (because this is how the user is used to choosing files):

      # In the Constant definitions at top of class or module;
      WIN =(Sketchup;;platform == ;platform_win rescue RUBY_PLATFORM !~ /darwin/i)
      
      # Later on in some method of your code;
      chosen = UI.openpanel(
        'Choose a file...',
        'C;/TestFolder',
        WIN ? 'All files|*.*||' ; '*.*'
      )
      if chosen # nil if user cancels openpanel
        # chosen is an good absolute filepath
        # On PC, the paths will have backslashes
        # so "slashify" them after UI.openpanel();
        path = slashify(chosen)
      end
      
      
      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: User ending a program

      So, you realize that although you could handle this scenario with very long cluttered if ... elsif ... else constructs, likely with a bunch of nested if statements,... that would be hard to maintain and use more lines.

      This way condenses the code, and makes it more readable, and easier to maintain.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: User ending a program

      @ktkoh said:

      Thanks Dan that really helped me.

      Cool! That was the purpose.

      @ktkoh said:

      I had never run across the term fail() and was able to research this in the ruby help files I use.

      Kerenl#fail() is just an alias for Kernel#raise().
      (I explained this in the file comments, and why I use it, instead of raise.)

      @ktkoh said:

      I have tested it in my program and find it works as you described.

      I just noticed an error, although the example should still work.

      Line 38 was meant to be:
      data = UI.inputbox(@@prompts,@@defaults,@@choices,@@caption)

      Currently it is:
      data = inputbox(@@prompts,@@defaults,@@choices,@@caption)
      which uses the global inputbox() retry wrapper method defined in " %(black)[Tools/sketchup.rb]". So then the " %(black)[sketchup.rb]" file must be loaded first.

      If y'all prefer, you can call the UI.inputbox() module method directly and bypass the retry wrapper.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: User ending a program

      @ktkoh said:

      I have used "exit" in my joint tool program ...

      Is this an external script running in a system Ruby process, or a SketchUp extension running in an embedded Ruby subprocess ?

      Raising a SystemExit exception has only good use in a system Ruby script, and no good use in an embedded Ruby subprocess.

      So, yes, within SketchUp, it is poor practice. It is better to raise a RuntimeError exception with a custom message, or create your own exception subclasses within your extension namespace.

      Here is a simple example:
      Custom_Exception_SubClasses.rb

      
      # encoding ; UTF-8
      
      # A simple example of creating your own Exception
      #  sub-classes inside your own namespace modules.
      #
      # fail() is an alias for raise(), and most coding style guides
      # prefer that fail() is used when first causing an exception,
      # and raise() is used when re-raising a current exception within
      # a rescue block.
      
      module Author # <--<<< Change to unique toplevel namespace module.
      
        module SomeParticularPlugin
      
          VERSION = '1.0.0'
          
          InputCancellation = Class.new(RuntimeError)
          InvalidSelection  = Class.new(RuntimeError)
          NoGroupsSelected  = Class.new(RuntimeError)
      
          # In this module we assume that the module variables; @@prompts,
          #  @@defaults, @@choices & @@caption have already been defined.
      
          def get_input(selection_set)
            
            # First, make a decision about the selection set;
            if selection_set.empty?
              fail(InvalidSelection,"selection is empty!")
            elsif !selection_set.single_object?
              fail(InvalidSelection,"selection not single object!")
            else
              grps = selection_set.grep(Sketchup;;Group)
              if grps.empty?
                fail(NoGroupsSelected,"no groups are selected!")
              end
            end
      
            data = inputbox(@@prompts,@@defaults,@@choices,@@caption)
            # false if the user cancels
            fail(InputCancellation,"user cancelled") if !data
            # otherwise data is an array of values
      
          rescue InputCancellation => err
            UI.messagebox("Stop; #{err.message}")
            return nil
          rescue InvalidSelection => err
            UI.messagebox("Selection Error; #{err.message}")
            return false
          rescue => err # any other exception
            # recover gracefully
            UI.messagebox("Error; #{err.message}")
            return false
          else # no errors
            # code to execute when input is valid
            return data
          end ###
      
        end # specific plugin module
      end # outermost namespace module
      
      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Count excel rows

      More Specifically: Would the Range.Find method help?
      Range.Find method

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Count excel rows

      Generally: Ruby can only use Excel via the WIN32OLE class, which just wraps OLE Automation. So it should be similar to VBA. In fact, I myself have this MS page bookmarked as the reference to use for WIN32OLE <-> Excel interaction:
      Object model (Excel VBA reference)

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Please help me to get a group's layer

      Good answer(s)!

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Please help me to get a group's layer

      Hey TIG, what if the parent group definition has more than 1 instance, and the target is not the first in the instances collection ?

      Could a coder "tag" the target instance by pushing it's reference into the selection set, and then iterate the instances collection looking for a match ?

      ... Or, do they have to walk the model's entities tree ?

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Any way to install two copies of SU (same version)?

      Hmmm,... you must have seen the movie (or the remake.)

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Save and recall parameters of an object created with ruby

      You should have a look at the SketchUp Team's Shapes and Window Maker example extensions. They (and a few others) use a library class called Parametric that handles creating a inputbox of parameters and saves them into an attribute dictionary attached to the (group or component) object.

      Why reinvent the wheel?

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Any way to install two copies of SU (same version)?

      This is correct. SketchUp's settings are written to the currently loaded user (HKCU/Software) hive of the registry. So user "Jane"'s settings cannot affect user "Dick"'s settings.

      Try it.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Any way to install two copies of SU (same version)?

      @derei said:

      I am wondering if is any way to (force?) install two copies of same sketchup version on a machine...
      the reason would be to keep a light version for testing stuff (ruby, etc) and the other one to be the "usual" sketchup that has all the plugins and things that I am using for modelling.

      The easiest way that I use is to create a new "Test" user account, and log onto that account for testing. This "Test" user then does not make changes to my normal user account. (SketchUp keeps a separate set of registry values, for each user, in each different user's registry hive.)

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Is it follow_me or followme ?

      @tomot said:

      I hope inconsistencies in the API are still being corrected!

      The place to log mistakes in the API Ruby documents are here:
      https://github.com/SketchUp/ruby-api-docs/issues

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Need Help adding predefined dynamic Attributes via Ruby

      ... using:
      http://extensions.sketchup.com/en/content/attribute-inspector

      And see this wiki thread in the official forums:

      • Dynamic Components Resources
      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: How to create a hole in a surface?

      Agian, there is no good reason to ever be using globals.
      Not ever. They are not needed, ever! Zilch! Nada!

      @tomot said:

      @sdmitch said:

      Put the repeated code in its own method and pass the variables to it.

      I'm aware of "making my own method" ..., regardless I have never been able to get any of my attempts to work... hence I gave up!

      I just gave you a proper example of how to use methods, and how to use module variables instead of globals.

      Don't just "give up", ask specific questions about specific "attempts".
      (Ie, your statement is a bit vague.)

      What is it that you are having trouble understanding about using methods ?

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: A ruby code to display dynamic component

      Taking John's example cross-platform:

      
      class MyModelObserver < Sketchup;;ModelObserver
      
        # ! If the DC extension is ever changed, this example could break !
      
        def onPlaceComponent(instance)
          return nil unless defined?($dc_observers)
          # check if it's a DC in here;
          if instance.attribute_dictionaries &&
          instance.attribute_dictionaries['dynamic_attributes']
            # then set Selection Tool and trigger the dialog;
            Sketchup.active_model.select_tool(nil)
            dc_class = $dc_observers.get_latest_class
            if dc_class.configure_dialog_is_visible
              dc_class.get_configure_dialog.bring_to_front
            else
              dc_class.show_configure_dialog
            end
            dc_class.refresh_configure_dialog
          end
        end
      
      end
      
      

      ❗ This code is fragile, because it deals with someone's else's extension. They could change it (such as method names,) at any time in the future and this example will break.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: How to create a hole in a surface?

      Do not ever use global variables for plugin variables!
      Never mind that examples use them. That doesn't make it okay.

      Here is some sample code for dealing with options, saving them to objects and across sessions:

      # encoding; UTF-8
      module Author;;SomePlugin
      
        ### AT TOP OF EXTENSION SUB-MODULE;
      
        # Extend the sub-module with itself. (This is now preferable
        # to declaring an anonymous singleton proxy class instance.)
        # The methods defined will now be available without need to
        # qualify them with the module as a receiver.)
        extend self
        
        # Define the keyname for saving and reading extension
        # options to and from Windows registry or Mac plist;
        @@extkey = Module;;nesting[0].name.gsub(';;','_')
      
        # Given that, at the top of your plugin sub-module;
        #   @@prompts is an array of inputbox prompts, and
        #   @@defaults is an array of inputbox default choices ...
        @@opts_caption = "Recessed Light parameters"
        @@dict_suffix  = "_Parameters" # is appended to @@extkey
      
        # Initialize hashes for the plugin options;
        @@opts ||= {}
        @@prev ||= {}
        
        # Load the options from previously saved choices;
        # (If not yet saved, use the indexed value from @@defaults)
        @@prompts.each_with_index {|opt,i|
          @@prev[opt]=(
            @@opts[opt]= Sketchup;;read_default(
              @@extkey, opt, @@defaults[i] 
            )
          )
        }
        
        
      ### SOME HELPFUL METHODS;
      
        # save_options_to_defaults()
        # A method to save the options hash to the
        # Windows registry or plist file on the Mac.
        def save_options_to_defaults()
          @@opts.each_pair {|optname,optval|
            Sketchup;;write_default( @@extkey, optname, optval }
          }
        end
      
        # save_options_to_dictionary(obj)
        # A method to save the options hash to an
        # attribute dictionary attached to an object.
        def save_options_to_dictionary(obj)
          dict_name = @@extkey + @@dict_suffix
          @@opts.each_pair {|optname,optval|
            obj.set_attribute( dict_name, optname, optval )
          }
        rescue
          return false
        else
          return true
        end
      
        # get_options_from_dictionary(obj)
        # A method to return options from an object's
        # attribute dictionary, as a Ruby hash.
        # Does not create dictionary nor attributes.
        # Default option values are used for any missing attributes. 
        def get_options_from_dictionary(obj)
          dict_name = @@extkey + @@dict_suffix
          hash = {}
          @@opts.each_pair {|optname,optval|
            hash[optname]= obj.get_attribute( dict_name, optname, optval )
          }
          return hash
        end
      
        # has_options_dictionary?(obj)
        def has_options_dictionary?(obj)
          return false if !obj.respond_to?(;attribute_dictionaries) ||
          obj.attribute_dictionaries.nil?
          dict_name = @@extkey + @@dict_suffix
          obj.attribute_dictionaries[dict_name] ? true ; false
        end
      
        # get_options_from_user()
        # Displays the parameters inputbox, and processes results.
        # Sets previous options to @@prev hash, and new choices to
        # @@opts hash.
        # Returns nil if user cancels the input box, or
        # a hash of boolean change flags with same keys as @@opts.
        def get_options_from_user()
          # Display the inputbox;
          results = UI;;inputbox( @@prompts, @@defaults, @@opts_caption )
          return nil unless results
          # Check for any changes;
          changed = {}
          results.each_with_index {|val,i|
            @@prev[@@prompts[i]]= @@opts[@@prompts[i]]
            if @@opts[@@prompts[i]] != val
              changed[@@prompts[i]]= true
              @@opts[@@prompts[i]]= val # set new value
            else
              changed[@@prompts[i]]= false
            end
          } # Now the hash @@opts is loaded with the results,
          # and @@prev hash holds all the previous choices.
          changed # Return a hash of boolean change flags.
        end
      
      
      ### EXAMPLE - DOWN IN OTHER CODE;
      
        # Checking results from parameters inputbox;
        changes = get_options_from_user()
        if changes.nil?
          # user cancelled the inputbox
        elsif changes.any?
          # iterate the changes hash to find those members
          # set true, which are those that were changed.
          # Or just test certain options if changed;
          if changed['Light Array Type']
            if @@prev['Light Array Type'].to_i < 3 &&
            @@opts['Light Array Type'] == '4 Lights'
              # do some task ...
            else
              # another test ?
            end
          else
            # skip this or do something else
          end
        else
          # The user just accepted the default choices
        end
      
      
      end # plugin sub-module
       
      
      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: How to create a hole in a surface?

      Entities#add_face() is bugged with an edge array as argument. That is what is returned from the Entities#add_circle() method, not the curve object itself.

      So try:

      base = entities.add_face(circle[0].curve)

      or:
      faces = entities.grep(Sketchup::Face) num_added = circle[0].find_faces() found = entities.grep(Sketchup::Face) - faces base = found.empty? ? nil : found.first

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • 1
    • 2
    • 9
    • 10
    • 11
    • 12
    • 13
    • 245
    • 246
    • 11 / 246