HtmlDialog closing by itself

Hi,

I am trying to start an extension using HtmlDialog or WebDialog and when I try activating the tool it opens with no problems but then after few seconds it auto closes the dialog. I am not sure what I am doing wrong, it probably something silly of my part. Nevertheless, I would appreciate any type of help.

Here is what I’ve…

\Plugins\test.rb

    require 'sketchup.rb'
    require 'extensions.rb'

    module Tester
    module Test

    	unless file_loaded?(__FILE__)
          ex = SketchupExtension.new('Test', 'test/app.rb')
          ex.description = 'Test for SketchUp.'
          ex.version     = '1.0.0'
          ex.copyright   = 'Test © 2018'
          ex.creator     = 'Tester'
          Sketchup.register_extension(ex, true)
          file_loaded(__FILE__)
        end

    end # module Test
    end # module Tester

\Plugins\tester\app.rb

require 'sketchup.rb'

module Tester
module Test

	class App

		def initialize
			
			dlg = UI::HtmlDialog.new(
			{
			  :dialog_title => "Dialog Example",
			  :preferences_key => "com.sample.plugin",
			  :scrollable => true,
			  :resizable => true,
			  :width => 600,
			  :height => 400,
			  :left => 100,
			  :top => 100,
			  :min_width => 50,
			  :min_height => 50,
			  :max_width =>1000,
			  :max_height => 1000,
			  :style => UI::HtmlDialog::STYLE_DIALOG
			})
			
			dlg.set_url("http://www.sketchup.com")
			dlg.show
			
		end # initialize
		
	end # class


	unless file_loaded?(__FILE__)
      menu = UI.menu('Plugins')
      menu.add_item('App') {
        Sketchup.active_model.select_tool Tester::Test::App.new
      }

      file_loaded(__FILE__)
    end


end # Test
end # Tester
  • I am testing on SketchUp 2017 Pro
  • I have Google Chrome Version 64.0.3282.186 (Official Build) (64-bit)
  • Also just in case I tryed on another computer and I had same problem

Any help will be Welcome!

1 Like

(1)

Use an @dlg (instance variable reference) that is persistent.
The local reference dlg will be destroyed when the initialize() method finishes.


(2)

      menu.add_item('App') {
        Sketchup.active_model.select_tool Tester::Test::App.new
      }

! class App is NOT yet an abstract Tool class. It has no tool callback methods, nor any of the control methods such as activate(), deactivate(), suspend() or resume().

You are also in the same namespace and you can refer to the class as App. NO need for full qualification.

2 Likes

That solved my problem…

Also, thanks for the extra info about class App which I was not aware of.

1 Like

What is that? … a porg ?

2 Likes

Haha… I see you’re a Star Wars fan.

1 Like

Wait, does the Ruby garbage collector destroy and close HtmlDialogs?

I have also run into them sometimes in rare cases disappearing but haven’t been able to reproduce it. has just stopped happening and I have forgot about it. Maybe adding a callback that references the dialog variable prevents it from being garbage collected and is why I so rarely have seen this, or maybe I just tend to use instance variables that persists, I don’t really know…

Anyhow I think this is a bug. If you use a passive dialog to show some information, e.g. an About dialog, there is no need to keep a reference for the dialog in Ruby, other than as a workaround for this bug.

I’m currently at my work where I only have access to SU16 so I can’t test this myself at the moment.

1 Like

Filed as #60.

Yes. True. References held by SketchUp also prevent garbage collection.

A prime example is an unreferenced tool instance, which the tool stack collection is actually the reference holder.

I do NOT think so.

If an object is unreferenced, GC can destroy it. The API should encourage good coding practice.

I have no problem with the GC destroying the Ruby object. The problem is that destroying the object closes the dialog. There are valid use cases for creating a dialog with no callbacks, to show static information, e.g. a manual or an About dialog containing the plugin version number. In these situations I wouldn’t call it good coding practice, but a hack, to force a persistent reference just to keep the dialog from closing.

A coder is responsible for the persistence of the objects they create not the API, nor the programming language.

Saying the same as @DanRathbun from the opposite perspective, in languages with automatic garbage collection a reference is what keeps an object from being reaped. No reference, poof it is gone! This results in no accidental memory leaks, a major contrast with for example C.

1 Like

Again, I have never said the object shouldn’t be garbage collected! The Ruby object can still be garbage collected just as it already is. That isn’t the problem! The problem is that garbage collecting the Ruby object closes the chromium browser window. Closing the window and destroying the object are two (2) different concepts. Doing one doesn’t necessarily mean doing the other.

What you appear to be missing is the difference between a Ruby variable, a Ruby object, and a system resource owned by that object. A Ruby object is a data structure managed by the interpreter. Your code accesses that object via a pointer (reference) known as a variable. The object in this case creates the Chromium window and contains a pointer to the window resource.

