http://qs321.pair.com?node_id=587369

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

Fellow monks,

I'm still relatively new to the world of Win32::GUI and so I'm trying to port a working Tk stock price app that I wrote. BBFU was kind enough to point me to the direction of Timer for the timed calling of a subroutine, but now I'm stumped by the actual act of refreshing text labels.

In my Tk program, I write the labels, and when I go to refresh, I replace them all with blank lines, then rewrite them. Maybe not the best way to do it, but in Tk I'm not noticing any delay in the "erasing" and the "rewriting".

My first thought was to write a small test app to see if I could figure this out, but I'm stumped as to why once the string is at it's max length, it won't "erase" the extra bits when fed a smaller string.

use strict; use Win32::GUI(); my $interval = 100; my $main = Win32::GUI::Window->new( -name => 'Main', -width => 150, -height => 100, -onTimer => \&redraw_Timer, ); $main->AddTimer( "redraw_Timer", $interval); $main->Show(); Win32::GUI::Dialog(); sub Main_Terminate { -1; } sub redraw_Timer{ my $temp = localtime; return $main->AddLabel(-text => $temp); }

Now when translating this to my stock price app, I've got 8-10 stocks that I'm watching, and obviously as prices flucuate, I'm going to be stung by having something at +.90 perhaps go to +1.12, then down to +.81 and I'm going to have extra characters that are still being displayed.

In my stock app I'm displaying the initial results like this:

foreach (@test) { $_ =~ s/"//g; my (@color) = $change > 0 ? [ 0, 0, 255 ] : [ 255, 0, 0 ]; $main->AddLabel( -text => $arrow, -font => $arrow_font, -left => 55, -foreground => @color, -background => COLOR, -top => $h, ); # current security price $main->AddLabel( -text => pack( "A10", $current ), -font => $sec_font, -left => 70, -top => $h, -background => COLOR, ); # change in security price $main->AddLabel( -text => pack( "A10", $change ), -font => $sec_font, -left => 130, -top => $h, -background => COLOR, ); }

where test is a quote delimited string returned from Yahoo Finance. I'm not bothering with Text::CSV as it's never returning anything with an embedded apostrophe in it.

So when I want to update the information, I'm using the same technique but blank lines instead of the data that I'm scraping.

Is there a better way to go about displaying the data in one fell swoop that I've missed? If so, can someone point me to either the spot in the docs or some kind of tutorial?

Thanks in advance!

Revolution. Today, 3 O'Clock. Meet behind the monkey bars.

Replies are listed 'Best First'.
Re: Refreshing text without flicker with AddLabel using Win32::GUI
by bmann (Priest) on Dec 02, 2006 at 08:00 UTC
    Hi Popcorn Dave,

    Two things:

    First, the sample code adds a label for each timer event rather than updating the existing label. If you watch memory usage as the test runs, it climbs gradually at each timer event.

    Assign the original label to a variable when it is created. Then in redraw_Timer, instead of AddLabel just update $label->Text. This was enough to get rid of the flicker on my system.

    This also solves the problem with the extra characters, since it redraws the label text every time.

    use strict; use Win32::GUI(); my $interval = 100; my $main = Win32::GUI::Window->new( -name => 'Main', -width => 150, -height => 100, -onTimer => \&redraw_Timer, ); $main->AddTimer( "redraw_Timer", $interval); # add the following line my $label = $main->AddLabel( -text => scalar localtime ); $main->Show(); Win32::GUI::Dialog(); sub Main_Terminate { -1; } sub redraw_Timer{ my $temp = localtime; # replace the following line # return $main->AddLabel(-text => $temp); $label->Text( $temp ); }

    Second, there is a noflicker option to Win32::GUI::Window->new, which for some reason defaults to 0.

      Second, there is a noflicker option to Win32::GUI::Window->new, which for some reason defaults to 0.

      I was curious, so I investigated.

      When this option is off, window updates are performed as follows:

      • The part of the window that needs to be redrawn (or more) is redrawn.

      When this option is on, window updates are performed as follows:

      • A canvas (Device Context) is created.
      • A bitmap is allocated for the canvas.
      • The bitmap is initialized to the background colour.
      • The whole window (and child windows) is painted onto this canvas.
      • The bitmap is copied over the window's bitmap.
      • The bitmap is deallocated.
      • The canvas is deallocated.

      Think of it as buffering the output so that all the changes to the window's pixels occurs as close to instantly as possible.

      The latter takes longer to update the window, uses more resources and is probably rarely needed.

      Thanks very much for your reply! You've helped me get leaps and bounds beyond where I was before.

      I've finally had a few moments to try to play with what you suggested, and so naturally I'm stuck again. :)

      From what you described, I need to pre-define my labels so that I can just refresh the text that is being assigned to them. So far, good. Since this is a stock quote app that I'm writing, I've got a list of stocks I read out of a config file and save to a hash.

      My thought was to do something along the lines of:

      my %stocks = (^DJI =>{sec_label => $main->AddLabel( stuff...), current_label => #current price of stock, arrow_label => #direction of stock, change_label => #price change, pctchg_label => #percent change} )

      and I'm building this dynamically since I may add or delete stocks at any time and that's working out perfectly.

      What I'm getting stuck on is how to put parameters in the

      $stocks{$key}{arrow_label}->Text( $var );
      part where I'm just updating the text. In the arrow_label part of the hash, I want to be able to change the color to either red or green depending if a stock is going down or up respectively. Is there even a way to do that or am I stuck with just text?

      Revolution. Today, 3 O'Clock. Meet behind the monkey bars.
        Once the label is created, Win32::GUI doesn't expose a method to let you change the color. You might be able to use a custom Hook method to override painting the label.

        An alternative would be to create two labels, one red and one green. Make them identical in size and location in the window. Update the green one when the stock goes up, update the red one if the stock goes down.