wxRuby GUI textctrl not updating until end

I have a wxRuby application that is working fine, except that it does
not update the multi-line textctrl box until the application has
completed processing.

The program has a couple of nexted loops. On the inner loop, the program
processes a file, then displays out the results, as outlined below:


Start of inner loop
process one file…very heavy cpu load
puts “File number #{@new_file_count} processed”
@textbox.append_text(“File number #{@new_file_count} processed\n”)
End of inner loop

The puts line is displayed in the terminal window on each pass through
the loop.
The “write to gui” line, @textbox…, does not display until all of the
files have been processed.

Ruby should process the gui line before going any further, so that
implies that the gui does not process the line until later.

What is going on here, and what do I do to get the lines displayed in
the gui as each loop completes?

Thanks,

Gary

Hi Gary,

Gary H. wrote:

I have a wxRuby application that is working fine, except that it does
not update the multi-line textctrl box until the application has
completed processing.

What is going on here, and what do I do to get the lines displayed in
the gui as each loop completes?

Have you used a thread for the busy loop? If not, that is likely your
problem.

I have been using the technique in the sample below to keep my GUIs
updated. A timer in the Frame gets the GUI updated at regular
intervals, and a Thread in the ‘busy loop’ is needed to separate the
work from the GUI. If you leave the Thread out from ‘print_value’, then
the GUI is updated only at the end. If you leave out the timer, then
the Thread does not seem to work reliably, e.g. if you pass focus to
another window.

Let me know if this helps in your application. I would be interested to
hear of a better solution though.

cheers,

Peter.

require ‘wx’

class MyFrame < Wx::Frame
def initialize
super(nil, :title => “Controls”)

@text_field = Wx::TextCtrl.new(self, :style => Wx::TE_MULTILINE)
button = Wx::Button.new(self, :label => "Show Values")
evt_button(button) {print_value}

main_sizer = Wx::BoxSizer.new Wx::VERTICAL
main_sizer.add @text_field
main_sizer.add button
set_sizer main_sizer

# -- this timer interrupts every 100ms and gets next thread active
# -- if you don't have the timer, the GUI does not update if window
#    in background
timer = Wx::Timer.new(self, Wx::ID_ANY)
evt_timer(timer.id) {Thread.pass}
timer.start(100)

end

def print_value
Thread.new do
10.times do |i|
puts “#{i} cycle”
@text_field.append_text “#{i} cycle\n”
sleep 1.0
end
end
end
end

Wx::App.run do
MyFrame.new.show
end

Hi Peter,

Your approached worked great for my application!

The sleep line did not seem to have any effect, so I deleted it.

Thanks for your help!

Gary

I have found that the recommended solution of placing cpu-intensive code
inside a thead only works well for some applications.

In particular, in my most recent application, placing the disk I/O
intensive code within a thread made a one second operation take 75
seconds. And that was with a timer setting of only 2 ms.

Regardless, this all seems backwards to me. My understanding of how this
is working is that the thread is getting one pass each time the timer
times out. What I really want is for the “intensive code” to get all of
the available time, except that I want to “interupt” the intensive code
every 100ms to update the GUI and check for GUI clicks, etc.

In other words, the entire application, except for the intensive code,
would need to be inside the thread that the timer is giving attention
to. I’m not sure how to do that, or if that is the best approach.

Present code outline:

class Myframe < Wx::Frame
def initialize
normal stuff

# Timer interrupt to service thread
timer = Wx::Timer.new(self, Wx::ID_ANY)
timer.start(2)
evt_timer(timer.id) {Thread.pass}

end

def various other def’s

end

def intensive_routine
Thread.new do
until done do
thousands of disk accesses…
@msgbox.write_text( “Display status for user…” )

end
end
end

def on_init
evt_button( @quit_btn ) { on_quit_btn_clicked }

end

end

The above code is a greatly simplified representation of working code.
When the code is run with the “Thread.new” commented out, the program
completes quickly, but it does not update the GUI or pay attention to
GUI mouse clicks until the intensive routine has completed. When run
with the “Thread.new” uncommented, the GUI is responsive as desired, but
the intensive routine is running at a snail’s pace.

I would appreciate guidance on this.

Thanks,

Gary

Threads can be used to keep the GUI responsive during an I/O or CPU
intensive operation, but I have now found what seems like a better way.
The following command causes the GUI to be updated, with any pending GUI
events being handled:

Wx::THE_APP.yield()

This is what I was looking for all along! No need for threads or timers.
The “intensive routine” receives only a minor impact on its performance
using this statement.

Simplified example code outline:

class Myframe < Wx::Frame
def initialize
normal stuff
end

def various other def’s

end

Very CPU intensive routine:

def on_get_busy_btn_clicked
update_rate = 1000 # Every 1000 iterations
byte_number = 0 # Gets incremented on each iteration

until done do
  thousands of disk accesses, and after each read:  byte_number += 1
  @msgbox.write_text( "Display status for user..." ) # Not on each 

iteration
# Check GUI to see if it needs processing:
Wx::THE_APP.yield() if byte_number%update_rate == 0

end
end

def on_init
evt_button( @get_busy_btn ) { on_get_busy_btn_clicked }

end

end

The above code is a greatly simplified representation of working code.
The “intensive routine” gets nearly full CPU attention for quick
processing. The occasional call to service the GUI keeps the GUI “alive”
and responisve.

The answer was in the WxRuby doc’s under “miscellaneous, App”, which
brings up app.html.

Regards,

Gary

Gary H. wrote:

I have found that the recommended solution of placing cpu-intensive code
inside a thead only works well for some applications.

In particular, in my most recent application, placing the disk I/O
intensive code within a thread made a one second operation take 75
seconds. And that was with a timer setting of only 2 ms.

Hi Gary,

Regardless, this all seems backwards to me. My understanding of how this
is working is that the thread is getting one pass each time the timer
times out. What I really want is for the “intensive code” to get all of
the available time, except that I want to “interrupt” the intensive code
every 100ms to update the GUI and check for GUI clicks, etc.

I agree. I also don’t have a good mental model of what is happening, as
there is an interaction between the Ruby threads and the wx library. I
wish I could say I understood this. The following is the result only of
some experimentation, so please treat with caution!

My belief is that things should happen as you describe. When using
wxRuby, the code is using the GUI thread, which is why we must put the
heavy computation in its own thread and use a Timer. The Timer is there
so that the GUI thread wakes up every specified interval, updates the
displays/responds to events, and then passes control back to the Thread.
We have to make our own Timer (unlike with, for example, Java/Swing)
because of the separation between Ruby and the wx library, but that’s
where it gets hazy for me. I don’t think my previous example properly
captured that separation, because a GUI update is being done in the
computation Thread, and this perhaps makes the approach slow in your
case.

I experimented with my code from before, replacing the ‘sleep’ method
with an intensive computation which returns partial results as it goes
along. The best approach I could find, both in computing time and
conceptually, was to move all the GUI updates into the Timer, and to
reduce the calls to ‘append_text’ by aggregating the results. About
this last point, adding 100_000 lines to the text control separately
made the program 50 times slower than concatenating the same 100_000
lines and adding them to the text control in one go.

My test code now looks like the following:

class MyFrame < Wx::Frame
def initialize
super(nil, :title => “Heavy Computation Example”)

@text_field = Wx::TextCtrl.new(self, :style => Wx::TE_MULTILINE)
button = Wx::Button.new(self, :label => "Show Values")
evt_button(button) {print_value}

main_sizer = Wx::BoxSizer.new Wx::VERTICAL
main_sizer.add @text_field
main_sizer.add button
set_sizer main_sizer

@results = []                       # for collecting the results 

together

# all the GUI updating is done here
Wx::Timer.every(100) do                            # wake up every 

100ms
@text_field.append_text “#{@results.join(”\n")}" # add text en
bloc
@results = [] # and clear
results
Thread.pass # return to
calculation
end
end

def print_value
# all the computation is done here
Thread.new do
sum = 0
1.upto(N-1) do |i|
sum += 1 if lots_of_work i # this is the ‘busy computation’
@results << sum # collect the result
end
end
end
end

I quite like the final code. There’s a conceptual separation of GUI
update and computation: it’s a traditional model-view separation. The
GUI is updated only every 100ms, reporting every intermediate result,
and the computation time(*) is indistinguishable from that of doing the
calculation from a non-GUI Ruby program (although I’ve not tried
anything taking longer than 10 seconds).

Getting back to your application, my suggestion is to move the
‘write_text’ call out of the ‘intensive_routine’ and into the timer
block. Then, reduce the number of calls to ‘write_text’ by collecting
the update lines together, and join them to add in a single call to
‘write_text’.

Let me know if this is a better/worse solution for you.

cheers,

Peter.

(*) Note, I did find execution times for the wxRuby program when the
Thread was run a second or third time were dramatically quicker than the
first run. I’m not sure why, or what to do about it.