Ace128 has asked for the wisdom of the Perl Monks concerning the following question:

Well, I have tried with threads and repeat ($mw->update) to try to get the progressbar updated while it goes through dirs recursivly, without luck.

Ok, from the beginning. I have this GUI in Tk. On that I have an Entry and a Button. In the entry you enter a path. Button calls a sub to count files, and then again but this time working with the files. After first go the progressbar is updated with the total files. Now, next, when it goes through, the progressbar is supposed to be updated live, but isnt until the recursive work is done. Actually, first I wanted the progressbar to be shown ontop ( in a new toplevel), but it didnt work properly. So, it ended up in same frame... Anyway, how to make the progressbar to update (and, well, specially the bar shown!) while script goes through dirs recursivly?

Ace

Replies are listed 'Best First'.
Re: Updating the Tk::ProgressBar live
by davidrw (Prior) on Sep 06, 2005 at 01:03 UTC
    What code do you have for the progress bar (how are you declaring/making toplevel/updating? Looking at Tk::ProgressBar it looks like the -to attribute should be set to the total files, and then as you go through and process files just do $ProgressBar->value( $files_completed )
      Well, the problem isnt that its not working. Its the gfx that aint updating _while_ working! Code is something like this: :)
      $mw->Label(-text => "Path:")-> grid( -row => 0, -column => 0, -sticky => 'w'); my $pathEntry = $mw->Entry(-width => 50, -textvariable => \$fu +ll_path)-> grid( -row => 0, -column => 1, -sticky => 'w'); $mw->Button(-text => "Browse...", -command => sub {print $full +_path})-> grid( -row => 0, -column => 2, -sticky => 'n'); $mw->Button(-text => "Add/Update!", -command => [\&addUpdateBu +ttonPressed])-> grid( -row => 1, -column => 1, #-columnspan => 3, -sticky => 'n'); $mw->Label(-text => "Progress:")-> grid( -row => 2, -column => 0, -sticky => 'w'); $progressLabel = $mw->Label(-text => "0 / Unknown")-> grid( -row => 2, -column => 1, -sticky => 'n'); my $variable = 24; $progressBar = $mw->ProgressBar( -width => 16, -length => 400, -anchor => 'w', -from => 0, #-to => 100, -blocks => 10, -gap => 0, -colors => [0, 'green', 50, 'yellow' , 80, 'red'], -variable => \$statics{'files'} )-> grid( -row => 3, -column => 0, -columnspan => 3, -sticky => 'n'); &centerWindow($mw, 410, 100); $pathEntry->focus; MainLoop; sub addUpdateButtonPressed { print "Getting statics from: " . $full_path . " ...\n"; my $total_files = &countFilesTotally($full_path); print "Files found: " . $total_files . "\n"; $statics{'files'} = 0; $progressLabel->configure(-text => "0 / " . $total_files); $progressBar->configure(-to => $total_files); $progressBar->repeat(500, sub {$progressBar->update}); #threads->create("recursive", $full_path); &recursive($full_path); } sub recursive { my $dir = shift; .. do alot of stuff ... }
        What if you try to explictily update it with $progressBar->value( $statics{'files'} ) in your recursive loop instead (and remove the -variable line from the $mw->ProgressBar() call)? Could be that it's the linked scalar wthat's not working properly (i've heard of issues like that before, too)..

        Side question -- i don't see a "my" in front of $progressBar .. are you not using strict/warnings, or just declaring some stuff at the top c-style?
Re: Updating the Tk::ProgressBar live
by pg (Canon) on Sep 06, 2005 at 01:48 UTC

    That repeat in your code does not work properly, and it does not really make sense to increment progress by 1 every 500 ms, as if you know how long it takes the recursive task to complete. As it is recursive, it is kind of tricky to calculate the progress, as you probably even do not know the size of the entire work. Assume you can, then every time after you calculate the prgress, call update.

    use Tk; use Tk::ProgressBar; my $mw = MainWindow->new(); my $value = 1; $mw->Button(-text => "Add/Update!", -command => [\&addUpdateButtonPres +sed])->pack(); $progressBar = $mw->ProgressBar( -width => 16, -length => 400, -anchor => 'w', -from => 0, -to => 10, -blocks => 10, -gap => 0, -colors => [0, 'green', 50, 'yellow' , 80, 'red'], -variable => \$value )->pack(); MainLoop; sub addUpdateButtonPressed { while (1) { $value ++; $progressBar->update(); ($value < 10) ? sleep 1 : last; } }
      I do the recursive thing twice. Once to count files. Next to actually do something! (I actually got 2 seperate recursive functions for this purpose) Also, the "recursive" function calls itself when a new dir is found.
Re: Updating the Tk::ProgressBar live
by zentara (Cardinal) on Sep 06, 2005 at 13:11 UTC
    I've seen similar problems with the progressbar before, although I don't have an example handy. Sometimes it just won't update from the scalar ref in realtime. It probably has something to do with the event loop scheduling. In those cases, just explicity set the progressbar to the new value in your update routine
    $ProgressBar->value($value); #and just to force the event loop $mw->update;

    I'm not really a human, but I play one on earth. flash japh
Re: Updating the Tk::ProgressBar live
by Errto (Vicar) on Sep 06, 2005 at 16:32 UTC

    pg and zentara are correct that the way to handle this is to explicitly recalculate the progress value and then call $mw->update regularly within your directory processing subroutine. Just by way of explanation, what's happening is that when your callback function addUpdateButtonPressed gets called, the Tk MainLoop is, in effect, suspended until that function returns. This is why you won't see any updates in the progress bar while this is happening. In fact, the entire GUI will essentially be seen to freeze (you can probably see this if you, say, move another window in front of yours and then move it away again). So since MainLoop is no longer processing events for you, you need to call the update method to do it yourself. Don't use repeat - it won't do you much good because you have your own loop doing the work.

    The alternative is to use threads. One thread runs the MainLoop but has a repeat call to a sub that will update the progress bar, and the other thread does the real work. But be careful - you cannot simply share the Tk objects between two threads, so you'll have to basically come up with your own communication method.

      Yep, the $mw->update did the trick! Lovely! Thanks monks. This is waaay better than using another thread!

      Ace