• Login
sketchucation logo sketchucation
  • Login
🤑 SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

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.
  • B Offline
    bomastudio
    last edited by 9 Jan 2016, 10:04

    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

    Page Not Found

    Web word processing, presentations and spreadsheets

    favicon

    (drive.google.com)

    
    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 9 Jan 2016, 17:38

      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 9 Jan 2016, 20:21

        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
        • B Offline
          bomastudio
          last edited by 9 Jan 2016, 21:40

          @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 9 Jan 2016, 22:35

            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
            • B Offline
              bomastudio
              last edited by 9 Jan 2016, 23:06

              😲 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 9 Jan 2016, 23:39

                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 10 Jan 2016, 08:24

                  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 10 Jan 2016, 15:35

                    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
                    1 / 1
                    • First post
                      1/9
                      Last post
                    Buy SketchPlus
                    Buy SUbD
                    Buy WrapR
                    Buy eBook
                    Buy Modelur
                    Buy Vertex Tools
                    Buy SketchCuisine
                    Buy FormFonts

                    Advertisement