[code] Automatic Observer attachment at beginning of session

This was previously hidden in the SDK category as a response to a mixed code extension.

I have moved it here into the Ruby category.

This also serves as the example for what was discussed in topic:


Attach_Model_Observers.rb (3.8 KB)

# encoding: UTF-8

module SomeAuthor # <<---<<< Change to your toplevel namespace module
  module SomeNiftyExtension # <<---<<< Change to your extension submodule

    extend self

    def attach(model)
      model.add_observer(ModelSpy.new(model))
    end

    def expectsStartupModelNotifications
      true
    end

    def onNewModel(model)
      attach(model)
    end

    def onOpenModel(model)
      attach(model)
    end

    def onQuit()
      # execute onQuit tasks
    end

    class ModelSpy < Sketchup::ModelObserver
      attr_reader :model
      def initialize(model)
        puts "#{self.class.name}\n  in '#{__method__}'"
        @model = model
        # Hash to hold observer references:
        @spy = {}
        # Add all of the implemented observers to model collections:
        model.definitions.add_observer(
          @spy[:definitions]= DefinitionsSpy.new(model.definitions)
        )
        # ... etc, ... same for Entities, Materials, Layers (if defined) ...
      end
      #
      ### ... ModelObserver callback method definitions ...
      #
    end # class

    class DefinitionSpy < Sketchup::DefinitionObserver
      def initialize(definition)
        puts "#{self.class.name}\n  in '#{__method__}'"
        # On model load, attach observers to each existing instance:
        definition.instances.each { |instance|
          instance.add_observer(InstanceSpy.new)
          instance.add_observer(EntitySpy.new)
        }
      end
      def onComponentInstanceAdded(definition, instance)
        puts "#{self.class.name}\n  in '#{__method__}':"
        puts "  #{definition.name}: #{instance}"
        # Attach appropriate observers to the new instance:
        instance.add_observer(InstanceSpy.new)
        instance.add_observer(EntitySpy.new)
        # Do something with the new instance ...
      end
    end # class

    class DefinitionsSpy < Sketchup::DefinitionsObserver
      def initialize(definitions)
        puts "#{self.class.name}\n  in '#{__method__}'"
        # On model load, attach an observer to each existing definition:
        definitions.each { |definition|
          definition.add_observer(DefinitionSpy.new(definition))
        }
      end
      def onComponentAdded(definitions, definition)
        puts "#{self.class.name}\n  in '#{__method__}': #{definition}"
        # Attach an observer to the new definition:
        definition.add_observer(DefinitionSpy.new(definition))
        # Do something else with the new definition ...
      end
    end # class

    class EntitySpy < Sketchup::EntityObserver
      def initialize
        puts "#{self.class.name}\n  in '#{__method__}'"
      end
      def onChangeEntity(entity)
        puts "#{self.class.name}\n  in '#{__method__}': #{entity}"
        # Do something with the changed entity ...
      end
    end # class

    class InstanceSpy < Sketchup::InstanceObserver
      def initialize
        puts "#{self.class.name}\n  in '#{__method__}'"
      end
      def onClose(instance)
        puts "#{self.class.name}\n  in '#{__method__}': #{instance}"
        # Do something with the closed instance ...
        # The instance's internal coordinates are in local values.
      end
      def onOpen(instance)
        puts "#{self.class.name}\n  in '#{__method__}': #{instance}"
        # Do something with the opened instance ...
        # All API methods will use and return WORLD coordinates !
      end
    end # class

    # .. other observer class definitions ...

    # RUN ONCE AT STARTUP:
    if !defined?(@loaded)

      # Define UI objects here ...

      # Attach this module as an AppObserver object:
      Sketchup.add_observer(self)

      # Mark this extension as loaded:
      @loaded = true
    end

  end # extension submodule
end # namespace module

Then when a component or group instance is moved, the onChangeEntity callback method in the EntitySpy will get called.

NOTE: The puts statements like puts "#{self.class.name}\n in '#{__method__}'" sending output to the Console are just for testing. They can be deleted, commented out OR a conditional if @debug added at the end. (In the latter case you’ll need to define a @debug boolean variable at the top of the extension submodule.)

1 Like

More information … (in progress) …

1 Like