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
2 Likes

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

1 Like

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

1 Like

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
2 Likes

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