Model.place_component component_definition, boolean cannot be undone?

It seems to me that model.place_component component_definition, boolean cannot be undone. This would make extensions with place_component at the end impossible to undo in one.

Can you provide a snippet that reproduce this? I don’t see any issues with undo when I try to reproduce.

model = Sketchup.active_model
definition = model.definitions[0]
model.place_component(definition)
model = Sketchup.active_model
definition = model.definitions[0]
model.place_component(definition, true)

Both of these allow me to undo.

Could it be that you have some extension installed that interfere with undo?

tt_su,
thanks for getting back. What I mean is this:-

model = Sketchup.active_model
model.start_operation “test”, true
ents = model.entities
ents.add_edges [0,0,0],[50,50,50]
definition = model.definitions[0]
model.place_component(definition)
model.commit_operation

If you run this code a line is drawn and then the place_component method lets you place a component in the drawing. If you try to undo this it takes 2 undos because the placing of the component occurs after the script has run. It’s not really an issue unless it stops my extension being accepted because it is supposed to undo in one according the the extension warehouse reviewer rules.

If I remember well, the script is stopped when you do model.place_component(def)
It doesn’t continue to the model.commit_operation so that explains why you need more than 1 undo.

maxB,
I’m not sure what you mean by ‘the script stopped’. The script stops when I do model.commit_operation. That is after model.place_component.

As an experiment, you could puts a message between the place_component and the commit_operation to verify maxB’s suggestion.

1 Like

I tried to do something like this before and I know quite for sure the place_component breaks the script in some way.
You can add a puts after it and it will show. You can draw even a line after it and it will work but if it’s in a subroutine I know quite for sure the code stops at some point. This issue cost me quite some hours a while ago, that’s why I remember.

A proper coder might give a better explanation and really explain what’s going on here.

slbaumgartner, maxB,
I tried putting a puts after the place_component but it still requires 2 undos. It did not break the script. Placing the component happened after the puts showed in the ruby console. The first undo was Component the 2nd test, the name of my script undo. I think it is place_component which is the issue. It isn’t really a problem, more of a curiosity. Thanks for your comments.

I used the following modified snippet to experiment, using UI.messagebox instead of puts so that Ruby is blocked until the messagebox ok is clicked.

def place_it
  model = Sketchup.active_model
  model.start_operation "test", true
  ents = model.entities
  ents.add_edges [0,0,0],[50,50,50]
  UI.messagebox("edge drawn")
  definition = model.definitions[0]
  model.place_component(definition)
  UI.messagebox("component placed")
  model.commit_operation
  UI.messagebox("committed")
end

If you run this code (after creating a component so there is something to place!), you will see that the GUI tool to do the placement does not receive control until after the Ruby returns. As a result, the component placement does not happen within the start_operation/commit_operation in the Ruby. I think this is because this Ruby has no way to hand off control to the GUI and then get it back again, so the GUI tool is queued up but not activated. I haven’t tried it (no time right now), but there are methods in the Tool class protocol that let a Ruby Tool be suspended and resumed. I wonder if the place_component method would chain the way you want if it was fired from within a Ruby tool and the commit_operation was in the resume method?

I tried your code and as you say, place_component acts like a Sketchup tool and the script kind of ignores it. I suspect you are right that the place_component method is a fundamentally different beast to other model methods because it requires the user to place the component in his own time and within the model window. This requires a lot of attention from the computer, processing and interpreting the cursor position in order to locate the component correctly. Short of writing my own place_component method that somehow grabs a point from the cursor and places the component using entities.add_instance definition, transform I don’t think this can be undone in just one operation. It is only a problem if the Extension Warehouse extension review team reject such a script.

By the way I know there have been alot of discussions about the difficulties of model.start_operation, commit_operation in relation to tools. A tool loops calls to tool methods that get called maybe hundreds of times. model.start_operation, commit_operation does not cope with this well as far as I can ascertain from reading the discussions, though I haven’t tried it myself.

I remember I did try adding an observer to ‘resume’ the script after place_component was finished. It gave me some trouble so I dropped it. In the end I just created my own sub-routines in my tool to add a component instead.

Yeah, one of the problems I found with the model#place_component is that the GUI it activates does not resume your tool when you have placed a component (even if you omit argument telling it to place more than one). With some GUI tools such as orbit you can press escape to resume the native tool that was active when the orbit tool was selected, but it seems this does not work with model#place_component. So, place_component activates when your Tool’s current callback returns control to the GUI, your Tool gets a suspend callback, and then when the place component completes, your tool gets a deactivate. It never sees a resume callback. Place_component exits to the move tool instead of resuming your tool.

I think that because the only ‘problem’ the place_component issue causes in my script is that it leaves it with 2 undos, I will submit it for review and hope that the reviewer will be sensible about it and let it pass.

Yea, model.place_component poses a challenge with undo operations. For the moment I don’t think there is any way around it. But at least ensure that model.place_component doesn’t leave any additional operation orphan - as anything after that call would be.

(You can add notes to EW reviewers to highlight this issue - should be ok for now.)

I guessed this would be the case. It is the last thing my extension does so it should be OK. Just 2 undos to get back to where to model was before the extension was called.

Thanks for your comment.

@Camlaman, and All interested, …

I have posted an example of a single undo using model#place_component():

Dan,
thank you for your help and the code. I don’t understand it at first read through but I will look at it more closely in due course.

Well if you have questions about it, post them in that thread.

What a crappy implementation of the function. Adding to the API while it breaks the scripts. Very sloppy.