Delay or Async Operation

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?

timer

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.)

It seems to work fine (I’m testing in SU 2017 Make, since that is the oldest version I support).

This is my code:

timerid = UI.start_timer(27, false) {
			UI.stop_timer(timerid)
			UI.messagebox("Message to user goes here.")
    		}

I’ve tested it in SU 2017 and SU 2022 Pro and it doesn’t seem to have any issues so far.

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