by Roschetch » Thu Mar 20, 2014 6:31 pm
This is my first post in this forum. So, allow me to present myself. I have been playing with Sketchup for some time, around two years. I am a newbie in Ruby and Sketchup extensions writing. However, I explored some plugin writings learning from other's codes and reading some Ruby tutorials. My previous programming experience was mainly with non-object oriented languages. My current ambition is to write a SU tool. To understand the effect of start_operation - commit_operation I did a simple change in the linetool.rb code, the classical example code of a SU tool. My code change was to embrace each five line drawings with the start and commit operations in order to allow me to undo each five line drawings at a time. It did not work. The undo operation keeps undoing one line drawing at a time. I could not find anywhere any restriction in the use of start-commit except that they should not be nested. What is my misconception? Is there any way to group several model changes in a tool in order to undo them as a whole? Here, I suppose the changes were the result of several user interactions so they are not necessarily produced by only one tool method. Thank you. Ronaldo
-
Roschetch
-
- Posts: 15
- Joined: Fri Feb 21, 2014 1:51 pm
- Location: Miguel Pereira, Brazil
- Name: Ronaldo
- Operating system: Windows
- SketchUp version: 8
- License type: Free/Make
- SketchUp use: hobby
- Level of SketchUp: Beginner
by Dan Rathbun » Thu Mar 20, 2014 8:21 pm
Your tool instance will need to keep a @counter variable, set it to 1 in the activate method. - Code: Select all
op_name = "Draw 5 Lines" if @counter == 1 model.start_operation(op_name,true) else model.start_operation(op_name,true,false,true) end
# op code
model.commit_operation @counter >= 5 ? @counter = 1 : @counter += 1
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by Dan Rathbun » Thu Mar 20, 2014 8:23 pm
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by Roschetch » Thu Mar 20, 2014 10:37 pm
Thank you, Dan. Great, it works now!
But... I do not grasp why! I confess that I did not understand exactly the meaning and function of the boolean parameters of start_operation. Would you be kind to clarify their meaning and how it works in your code?
Ronaldo
-
Roschetch
-
- Posts: 15
- Joined: Fri Feb 21, 2014 1:51 pm
- Location: Miguel Pereira, Brazil
- Name: Ronaldo
- Operating system: Windows
- SketchUp version: 8
- License type: Free/Make
- SketchUp use: hobby
- Level of SketchUp: Beginner
by Dan Rathbun » Fri Mar 21, 2014 10:23 am
They are chaining flags.
The 3rd argument is a forward chaining flag (which can be problematic.)
The 4th argument is a backward chaining flag.
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by tt_su » Fri Mar 21, 2014 10:30 am
The second argument is disable_ui - it suspends a lot of the UI from updating - leading to better performance. Normally you want this. But if you use start_operation in a tool and call it when you activate the tool and commit when you finish using it then you don't want to disable the UI as otherwise the user won't see the actions that the tool performs. The third and fourth are methods that are rarely needed. Mainly for appending operations to observer events - but this is very tricky to get right. Our API really isn't designed well for that. 
-

tt_su
- SketchUp Team

-
- Posts: 1035
- Joined: Thu Aug 01, 2013 6:16 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Mac
- SketchUp version: 2014
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by tt_su » Fri Mar 21, 2014 10:35 am
A few other general guidelines to using custom tools and start_commit_operation, if you leave an operation open, say start_operation in "activate", then you want to monitor the onCancel event of the tool and call abort_operation.
And there is a chance that another tool or function might kick in after "activate" and before you commit, so you would want to monitor for model changes with an observer - as operations cannot be nested. If a new operation kicks in while another is open it will just close the first one.
Probably a bit more info that you want as a beginner, but I just wanted to give a rough outline of the whole complexity - so you keep it in mind.
If you want to study only start_operation and commit_operation you will probably find it easier to do so outside the context of a custom tool. Tools adds quite a bit of complexity and other considerations.
-

