• Login
sketchucation logo sketchucation
  • Login
🤑 SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

How to run a frame change observer

Scheduled Pinned Locked Moved Developers' Forum
8 Posts 3 Posters 938 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 19 May 2016, 22:24

    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
    • T Offline
      TIG Moderator
      last edited by 20 May 2016, 11:00

      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 20 May 2016, 11:46

        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
        • T Offline
          TIG Moderator
          last edited by 20 May 2016, 12:46

          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 20 May 2016, 15:00

            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
            • T Offline
              TIG Moderator
              last edited by 20 May 2016, 17:24

              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
              • D Offline
                Dan Rathbun
                last edited by 22 May 2016, 01:12

                @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
                • D Offline
                  Dan Rathbun
                  last edited by 22 May 2016, 02:06

                  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
                  1 / 1
                  • First post
                    7/8
                    Last post
                  Buy SketchPlus
                  Buy SUbD
                  Buy WrapR
                  Buy eBook
                  Buy Modelur
                  Buy Vertex Tools
                  Buy SketchCuisine
                  Buy FormFonts

                  Advertisement