2009/6/6 Svend Haugaard SÃ¸rensen [email protected]
# changing a progress bar, label, or whatever.
function. That only run once and the gets removed from the event que.
Another solution to this, instead of using evt_idle, is two fold, and it
more trickier to setup, but get’s the job done, of keeping the app
like it is active. The first thing, is to continue with asking your
once your dialog is answered / dismissed / whatever, you proceed with
basic logic of what the next step should be. For this case, I’ll give
an example of asking a user for a file to process, and how to proceed,
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
the event. And all goes on as normal. But if it’s Wx::ID_OK, then we
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
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
process anything, or start anything at the moment. At this point, we
to decide on which design we can go with, and this depends on what kind
processing we are doing.
We can do the first process, which is an Un-interrupted process, in
create a new task, or in this case, Thread, where our tight knitted
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
this in a moment. After you completed your loop, and end the new Thread
created in the writting of it, you add a new Wx::Timer event. The
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
similar to the Ruby Threads above, but instead of creating a thread and
timer, you only create a Wx::Timer, and use some instance variables to
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
of the Timer event, you do your actual processing of the file. So we
the processing of the file from the Thread, into the Timer Event
Now, with the two processes explained, as far as the programming design
it, I’ll go into detail about Ruby Threads, and usage of Timers with
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
managed by the Ruby interpreter itself, and never goes out to the
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
the Ruby 1.8 Series, and earlier actually, is cause the Ruby 1.9 series,
be dubbed 2.0, uses Operating System Threads. And as Alex has explained
me on this subject, You can create threads in Ruby 1.9, and Wx::Ruby,
things will work, as long as you keep any updates that you do the GUI in
Main Thread. So if you need to update a progress bar, or display some
in a window, it’s best to do that in the main thread, and not the
that you create for the processing of the file.
To further enlighten as to why Green Threads can be problematic, is
truely only 2 threads ever created in the entire running of your
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
limited when there are 2 Operating System threads running in a single
Program. So basically what happens, when you see a “Non-Responsive”
in wxRuby, when you know that your program is processing stuff, comes
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
say hey, Ruby, give wxWidgets some time to process stuff. And we give
we call, time share, between the threads, that has to be distinctively
written into the code, and passed around between the two OS Thread.
are doing, is that in our Ruby thread, we’re saying, okay run this loop,
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
event processing. When there is no other events to execute, or
when our timer ticks, it passes control back to the Ruby interpreter,
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
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
in, as evt_idle will be called when wxWidgets has nothing else to do,
you still need to put the Thread.pass into your thread, once your done
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
the looking non-responsive application window. You have to keep the
between the Ruby 1.8 threads, and the wxWidgets OS thread, so that both
continue to execute.
I hope that this enlightens some of you now, and future programmers as
why there are certain things that need to be done, in order to make Ruby
Green Threads cooperative with Operating System Threads, as this would
needed for any C/C++ Library, that utilizies a library that uses
System Threads, when your stuck with Green Threads in Ruby 1.8.