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

Hi,

I'm having issues updating the progressbar color. I've tried using -colors, it didnt work, so I'm trying -foreground, still doesnt work.

Progress bar gen:
my %PBCfg = ( -width => "20", -length => 500, -from => "0", -to => "100", -blocks => "500", -gap => '2', -borderwidth => '2', #-colors => [0, 'green', 50, 'green' , 80, 'green'] -foreground => 'green' ); my $Prb = $TBL_frame->ProgressBar(%PBCfg, -troughcolor=>'white',-varia +ble=>\$T_testers{$tst}->{"ProgS"})->pack(-side=>'left', -fill=>'x');

Now, I'm trying to change the color

$T_testers{$tst}->{'Status_B'}->configure(-text=>"Down",-background => + "red" ); $T_testers{$tst}->{"ProgBar"}->configure(-foreground => "red" );$mw->u +pdate;

the botton changes color everytime I try, but the ProgressBar changes the color only once after init. Any ideas?

Replies are listed 'Best First'.
Re: TK::ProgressBar Color update
by zentara (Cardinal) on Oct 07, 2010 at 11:18 UTC
    As an afterthought, the Tk::Progressbar is not a very solid widget. It is prone to memory leaks, and setting options can be tricky, as you have found out. If you need to manipulate the progressbars, you might want to build your own on a Tk::Canvas.

    A crude example:

    #!/usr/bin/perl use warnings; use strict; use Tk; my $w=20; my $x=0; my $y=0; my %colors = ( 0 => ['black','yellow'], 1 => ['yellow','black'], 2 => ['white','green'], 3 => ['green','white'], 4 => ['grey','red'], 5 => ['red','grey'], 6 => ['blue','white'], 7 => ['white','blue'], 8 => ['orange','grey45'], 9 => ['grey45','orange'], ); my %bardata = ( 0 => rand 200, 1 => rand 200, 2 => rand 200, 3 => rand 200, 4 => rand 200, 5 => rand 200, 6 => rand 200, 7 => rand 200, 8 => rand 200, 9 => rand 200, ); my %bars; my $mw=tkinit; my $c = $mw->Canvas->pack; for (0..9) { $bars{$_} = $c->createRectangle($x,$y,$x+20,$bardata{$_}, -fill=> ${$colors{$_}}[0], ); my $text = $c->createText($x+10,$y+10, -anchor=>'center', -fill => ${$colors{$_}}[1], -text => $_ ); $x+=20; } $mw->repeat(200, sub{ &update }); MainLoop; ########################################################## sub update{ $x=0; $y=0; %bardata = ( 0 => rand 200, 1 => rand 200, 2 => rand 200, 3 => rand 200, 4 => rand 200, 5 => rand 200, 6 => rand 200, 7 => rand 200, 8 => rand 200, 9 => rand 200, ); for (0..9) { $c->delete( $bars{$_} ); $bars{$_} = $c->createRectangle($x,$y,$x+20,$bardata{$_}, -fill=> ${$colors{$_}}[0], ); my $text = $c->createText($x+10,$y+10, -anchor=>'center', -fill => ${$colors{$_}}[1], -text => $_ ); $x+=20; } }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: TK::ProgressBar Color update
by zentara (Cardinal) on Oct 07, 2010 at 10:39 UTC
    Hi,

    Here is an example that does what I think you want. It is a hack that swaps colors. The problem with it, is once all colors are swapped to red, it is tricky to restore original bar colors. I used red1, red2, red3 to differentiate and restore. It would seem that you could itemconfigure the progressbar for -colors, to restore original bar colors, but it dosn't seem to work.

    I hope it stimulates a solution for you.

    #!/usr/bin/perl use strict; use Tk; use Tk::ProgressBar; our $percent_done = 0; our $stopped = 0; my $mw = MainWindow->new(qw/-title Working/); my $progress = $mw->ProgressBar( -width => 20, -height => 400, -from => 0, -to => 100, -blocks => 50, -colors => [0, 'blue',30,'yellow',70,'orange'], -variable => \$percent_done )->pack; my $buttonF = $mw->Frame->pack; my $startB = $buttonF->Button( -text => 'start', -command => sub { $stopped = 0; $percent_done = 0; # hack to restore colors, I use different red shades # but you could store the information in a hash somehow # to get back to the original colors swapColor($progress, "red1", "blue"); swapColor($progress, "red2", "yellow"); swapColor($progress, "red3", "orange"); foreach my $x (1 .. 100) { select(undef,undef,undef,.05); if ($stopped) { swapColor($progress, "blue", "red1"); swapColor($progress, "yellow", "red2"); swapColor($progress, "orange", "red3"); return; } $percent_done = $x; $progress->update; } })->pack(-side => 'left'); my $stopB = $buttonF->Button( -text => "stop", -command => sub { $stopped = 1; } )->pack(-side => 'left'); MainLoop; sub swapColor { my ($pb, $oldColor, $newColor) = @_; foreach my $tag ($pb->find("all")) { print $tag,'->',$pb->itemcget($tag, "-fill"),"\n"; if ($pb->itemcget($tag, "-fill") eq $oldColor) { $pb->itemconfigure($tag, -fill => $newColor); } } print "\n\n"; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: TK::ProgressBar Color update
by kcott (Archbishop) on Oct 08, 2010 at 09:33 UTC

    I can see 2 potential problems here: -colors and -variable.

    -colors

    It looks like you've read the documentation to get 0, 50 and 80 (which is good) but not fully understood it (less good). The doco is not very clear about this option so that's not overly surprising.

    What you actually have says:

    • When the dereferenced value of the scalar reference supplied to the -variable option (the progress value) is 0, start showing the progress bar in green. Keep showing it as green until the progress value reaches 50.
    • When the progress value reaches 50, stop showing the progress bar in green from this point forward and instead show it in green. Keep showing it as green until the progress value reaches 80.
    • When the progress value reaches 80, stop showing the progress bar in green from this point forward and instead show it in green. Keep showing it as green until the progress value reaches 100 (the value supplied to the -to option).

    So what you really want here is:

    -colors => [ 0, 'green' ],

    -variable

    This may be perfectly fine but it leaps out at me as a source of potential error. Please check the following:

    • You declared something like: my %T_testers = ($tst => { ProgS => 0, ... })
    • %T_testers is visible to both my $Prb = $TBL_frame->ProgressBar(...); and the routine whose progress you're measuring (the progress routine).
    • You are setting $T_testers{$tst}->{"ProgS"} to zero before the progress routine starts.
    • You are incrementing $T_testers{$tst}->{"ProgS"} as the progress routine proceeds such that it reaches 100 when the progress routine finishes.

    Perl/Tk is one of my favourite toys so I don't mind helping with this. However, if you require further assistance, please do the following:

    • Ensure you have use strict; and use warnings; in your code.
    • Provide an actual description of what you want your GUI to do with respect to this progress bar.
    • Provide a full description of what, if anything, went wrong. I'm sorry but "... it didnt work ..." [sic] is completely unacceptable.
    • Show enough of the code so that all of the points indicated above (declarations, assignments, increments, etc.) can be seen.

    -- Ken

Re: TK::ProgressBar Color update
by zentara (Cardinal) on Oct 08, 2010 at 13:08 UTC
    Hi again, while hacking on this problem, I noticed 1 peculiarity, the color changes work as you would expect, IF you resize the $mw after hitting the buttons. As an example of this behavior, I rewrote the script above to use a timer (instead of a hackish select delay) and just used configure on the -colors.

    If you run this script, and hit Stop, the bar will turn red only after you just SLIGHTLY resize the window. The same occurs after a second START, the bar returns to original colors after a slight resize of the window.

    I tried update, packPropagate, idletasks, etc. to stimulate the update, but no luck.

    Does anyone know how to trigger the progressbar's deep update without a window resize? I suppose as a hack, the Start and Stop buttons could include some code to do a 1-pixel resize of the $mw. :-)

    #!/usr/bin/perl use strict; use Tk; use Tk::ProgressBar; our $percent_done = 0; our $stopped = 0; my $mw = new MainWindow; $mw->geometry('200x100+100+100'); my $progress = $mw->ProgressBar( -width => 40, -height => 20, -from => 0, -to => 100, -blocks => 50, -colors => [0, 'blue',30,'yellow',70,'orange'], -variable => \$percent_done )->pack(-expand=>1, -fill=>'x'); my $buttonF = $mw->Frame->pack; my $timer; my $startB = $buttonF->Button( -text => 'start', -command => sub { $stopped = 0; $percent_done = 0; #restore original colors $progress->configure('-colors',[0, 'blue',30,'yellow',70,'orange'] ); $mw->idletasks; # these commands $mw->update; # don't seem to work #but a window resize does $timer = $mw->repeat(50, sub{ $percent_done++; $progress->update; if ($stopped) { #set red colors $progress->configure('-colors', [0, 'red',30,'red',70,'red'] ); $mw->idletasks; # these commands $mw->update; # don't seem to work #but a window resize does $timer->cancel; } }) } )->pack(-side => 'left'); my $stopB = $buttonF->Button( -text => "stop", -command => sub { $stopped = 1; } )->pack(-side => 'left'); MainLoop;

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
      Does anyone know how to trigger the progressbar's deep update without a window resize?

      Short answer: yes :-)

      Longer answer:

      I now think I see what Ohad's problem is. I originally thought the progress bar wasn't changing as the variable was being updated.

      With reference to your earlier "... you might want to build your own on a Tk::Canvas.", Tk::ProgressBar is already a derived widget based on Tk::Canvas.

      The -colors option is configured as PASSIVE. It's not intended to be changed after creation of the widget. However, it can be done by accessing the private method _layoutRequest().

      WARNING! Accessing private methods is bad!
      The code maintainer may change it, or even remove it, at any time.
      Use the following code entirely at your own risk!

      OK, that's my arse covered :-) Here's the solution:

      #!perl use strict; use warnings; use Tk; use Tk::Button; use Tk::ProgressBar; my $mw = MainWindow->new(); my $progress; my $toggle = 0; my @colours = ( [ 0 => q{#ff0000}, 30 => q{#00ff00}, 60 => q{#0000ff} ], [ 0 => q{#ffff00}, 30 => q{#00ffff}, 60 => q{#ff00ff} ], ); my $pb = $mw->ProgressBar( -width => 20, -length => 200, -colors => $colours[$toggle], -variable => \$progress, )->pack(); $mw->Button(-text => q{Jump WITH Change}, -command => sub { $progress ||= 0; $progress = ($progress + 10) % 110; $toggle ^= 1; $pb->configure(-colors => $colours[$toggle]); Tk::ProgressBar::_layoutRequest($pb, 1); } )->pack(); $mw->Button(-text => q{Jump NO Change}, -command => sub { $progress ||= 0; $progress = ($progress + 10) % 110; $toggle ^= 1; $pb->configure(-colors => $colours[$toggle]); } )->pack(); $mw->Button(-text => q{Exit}, -command => sub { exit })->pack(); MainLoop;

      Tested successfully under Windows and Cygwin.

      -- Ken

        Thanks for this excellent lesson! I was trying to go the route of eventGenerate('configure'), to trigger the update, but was running into errors.
        Tk::ProgressBar::_layoutRequest($pb, 1);

        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh

        Thanks!!

        For your time and effort, the layoutRequest works!!
Re: TK::ProgressBar Color update
by Anonymous Monk on Oct 07, 2010 at 09:58 UTC