This is at least two pay grades about my current Ruby programming ability so I am asking here for some guidance.
I want to display a UI.messagebox however I would like it to be delayed for about 10-30 seconds after where it appears in my code. Is there such a way to do this type of thing in the Ruby API?
module Dezmo
module TestUImsg
extend self
def msg_delayed(time, msg)
UI.start_timer(time, false) {
UI.stop_timer(@cont_timer) if @cont_timer
UI.messagebox(msg)
Sketchup.set_status_text
}
end
def count
sec = 0
@cont_timer = UI.start_timer(1, true) {
sec += 1
Sketchup.set_status_text(sec.to_s)
Sketchup.active_model.active_view.invalidate
UI.stop_timer(@cont_timer) if @cont_timer && sec > 40
}
end
def run
result = UI.messagebox('Do you like Ruby?', MB_YESNO)
if result == IDYES
msg_delayed(5, 'Ruby likes you too')
else
msg_delayed(10, 'Why?')
end
count
end
end
end
Dezmo::TestUImsg.run
Not sure why I didn’t think of this I guess I thought I could use a timer only for animations which I do for my door/window opening and closing module.
I think in older versions putting a modal messagebox inside a timer block caused a infinite number of messagebox dialogs to appear. (You had to force kill the SketchUp application.)
I don’t know if this is an ongoing problem, but there is a comment in TT’s safer_observer_events.rb
that seems relevant to the situation we’re facing here. I quote:
# (!) Calling UI.stop_timer appears to make SketchUp prone to crashing so
# we will instead keep this variable instead to prevent triggering
# the event multiple times.
Below is my implementation of a safer messagebox following TT’s example.
# A hack for opening a UI::messagesbox from a timer event
# https://github.com/SketchUp/sketchup-safe-observer-events/blob/master/src/safer_observer_events.rb
#
# Example call:
# display_safe_messagebox() { UI.messagebox('Example Completed.') }
#
def self.display_safe_messagebox(&block)
executed = false
UI.start_timer( 0, false) {
next if executed # use next when in a proc-closure (for ruby console)
executed = true
block.call
}
end
Yes, beware of doing anything that blocks the thread in a timer callback, such as opening a modal dialog. The timer will continue to execute and you need a guard like what @sWilliams mentions.
Here’s a utility file I often use:
module Example
module Execution
def self.delay(seconds, &block)
done = false
UI.start_timer(seconds, false) do
next if done
done = true
block.call
end
end
def self.defer(&block)
self.delay(0.0, &block)
end
class Debounce
# @param [Float] delay in seconds
def initialize(delay)
@delay = delay
@time_out_timer = nil
end
def call(&block)
if @time_out_timer
UI.stop_timer(@time_out_timer)
@time_out_timer = nil
end
@time_out_timer = UI.start_timer(@delay, &block)
nil
end
end # class
end
end # module