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.
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.
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
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… 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.
Edit: I see your added note above . That is explains me a little more…
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.
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).
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.