So, when the variable goes out of existence, there is no remaining reference to the object. Ruby must garbage collect (reap) the object to prevent a memory leak. But when the object is reaped, there is no remaining connection between the window and Ruby. To draw an analogy, would you expect the hook to a file to remain open after the associated Ruby File object is GC’d?

The disconnect has several serious consequences. Your code can no longer send or receive anything from the window. What becomes of action callbacks and javascript that tries to invoke them? Sure, the user can click the close button to get rid of the window, but there is no way to assure that ever happens and no remaining way for your code to close the window; the window becomes a leak. The window remains on-screen even after your code cedes control back to the GUI and the user is doing things unrelated to your extension; it becomes clutter.

In short, I think that SketchUp is doing the right thing and it is up to you to retain a reference to the window if you want it to stay open.

2 Likes

No, I wouldn’t. But I wouldn’t expect the file to be deleted from the drive just because there is no Ruby object referring to it. I think the underlying problem is how the window is owned by the Ruby object and can’t be disconnected from it. The window resource should IMO remain in memory, and the window should remain shown on the screen, even without a Ruby object refereeing it, until the window is explicitly closed.

There are valid use cases for showing a window without referencing it ever again in your code. A plugin can very well have an “About ” window that merely shows static information, and is closed by the close button on the window frame. Such a window doesn’t need callbacks or javascript to be pushed onto it.

Keeping a variable that you never again reference, e.g. by defining a dummy callback that will never be called or define a class or instance variable that is never referenced outside of the method, just to keep the window open, doesn’t make much sense. That, if anything, is clutter.

And therein lies the rub: the user is the only remaining thing that can close the window until the program exits.

Respectfully, I disagree.

1 Like

And that’s not a problem. If the user wants the dialog closed, they can close it. The dialog shouldn’t just disappear for no apparent reason in front of their eyes.

What would you even name a callback that you don’t ever call? Or a variable that you never reference?

I’ll be blunt: I think that leaving something onscreen when your extension is no longer in control is bad design, kind of like having a SketchUp dialog stay atop when you open a different app. Conversely having it disappear when I choose a different Tool does not seem strange to me nor is it “for no apparent reason”. The reason is that your Tool is no longer active so it’s info window is out of context to what I am currently doing.

There’s no need for a callback you won’t call. Name the variable anything you like. “@steve_is_an_idiot” would be a private joke but I wouldn’t mind!

Feel free to disagree and code however you like, but let’s not argue because you won’t change my mind :wink:.

1 Like

Not every dialog is related to a tool. If you open the Extension Manger it doesn’t close again because you activate Push/Pull, or because you open 3D Warehouse. It is up to you to close it once you are done with it. SketchUp doesn’t care whether it is open or not.

Which of these 3 methods do you think is the cleanest and least “hacky”?

def show_static_info
  dlg = UI::HtmlDialog.new(dialog_title: "About Some Extension")
  dlg.set_html("Version: 1.0.0")
  dlg.show

  # HACK: Add dummy callback to prevent garbage collector from closing the
  # window without the users consent.
  dlg.add_action_callback("dummy_callback") { dlg }
end

def show_static_info
  dlg = UI::HtmlDialog.new(dialog_title: "About Some Extension")
  dlg.set_html("Version: 1.0.0")
  dlg.show

  # HACK: Use persistent dummy instance variable to prevent garbage collector
  # from closing the window without the users consent.
  # Probably not a good idea as the dialog object can never be garbage
  # collected, not even after the user has closed it.
  # Also clutters the object instance namespace.
  @arbitraty_dummy_variable_that_is_never_to_be_referenced = dlg

end

def show_static_info
  dlg = UI::HtmlDialog.new(dialog_title: "About Some Extension")
  dlg.set_html("Version: 1.0.0")
  dlg.show

  # HACK: Reference the dialog object in the set_on_closed block to prevent the
  # garbage collector from closing the window without the users consent.
  dlg.set_on_closed { dlg }
end

In your second technique, after setting @arbitraty_…

dlg.set_on_closed { @arbitraty_... = nil}

Setting a variable to nil breaks its reference to the original object, leaving that object subject to GC. There is overhead for the variable, but it is fixed and trivial compared to the dialog object it originally referenced. This is just management of resources allocated by the code, not a hack at all.

2 Likes

I can’t see how it is not a hack to write some extra code for the sole purpose of affecting the Ruby garbage collection. The point of automatic garbage collection is that it’s automatic! If the code serves no purpose in the extension itself but only exists to prevent unwanted garbage collection I would definitely call that code a hack, and the unwanted garbage collection a bug.