When get_element_value do not support on HTHML dialog. So i deicide make a method called get_value to replace it. This is my code at first i write this method.
# param 1: dialog was created before, html id
def get_value(dlg,id)
# temporaty var
@get_value_tmp_data ='';
# add new action callback
dlg.add_action_callback("getvalue"){|dialogs, var|
# assgin data
@get_value_tmp_data = var
puts 'go new value:' +@get_value_tmp_data
}
dlg.execute_script("get_element_value('"+id+"')")
puts 'last checking before return:' @get_value_tmp_data
return @get_value_tmp_data
end
The javascript is:
function get_element_value(e) {
var value = $(e).val();
window.location=âskp:getvalue@â+value;
}
When i run the medthod everything seem be ok but the last checking before return is not. That return the old value = ââ
Thanks for reading and helping me.
As John suggest, passing the values directly to the sketchup. callback is the way to go with HtmlDialog. Because HtmlDialog is all async one need a different way to pass data - this means using callbacks. Another aspect that is different is that where one often used input element to pass data with the old WebDialog because the skp: callbacks had encoding and size issues the new sketchup. callbacks are much better in that they donât have any size limits and they also automatically convert simple types, so you can send arrays or hashes of data from JS to Ruby with ease.
@nhanco102 Iâm trying to find an efficient solution to emulate the deprecated get_element_value method in UI::HtmlDialog, and I ended up here. In my case, it is not working either with
Changed the name of get_element_value() to request_element_value()
because there is no way to receive synchronous values from JS in the UI::HtmlDialog class.
This is example code. No copyright intended nor warranty implied.
# Ruby
module MyTopLevelNamespaceModule::MyExtensionModule
class ChromeDialog < ::UI::HtmlDialog
def initialize(*args)
super
@values = {}
set_on_closed() # no block argument uses default block !
self
end
def attach_callbacks
add_action_callback('receiveValue') do |not_used,id,val|
receive_value(id,val)
end
# ... Add more callbacks here as needed ...
end
def request_element_value(id)
return unless id.is_a?(String) || id.respond_to?(:to_s)
execute_script("sendValue('#{id}');")
end
def receive_value(id,val)
@values[id]= val
process_value(id)
end
def process_value(id)
# Do something with @values[id]
end
def process_values
@values.keys.each { |id| process_value(id) }
@values
end
def set_file(*args)
super
self # allows chaining
end
def set_html(*args)
super
self # allows chaining
end
def close(*args)
super # call close() in UI::HtmlDialog superclass
# If a set_can_close() block is defined and returns true then ...
# Any set_on_closed() block would run here ...
# The CEF window would close here ...
unless @internal_reference.nil?
# Wait a bit for the window to close
sleep(0.5)
# Release the internal reference if the window was closed:
@internal_reference = nil unless self.visible?
# If the instance is not externally referenced then Ruby GC
# can destroy the unreferenced object next time it runs.
end
self # allows chaining
end
def set_on_closed(&block)
if block_given?
super Proc::new {
block.call
@internal_reference = nil
}
else # default block to release internal reference:
super { @internal_reference = nil }
end
end
def show(*args)
if self.visible?
self.bring_to_front
else
attach_callbacks()
@internal_reference = self # backup reference whilst dialog is open
super
end
self # allows chaining
end
def show_modal(*args)
return self if self.visible?
attach_callbacks()
@internal_reference = self # backup reference whilst dialog is open
super # this is a modal call !
self # allows chaining
end
end # dialog subclass
end # extension module
// JavaScript:
function sendValue(id) {
sketchup.receiveValue( id, document.getElementById(id).value );
}
I updated the example, as it really should not have worked as it was.
I had not actually attached an action callback to the dialog object.
This is now done in the attach_callbacks() method, inside the show() and show_modal() method overrides.
This needs to be done this way because action callbacks are detached if the dialog is closed.
I believe that the interaction with the CEF is asynchronous by design.
Iâve not yet found a way to get a desired value directly from a single method call.
But I did not yet try a wait loop.
def get_element_value(id)
return unless id.is_a?(String)
execute_script("sendValue('#{id}');")
while @value.nil?
sleep 0.3
end
value, @value = @value, nil
return value
end
But this opens up the possibility of getting lost in an infinite loop, if a Javascript error occurs in the dialog, and the Ruby callback is never called. (I usually have a bailout after a certain number of seconds, or loop number.)
I donât know, ⌠I wouldnât bet on it., ⌠The SketchUp Team / Trimble employees never make public statements about specific future plans. (There are entire threads discussing this, and numerous employee posts stating this policy.)
I tried the wait loop. It didnât work. Apparently the sketchup.receiveValue canât trigger the callback until the loop is exited, which is too late.
The HtmlDialog is pretty useless without this capability.
FYI : The âsnippetâ was not actually a drop in test for the greater asynchronous example above.
It was just an idea. (Ie, the full example has no @value variable which would need to be assigned in the add_action_callback block [instead of calling the receive_value method.])
I think I know what you mean, though. Ruby is busy in the âwait loopâ and never ready to process the action callback.
Exactly. I was trying to think of ways to use threads to get around the problem, but havenât come up with a good idea yet. Iâll let you know if I do.
His code is based upon my example (in this thread.)
It would be a messy example if it used one method.
I chose for this example, to use a âtriggerâ method, 1 callback that receives the value, and a separate method that can process any of the values in the @values hash.
Asside: I know, you could actually have the value parameter optional, and then have a conditional inside the method, and then 1 arg makes it a âtriggerâ and 2 args and it is being called from the dialogâs JS.
But this is not as simple an example as I show above.
In my example I choose to have the âreceive_value()â method first quickly assign the value to a hash (whose keys are id strings,) and then call a separate âprocess_value()â method, because it may likely be needed to have other methods initiate the processing of values at other times than when a dialog receives a value. (Such as triggered by a API observer, etc.)
It also occured to me that sometime, it may be advantageous to iterate the @values hash and process all values. This again makes sense to have the process routine in a separate method than the receive callback from JS.
EDIT: Iâve added a process iterator method to the example to illustrate this point âŚ
def process_values
@values.keys.each { |id| process_value(id) }
end
However, if you have a better idea / example, PLEASE post it rather than tearing mine down.
Many method overrides return the dialog instance to allow call chaining.
It keeps an internal reference to itself when the CEF window is open so
the window doesnât disappear unexpectedly.
This reference is set to nil when the window closes to allow garbage collection
if there is no external reference to the object (as is usual behavior.)
The HtmlDialog is pretty useless without this capability.
Freezing (with wait) an asynchronous operation to make it synchronous will cause a deadlock in a single threaded application.
Itâs better to accept thinking in the asynchronous paradigm.
(The Ruby interpreter is not the actor that fetches a value from the dialog and continues processing it. The dialog is the actor that triggers sending a value to the âserverâ, and the âserverâ/Ruby interpreter only starts processing when the callback is triggered.)
Well phrased. As an example, if one had a dialog for exporting, there may be several âoption controlsâ. If the user clicks an âexport â or âokayââ button/control, the callback would include the state of the âoption controlsâ.