Update webdialog without user interaction


#1

I have a requirement wherein I need to update a WebDialog , using the onSelectionBulkChange listener. basically it just needs to print out the selected entity’s dimensions.
Currently I need to manually click an ‘update’ button on the web-dialog to show any change in a entity’s dimensions since it’s required to provide a callback.

Can someone please point me a way to update the dialog, without having to click on the update button. i.e. update the dialog in realtime.

Thanks you for your valuable time


#2

Think less complicated…
To update a webdialog you don’t use callbacks but WebDialog#execute_script(String). Previously you have been triggering this method by a callback from the button, but you don’t need to. Here, you want to trigger execute_script from the observer.

class MySelectionObserver < Sketchup::SelectionObserver

  # Give the observer a contact so it knows where to send callbacks to!
  def initialize(tool)
    @tool = tool
  end

  def onSelectionBulkChange(selection)
    # Just tell your tool about the change. Keep the implementation datails  
    # on how to process this data in your tool's class.
    @tool.refresh(selection.to_a)
  end

end

class MyTool

  def initialize(model)
    @model = model
    # Pass to the observer the instance of this tool, 
    # so that the observer can call methods of this tool.
    observer = MySelectionObserver.new(self)
    @model.selection.add_observer(observer)
    # Create the dialog.
    @dialog = UI::WebDialog.new()
    # …
  end

  def refresh(entities)
    # Call a JavaScript method on the webdialog to accept the new data.
    # just some example data (array of strings)
    entity_types = entities.map{ |e| e.typename }.uniq
    @dialog.execute_script('MyTool.refresh(#{entity_types.inspect})')
  end

end

#3

Even more simple, just rewrite the html with the new values.
It happens so fast the user will never see the blanking.)

  def update_html(dims)
    @html = %Q{
      <html>
        <head>
        </head>
        <body>
          <br/>
          Thickness: #{dims[0].to_s}<br/>
          Width: #{dims[1].to_s}<br/>
          Length: #{dims[2].to_s}<br/>
          <br/>
        </body>
      </html>
    }
  end # update_html()

  def show_html(dims)
    update_html(dims.sort)
    @dlg = UI::WebDialog.new("Dims")
    @dlg.set_html(@html)
    RUBY_PLATFORM !~ /darwin/ @dlg.show : @dlg.show_modal
  end

  def update_dims(dims)
    update_html(dims.sort)
    @dlg.set_html(@html)
  end

Call show_html(dims) to initially show the dialog.
Then reset the dialog’s html with new values by calling the update_dims(dims) method.


#4

Hi @Aerilius Thanks for the quick response! Just a quick question though,

I am getting the

Error:#NoMethodError: undefined method `execute_script’ for nil:NilClass

when I call the


js_command = “passedFromRuby(’#{dimensions[0]}’,’#{dimensions[1]}’,’#{dimensions[2]}’)”

@dashboardDialog.execute_script(js_command)

Also where do I specify the location with the skp query? onload of html body?

  query = 'skp:get_data@' + actionName;
  window.location.href = query;

Do I even need it?

Thanks for your help!


#5

The NoMethodError says that execute_script was called on a variable that has the value nil. Check your source code to ensure WebDialog has already been instantiated and assigned to @dashboard_dialog.

Depends on for what you need it… If you don’t know for what you need it for this task, you probably don’t need it. If you follow Dan’s suggestion and rewrite and set the html, or if you pass all the new data via execute_script, you don’t need it.

It is not a good idea to use bare-bone window.location.href = 'skp:… because that low-level “API” is cumbersome (ugly) and achieves only that these couple of lines of low-level code are repeatedly spread over your source code, and it becomes hard to maintain.
Everyone is building their own solutions over and over again. Instead it would be better to re-use existing libraries (see thomthom’s and my extensions). Maybe we should again start efforts to either get a proper API from SketchUp or an officially endorsed and library that is promoted over the ‘skp’ way.
What you can do is at least to abstract the exact implementation it into a function, like:

function callRuby (callbackName, parameter) {
  var query = 'skp:' + callbackName + '@' + parameter;
  window.location.href = query;
}

…and then callRuby('get_data', 'something'). The existing libraries simplify this even more by handling conversion of arguments and serialization to a string query, allowing any amount of arguments, and optional direct callback mechanisms.

# JavaScript
AE.Bridge.callRuby('callbackName', 'some arguments', 81, 
  function(returnValue) { 
    /* callback */
})
# Ruby:
@dialog.call_function('functionName', ['stringArgument', 42], {'key' => 'value'})

Also take care of variable scope in JavaScript: Everything variable that you forgot to initilize with var is global and not limited to the function scope in which it is used.