How not to freeze Sketchup when Ruby is running?

My plugin draws many models with faces. It takes a few minutes or more to finish all models. The Sketchup freezes during the time. Is there any method to avoid freezing UI?

Is the code generating the models between the #start_operation and #commit_operation method?
Use a model undo operation and set the 2nd argument true to disable the UI.

model.start_operation('Generate models with faces', true)
   #Your Code
model.commit_operation

The #start_operation method is used to notify SketchUp that a new operation (which can be undone) is starting.

The #commit_operation method commits an operation for undo and is normally called at the end of a method.

1 Like

Beside the model.start_operation will help, the general answer is:
No. You can not avoid UI freeze if you are doing extensive task with Ruby.

I do use start_operation(), but it seems not to help much.

I tested using UI.start_timer() to wrap the drawing of grouped models, like this:

UI.start_timer(0.1, false) {
  drawModels()
}

It seems working!

Beware of how this deals with undo operations. For a user it’s important to undo an action. For extensions submitted to Extension Warehouse we expect extensions that modify the model to allow the user to easily undo each user action in a single undo.

2 Likes

How about …

model.start_operation('Generate models with faces', true)
  id = UI.start_timer(5.0, true) { UI.refresh_toolbars }
  drawModels()
  UI.stop_timer(id)
model.commit_operation

:question:

What would the purpose of that be? You start model.start_operation with “disable_ui” but then force a UI update at intervals?

The purpose is hopefully so that the application does not go into “Not responding” mode. (I chose 5 secs because I believe the Windows default for PeekMessage is 6 seconds.)

The “disable_ui” flag is meant to prevent the inspectors (chiefly the Outliner) from updating until the operation is over.

ADD NOTE:

I did not propose to update the entire UI. In my example above I purposefully used the UI.refresh_toolbars method call to only (hopefully) update the toolbars and prevent (hopefully) Windows from replacing the application window with a ghost window.

Don’t you meant: UI.refresh_inspectors ?

NO …

… I did not and do not (mean that).


If my example used the UI.refresh_inspectors call, then Thomas’ observation would be true.
Ie, there would be no point in using the “disable_ui” true flag and then trying to refresh the inspectors inside the operation.

Dezmo, to understand what I am getting at you need to understand how the Windows C API works and how it’s window objects process messages along with what fundamentally causes a window to go into “Not responding mode”.

Yes, Dan, I will need some time to do so… :blush: currently I’m far-far away of it…

But the reason for which I thought like this, because:

In the documentation of UI.refresh_toolbars :
Tells SketchUp to refresh all floating toolbars. This is useful when you need to manually force a refresh after you’ve made a change to the document via Ruby. Generally, SketchUp will keep these in sync for you, but occasionally it does not, such as when Sketchup::Model#start_operation has disabled UI updates. This only affects macOS, on Windows the toolbars are always refreshing.

So, if it will not affects Windows, does it make sense to use?

However it is just my understanding and only reflects to the Ruby API doc. There is a much-much better chance that you are right. :peace_symbol:

Edit: I see your added note above . That is explains me a little more…:slight_smile:

Perhaps, perhaps not. If, as you point out, the docs say this does not effect Windows. (I never actually read it that way, only that the out of sync situation happens on Mac.) But you could be correct.

I really wish the API doc authors would put such platform limitation exclusions in their own paragraph and emboldened. (It would be much clearer.)

It was a query to Thomas … not a solution. (Apparently he didn’t understand the query either. And that was specifically if calling UI.toolbars could prevent a ghost window situation. Ie, I was hoping that that method would do a PeekMessage call. So perhaps the real “hack” would be like the one Thomas included in TT_Lib and that is to actually make a call to PeekMessage on Windows using the Win32API wrapper or Fiddle library directly.)

I’ve got to move on to other things today.

1 Like

If you have a long method generating geometry (say 10000 faces or more), then you need to make it re-entrant to cut the job into pieces (say every 1000, or based on time elapsed). This can be done by a kind of robot approach.

The main robot method calls view.refresh and then the geometry generation method at the last interruption point. It should be called within a timer (delay 0 should be OK).

2 Likes

How do you preserve undo with this? Keeping the operation open?

Indeed, everything is encapsulated between a model.start_operation (with the true flag set for GUI not updating) and a model.commit_operation.

The fact is that view.refresh has lower priority than the execution of Ruby code in sequence. Same, by the way, for updating a Web dialog.

What would be nice is to have a method view.force_refresh which is blocking until the view is refreshed. After all, Sketchup.set_status_text executes correctly and instantly updates the status bar.

Then we could have this kind of pseudo code

def execute_long_geometry
  model = Sketchup.active_model
  view = model.active_view
  model.start_operation 'Long generation', true
  time_start = Time.now
  for i in 0..10000
    create_geometry(i)    #supposed to take long

    #Periodically check if it's time to refresh the viewport
    if i > 0 && i.modulo(1000) == 0 && (Time.now - time_start) > 0.5
       view.force_refresh
       time_start = Time.now
    end
  end
  model.commit_operation
end

The benefit would be to avoid to have re-entrance for the generation method, which is always painful to implement.