tt_su
- SketchUp Team

-
- Posts: 1035
- Joined: Thu Aug 01, 2013 6:16 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Mac
- SketchUp version: 2014
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by Roschetch » Fri Mar 21, 2014 11:11 am
Thank you again, Dan. And I will follow your advice to wrap the operations.
And thank you, Thomas, to point out the long road of study I will have ahead.
Cheers.
Ronaldo
-
Roschetch
-
- Posts: 15
- Joined: Fri Feb 21, 2014 1:51 pm
- Location: Miguel Pereira, Brazil
- Name: Ronaldo
- Operating system: Windows
- SketchUp version: 8
- License type: Free/Make
- SketchUp use: hobby
- Level of SketchUp: Beginner
by archidave » Wed Aug 06, 2014 8:51 pm
Hi all, I've been trying to get to grips with Sketchup operations for some time now and reading the comments above from Dan and Thomas and also these older posts has really helped shed some light on it for me. <code> wrapping operationsModel Operation blockform methodsRuby, Undo Stack and dynamic componentsstart/commit_operationI realised part of the problems I was experiencing was due to nested operations, however I was still left with a conundrum; I need nested operations - because I have some code in low-level methods that works with Dynamic Components for example, and not encapsulating it with an operation either leads to very slow operation or littering the undo stack with "Properties". Putting an operation inside these methods fixed all the problems I was having there. However, these low-level methods can sometimes be called inside other methods with their own operation wrappers, for example when an action is invoked from the user-interface I believe I have come up with a solution that allows low-level methods to have their own operation when they are called in isolation, but when they are called as part of a larger chain of operations, they become appended to the larger operation. When a start_operation is called, the operation name is also pushed on to the module's own stack. If there is already a higher level operation on the stack, the new operation is made transparent. When the operation finishes, commit_operation is only called if the stack is empty, so that the whole composite operation is committed as the highest first (highest level) operation called. This seems to work well in the trivial example below. Would appreciate your comments! Can you see any potential problems? cheers Dave - Code: Select all
module DME_Test @@op_stack = [] @@model = Sketchup.active_model def self.model # could handle changes of active_model here @@model end
def self.error(op_name, err, call_stack=nil) model.commit_operation if !@@op_stack.empty? # should it abort_operation or commit_operation that was executed so far? @@op_stack.clear ::UI.beep Sketchup.status_text="ERROR: #{op_name}" ::UI.messagebox("ERROR: #{op_name} - Operation did not complete due to an error.") raise if $DEBUG # or module specific debug flag end
# command level operations - directly called by user through menu items or toolbar def self.cmd1 op_name = "Cmd1" begin @@op_stack.push(op_name) if model.start_operation(op_name, disable_ui=false) macro_op(0,0,1,1) puts @@op_stack.pop model.commit_operation if @@op_stack.empty? rescue => err self.error(op_name, err, caller) else # ensure # end end def self.cmd2 op_name = "Cmd2" begin @@op_stack.push(op_name) if model.start_operation(op_name, disable_ui=false) # -- statements macro_op(1,1,1.5,1.5) # -- end statements puts @@op_stack.pop model.commit_operation if @@op_stack.empty? rescue => err self.error(op_name, err, caller) else # return result ensure # end end
# A macro operation method, which may be combined with others in macro operations def self.macro_op(x,y,x_dim,y_dim) begin op_name = "draw square (#{x_dim} x #{y_dim})" prev_trans = !@@op_stack.empty? @@op_stack.push(op_name) if model.start_operation(op_name, disable_ui=false, trans=false, prev_trans) # -- statements grp = model.active_entities.add_group pt1 = minor_op(x,y) pt2 = minor_op(x,y+y_dim) pt3 = minor_op(x+x_dim,y+y_dim) pt4 = minor_op(x+x_dim,y) grp.entities.add_line(pt1,pt2) grp.entities.add_line(pt2,pt3) grp.entities.add_line(pt3,pt4) grp.entities.add_line(pt4,pt1) # -- end statements puts @@op_stack.pop model.commit_operation if @@op_stack.empty? rescue => err self.error(op_name, err, caller) else # return result return grp ensure # end end
# A minor operation method, which may be combined with others in macro operations def self.minor_op(x,y) begin op_name = "add point (#{x},#{y})" prev_trans = !@@op_stack.empty? @@op_stack.push(op_name) if model.start_operation(op_name, disable_ui=true, trans=false, prev_trans) # -- statements pt = Geom::Point3d.new(x,y,0) model.active_entities.add_cpoint(pt) # -- end statements puts @@op_stack.pop model.commit_operation if @@op_stack.empty? rescue => err self.error(op_name, err, caller) else # return result return pt ensure # end end end
Last edited by archidave on Tue Aug 12, 2014 12:48 pm, edited 3 times in total.
-
archidave
-
- Posts: 16
- Joined: Tue Mar 06, 2012 8:44 pm
- Location: London
- Name: Dave
- Operating system: Mac
- SketchUp version: 7
- License type: Free/Make
- SketchUp use: architecture
- Level of SketchUp: Advanced
by Dan Rathbun » Fri Aug 08, 2014 1:39 pm
FYI.. the Test module is a built-in standard Ruby module that should not be modified, except by Trimble (IE, they add some SketchUp specific testing methods to it.)
Use something unique like DaveTest or Testing ...
.. and whilst on the subject, it is also a no-no to modify the global test() method which is defined in module Kernel. (Except of course within your own class or module namespaces.)
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by archidave » Tue Aug 12, 2014 12:46 pm
Thanks Dan, useful to know, have edited.
Do you have any comments on the validity of the scheme above?
I've made some further changes in trying to implement this into my plugin - for handling multiple models on Mac, a separate stack is required for each model. Also as an alternative: rather than making subsequent operations transparent, when there is already an op on the stack, both the start_operation and commit_operation could be conditional on the stack being empty.
-
archidave
-
- Posts: 16
- Joined: Tue Mar 06, 2012 8:44 pm
- Location: London
- Name: Dave
- Operating system: Mac
- SketchUp version: 7
- License type: Free/Make
- SketchUp use: architecture
- Level of SketchUp: Advanced
by Dan Rathbun » Tue Aug 12, 2014 8:12 pm
archidave wrote:Do you have any comments on the validity of the scheme above?
No not really. We've discussed the API having more access to the real operation stack. IE, query methods to get the current op name, op count, etc. We'd need some kind of Sketchup::Operation class, and model.undo_stack() and model.redo_stack() methods.
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by davepenney » Thu May 21, 2015 3:22 am
I've been running into a funny situation with my plugins FlatText and FlatText FREE, where I can undo an operation with one Undo command in SKP 2014 & 2015, but in earlier versions (2013 & v8) I get a jillion Undos on the stack, not just the one. Happens on both Mac and Windows.
Basically, the plugin grabs some pre-made components and assembles them into a group in the model. This part works fine and can be undone with just one Undo command in any version of SKP v8 thru 2015.
The plugin also lets you edit the groups and this is where the weirdness happens. When you use the Revise command in SKP 2014 or 2015, it can be undone with just the one Undo command. But when you use the command and try to undo it in SKP 2013 or v8, you have to wade through nearly all of the several dozen operations that the command performed (stuff like Erase, Hide, Move, Properties, etc.). The very last "Undo" is my command, so it's like my seat-operation committed almost immediately! Even using the dreaded "third parameter true" form of start_operation() does not fix this bug.
If this is just the way the API worked back then, I suppose I can just shrug and move on, making my plugin require 2014 or later. But if this is pointing to a deeper problem in my code, I'd sure like to know about it.
Has anybody else run into this?
Thanks much, Dave
-
davepenney
- PluginStore Author

