FXProgressbar: how to update during lengthy task

There’s surely an easy answer to this. How do you update a progress bar
during a lengthy calculation? The examples that come with FXRuby only
have a process bar updated while the app is idle.

The complete code is below. Just run it an type Ctrl-S. Nothing is
saved, it’s just a loop. The progress bar isn’t updated until the loop
is done. I tried wrapping the loop in a thread, and I tried calling
#update. I’m using FXRuby 1.0.13 and fox 1.0.11 and ruby 1.6.7.

#!/usr/bin/env ruby

require "fox"
include Fox

class ViewWindow < FXMainWindow
include Responder

def initialize(app)
super(app, “test”, nil, nil, DECOR_ALL, 0, 0, 0, 0)

 menubar = FXMenubar.new(self, 

LAYOUT_SIDE_TOP|LAYOUT_FILL_X|FRAME_RAISED)

 @progress_target = FXDataTarget.new(0)
 @progress_bar = FXProgressBar.new(self, @progress_target,
   FXDataTarget::ID_VALUE, LAYOUT_FILL_X|PROGRESSBAR_PERCENTAGE)

 filemenu = FXMenuPane.new(self)
 FXMenuTitle.new(menubar, "&File", nil, filemenu)
 FXMenuCommand.new(filemenu, "&Save...\tCtl-S\tSave file.", nil).
   connect(SEL_COMMAND, method(:onCmdSave))
 FXMenuCommand.new(filemenu, "&Quit\tCtl-Q").
   connect(SEL_COMMAND) {getApp().exit(0)}

end

def onCmdSave(sender, sel, ptr)

Thread.new do

   20.times {|i|
     sleep(0.1)
     @progress_target.value = i
   }

end

 return 1

end

def create
position(0, 0, 200, 100)
super
show
end
end

app = FXApp.new(“App”, “Foo”)
app.init(ARGV)
window = ViewWindow.new(app)
app.create
app.run

“Joel VanderWerf” vjoel@PATH.Berkeley.EDU wrote in message
news:3D83A5C4.5090806@path.berkeley.edu…

There’s surely an easy answer to this. How do you update a progress bar
during a lengthy calculation? The examples that come with FXRuby only
have a process bar updated while the app is idle.

Because all of the progress bar updating is taking place in the onCmdSave()
callback the main event loop doesn’t have a chance to process the repaint
events until it returns. So basically, we need to do something to “nudge”
the FOX event loop to go ahead and do its thing right away, as soon as
you’ve updated the progress value.

One approach (the one you’re using) is to update the value of a data target
associated with the progress bar. Since FOX’s GUI update mechanism is the
magic that happens under the hood to make data targets work, we want FOX to
go ahead and do a “GUI update” pass as soon as you’ve changed the data
target’s value. When that happens, the progress bar will look at its data
target, grab the new value and repaint itself. To accomplish this just add a
call to getApp().forceRefresh in the loop:

def onCmdSave(sender, sel, ptr)
    20.times { |i|
      sleep(0.1)
      @progress_target.value = i
      getApp().forceRefresh
    }
  return 1
end

A different tack, assuming you didn’t use a data target for the progress,
would be to just call FXProgressBar#progress=() directly in the loop:

def onCmdSave(sender, sel, ptr)
    20.times { |i|
      sleep(0.1)
      @progress_bar.progress = i
    }
  return 1
end

Note that in this case we’re not relying on the GUI update mechanism to get
the progress bar to ask its data target what the current value is, so
there’s no need to force a refresh walk.

Hope this helps,

Lyle