Use of start/commit_operation
-
Hi,
I am trying to use start_operation/commit_operation to implement undo/redo for a plugin. I am unsure if I am going about it the right way. Attached is a sample of the code that I am trying to write. The purpose is to allow the user to specify coordinates that are used in other parts of the plugin - in this example the coordinates are defined when the left button is released. For visual feedback, the selected coordinates are shown as construction point - which are deleted when the tool is unselected.
From the redo/undo point of the issue: the tool creates the correct undo/redo statement, but SketchUp also creates "Erase" and "Guide" undo/redo when the tool is deactivated/activated.
Is there any way to let SketchUp know not to create the "Erase" and "Guide" undo/redo? Or should I use another visual feedback mechanism in place of construction point (such as through the draw function or other)?
Thanks a lot for your help.
JulienNote: the code also has issues if the undo button is press while the tool is active or the construction point is erased. A test e.deleted? in the deactivate would avoid the error message, but it might be best handled with an entity observer.
module Hibou class TestingOperationCommitTool def initialize() @cPoints = [] end # def initialize def activate() begin dictionary = getDictionary() if ((nCoordinate = dictionary["nCoordinate"]) != nil) for i in 0...nCoordinate x, y, z = dictionary["Coordinate_#{i.to_s}"].split(",") @cPoints.push(Sketchup.active_model.entities.add_cpoint(Geom;;Point3d.new(x.to_f.m, y.to_f.m, z.to_f.m))) end end rescue Exception => e UI.messagebox("Error; #{e.to_s}.\nBacktrace; #{e.backtrace}") end end # def activate def deactivate(view) begin @cPoints.each() { |e| Sketchup.active_model.entities.erase_entities(e) } rescue Exception => e UI.messagebox("Error; #{e.to_s}.\nBacktrace; #{e.backtrace}") end end # def deactivate def onLButtonUp(flags, x, y, view) begin model = Sketchup.active_model inputPoint = Sketchup;;InputPoint.new() inputPoint.pick(view, x, y) if (inputPoint.valid?()) model.start_operation("Define coordinate") @cPoints.push(Sketchup;;active_model.entities.add_cpoint(p3d = inputPoint.position)) dictionary = getDictionary() dictionary["nCoordinate"] = @cPoints.length dictionary["Coordinate_#{(@cPoints.length-1).to_s}"] = [p3d.x.to_m, p3d.y.to_m, p3d.z.to_m].join(",") model.commit_operation end rescue Exception => e model.abort_operation UI.messagebox("Error; #{e.to_s}.\nBacktrace; #{e.backtrace}") end end # def onLButtonUp private def getDictionary() return Sketchup.active_model.attribute_dictionary("HibouTestingOperation", true) end # def getDictionary end # class TestingOperationCommitTool end # module Hibou if (not file_loaded?(__FILE__)) UI.menu("Plugins").add_item("Hibou-Testing") { Sketchup.active_model.tools.push_tool(Hibou;;TestingOperationCommitTool.new()) } file_loaded(__FILE__) end
-
If these guide points are only used for visual illustrations while using the tool then use the
View.draw
method to draw them to the viewport instead of adding entities. Drawing to the viewport will be faster and cleaner (no extra undo points). -
On a sidenote - you can make operations transparent, using the third and fourth argument of [ruby]start_operation[ruby]. Avoid the third because it's hard to predict what will happen. The second one let you make the operation you commit transparent. However, since it was added in SU7 and until some of the first releases of SU8 the Undo name overwrote the previous one - not something you want, making the "Edit > Undo xxxx" menu item confusing. However, in one of the recent updates of SU8 they fixed this.
But again,in the scenario you describe you are better off not doing this - just draw directly to the viewport.
-
Thanks for the feedback. I think that drawing from the draw method using View.draw is the way forward in my case - and avoid adding entities to the model.
Beyond the code extract shown, the user is also able to select the input point to change their location (or delete). I used PickHelper.picked_element (or a loop through PickHelper.all_picked) to retrieve the construction point. If I am drawing the point using View.draw, PickHelper.picked_element/all_picked will not pick the point drawn with View.draw and I will have to test using PickHelper.test_point. Is it correct?
Thanks in advance. Julien
-
Correct.
Btw, I've tried to make an overview over the various methods of the PickHelper: http://www.thomthom.net/thoughts/2013/01/pickhelper-a-visual-guide/
-
Thanks a lot for the link and taking the time to writing it. It is very clear and detailed.
Just a quick question: on the last page (section Segments and Points), the example for testing multiple points faster reads:
<span class="syntaxdefault">pickhelper</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">init</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">x</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">y </span><span class="syntaxkeyword">)<br />for </span><span class="syntaxdefault">point in points<br /> p pickhelper</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">test_point</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">point</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">x</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">y </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">end</span>
Shouldn't the second call reads "test_point( point )" or does it not matter?
-
That's true! I've made a typo there. Thanks for catching that. I'll correct it.
-
Another thing:
DONT:
Sketchup.active_model.entities.erase_entities(e)
DO:
Sketchup.active_model.**active_entities**.erase_entities(e)
WHY?
If the user changes the editing context, and is within a group or component, the first call will cause SketchUp to BugSplat! -
-
Dan and thomthom, thanks for your time and answer.
Advertisement