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

I'm trying to update a Text widget as I transfer files using Net::FTP. print statements to STDOUT work as expected, and write updates to the command line as each file is transferred, but $text->insert() statements seem to be queued up, and the text box updates only once when the entire process is complete.

Hash marks are also written to STDOUT as the file is transfered, but when I redirect STDOUT to the textbox using tie:
tie *STDOUT, ref $tx_output, $tx_output;
it does not update until the entire FTP process is complete.

Any ideas? I've included the subroutine which does the work, but I'm omitting the widget definitions to conserve space.
sub transfer { my $ftp = Net::FTP->new("$ftp_server", Debug=> 0, Hash=> 1) or die "ERROR $!\n"; $ftp->login( "$login", "$password"); foreach my $file ( @source_files ) { my @fields = split( '/', $file ); my $dest_file = pop( @fields ); print "Transfer: $file - $dest_file\n"; $text->insert('end', "Transfer: $file - $dest_file\n"); $text->see('end'); if ( $ftp->put( "$file", "$dest_dir/$dest_file" ) ) { $text->insert('end', " FILE TRANSFERRED\n"); } else { $text->insert('end', " ERROR: $!\n"); $text->insert('end', " SOURCE: $file\n"); $text->insert('end', " DEST: $dest_dir/$dest_file\n"); } } $ftp->quit(); }

Replies are listed 'Best First'.
Re: Update Text Widget during FTP in Perl/Tk
by zentara (Cardinal) on Oct 14, 2004 at 12:58 UTC
    Your ftp transaction is blocking the Tk EventLoop. The terms you want to look for are "blocking and non-blocking". This is the same sort of problem you can experience in a plain Perl script, when you use "system" to run something, instead of doing a "fork-and-exec". In the system case, your program will run the called command ( in your case ftp ), and the rest of the script will wait for ftp to finish. If you "fork-and-exec", the ftp is spawned off as a new independent process, so your original program can continue to run. The problem now becomes "how do you get the ftp data back into your original program?"

    This has come up so often, that the Tk::ExecuteCommand module was developed. Try running your ftp through Tk::ExecuteCommand. You could put your ftp code into a second helper script.

    Another option is to use Tk::fileevent (whichs reads files in a non-blocking manner), and use IPC::Open3 to run the ftp stuff. There are other ways to use fileevent too, like the piped form of open. Here is some sample code showing how to integrate IPC::Open3,fileevent, and Tk.

    #!/usr/bin/perl -w use strict 'vars'; #by John Drukman $|=1; use Tk; use IPC::Open3; local *IN; local *OUT; local *ERR; my $pid=open3(\*IN,\*OUT,\*ERR,'/bin/bash'); # set \*ERR to 0 to send STDERR to STDOUT my $inwin=new MainWindow; my $entry=$inwin->Entry(-width => 80)->pack; $inwin->Button(-text => 'Send', -command => \&send_to_shell)->pack; my $outwin=new MainWindow; my $textwin=$outwin->Text(-width => 80, -height => 24, -state => 'disabled')->pack; $outwin->fileevent(OUT,'readable',\&get_from); MainLoop; sub send_to_shell { my $cmd=$entry->get() . "\n"; write_textwin("> $cmd"); print IN $cmd; } sub get_from { my $buf=''; sysread OUT,$buf,4096; write_textwin($buf); } sub write_textwin { my $str=shift; $textwin->configure(-state=>'normal'); $textwin->insert('end',$str); $textwin->see('end'); $textwin->configure(-state=>'disabled'); } __END__

    I'm not really a human, but I play one on earth. flash japh
Re: Update Text Widget during FTP in Perl/Tk
by saintmike (Vicar) on Oct 14, 2004 at 00:17 UTC
    The problem I see with this approach is that while Net::FTP::put() is busy transferring bytes, the GUI is left alone and stops processing user events. It will look 'frozen'.

    This thread shows some ways of resolving the dilemma.

Re: Update Text Widget during FTP in Perl/Tk
by TedPride (Priest) on Oct 14, 2004 at 10:54 UTC