Forum: wxRuby Refreshing Window

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
9e6d05909dc733af570faadce1392e67?d=identicon&s=25 Michael Satterwhite (msatterwhite)
on 2009-06-06 21:10
If a long (relatively) operation takes place after dismissing a dialog,
the underlying window is in an indeterminate state until the operation
is complete. It would be good to be able to periodically display a
status update - or let the user know what's happening. To do this, I
need some way to force the system to go back through the event loop so
that the screen gets properly updated.

I'm probably a bit dense here, but I don't see how this could be done.
I'm sure some of you have already encountered this and figured out how
to do it.

How can this be done? Enquiring minds would love to know.

---Michael
F19a4807544338636bf12e76aff178af?d=identicon&s=25 Jonah Dahlquist (nonybd)
on 2009-06-06 23:45
I posted with a similar problem.  The solution was to use evt_idle.
Just set it like this:

# run dialog box
# user replies
evt_idle do
    # do whatever it is you want to do, including
    # changing a progress bar, label, or whatever.
end

Instead of

# run dialog box
# user replies
# do whatever...

The evt_idle just doesn't allow the process to run until nothing is
happening.

However, please note that the window will still be frozen, but whatever
changes you make will be applied.

I'll follow this to find out if it's possible to keep it from being
frozen altogether :)

Cheers
Eee774a82313b9081986dba418781adc?d=identicon&s=25 Svend Haugaard Sørensen (Guest)
on 2009-06-07 06:41
(Received via mailing list)
On Sat, 6 Jun 2009 23:45:38 +0200
Jonah Dahlquist <lists@ruby-forum.com> wrote:

> Instead of
>
> I'll follow this to find out if it's possible to keep it from being
> frozen altogether :)
>
> Cheers
yes I have solved it the same way.

But I was wondering if it were possible to make a 'evt_idle_once'
function. That only run once and the gets removed from the event que.
It seems a little inefficient, that the block is call every time there
is some idle time, even if it is only needed to run it once.
3396e4a3df8a840faec520af8555a400?d=identicon&s=25 Mario Steele (Guest)
on 2009-06-07 14:12
(Received via mailing list)
2009/6/6 Svend Haugaard Sørensen <shs@demosophia.net>

> >     # changing a progress bar, label, or whatever.
> >
> function. That only run once and the gets removed from the event que.
> http://rubyforge.org/mailman/listinfo/wxruby-users
>


Another solution to this, instead of using evt_idle, is two fold, and it
bit
more trickier to setup, but get's the job done, of keeping the app
looking
like it is active.  The first thing, is to continue with asking your
dialog,
once your dialog is answered / dismissed / whatever, you proceed with
your
basic logic of what the next step should be.  For this case, I'll give
you
an example of asking a user for a file to process, and how to proceed,
in
logical human terms, instead of actual code.

First would be to prompt the user with a file dialog, with the run of
show_modal on a new instance of Wx::FileDialog.  The return value of
show_modal() should be either Wx::ID_OK, or Wx::ID_CANCEL.  If it's
Wx::ID_CANCEL, then simply return from the method that you created to
handle
the event.  And all goes on as normal.  But if it's Wx::ID_OK, then we
move
to the next step of gathering the information provided by the Dialog.

So we get the name of the file, and store it into an instance variable
on
our object that we are currently working from, weither it be window, or
whatever you call the event from.  Once we have saved the filename, we
don't
process anything, or start anything at the moment.  At this point, we
need
to decide on which design we can go with, and this depends on what kind
of
processing we are doing.

We can do the first process, which is an Un-interrupted process, in
which we
create a new task, or in this case, Thread, where our tight knitted
program
logic will run in, and you just type it out as one big loop that will
actually process the file in question.  The thing you need to note here,
that with this kind of design, you need to put somewhere in the loop (I
usually do it at the end of the loop iteration), a Thread.pass, which
switches between the internal Ruby Threads.  I will explain the reason
for
this in a moment.  After you completed your loop, and end the new Thread
you
created in the writting of it, you add a new Wx::Timer event.  The
Wx::Timer
event will have one purpose, and that's to allow internal Ruby context
switch between threads, again using the Thread.pass method.

However, if your processing can be done in intervals, and isn't intricle
that you need to process everything immediately, then you can do what I
call, timer based execution.  With Timer based execution, you do
something
similar to the Ruby Threads above, but instead of creating a thread and
a
timer, you only create a Wx::Timer, and use some instance variables to
keep
track of what your doing.  And what you do, is you set the timer for a
pre-determined interval, at which it will execute, and during the
execution
of the Timer event, you do your actual processing of the file.  So we
move
the processing of the file from the Thread, into the Timer Event
handler.

Now, with the two processes explained, as far as the programming design
of
it, I'll go into detail about Ruby Threads, and usage of Timers with
this.
It should also be noted, that with this setup, is optimal for Ruby 1.8
series.  The reason for this, and the reason for the Timer in the first
place for this method, is that in Ruby 1.8 series, Threads are green
threads.  Meaning that they are software layer threads.  They are
entirely
managed by the Ruby interpreter itself, and never goes out to the
Operating
System itself to control the thread.  This is where you have quite a few
problems with Wx::Ruby, and Ruby itself.  Now the reason why it's
limited to
the Ruby 1.8 Series, and earlier actually, is cause the Ruby 1.9 series,
to
be dubbed 2.0, uses Operating System Threads.  And as Alex has explained
to
me on this subject, You can create threads in Ruby 1.9, and Wx::Ruby,
and
things will work, as long as you keep any updates that you do the GUI in
the
Main Thread.  So if you need to update a progress bar, or display some
text
in a window, it's best to do that in the main thread, and not the
sub-thread
that you create for the processing of the file.

