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

I am trying to download a website when a button is clicked.....the website is rather large. When I created the subroutine that processed the command, I added in the update function for both the window and the button, but non of that seems to matter - what am I doing wrong?
sub aaa { $get_file = get("http://www.a.com/file.xml"); $button->update; $mw->update; }

Replies are listed 'Best First'.
Re: LWP::Simple causing Tk error
by graff (Chancellor) on Oct 14, 2005 at 01:06 UTC
    You mean you're trying to load a very large XML stream from a web site? You seem to be assuming that the data is not too large to fit in memory...

    Anyway, maybe you need to fork a separate process to use LWP::Simple, fetch data from the web site, and store it to disk in a file, so that the Tk script can keep moving along, responding to user input events like it normally does, and maybe check teh status of the child process and the status of the XML data file. Once it's ready, notify the user somehow, or just do whatever needs to be done with the data.

Re: LWP::Simple causing Tk error
by pg (Canon) on Oct 14, 2005 at 01:41 UTC

    Those two updates obviously will not work the way you expected. They only get accessed after the downloading is completed. During the downloading, your GUI will just sort of "hang".

    I have some script, sort of still "work in progress", but it helps you to attack this problem. What I did was that, I wrote the download part myself, instead of using any module so that I know the progress, and can update the progressbar every time after a portion of the downloading is done: (I am still working on it, so lots of stuffs are hardcoded, but the idea is certainly there. Take a look.)

    use Tk; use Tk::Progressbar; use IO::Socket::INET; use IO::Select; use strict; use warnings; my $mw = MainWindow->new(); $mw->Button(-text => "Download", -command => \&download)->pack(); MainLoop; sub download { my @documents; for (1..20) { push @documents, "song$_.Wma"; } my $connections; my $selector = IO::Select->new(); while (1) { if (my @readers = $selector->can_read(5)) { for my $reader (@readers) { my $line; if (my $length = sysread($reader, $line, 2048)) { syswrite($connections->{$reader}->{"file"}, $line) +; $connections->{$reader}->{"received"} += $length; $connections->{$reader}->{"progress"}->value($conn +ections->{$reader}->{"received"} / $connections->{$reader}->{"length" +} * 100); $mw->update(); } else { close $connections->{$reader}->{"file"}; delete $connections->{$reader}; $selector->remove($reader); close $reader; } } } else { if (my $document = pop @documents) { if (my $connection = IO::Socket::INET->new(Proto => "t +cp", PeerAddr => "music.eting.cn", PeerPort => 80)) { binmode($connection); $selector->add($connection); print $connection "GET http://blah.com/$document H +TTP/1.1\r\nHost: 0.0.0.0\r\n\r\n"; my $line; my $offset = 0; while (1) { my $len = sysread($connection, $line, 2048, $o +ffset); print $line; if ($line =~ m/\r\n\r\n/) { last; } else { $offset += $len; } } $line =~ m/Content-Length: (\d*)/; print "$1\n"; $connections->{$connection}->{"length"} = $1; my $file; open $file, ">", $document; binmode($file); $line =~ m/.*?\r\n\r\n(.*)/s; syswrite($file, $1); $connections->{$connection}->{"received"} = length +($1); $connections->{$connection}->{"file"} = $file; $connections->{$connection}->{"document"} = $docum +ent; my $frame = $mw->Frame()->pack(); $frame->Label(-text => $document)->pack(); $connections->{$connection}->{"progress"} = $frame +->ProgressBar( -width => 20, -length => 200, -anchor => 'w', -from => 0, -to => 100, -blocks => 1, -colors => [0, 'green'])->pack(); $mw->update(); } } else { unshift @documents, $document; } } } }
Re: LWP::Simple causing Tk error
by zentara (Cardinal) on Oct 14, 2005 at 10:29 UTC
    You can also use threads, like in ztk-BBC-World-News-Rss-TickerTape

    You can also use a callback in the LWP's request statement. Just sprinkle "$mw->update" frequently in the callback. This is a clunky solution, because of what may happen if the network connection hangs.

    #!/usr/bin/perl -w #from lwpcook use LWP::UserAgent; $ua = LWP::UserAgent->new; $URL = 'http://zentara.net/zentara1.avi'; $filename = substr( $URL, rindex( $URL, "/" ) + 1 ); #print "$filename\n"; open( IN, ">$filename" ) or die $!; my $expected_length; my $bytes_received = 0; my $res = $ua->request(HTTP::Request->new(GET => $URL), sub { my ( $chunk, $res ) = @_; $bytes_received += length($chunk); unless ( defined $expected_length ) { $expected_length = $res->content_length || 0; } if ($expected_length) { printf STDERR "%d%% - ", 100 * $bytes_received / $expected +_length; } print STDERR "$bytes_received bytes received\n"; # XXX Should really do something with the chunk itself print IN $chunk; } ); print $res->status_line, "\n"; close IN; exit;

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