Observers work differently depending on how Sketchup starts?
-
Has anyone else seen this:
We have a plugin which uses observers. It also stores attributes in the model.
If we start Sketchup by selecting the desktop icon, and then open a model with our attributes in it, everything works fine.
However, if we start Sketchup by double-clicking on the model name in explorer, the model opens, and our tools are present, but they do not work correctly. Our attributes are present in the model, but observer callbacks which use the attributes do not work.
We have Sketchup::AppObservers set up for both onNewModel and onOpenModel events, but in the second case described above, they don't seem to be triggered.
Thanks for any help.
-
Yes old problem...
AND... it's quite clear in the text of the API dictionary:
@unknownuser said:
(https://developers.google.com/sketchup/docs/ourdoc/appobserver#onOpenModel)":2oo7mjja]
NOTE: If a skp file is loaded via the command line or double-clicking on a skp in explorer (which is also is the command line) then this observer will not be called. The Ruby interpreter in SketchUp is initialized after command line processing so the observer won't be added in time to get the notification.
-
OK here's a brainstormin' session for a workaround:
1) Assume each time Sketchup starts, it gets a different process id (during a certain time frame, likely 24 hours, if your computer is always on.)
2) This pid is stored in the Ruby global
$$
or you can call:Process.pid()
3) You can get the time via
Time.now()
4) Your plugin has it's
AttributeDictionary
attached to the model, so you can write a "secret" attribute (these usually begin with an underscore character,) for registering the fact of if the model has been "init'ed" with respect to your plugin.5) In your code, use a
UI.start_timer
block to check for a situation where a model has not been "init'ed" normally, with respect to your plugin. If not, the timer times out after say 20 seconds, and explicitly calls yourAppObserver
subclass instance'sonOpenModel
callback.
In a normal situation, your code will stop the timer, before it times out.6) In both scenarios, you write a attribute value consisting of the date and the pid number to your "_inited" attribute.
Challenges:
a) What if the user closes a particular model, by opening another one, then opens the first model again manually ?
- The date will be the same.* Sketchup's pid will still be the same.
-
I just thought of something else...
.. users do not like it when a plugin modifies the model, when they have not made any manual changes.
If they open a model just to view it, they expect to be able to close it, (or open another,) without being prompted to save the model.
Be sure that any plugin settings you are saving within any model's attribute dictionary, is specific to THAT particular model.
Any general plugin settings should instead be saved to a config file, or into the registry (Windows) or plist (Mac.)
see: Sketchup # write_default()
and: Sketchup # read_default()So in the previous post... strike out saving a "secret" attribute in a model's attribute dictionary, and instead save a attribute using
write_default()
. -
We needed a Sceneobserver being attached whenever a model is openend (through the menu item or when double clicking a skp file in Windows Explorer.
And did it like this:Ath the end of the ruby file:
PRODUCE.sceneobserver # Observer that watches the application for opening of models from the file menu and creates scenes observer automatically. class MyAppObserver < Sketchup;;AppObserver def onOpenModel(model) PRODUCE.sceneobserver end end
This handles both possibilities.
Now to avoid creating 2 observers just check if there is already an observer presentdef PRODUCE.sceneobserver #add the scene observer after PRODUCE.scenes or when scenes need to be reloaded # if ($xDframesobserverid==nil or $xDframesobserverid=='') #no frame observer existing or it has been removed #UI.messagebox ('make observer') $xDframesobserverid=Sketchup;;Pages.add_frame_change_observer(XdFrameObserver.new) end end
Hope this helps
-
Cleaned up examples:
(1) Using global variables for a certain plugin is a no-no (use module variables instead.)
(2) Defining "author" or specific plugin classes at withinObject
is a no-no (define them within an author or company module namespace.)
(3) Class and module identifiers should beCamelCase
. Constants should beALLCAPS
.module Pout; end module Pout;;Produce # Declare module variables at top of module; @@xDframesobserverid==nil # Observer that watches the application for opening # of models from the file menu and creates scenes # observer automatically. class ProduceAppObserver < Sketchup;;AppObserver def onOpenModel(model) Pout;;Produce.sceneobserver end # callback end # class # at end of module; Sketchup.add_observer( ProduceAppObserver.new ) end # module
Now to avoid creating 2 observers just check if there is already an observer present:
module Pout;;Produce # define a Frame Change Observer class XdFrameObserver def frameChange(fromPage,toPage,percent_done) # respond depending on float value of percent_done end # callback end # class def self.sceneobserver() # # Add the scene observer after Pout;;Produce.scenes(), # or when scenes need to be reloaded. # if (@@xDframesobserverid==nil or @@xDframesobserverid=='') # No frame observer existing or it has been removed @@xDframesobserverid=Sketchup;;Pages.add_frame_change_observer(XdFrameObserver.new) end # if end # def method end # module Pout;;Produce
-
@pout said:
This handles both possibilities.
The API docs say this won't work... because the file is already open (on PC,) by the time the AppObserver gets loaded.
Are you sure it works on PC ??
On the Mac, the model is opened AFTER all the scripts are processed.
-
Dan,
I have done some testing with this and it seems to work yes.
Thx for cleaning up the script!
-
@dan rathbun said:
The API docs say this won't work... because the file is already open (on PC,) by the time the AppObserver gets loaded.
Dan,
In my script:
When double-click on SU file- opening the model in SU
- execute PRODUCE.sceneobserver (automatically)
- A Appobserver is also created (which fires PRODUCE.sceneobserver again, but since there already is an observer there is no new one created)
- When a model is then opened from the file menu the Appobserver is called
Logically and after testing this seems to work ok.
My code here is not complete though, since in my code there is another check to see if the framobserver must be created or not (depending if the opened model has some specific settings)
Advertisement