Running code after a modal dialog is shown

Hi all,

I’m looking for a way to get a block of code to execute once a modal
dialog has been displayed (via a call to show_modal.) I was hoping to
find some event that would fire when the window was shown, but the only
one that seemed vaguely promising (evt_window_create) seems to do
nothing
at all, at least in my environment (Ruby 1.9.1 on Win32, wxruby 2.0.1.)
Here’s a very simple (non-modal) example, which fails to print
‘Created!’
as I’d (perhaps naively) expect it to:


require “wx”

class TestFrame < Wx::Frame
def initialize(*args)
super(*args)
puts ‘In Initialize!’
evt_window_create { puts ‘Created!’ }
end
end

class TestApp < Wx::App
def on_init
TestFrame.new(nil, -1, ‘Test!’).show()
end
end

TestApp.new.main_loop

(incidentally, the RubyForge doc page here:
http://wxruby.rubyforge.org/doc/windowcreateevent.html
still refers to this event as ‘evt_create’, which might be worth fixing,
but that’s a different issue.)

I don’t know if I’m misusing this event, or if there’s a bug here
somewhere, but at any rate I’m open to other approaches if this isn’t
the
right way to go. My current solution is an icky hack involving timers
which I’d love to be able to replace with something more sensible. I’m
pretty new to both Ruby and wxRuby, so I apologize in advance if I’m
missing something obvious here.

Ben

Ben Van Hof wrote:

I’m looking for a way to get a block of code to execute once a modal
dialog has been displayed (via a call to show_modal.)

I would probably do it by subclassing the modal dialog, providing your
own show_modal method and executing the code there after super has been
called. If you want it to run variable code provided by the caller, pass
and capture in the constructor. eg (untested)

class MyDialogWithShowHook < Wx::Dialog
def initialize([args], &block)
super(*args)
@hook = block
end
def show_modal
super
@hook.call
end
end

I was hoping to find some event that would fire when the window was
shown, but the only one that seemed vaguely promising
(evt_window_create) seems to do nothing at all, at least in my
environment (Ruby 1.9.1 on Win32, wxruby 2.0.1.)

There is Wx::ShowEvent which can be requested for processing with
evt_show. However in wxWidgets it isn’t provided on OS X, at least in
some versions, so you might want to avoid that. It’s not a CommandEvent
so you need to call it on the instance you’re capturing. Again, untested

dlg = Wx::Dialog.new(…)
dlg.evt_show { puts “i’m here” }
dlg.show_modal

evt_window_create is low level and only generally meant for debugging
rather than GUI logic control. evt_window_create I think fires in a
parent frame when its child windows are created, and for app when any
window is created. But the exact meaning and timing of “created” may
vary with the underlying toolkit - on OS X, for example, I think it
fires for all the child widgets in the frame as the frame’s constructor
returns.

(incidentally, the RubyForge doc page here:
http://wxruby.rubyforge.org/doc/windowcreateevent.html
still refers to this event as ‘evt_create’, which might be worth
fixing, but that’s a different issue.)

Thanks, I’ve fixed that in SVN.

I don’t know if I’m misusing this event, or if there’s a bug here
somewhere, but at any rate I’m open to other approaches if this isn’t
the right way to go. My current solution is an icky hack involving
timers which I’d love to be able to replace with something more sensible.

Hopefully the above will help, but if you want to say more about what
you’re trying to achieve I’m sure people will be happy to offer some
advice.

alex

On Thu, 24 Sep 2009, Alex F. wrote:

I would probably do it by subclassing the modal dialog, providing your
own show_modal method and executing the code there after super has been
called. If you want it to run variable code provided by the caller, pass
and capture in the constructor. eg (untested)

Thanks, Alex, for your quick and thoughtful response. I tried your
first
approach out; the problem is that when you call super, control doesn’t
return until the modal dialog has been closed:

def show_modal
super
@hook.call # we don’t get here until the dialog is closed
end

There is Wx::ShowEvent which can be requested for processing with
evt_show. However in wxWidgets it isn’t provided on OS X, at least in
some versions, so you might want to avoid that. It’s not a CommandEvent
so you need to call it on the instance you’re capturing. Again, untested

dlg = Wx::Dialog.new(…)
dlg.evt_show { puts “i’m here” }
dlg.show_modal

I gave this a try, too, but evt_show fires before the window is actually
visible on the screen (at least on win32), and so still doesn’t get me
what I want.

Hopefully the above will help, but if you want to say more about what
you’re trying to achieve I’m sure people will be happy to offer some
advice.

Makes sense. I’ve got a long-running external process that I want to be
able to report progress for, using a small modal dialog that just
contains
a progress bar and cancel button (it needs to be modal, since I’ve got a
main window underneath it that I don’t want the user interacting with
while the process runs.) My initial approach was to spawn a worker
thread
and communicate progress back to the UI periodically, but ran into some
ugly problems having a popen’ed process on one thread and wxRuby on
another. I wasted way too much time trying to get that to work, and am
basically considering that avenue of investigation closed (if you’re
really interested in the gory details I can provide them, but I think it
boils down to a problem with having non-Ruby code in both threads
interacting badly with the GIL.)

My current approach, then, is to keep it all in one thread: open the
dialog, start the process, and keep the UI responsive by periodically
calling Wx::get_app().yield. This all works great, except that I’m
currently having to use a timer event to get my code started once the
modal dialog is displayed, and it seems like there should be a
less-hacky
solution.

Anyway, I hope that’s enough background on what I’m trying to accomplish
here. If it spurs any additional ideas, I’d love to hear them.

Ben