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
on 2009-06-06 21:10
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
on 2009-06-07 06:41
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.
on 2009-06-07 14:12
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
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.
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
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.