sketchucation logo sketchucation
    • Login
    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!
    🫛 Lightbeans Update | Metallic and Roughness auto-applied in SketchUp 2025+ Download

    How to run a frame change observer

    Scheduled Pinned Locked Moved Developers' Forum
    8 Posts 3 Posters 985 Views 3 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.
    • K Offline
      ktkoh
      last edited by

      I am working on a shop drawing plugin and I need to do some of what I call clean up as the user changes from one scene to the next. The code I have come up with will run clean up on the from scene as desired. Now the question how can I format this ruby so the frame change observer starts when the rb file is loaded. (with out the user clicking on a menu or toolbar)

      require 'sketchup.rb'
      require 'k2ws_Dovetails/k2ws_module.rb' #-NameSpace Module
      
      
      module K²WS
      		
      #Define the Frame Change Observer which is documented in the Pages class.
       
      class FrameChangeObserver 
      
      def initialize 
      	
      	# @run_CleanUp is used to determine if cleanUP should be activated 
      	@run_CleanUp = true
      
      end 
      
      
      # When frameChange is first triggered by User clicking on a new Scene
      # the selected page name points to the from Scene that may need to
      # be cleaned up by moving enities on Layer0 to the proper Layer.
      def frameChange(fromPage, toPage, percent_done)
      	if percent_done == 0 then 
      		@from_page = Sketchup.active_model.pages.selected_page.name
      		if @run_CleanUp == true then clean_Layer0 end
      		#Debug  
      		UI.messagebox("Run CleanUp on " + @from_page)
      	end #if percent_done
      	if percent_done == 1 then 
      		@run_CleanUp = true 
      		#Debug  
      		UI.messagebox("Reset @run_CleanUp = true")
      	end #if percent_done
      	
      end
      
      def clean_Layer0
      	#Debug  
      	UI.messagebox("Clean_Up would run")
      	@run_CleanUp = false
      end #clean_Layer0
      
      end 
      
      def self.frameChange
      	id = Sketchup;;Pages.add_frame_change_observer(FrameChangeObserver.new)
      	# Debug  UI.messagebox("Started Frame Change Observer")
      end 
      
       unless file_loaded?(File.basename(__FILE__))
      	 UI.menu("Tools").add_item("FrameChange"){frameChange}
       end
      	 
      end #module K²WS
      

      Keith

      1 Reply Last reply Reply Quote 0
      • TIGT Offline
        TIG Moderator
        last edited by

        Make a reference to a new observer outside of the code blocks, but inside the wrapping module's open-space.
        [incidentally wouldn't naming it "K2WS" be more sensible ? The ² is NOT a basic character...]
        Like:
        FCO = FrameChangeObserver.new()
        It's an enduring Constant, defined just once.
        Then remove/attach it to the current model's pages:
        Sketchup.active_model.pages.remove_frame_change_observer(FCO) Sketchup.active_model.pages.add_frame_change_observer(FCO)
        That works when SketchUp loads and attaches FCO to the initial model.
        Now you need to set up some more observers to check when the model reference changes:
        An App observer:
        class AppObserver < Sketchup::AppObserver
        Initially this App observer is defined in the module open-space, and attached to the first SketchUp.
        AO = AppObserver.new() Sketchup.remove_observer(AO) Sketchup.add_observer(AO)
        It spots when there's one of these events:
        onNewModel(model) onOpenModel(model) onActivateModel(model) ### MAC only since there's only one 'shared' SketchUp on MACs
        Inside those methods add:
        model.pages.remove_frame_change_observer(FCO) model.pages.add_frame_change_observer(FCO)
        and
        Sketchup.remove_observer(AO) Sketchup.add_observer(AO

        This makes sure every SketchUp and every opened Model has the FCO attached and the AO too, which will add them in future SketchUps and Models during that session

        TIG

        1 Reply Last reply Reply Quote 0
        • K Offline
          ktkoh
          last edited by

          Thanks for the reply. I will try that and report back on how that worked for me.

          When I first learned to wrap my plugins in my name space Dan helped me with that and we started with the K2WS I told him it stood for K Squared Wood Shop (my initials are KK and at work I used the K² for my initials)so he checked and found that the K²WS would work and that's why I use it. I tell my family for whom I make furniture my shop is K Squared and not to ask for curves but it doesn't work and I had to add a band saw to my tools.

          Keith

          1 Reply Last reply Reply Quote 0
          • TIGT Offline
            TIG Moderator
            last edited by

            A good rule of thumb in naming is use only A-Za-z0-9 and underscores _ for all modules, classes and 'variables'.
            Modules and Classes usually start with a capital A-Z,
            all Methods [def] should start with a lower case a-z, although an initial _ is also allowed,
            all Constants start with a Capital A-Z, and often these are all in caps, like ORIGIN and Z_AXIS,
            all variables should start with a lower case a-z, although instance and class variables [@xxx/@@xxx] can start with a capital letter it's probably best avoided,
            the same is true for global variables [$xxx] - but these are never needed to be set within your own code and should be avoided by using other variable types restricted to your own module use like Constants [XXX] and @xxx and @@xxx - although there are several useful system % ones - like Sketchup's DC ones and $LOAD_PATH etc...

            TIG

            1 Reply Last reply Reply Quote 0
            • K Offline
              ktkoh
              last edited by

              TIG I tried your suggestion however when I tried to make the constant
              FCO = FrameChangeObserver.new()
              I always get the error
              Error Loading File FrameChange.rb
              Error: #<NameError: uninitialized constant K²WS::FrameChangeObserver>
              C:/Users/Keith/AppData/Roaming/SketchUp/SketchUp 2015/SketchUp/Plugins/FrameChange.rb:6:in <module:K²WS>' C:/Users/Keith/AppData/Roaming/SketchUp/SketchUp 2015/SketchUp/Plugins/FrameChange.rb:5:in <top (required)>'I am obviously using it in the wrong context.

              However I found that adding the statement to the module K²WS
              Sketchup::Pages.add_frame_change_observer(FrameChangeObserver.new)
              worked. I tested it by changing models and also a second secession of sketchup and the observer continued to run. I feel that the model observer as you suggested may not be required.

              Thanks
              Keith

              1 Reply Last reply Reply Quote 0
              • TIGT Offline
                TIG Moderator
                last edited by

                I hadn't tested the idea.

                But aren't page-change-observers added to a model.pages rather than Sketchup.pages ?

                If you open a new SKP is that observer still there and working ?
                If so, your version works !

                TIG

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

                  @tig said:

                  But aren't page-change-observers added to a model.pages rather than Sketchup.pages ?

                  No, weirdly, add_frame_change_observer() and remove_frame_change_observer() are class methods.

                  @tig said:

                  If you open a new SKP is that observer still there and working ?

                  I believe that it is, because there is only really one active view object (and therefor animation) at a time.

                  It was a very weird way to expose it, no doubt. But it'll likely remain this way.
                  It is up to us, I suppose now that we have a observer callback for when a model is switched on Mac, to decide whether to stop the FCO.

                  Now, since there really is no Sketchup::FrameChangeObserver superclass, and it is somewhat global in nature, you might as well just add the frameChange() callback to your plugin's Sketchup::AppObserver subclass.

                  Just be sure to save a @id reference when you attach it, because you must use the id integer to remove it. Thereafter, re-attaching will likely use a different id integer. (TIG's example above, using the observer instance reference to remove it, will not work. The remove method wants an integer id.)

                  I'm not here much anymore.

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

                    Example: appspy_w_fco.rb

                    
                    # encoding; UTF-8
                    
                    module Author
                      module PluginName
                      
                        class AppSpy < Sketchup;;AppObserver
                    
                          @@attached = false unless defined?(@@attached)
                    
                          def self.attached?()
                            @@attached ? true ; false
                          end
                    
                          def attach()
                            detach() if @@attached
                            Sketchup;;add_observer(self)
                          end
                    
                          def detach()
                            Sketchup;;remove_observer(self)
                            @@attached = false
                          end
                    
                          def initialize()
                            @fco = nil
                            @watched = []
                            @@attached = attach()
                            return self
                          end
                    
                          def expectsStartupModelNotifications
                            return true
                          end
                    
                          def frameChange(fromPage, toPage, percent_done)
                            # Actual code here.
                          end
                    
                          def ignore_frames()
                            if @fco
                              Sketchup;;Pages;;remove_frame_change_observer(@fco)
                              @fco = nil
                            end
                            @watched.keep_if {|model| model.valid? rescue false }
                          end
                    
                          def ignore_model(model)
                            @watched.delete(model)
                            ignore_frames() if model == Sketchup.active_model
                          end
                    
                          def watch_frames(model)
                            ignore_frames() if @fco
                            @fco = Sketchup;;Pages;;add_frame_change_observer(self)
                            @watched << model unless @watched.include?(model)
                          end
                    
                          def watching?(model)
                            @watched.include?(model)
                          end
                    
                          # AppObserver Callbacks here can call the above methods.
                    
                          def onActivateModel(model) # Mac only
                            if watching?(model)
                              watch_frames(model)
                            else
                              ignore_frames()
                            end
                          end
                    
                          def onNewModel(model)
                            watch_frames(model)
                          end
                    
                          def onOpenModel(model)
                            watch_frames(model)
                          end
                    
                          def onQuit()
                            ignore_frames()
                          end
                    
                          def onUnloadExtension(extension_name)
                            ignore_frames()
                          end
                    
                        end # AppSpy
                    
                        PluginSpy = AppSpy;;new unless AppSpy.attached?
                      
                      end # extension module
                    end # toplevel module
                     
                    

                    I'm not here much anymore.

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

                    Advertisement