sketchucation logo sketchucation
    • Login
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info

    Webdialog for a progressBar

    Scheduled Pinned Locked Moved Developers' Forum
    9 Posts 3 Posters 1.4k Views 3 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • bomastudioB Offline
      bomastudio
      last edited by

      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

      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>
      
      
      1 Reply Last reply Reply Quote 0
      • thomthomT Offline
        thomthom
        last edited by

        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.

        Thomas Thomassen — SketchUp Monkey & Coding addict
        List of my plugins and link to the CookieWare fund

        1 Reply Last reply Reply Quote 0
        • G Offline
          Garry K
          last edited by

          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 dialog

          When 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
          
          
          1 Reply Last reply Reply Quote 0
          • bomastudioB Offline
            bomastudio
            last edited by

            @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....

            1 Reply Last reply Reply Quote 0
            • G Offline
              Garry K
              last edited by

              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
              
              
              1 Reply Last reply Reply Quote 0
              • bomastudioB Offline
                bomastudio
                last edited by

                😲 I can't understand how you link the loop with those two functions....

                EDIT: OK OK OK!!!! It works!!!!

                1 Reply Last reply Reply Quote 0
                • G Offline
                  Garry K
                  last edited by

                  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.

                  1 Reply Last reply Reply Quote 0
                  • thomthomT Offline
                    thomthom
                    last edited by

                    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.

                    Thomas Thomassen — SketchUp Monkey & Coding addict
                    List of my plugins and link to the CookieWare fund

                    1 Reply Last reply Reply Quote 0
                    • G Offline
                      Garry K
                      last edited by

                      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.

                      1 Reply Last reply Reply Quote 0
                      • 1 / 1
                      • First post
                        Last post
                      Buy SketchPlus
                      Buy SUbD
                      Buy WrapR
                      Buy eBook
                      Buy Modelur
                      Buy Vertex Tools
                      Buy SketchCuisine
                      Buy FormFonts

                      Advertisement