-
- Posts: 1
- Joined: Fri Jan 31, 2014 1:57 am
- Name: David Penney
- Operating system: Mac
- SketchUp version: 2020
- License type: Pro
- SketchUp use: engineering and mechanical design
- Level of SketchUp: Intermediate
by thomthom » Mon May 25, 2015 9:57 am
davepenney wrote:If this is just the way the API worked back then, I suppose I can just shrug and move on, making my plugin require 2014 or later. But if this is pointing to a deeper problem in my code, I'd sure like to know about it.
For reference - I looked into this with David and found that it was due to a bugged version of model.definition.load which was fixed at SU2014. Prior to that it would create a "native" operation and not an Ruby operation which could be wrapped in start/commit_operation.
-

thomthom
- PluginStore Author

-
- Posts: 19478
- Joined: Tue Nov 13, 2007 12:47 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by Anton_S » Mon Oct 09, 2017 12:49 am
Hello, I'm returning to this old topic because of a nested operations question. I need a way to do crazy geometry manipulation, multiple times and display results in the process, but without updating the undo stack. If I have one operation, with disable_ui parameter set to false, the operation will be slow. If I have one operation with disable_ui parameter set to true, the operation will be fast but the resulting view will be ugly; that is, the view won't display well until operation is committed. What I need is a way to do multiple fast operations but without flooding the undo stack. The fourth parameter of start_operation allows making transparent operations, which is what I need. The code below is what I think is its implementation should be. Although, I have second thoughts; I'm not sure if this is the correct implementation. Docs say that starting a new operation will implicitly end the previous operation. So, should I even bother calling the commit_operation the second time, after the while loop? Does anybody know if this is the right implementation? The docs don't explain this in detail unfortunately. - Code: Select all
model = Sketchup.active_model model.start_operation('Main OP', false, false, false) x = 0 while (x < 100) model.start_operation('Sub OP', true, false, true) # Do some crazy geometry manipulation, # that I don't want the observer to respond while I'm doing it... model.commit_operation # Committing operation will allow the observers to respond and update view properly, # but I'm not sure whether it commits the main operation as well... # Do something involving view, like exporting proper view into images # Increment counter x += 1 end model.commit_operation
Also, ignore that I didn't add all the begin/rescue/ensure blocks... They would make this code a lot more complicated to understand. Thanks, Anton
-
Anton_S
- PluginStore Author

