Webdialog for a progressBar
-
Hi guys, I'm writing a simple script in order to show in a webdialog a progressbar using jQuery. It seems to work but when I try to update the value (inside a cicle) it looks freezed and doesn't update anything.....SU works and when the cicle is finished the progressbar is closed as I expected....
https://drive.google.com/file/d/0ByveTT29NhAlWmJ6bUExZjJ2SkU/view?usp=sharing
height = view.vpheight width = view.vpwidth dlgPROGRESSBAR = UI;;WebDialog.new("MyProgressBar", false, "", 350, 70, width/2, height/2, false); dlgPROGRESSBAR.navigation_buttons_enabled = false dlgPROGRESSBAR.bring_to_front() sourceHTML = File.join(File.expand_path("..",__FILE__),"progressbar.html") dlgPROGRESSBAR.set_file(sourceHTML) dlgPROGRESSBAR.show
In order to update the value and the label I use
js='$( "#progressbar" ).progressbar({value; 5});' dlgPROGRESSBAR.execute_script(js) js='$(".progress-label").text("Loading data ...");' dlgPROGRESSBAR.execute_script(js)
For example if I have 3 steps, say at 10%, 20%, while the latter goes from 20 to 100% into a cicle (iterate an array).
I can do a "manual" update, for step 1-2, with the code above (changing the value) while for the latter I try to do an automatic update inside the cicle:anArray = [...] #STEP 1 (0% - 10%) js='$( "#progressbar" ).progressbar({value; 10});' dlgPROGRESSBAR.execute_script(js) # STEP 2 (10% - 20%) js='$( "#progressbar" ).progressbar({value; 20});' dlgPROGRESSBAR.execute_script(js) # STEP 5 (20% - 100%) # function calling result = processMyArray(anArray) if result dlgPROGRESSBAR.close() end def processMyArray(array) begin nTOT = array.length n = 0 array.each{|e| incr = (20 + 100*n/nTOT).round(0) # starting from 20% .... js='$( "#progressbar" ).progressbar({value; ' + incr.to_s + '}); dlgPROGRESSBAR.execute_script(js) # do some stuff with e } return true raise return false end end
The html is
<!doctype html> <!--[if IE 7 ]> <html class="no-js ie ie7 lte7 lte8 lte9" lang="en-US"> <![endif]--> <!--[if IE 8 ]> <html class="no-js ie ie8 lte8 lte9" lang="en-US"> <![endif]--> <!--[if IE 9 ]> <html class="no-js ie ie9 lte9>" lang="en-US"> <![endif]--> <!--[if (gt IE 9)|!(IE)]> <!--> <html class="no-js" lang="en-US"> <!--<![endif]--> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <link rel="stylesheet" href="https://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css"> <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> <script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script> <style> body { font-family; "Trebuchet MS", "Helvetica", "Arial", "Verdana", "sans-serif"; font-size; 62.5%; } .ui-progressbar { position; relative; height; 20px; } .progress-label { position; absolute; left; 35%; top; 4px; font-weight; bold; text-shadow; 1px 1px 0 rgb(255,255,255); text-color; rgb(255,255,255); } #progressbar .ui-progressbar-value { background; rgba(0, 0, 110, 1); } </style> <script> $(function() { var progressbar = $( "#progressbar" ); var progressLabel = $( ".progress-label" ); progressbar.progressbar({ value; 0, // Number; A value between 0 and the max. Boolean; Value can be set to false to create an indeterminate progressbar. change; function() { // none to do }, complete; function() { progressLabel.text( "Complete!" ); setTimeout( function() {}, 1000 ); } }); }); </script> </head> <body> <div id="progressbar"><div class="progress-label">Loading...</div></div> </body> </html>
-
When you perform a task that CPU intensive in the main thread it blocks the UI from updating - WebDialogs included. The SketchUp API is also not multi-threaded - you cannot call it from other threads.
If you do some calculations you could try to do that in a background thread - provided it doesn't call the SketchUp API. Note that Ruby threads in SketchUp doesn't work well - there's something that prevents them from running unless the main thread is active as well - which then defeat the purpose. You might want to try a Ruby C Extension and do calculation intensive work in native C or C++ threads.
-
There is a way of doing this but you will slow things down and you can't put the code inside a start_operation / commit_operation.
You pass control back and forth between your ruby script and the web dialog through the use of callbacks.
dialog.execute_script(script)
window.location.href = 'skp:ruby_callback@';You need to rewrite your main loop and put the code into 2 functions instead of 1
Last line of first function creates the web dialogWhen the web dialog has completed setting up then perform a callback
In the callback setup a timer that does not repeat and set time as 0.01 or something like that.
Inside the timer call the second function, at the end of the second function make a call to the dialog to update the progress bar.require 'sketchup' module YourSpace module YourModule def show_progress_bar(title, width, height, left, top, total) build_html() @progress_total = total @dlg = UI;;WebDialog.new(title, false, "progress_bar_#{title}") @dlg.set_size(width, height) @dlg.set_position(left, top) @dlg.set_html(@@html_code) @dlg.set_on_close { @dlg = nil } @dlg.add_action_callback('ruby_callback') { |dialog, params| text = params.to_s check = text[0..5] if (check == 'loaded') # if you want to do something after loading end UI.start_timer(0.01, false) { your_second_fucntion() } } @dlg.show() end def update_progress_bar(data) if (data == @progress_total) @dlg.close() else percentage = data / @progress_total.to_f script = "from_ruby( '" << percentage.to_s << "' )" @dlg.execute_script(script) if (@dlg != nil) end end def build_html() @@html_code = <<-HTML <!DOCTYPE HTML> <html lang="en"> <head> <title>Progress Bar</title> <meta http-equiv="X-UA-Compatible" content="IE=9"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> </head> <style> #my_progress { width; 100%; height; 20px; position; relative; border; 1px solid #ccc; } #my_bar { width; 10px; height; 100%; position; absolute; background-color; blue; } </style> <body> <div id="my_progress"> <div id="my_bar"> </div> </div> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script> var progress_wd; function from_ruby(data) { var new_wd = progress_wd * parseFloat(data); $('#my_bar').width(new_wd); ruby_call(data); } function ruby_call(text) { window.location.href = 'skp;ruby_callback@' + text; } $(document).ready(function() { progress_wd = $('#my_progress').width() * 1.0; ruby_call('loaded'); }); </script> </body> </html> HTML end end end
-
@unknownuser said:
Inside the timer call the second function, at the end of the second function make a call to the dialog to update the progress bar.
At the end? But I need to update the progress DURING the loop is running....
-
With my test application the web dialog was updated 260 times.
I changed first function
def original_function() grps.each{ |grp| # Do your code in this loop } end def first_function() @total = @grps.length @counter = 0 show_progress_bar('progress bar', 400, 200, 0, 0, @total) end def second_function() grp = grps[@counter] # Do your code here - as if you were in a loop @counter += 1 update_progress_bar(@counter) end
-
I can't understand how you link the loop with those two functions....
EDIT: OK OK OK!!!! It works!!!!
-
It is sort of like a game of ping pong where the Ruby is on one side and the web dialog is on the other side. The ball is the CPU control - you simply pass it back and forth.
The timer is used to maintain the stack - otherwise you could get too deep and get a stack error.
-
You might want to time your operation before and after doing a timer-loop - I found them to slow down the total time quite a bit. You might want to not slice down to too thin slices. It's a balance between allowing the progress to be seen and total execution time.
-
Typically I do not use a progress bar. Instead I use Sketchup::set_status_text
I'm happy using a counter with a modulus. I generally try to update the status no more than once every second or two.
The same idea would work for a progress bar. All I was trying to do is give bomastudio a solution to his request and show him how to get around the problem of the web dialog being starved of cpu time. Without the timer loop you blow the bottom off the stack. With a timer loop you incur a performance penalty.
The interesting thing is the jquery ready function would only fire after the heavy processing had finished.
Advertisement