Unload / reload plugin without closing SketchUp
-
My plugin uses EntitiesObservers to monitor changes to geometry.
This is great while using the plugin, but a lot of unneeded overhead when NOT using the plugin.I know you can turn extensions on and off in the preferences window, but that requires you to restart SketchUp. And I don't want people having to restart every time when they do or do not want to use my plugin...
Is there a "simple" way to unload the plugin by, for example, keep track of any objects you make and when you want to unload it, make sure the objects are no longer "reachable"(that's probably the tricky part) and just let the garbage collector clean them out?
I have already made an array containing all my observer objects, so I only have to call "remove_observer" for every object in the array.
That cleans out the most obtrusive things, but then I end up with a "half-loaded" plugin. And when I want to reload it i have to figure out where to put these observers again. It would be much cleaner if the entire thing could be unloaded.Tips anyone?
Thanks!
Jan -
Ah! one thing I figured out:
Re-attaching the observers won't be as difficult as I thought because the observer objects are only disconnected, not destroyed.
So, if I keep track of where they were attached, I can easily re-attach them with "add_observer"(but this only partially solves my problem)
-
A bit hard without knowing how your model is structured - this is more a design issue.
Can't you keep a flag that reflects the enabled state of your plugin? One that will disable toolbar buttons and menus that cannot be used until enabled?
Because while I think there are Ruby methods to undefine methods and modules/classes, there is no way to remove toolbars and menus from the UI. -
@thomthom said:
Can't you keep a flag that reflects the enabled state of your plugin? One that will disable toolbar buttons and menus that cannot be used until enabled?
Yes, I'm doing that using "Sketchup.write_default", seems to work fine.
@thomthom said:
Because while I think there are Ruby methods to undefine methods and modules/classes, there is no way to remove toolbars and menus from the UI.
I would like to know how to undefine methods and modules/classes, that would help me out. I don't need to close the toolbar(however it would be great if you could reduce it to a single on/off button).
Everything I can find on that subject says that you don't need to make destructors because the garbage collector takes care of any "unreachable" objects. -
<span class="syntaxdefault">module Farm</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">cow</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> puts </span><span class="syntaxstring">'moo'</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> end</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> end<br /></span><span class="syntaxcomment"># => nil<br /><br /></span><span class="syntaxdefault">Farm</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">cow<br /></span><span class="syntaxcomment"># => moo<br /># => nil<br /><br /></span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">send</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">;</span><span class="syntaxdefault">remove_const</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">;</span><span class="syntaxdefault">Farm </span><span class="syntaxkeyword">)<br /></span><span class="syntaxcomment"># => Farm<br /><br /></span><span class="syntaxdefault">Farm<br /></span><span class="syntaxcomment"># => Error; #<NameError; (eval);155; uninitialized constant Farm><br /># => (eval);155 </span><span class="syntaxdefault"></span>
-
As TT says... you can readily load new extensions/tools/commands and activate their toolbars etc, loading them will also add menu items.
Later on you can also check for some 'state' and if it's 'false' make your loaded commands' menu items 'grayed-out' [https://developers.google.com/sketchup/docs/ourdoc/command#set_validation_proc].
You can also 'uncheck' an extension etc but that only becomes effective on a restart.
You cannot remove menu items, only enable/disable them - until a restart realizes any new 'extension' settings.
Toolbar buttons can also be disabled using 'set_validation_proc' on the command and the toolbar itself auto-hidden too...
However, you could 'hide' any context-menu items - your 'test' for displaying them can also look at your tool's 'state' and only display the item[s] when that state is 'true'.
You could also try and overwrite your 'command' so that they do nothing [with some code that sets the [enduring] 'command' to now do 'nothing' - EDIT: TT beat me to that in his example], but I see little benefit unless their normal use is done obscurely, since access to the commands will be via toolbars/menus etc that can be disabled... -
@tig said:
but I see little benefit unless their normal use is done obscurely, since access to the commands will be via toolbars/menus etc that can be disabled...
Ditto. For the end user - as long as your plugin deactivates all observers - the only thing that will bother them is the menus and toolbars. I would have just focused on making them clearly display the disabled state of the plugin.
-
I also note that you refer to using
EntitiesObservers
- these are well known for causing issues. https://developers.google.com/sketchup/docs/ourdoc/entitiesobserver@unknownuser said:
WARNING: The methods of this observer fire in such a way that making changes to the model while inside of them is dangerous. If you experience sudden crashes, it could be because of this observer. A potential workaround is to use a ToolsObserver to watch what the user is doing instead.
So using an alternative IS recommended/possible...
If they are only attached during the very operation of you tool then maybe they'll be OK, but if they 'persist' into the operation of normal Sketchup then they can cause other innocent tools to crash, because they are then seen to be somehow changing the model's entities while the observer is active... I had an issue with one of my tools crashing because of a 3rd party tool made such an observer that persisted. I recoded to erase only the unwanted groups etc by using .clear! on their entities, so that the closing commit_operation cleaned up, rather than directly erasing them inside the start/commit block [which activated the crash]... Of course scripts should be allowed to do legitimate operations without fear of falling foul of someone else's ill-considered observer...
Indeed it was found that even a completely 'empty' EntitiesObserver will cause such unexpected crashes, if it's left active during the operation of some other tool that innocently changes the entities within a start/commit block. -
@tig said:
So using an alternative IS recommended/possible...
Hmmm, I already changed "SelectionObserver.onSelectionAdded" to "SelectionObserver.onSelectionBulkChange" because of a warning like that.
I will try to get rid of that one too!
But this is probably the most important to be able to disable all observers.
And yeah, maybe it's enough to just have a disable-observer button instead of trying to unload the entire plugin. If someone really wants it completely disabled then the unload-extension feature(with restart) can always be used(and is probably the only thing that makes sure it's completely unloaded).
-
@brewsky said:
And yeah, maybe it's enough to just have a disable-observer button instead of trying to unload the entire plugin. If someone really wants it completely disabled then the unload-extension feature(with restart) can always be used(and is probably the only thing that makes sure it's completely unloaded).
Yes. Keep track of your observers (and try to make them do as little as possible) and ensure you remove them all when not needed.
When I need to use observers I try to make them as light as possible - simply keeping a cache of the info I get from the observers (unprocessed) - then at safe points I process and react on them. -
Btw, have you seen this list: http://www.thomthom.net/software/sketchup/observers/ ?
-
@thomthom said:
simply keeping a cache of the info I get from the observers (unprocessed) - then at safe points I process and react on them.
You mean like, create an array of "objects that need to be updated", and process the array seperately(like once a second with a timer)?
Thanks for the list, I will try to pick the least buggy ones
-
@brewsky said:
You mean like, create an array of "objects that need to be updated", and process the array seperately(like once a second with a timer)?
No - not like a timer, because that might trigger in the middle of another operation, just like observer events. But just in time before you need to.
(Hard to give specific examples when I don't know how your plugin works.)Ideally we would have an observer event for when an operation completed - but alas.
-
@thomthom said:
(Hard to give specific examples when I don't know how your plugin works.)
I am using 2 kinds of objects:
- source objects (faces)
- geometry objects, like walls (groups)
When a source face is moved the wall needs to be re-drawn to match shape and position.
Check out this video(around 1:30): http://www.youtube.com/watch?v=J59EgH7LICs
The plugin will probably work best when the update is fired immediately...
-
@brewsky said:
When a source face is moved the wall needs to be re-drawn to match shape and position.
Problem is, if you modify the model on an entities event you might interrupt an existing process. Some other tool might be causing your observer to trigger in the middle of it's operation. That would either prevent it from completing or at worst crash SketchUp
I ran into lots of these problems myself a while ago when I tried to make a plugin reacting to observer events: http://sketchucation.com/forums/viewtopic.php?f=180&t=19011#p155886
You're running into the same type of issue as what I did, and also what Dynamic Components are doing. I talked to Scott about what he did to solve the matter. He said he used various observers to find safe points to react to. A key observer was the Tools observer, checking for the change of state with tools. They can usually be translated into operation commits and - possibly safe. I think he observed the Tools stack for when the native Move, Rotate and Scale tool completed.
-
@thomthom said:
I talked to Scott about what he did to solve the matter. He said he used various observers to find safe points to react to.
Thanks! Won't be easy to get it entirely stable I guess...
The "disable observers" button is probably a good starting point.
But I think it will need a lot of re-structuring before I have figured that out in it's entirety. -
Yea - the key is that you're plugin is not the only one operating and observer events can trigger in the middle of other operations. That's why I talked about caching the observer data until you find a "safe point" to execute. Mapping out this is important to avoid headache. Keeping observers to a minimum is best IMO - reduces overhead and risk of conflicts. Consider each observer if you really need it.
I'm a bit more liberal with observers when I use them within the operation of my own custom Tools. (Within a Tool class) as I then assume I have greater control over the environment.
Advertisement