Tuesday, February 19, 2008

Sharing data between SketchUp Ruby and Javascript

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
<html>
<script>
function callRuby(actionName) {
query = 'skp:get_data@' + actionName;
window.location.href = query;
}
</script>
<body>
<h3 id="output">You have 0 things selected.</h3>
<input type="button" onclick="callRuby('pull_selection_count')" value="Refresh">
</body>
</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
<html>
<script>
function callRuby(actionName) {
query = 'skp:get_data@' + actionName;
window.location.href = query;
}

function passFromRubyToJavascript(value) {
var message = "You have " + value + " items selected.";
document.getElementById('output').innerHTML = message;
}

</script>
<body>
<h3 id="output">You have 0 things selected.</h3>
<input type="button" onclick="callRuby('pull_selection_count')" value="Refresh">
</body>
</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.

13 comments:

Anonymous said...

It's nearly impossible to read the yellow text.

TB said...
This comment has been removed by the author.
TB said...

Thanks Scott for taking the time to post an example.

Todd

Jim said...

Speaking technically, you can only pass String objects back and forth, right? You would pass an "array" as a delimited string, and recreate the array on the other end, if need be.

Sang Ahn said...

Yes, that is correct. At the end of the day, you are passing strings back and forth.

Jim said...

I've looked at the json parser for Ruby on RubyForge. It appears even the 'pure' version of the json parser requires 'strscan', which is a binary library and is not shipped with SketchUp, plus it's not platform-independent. I'm sure your thinking about this and not planning on hacking together a makeshift parser using String#split. :)

I look forward to your next article.

Jim said...

Hello,

I am curious about the difference between creating a WebDialog by instantiation, as opposed to sub-classing? Pros and cons?

Thanks.

chrisglasier said...

This article really helped me to give birth to Namesets - see my Google web site - many thanks, Chris

Fredo6 said...

Dear Sketchup engineers,

WebDialogs works fine on the Windows/IE platform.

However on Mac, it seems that the mechanism of communication between Ruby and Javascript is ASYNCHRONOUS, so that in many cases, some scripts are not executed from Ruby (via dlg.execute_script), some call backs are not triggered from Javascript.

Am I right?

And is there a way to make sure that no communication (script and call backs) is lost between Ruby and javascript, and executed in the right order.

again, this works fine on Windows, and should work the same on Mac.

wow gold said...

Treats wow gold for Greatfather wow goldWinter: This buy wow goldquest is buy wow goldavailable at cheap wow goldlevel 10 and cheap wow goldis just a simple wow power levelingcooking quest. wow power levelingThe recipe power levelingrequired for the power leveling tickles your wow goldfancy. It is buy wow goldcertainly importantcheap wow goldthey wanted to.

inwowgold said...

buy wow gold,cheap wow gold,WoW Account,wow gold,buy wow gold,wow power leveling,Cheap WoW Account,wow gold,wow gold,world of warcraft gold,cheap wow gold,world of warcraft power leveling,world of warcraft gold,buy wow gold,Buy WoW Account,buy wow gold,wow power leveling,ffxi gil,ffxi gil,world of warcraft power leveling,wow gold,World of Warcraft Account,buy wow gold,sell wow gold,wow power level,wow gold for sale,power leveling,,wow power level,WoW Accounts for Sale,wow gold for sale,power leveling,cheap wow gold,wow power level,buy cheap wow gold.

chenlu said...

runescape money
runescape gold
runescape money
runescape gold
buy runescape gold buy runescape money runescape items
runescape accounts
runescape gp
runescape money
runescape power leveling
runescape money
runescape gold
dofus kamas
cheap runescape money
cheap runescape gold
Guild Wars Gold
buy Guild Wars Gold
lotro gold
buy lotro gold
lotro gold
buy lotro gold
lotro gold
buy lotro gold

Hellgate Palladium
Hellgate London Palladium
Hellgate money
Tabula Rasa gold tabula rasa money
Tabula Rasa Credit
Tabula Rasa Credits
Hellgate gold
Hellgate London gold
wow power leveling
wow powerleveling
Warcraft PowerLeveling
Warcraft Power Leveling
World of Warcraft PowerLeveling World of Warcraft Power Leveling runescape power leveling
runescape powerleveling
eve isk
eve online isk
eve isk
eve online isk
tibia gold
Fiesta Silver
Fiesta Gold
Age of Conan Gold
buy Age of Conan Gold
aoc gold

呼吸机
无创呼吸机
家用呼吸机
呼吸机
家用呼吸机
美国呼吸机
篮球培训
篮球培训班
篮球夏令营
china tour
beijing tour
beijing travel
china tour
tibet tour
tibet travel
computer monitoring software
employee monitoring

Anonymous said...

If you wanna buy some other things which are not listed in our website:
runescape gold
runescape accounts
runescape money
runescape power leveling
runescape gold
runescape accounts
runescape items
runescape gold
runescape accounts
runescape accounts
runescape money
runescape accounts
runescape items
wow cd key
please contact with our customer service with on-line chat or add our Email,we will stock it for you in a short time after you told us what you needed.