To further enlighten as to why Green Threads can be problematic, is
there's
truely only 2 threads ever created in the entire running of your
program.
The Thread that Ruby operates on, and the thread that wxWidgets runs in.
Even though a Ruby Thread in 1.8, is still a thread of sorts, it's
severly
limited when there are 2 Operating System threads running in a single
Ruby
Program.  So basically what happens, when you see a "Non-Responsive"
window
in wxRuby, when you know that your program is processing stuff, comes
from
the fact that Ruby's OS Thread, has taken priority for processing, and
simply ignores the wxRuby OS Thread, till it has completed all of it's
work.  Now remember, this only applies to Ruby 1.8, as Ruby 1.9 uses OS
threads fully in the Thread class implementation.

So, in our above usage of Ruby's Thread, and Wx::Timer, is a sort of way
to
say hey, Ruby, give wxWidgets some time to process stuff.  And we give
what
we call, time share, between the threads, that has to be distinctively
written into the code, and passed around between the two OS Thread.
What we
are doing, is that in our Ruby thread, we're saying, okay run this loop,
and
do some processing of the stuff, when it's completed it's work in this
iteration, we're going to go ahead, and tell the Ruby Interpreter, allow
other threads to run, which in turn, gives back control to wxRuby to do
it's
event processing.  When there is no other events to execute, or
specifically
when our timer ticks, it passes control back to the Ruby interpreter,
and
says alright, you can allow another thread to process some information.

So in a way, we have to do negotiation between the two threads, in order
to
make it work properly, and keep the application from looking like it's
dead-lock.  You could even use the evt_idle method, to put the
Thread.pass
in, as evt_idle will be called when wxWidgets has nothing else to do,
but
you still need to put the Thread.pass into your thread, once your done
with
a iteration loop of your processing code.  If you don't, the thread will
continue on, till it's finished processing, and you'll still be stuck
with
the looking non-responsive application window.  You have to keep the
balance
between the Ruby 1.8 threads, and the wxWidgets OS thread, so that both
can
continue to execute.

I hope that this enlightens some of you now, and future programmers as
to
why there are certain things that need to be done, in order to make Ruby
1.8
Green Threads cooperative with Operating System Threads, as this would
be
needed for any C/C++ Library, that utilizies a library that uses
Operating
System Threads, when your stuck with Green Threads in Ruby 1.8.

hth,

Mario
F19a4807544338636bf12e76aff178af?d=identicon&s=25 Jonah Dahlquist (nonybd)
on 2009-06-09 21:19
Great reply, thanks!  As to the evt_idle_once, all you have to do is
this:

evt_idle do
    evt_idle do end
    # your code
end

That empties the evt_idle function as soon as it is called.
0ba143872804741abb61bf862ac4bd75?d=identicon&s=25 Oisín Mac fhearaí (desty)
on 2010-02-18 21:37
Mario Steele wrote:
> It should also be noted, that with this setup, is optimal for Ruby 1.8
> series.  The reason for this, and the reason for the Timer in the first
> place for this method, is that in Ruby 1.8 series, Threads are green
> threads.  Meaning that they are software layer threads.  They are
> entirely
> managed by the Ruby interpreter itself, and never goes out to the
> Operating
> System itself to control the thread.  This is where you have quite a few
> problems with Wx::Ruby, and Ruby itself.  Now the reason why it's
> limited to
> the Ruby 1.8 Series, and earlier actually, is cause the Ruby 1.9 series,
> to
> be dubbed 2.0, uses Operating System Threads.  And as Alex has explained
> to
> me on this subject, You can create threads in Ruby 1.9, and Wx::Ruby,
> and
> things will work, as long as you keep any updates that you do the GUI in
> the
> Main Thread.  So if you need to update a progress bar, or display some
> text
> in a window, it's best to do that in the main thread, and not the
> sub-thread
> that you create for the processing of the file.

Perhaps it's a problem with the OS X implementation (1.9.1p376), but so
far I'm not getting any better results with threads in Ruby 1.9... I
start a thread from a Wx::App on_init like follows:

    Thread.new do
      loop do
        puts "1: #{Time.new}"
        puts "1b: #{Time.new}"
        puts "1c: #{Time.new}"
        puts "1d: #{Time.new}"
        ...

And once started, the output looks like:

1: 2010-02-18 20:18:47 +0000
1b: 2010-02-18 20:18:48 +0000
1c: 2010-02-18 20:18:51 +0000
1d: 2010-02-18 20:18:55 +0000

This is quite amazingly slow - I guess the call to Time.new and
.to_s/puts blocks and the thread sleeps, but up to 4 seconds between
simple expressions is a lot!

Using the Timer hack to cooperatively schedule the threads seems to
work, just like in Ruby 1.8.

Oisín
This topic is locked and can not be replied to.