How do I detect if the user has changed model units mid-session?

In Ruby, how do I detect if the user has changed the model units during a session?

I’m writing a plugin which sets different defaults for models with units in inches, or in metric.

I can check that when the user starts the plugin, but when they change the units mid-session, all that happens is that the last saved values in an inputbox are converted from one unit to the other, exactly.

| want the defaults to change to round numbers in the other units, as if the plugin were restarted in another session with different units.

I thought I’d seen this a long time ago on the forum, but can’t find it.

I think I need to use a Sketchup.model observer in some way but don’t know how.

The Ruby API doesn’t offer a useful example for me to follow.

Alternatively, can I use an instance variable to remember the last units used, and compare that with the units detected when the plugin is restarted in the current session?

Any help much appreciated.

# Check model units - inches or metric?
      @previous_units ||= nil
      # Uses a  function that returns either "inches" or "metric", defined below
      model_units = self.model_unit_type
      unless @previous_units == model_units || @previous_units == nil
        UI.messagebox("Units changed", MB_OK)
      end
 @previous_units = model_units

...
  def self.model_unit_type
  # Find which unit and format the model is using and define model unit type
  #   accordingly
  #   When LengthUnit = 0
  #     LengthFormat 0 = Decimal inches
  #     LengthFormat 1 = Architectural (feet and inches)
  #     LengthFormat 2 = Engineering (feet)
  #     LengthFormat 3 = Fractional (inches)
  #   When LengthUnit = 1
  #     LengthFormat 0 = Decimal feet
  #   When LengthUnit = 2
  #     LengthFormat 0 = Decimal mm
  #   When LengthUnit = 3
  #     LengthFormat 0 = Decimal cm
  #   When LengthUnit = 4
  #     LengthFormat 0 = Decimal metres

 # Get model units (imperial or metric) and length format.
    model = Sketchup.active_model
    manager = model.options
    if provider = manager["UnitsOptions"] # Check for nil value
      length_unit = provider["LengthUnit"] # Length unit value

      case length_unit
      when 0, 1 then model_unit_type = "inches" ## Imperial units
      when 2, 3, 4 then model_unit_type = "metric"
      end #end case

    else
      UI.messagebox " Can't determine model units - please set in Window/ModelInfo"
    end # if
  end

That seems to work. I’ve got it to pop up a messagebox when the units have changed since the plugin was last run in this session, and not to pop up if this is the first time the plugin has run, or the units haven’t changed since it was last run.

Now just have to get it to reset the defaults before re-opening the inputbox for new inputs, and make the test for a change into a method.

You use this observer …

… but you need to attach to each model’s options so you also need an …

… to watch the application for model open and model new events so as to attach each model’s units options provider as appropriate.

Are these units always visible is some ort of persistent UI? Otherwise the defaults could just be updated once the UI is shown, without any advanced observers.

The user interface just looks like this at the moment:

What I want to do is to change some of the defaults (for example. End at, Spacing and Text Height and maybe others) from 25, 10mm and 5mm respectively, when units are metric, but if the user changes units to inches, during the session, the next time the extension is run to open the inputbox, it should show 10, 1", and 0.25", as it would be if the user had had model units set to inches when the model was first opened.

The sample code I posted in the original question looks as if it ought to work without any observer.

You can just call model_unit_type whenever the inputbox is shown. I don’t see any reason to cache this value and update it when the unit changes, unless you have a HTMlDialog you want to live update the second the model unit changes.

[EDIT]
That’s what I’m planning to test out later today. And in fact, I may extend it beyond the binary choice between inches and metric to offer different defaults for the different options for each, such as Architectural, Fractional, Engineering, and mm, cm and m.

Slightly off topic but a lot of the phrases in this UI can be changed to contain the same info without being as verbose, making them more clear.

“L-R (Left to Right)” can simply be “Left to Right”, “Start number at (integer >=0)” can be changed to “Start at” with the default value conveying it’s an integer number, and some common sense combined with input validation making sure the value isn’t negative, “Increment by” can just be “Increment” and so on.

I can think about that again. I haven’t implemented any error trapping (yet), except the basics provided by the inputbox automatically to detect invalid types of response, which is why I thought (at first at least) to add the valid kinds of values the inputbox expects.

And I added L-R and R-L so I could just use the first three characters to tell which option had been chosen.

I could of course just test the first letter of Left to right or Right to left. However, with the extra short form at the beginning, I can later use direction[0..2] to get L-R for example, and use it in naming the component the extension creates - a series of separate Flat Text numbers running from Start at to End at in steps of Increment at the input spacing.

I could do it either way, I can see that.