-
- Posts: 1204
- Joined: Tue Nov 23, 2010 9:15 pm
- Name: Anton
- Operating system: Windows
- SketchUp version: 2018
- License type: Pro
- SketchUp use: hobby
- Level of SketchUp: Advanced
by Anton_S » Mon Oct 09, 2017 1:32 am
I think I figured it out. Enabling transparent mode for an operation will simply merge it with a previous non-transparent one, regardless if the previous operation is active or not. So, even this should work: - Code: Select all
model = Sketchup.active_model model.start_operation('Main OP', false, false, false) # A placeholder for all sub operations model.commit_operation x = 0 while (x < 100) model.start_operation('Sub OP', true, false, true) # Do some crazy geometry manipulation, # that I don't want the observer to respond while I'm doing it... model.commit_operation # Committing operation will allow the observers to respond and update view properly. # Do something involving view, like exporting proper view into images # Increment counter x += 1 end
Edit: Nevermind. transparent flag is limmited to 100 operations. After that it turns into a normal operation, flooding the undo stack. Running one operation with ui_disable set to false does update the view properly, so I'll stick with it, but it's slow...
-
Anton_S
- PluginStore Author

-
- Posts: 1204
- Joined: Tue Nov 23, 2010 9:15 pm
- Name: Anton
- Operating system: Windows
- SketchUp version: 2018
- License type: Pro
- SketchUp use: hobby
- Level of SketchUp: Advanced
by Ad Machine » 5 minutes ago
-
Ad Machine
- Robot
-
- Posts: 2012
-
Return to Developers' Forum
|