Send_action & send_to_layout(path) problem on Windows

Folks,

I’ve encountered a weird situation here.
Running with SU2020 on Win 10 and Mac Catalina returned different result.
I was using Sketchup.send_action(CONSTANT) to setup the top, front, right views first, i.e.

Sketchup.send_action("viewTop:")
page = Sketchup.active_model.pages.add "Top View"
Sketchup.send_action('viewFront:')
page = Sketchup.active_model.pages.add "Front View"

End up on Mac this works, returned with 2 new pages setup in respective Top and Front views - but on Windows, this doesn’t work at all using the same method - the camera location never changes, remains as “Last saved SketchUp view” which is a general perspective view there.

As my second step was to send these pages to LO, on Mac it works well - Perspective view showed up in LO as expected, but on Windows I got an error message from the console saying “Sketchup view must be Axonometric”

I also tried the numerical commands (but in the API we all know the description is these numbers may not be supported in the future where may eventually getting universalised) - using the numbers also doesn’t work at all on Windows

Sketchup.send_action(10501)
page = Sketchup.active_model.pages.add "Top View"
Sketchup.send_action(10502)
page = Sketchup.active_model.pages.add "Front View"

So 2 questions:
1 Why this send_action is not working properly on Windows?
2 send_to_layout why on Windows “requires” the view must be Axonometric while Mac works perfectly fine?

Thanks a ton in advance!

Sketchup.send_action is asynchronous (at least on Windows). This API is generally quite weird with unofficial PC only values. I would use the “proper” API to set up the camera position.

I think the API docs could be more clear that this method shouldn’t be preferred when there are alternatives.

Julia, I realized one thing after multiple time testing on Windows machine…
If I run send_action standalone first, after it returns a TRUE then run the model.pages.add - it works

But if I put them together in a def block… it sure fail… seems add page happens before send_action commits…

send_action is asynchronous (at least on Windows), meaning you basically ask SketchUp to please to something while it’s not busy. If you do that and then right after tell SketchUp to do something now (the usual approach in programming), that second action will be carried out first.

If you skip the send action stuff and instead use Camera#set to move the camera, everything should work just fine.

1 Like

Here are a couple of examples using send_action that work for simple models …


(1) Without the use of a ViewObserver … adding the scene page first, then doing a send_action, and updating the scene page after a delay:

module Example
  module MakeScenes # using a page.update timer

    extend self

    def add_standard_view_scene(pagename)
      case pagename
      when "Front View"
        arg = 'viewFront:'
      when "Back View"
        arg = 'viewBack:'
      when "Top View"
        arg = 'viewTop:'
      when "Bottom View"
        arg = 'viewBottom:'
      when "Right View"
        arg = 'viewRight:'
      when "Left View"
        arg = 'viewLeft:'
      when "Iso View"
        arg = 'viewIso:'
      else
        puts "Unknown pagename."
        return
      end
      model = Sketchup.active_model
      view  = model.active_view
      pages = model.pages
      if pages[pagename]
        puts "Page \"#{pagename}\" already exists!"
      else
        page = pages.add(pagename)
        puts "Page \"#{pagename}\" created."
        Sketchup.send_action(arg)
        UI.start_timer(0.5,false) {
          page.update(1)
        }
      end
    end

    if !@loaded
      menu = UI.menu('Camera')
      submenu = menu.add_submenu('Standard Scenes',3)
      submenu.add_item('Top')    { add_standard_view_scene('Top View')    }
      submenu.add_item('Bottom') { add_standard_view_scene('Bottom View') }
      submenu.add_item('Front')  { add_standard_view_scene('Front View')  }
      submenu.add_item('Back')   { add_standard_view_scene('Back View')   }
      submenu.add_item('Left')   { add_standard_view_scene('Left View')   }
      submenu.add_item('Right')  { add_standard_view_scene('Right View')  }
      submenu.add_item('Iso')    { add_standard_view_scene('Iso View')    }
      @loaded = true
    end

  end
end

(2) Using the extension module itself as a temporary ViewObserver object, that adds the new scene page after the send_action is done changing the view:

module Example
  module MakeScenes # using a ViewObserver callback

    extend self

    def add_standard_view_scene(pagename)
      case pagename
      when "Front View"
        arg = 'viewFront:'
      when "Back View"
        arg = 'viewBack:'
      when "Top View"
        arg = 'viewTop:'
      when "Bottom View"
        arg = 'viewBottom:'
      when "Right View"
        arg = 'viewRight:'
      when "Left View"
        arg = 'viewLeft:'
      when "Iso View"
        arg = 'viewIso:'
      else
        puts "Unknown pagename."
        return
      end
      view = Sketchup.active_model.active_view
      @view_changed = false
      view.add_observer(self)
      Sketchup.send_action(arg)
      @tid = UI.start_timer(1.0,true) {
        if @view_changed
          UI.stop_timer(@tid)
          pages = view.model.pages
          if pages[pagename]
            puts "Page \"#{pagename}\" already exists!"
          else
            pages.add(pagename)
            puts "Page \"#{pagename}\" created."
          end
        end
      }
    end

    def onViewChanged(view)
      puts "onViewChanged: #{view}"
      @view_changed = true
      view.remove_observer(self)
    end

    if !@loaded
      menu = UI.menu('Camera')
      submenu = menu.add_submenu('Standard Scenes',3)
      submenu.add_item('Top')    { add_standard_view_scene('Top View')    }
      submenu.add_item('Bottom') { add_standard_view_scene('Bottom View') }
      submenu.add_item('Front')  { add_standard_view_scene('Front View')  }
      submenu.add_item('Back')   { add_standard_view_scene('Back View')   }
      submenu.add_item('Left')   { add_standard_view_scene('Left View')   }
      submenu.add_item('Right')  { add_standard_view_scene('Right View')  }
      submenu.add_item('Iso')    { add_standard_view_scene('Iso View')    }
      @loaded = true
    end

  end
end

:bulb:

1 Like

Dan, thanks for your reply!
I’ve tried something else and end up I found out that:
if let’s say we are iterating an array of standard view names, on Mac we can do something like

standard_view_name = ["Top", "Bottom", "Front", "Back"]
standard_view_name.each do |name|
Sketchup.send_action('view#{name}:')
Sketchup.active_model.pages.add "#{name} View"
end

While on Mac this is perfectly fine, but on Windows with SU2019+, if we add something that kinda “pause” this iteration like a UI.messagebox or like your example with a UI.start_timer - right below the pages.add: It performs back to normal.

This is only happening on Win OS :slight_smile:
So maybe in case if anyone else is trying to do this, we can suggest developers to add this “breath moment” - but however, it’s really obvious something that specifically happening on Windows.

Else we can add that “darwin” to decide if the loop needs to include this “breath moment” too.

Really appreciate!

This has been suggested in the past. But it cannot be done, because the send_action method fires UI commands which many are asynchronous. The code for these commands cannot be changed because the UI (and so the users) use them.

If the rigmarole of a delay or a temporary ViewObserver is too much, then as Julia said, the only other alternative is to code the positioning and target for the view’s camera.


FYI, I had previously posted an example that used camera positioning …

1 Like

Thanks Dan. I read that post too haha!