Sharing data between SketchUp Ruby and Javascript
February 19th, 2008 | Published in Google SketchUp | 1 Comment
Posted by Scott Lininger, SketchUp Software Engineer
If you are building rich WebDialogs in your SketchUp Plugin, then you are probably using Javascript + DHTML (Dynamic HTML) to show the UI and handle user input. In practical terms, this means that you could easily have as much Javascript code in your plugin as Ruby code! But the SketchUp API documentation doesn't give much guidance on using Javascript to talk to SketchUp. What's a coder to do?
Sharing data between Javascript and Ruby can be a bit of a black art. The Ruby API provides two mechanisms that we can leverage:
1. WebDialog.add_action_callback() allows us to define a Ruby method that a WebDialog can call. This allows us to send data DOWN from Javascript to Ruby.
2. WebDialog.execute_script() allows us to run a bit of Javascript code from inside Ruby. This allows us to send data UP from Ruby to Javascript.
By using these two mechanisms together, we can create a nice, generic way for Javascript to get whatever it needs from Ruby.
Step 1: Using add_action_callback to request some data
For our example, let's imagine we have two files inside our Plugins directory: selectionInfo.rb and selectionInfo.html. Together, these files provide a new feature to SketchUp: a floating window that shows us how many objects are selected inside SketchUp.
Our first step is to show a WebDialog and establish our action callback. Here's the minimal code required to make this work:
selectionInfo.rb
# Create the WebDialog instance
my_dialog = UI::WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true)
# Attach an action callback
my_dialog.add_action_callback("get_data") do |web_dialog,action_name|
UI.messagebox("Ruby says: Your javascript has asked for " + action_name.to_s)
end
# Find and show our html file
html_path = Sketchup.find_support_file "selectionInfo.html" ,"Plugins"
my_dialog.set_file(html_path)
my_dialog.show()
selectionInfo.html
If you create these two files and save them into your Plugins directory, you'll see a very simple WebDialog the next time you restart SketchUp. Click on the "Refresh" button and you'll see a messagebox that was generated by our Ruby (but controlled based on data from the Javascript.)
Make sense? We now have a mechanism for sending data down to Ruby, which is a way for Javascript to ask Ruby a question. Now we just need Ruby to provide an answer...
Step 2: Using execute_script to send some data
The next step in our data sharing scheme requires us to respond. Let's add a few lines of code to our files... (Changed code is highlighted in yellow.)
selectionInfo.rb
# Create the WebDialog instance
my_dialog = UI::WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true)
# Attach an action callback
my_dialog.add_action_callback("get_data") do |web_dialog,action_name|
if action_name=="pull_selection_count"
total_selected = Sketchup.active_model.selection.length
js_command = "passFromRubyToJavascript("+ total_selected.to_s + ")"
web_dialog.execute_script(js_command)
end
end
# Find and show our html file
html_path = Sketchup.find_support_file "selectionInfo.html" ,"Plugins"
my_dialog.set_file(html_path)
my_dialog.show()
selectionInfo.html
Save these changes, and then restart SketchUp. Now when you click on the "Refresh" button, you get an updated web page showing the number of objects selected.
Looking ahead: Using JSON for more complex data
We've been working with an extremely simple example where we're passing a single number. In a real world application you would probably be sending more complex data. In this case, JSON (Javascript Object Notation) is the perfect mechanism. It's a way where you can pass nested objects and arrays in a single command.
We'll explore more about JSON in a future blog post. But for now, consider the following example.
# Passing lots of data about a component
js_command = "passFromRubyToJavascript({typename:'ComponentInstance',name:'Bryce',x:10.5,y:13.5,z:0.0})"
web_dialog.execute_script(js_command)
This kind of approach is a great way to allow Javascript to get large, structured data sets out of SketchUp whenever your application needs them. Stay tuned for a deeper exploration of this idea.
If you are building rich WebDialogs in your SketchUp Plugin, then you are probably using Javascript + DHTML (Dynamic HTML) to show the UI and handle user input. In practical terms, this means that you could easily have as much Javascript code in your plugin as Ruby code! But the SketchUp API documentation doesn't give much guidance on using Javascript to talk to SketchUp. What's a coder to do?
Sharing data between Javascript and Ruby can be a bit of a black art. The Ruby API provides two mechanisms that we can leverage:
1. WebDialog.add_action_callback() allows us to define a Ruby method that a WebDialog can call. This allows us to send data DOWN from Javascript to Ruby.
2. WebDialog.execute_script() allows us to run a bit of Javascript code from inside Ruby. This allows us to send data UP from Ruby to Javascript.
By using these two mechanisms together, we can create a nice, generic way for Javascript to get whatever it needs from Ruby.
Step 1: Using add_action_callback to request some data
For our example, let's imagine we have two files inside our Plugins directory: selectionInfo.rb and selectionInfo.html. Together, these files provide a new feature to SketchUp: a floating window that shows us how many objects are selected inside SketchUp.
Our first step is to show a WebDialog and establish our action callback. Here's the minimal code required to make this work:
selectionInfo.rb
# Create the WebDialog instance
my_dialog = UI::WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true)
# Attach an action callback
my_dialog.add_action_callback("get_data") do |web_dialog,action_name|
UI.messagebox("Ruby says: Your javascript has asked for " + action_name.to_s)
end
# Find and show our html file
html_path = Sketchup.find_support_file "selectionInfo.html" ,"Plugins"
my_dialog.set_file(html_path)
my_dialog.show()
selectionInfo.html
You have 0 things selected.
If you create these two files and save them into your Plugins directory, you'll see a very simple WebDialog the next time you restart SketchUp. Click on the "Refresh" button and you'll see a messagebox that was generated by our Ruby (but controlled based on data from the Javascript.)
Make sense? We now have a mechanism for sending data down to Ruby, which is a way for Javascript to ask Ruby a question. Now we just need Ruby to provide an answer...
Step 2: Using execute_script to send some data
The next step in our data sharing scheme requires us to respond. Let's add a few lines of code to our files... (Changed code is highlighted in yellow.)
selectionInfo.rb
# Create the WebDialog instance
my_dialog = UI::WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true)
# Attach an action callback
my_dialog.add_action_callback("get_data") do |web_dialog,action_name|
if action_name=="pull_selection_count"
total_selected = Sketchup.active_model.selection.length
js_command = "passFromRubyToJavascript("+ total_selected.to_s + ")"
web_dialog.execute_script(js_command)
end
end
# Find and show our html file
html_path = Sketchup.find_support_file "selectionInfo.html" ,"Plugins"
my_dialog.set_file(html_path)
my_dialog.show()
selectionInfo.html
You have 0 things selected.
Save these changes, and then restart SketchUp. Now when you click on the "Refresh" button, you get an updated web page showing the number of objects selected.
Looking ahead: Using JSON for more complex data
We've been working with an extremely simple example where we're passing a single number. In a real world application you would probably be sending more complex data. In this case, JSON (Javascript Object Notation) is the perfect mechanism. It's a way where you can pass nested objects and arrays in a single command.
We'll explore more about JSON in a future blog post. But for now, consider the following example.
# Passing lots of data about a component
js_command = "passFromRubyToJavascript({typename:'ComponentInstance',name:'Bryce',x:10.5,y:13.5,z:0.0})"
web_dialog.execute_script(js_command)
This kind of approach is a great way to allow Javascript to get large, structured data sets out of SketchUp whenever your application needs them. Stay tuned for a deeper exploration of this idea.
April 2nd, 2011 at 8:22 am (#)
This article was exactly what I was looking for, It just runs,
thank a lot