Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Keeping the user informed: A Tk/Threads question

by Grygonos (Chaplain)
on Jul 07, 2004 at 14:24 UTC ( #372392=perlquestion: print w/ replies, xml ) Need Help??
Grygonos has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I have a script that will be used by our production area to produce reports based on client data. This client data may comprise 1 month, or 1 year. Regardless, the data must be imported by the production staff. When the users chooses to add dat,. a Tk::DialogBox is shown. The user then, selects a file and enters a unique id number for the data. Once the user clicks OK the file is processed and added to the block of data from which the reports will be produced. This data may take 10 to 15 minutes .. at most 30 minutes to import. What can I do to keep the user informed that the process is indeed still "working as intended" and not DoA. I have read that Tk is not thread safe, I assume since it has its own command loop and what not. How can I keep a small window open that lets the user know what kind of progress is being made on the data importation?

Thanks for your help,

Comment on Keeping the user informed: A Tk/Threads question
Re: Keeping the user informed: A Tk/Threads question
by Joost (Canon) on Jul 07, 2004 at 14:45 UTC
    Good question. If you can spread your processing in smaller bits and you can get the smaller bits called from the MainLoop, everything will work. The way I do it in one application is that I build my own MainLoop:

    use Tk::Event qw(DONT_WAIT); while (1) { if ($self->run) { # test if we need to process $self->process_a_bit() # process a tiny bit } return if $self->quit; # test if quit status was set DoOneEvent(DONT_WAIT); # do normal Tk event select undef,undef,undef,0.0001; # wait for a bit }
    I did this, because my processing was not easily adjusted into the normal Tk::Eventloop processing and it needed to be called a lot ("live" 44Kz audio stream processing, with 100 samples per process_a_bit call)

    If you want to go this route, you want to adjust the timing of the select() and process_a_bit() calls until your GUI and processing run fast enough, and it doesn't lock up the whole machine when there is no processing being done :-)

    Alternatively, if you're able to run your processing based on file events or signals, you can take a look at the Tk::Eventloop documentation for registering extra events in the normal MainLoop.

    Oh yes, and you want something like Tk::ProgressBar but you probably know that already :-)

      Yeah Tk::ProgressBar would be best, but I don't know (until its already over) how far along a file would be at point X. Basically the data is being read from the file in a while <DATA> loop, to me that doesn' tmuch lend itself to that... is there a way to pop up a dialog and auto-dismiss it? or something?

      /me needs to buy Mastering Tk
Re: Keeping the user informed: A Tk/Threads question
by gri6507 (Deacon) on Jul 07, 2004 at 15:35 UTC
    There was a recent post 367593 which was asking a very similar question. My response 367607 should still be applicable.
Re: Keeping the user informed: A Tk/Threads question
by BrowserUk (Pope) on Jul 07, 2004 at 15:35 UTC

    There is nothing to stop you from using Tk and threads in the same program. Just don't use Tk on more than one thread.

    I'd offer a some sample code to prove this, but I can't find a suitable Tk starting point. Lot's of sample code, but none that actually does anything other than maintain the interface.

    If you can provide a sample app that puts up a window with a ProgressBar that actually move without manual intervention, then I'll modify it to run a long running process in a thread and have the progress bar update as a result.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon
Re: Keeping the user informed: A Tk/Threads question
by BrowserUk (Pope) on Jul 07, 2004 at 18:40 UTC

    Here ya go. Put the long running code into the work() sub and periodically update $progress (as shown) with a value between 0-100.

    #!perl -slw use strict; use threads qw[ async ]; use threads::shared; our $WORKMAX ||= 1_000; ## A shared var to communicate progess between work thread and TK my $progress : shared = 0; sub work{ for my $item ( 0 .. $WORKMAX ) { { lock $progress; $progress = ( $item / $WORKMAX ) * 100; } select undef, undef, undef, 0.001; ## do stuff that takes time } } threads->new( \&work )->detach; ## For lowest memory consumption require (not use) ## Tk::* after you've started the work thread. require Tk::ProgressBar; my $mw = MainWindow->new; my $pb = $mw->ProgressBar()->pack(); my $repeat; $repeat = $mw->repeat( 100 => sub { print $progress; $repeat->cancel if $progress == 100; $pb->value( $progress ) } ); $mw->MainLoop;

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon
      You must be really bored today :) Thanks for the sample code. I haven't done perl threads before, although I'm pretty experienced with Java's threads, and have done threads in C before. I'm gonna read up on it a bit and see what comes of it. Thanks again :)
Re: Keeping the user informed: A Tk/Threads question
by zentara (Archbishop) on Jul 08, 2004 at 14:36 UTC
    "Once the user clicks OK the file is processed and added to the block of data from which the reports will be produced. This data may take 10 to 15 minutes .. at most 30 minutes to import. What can I do to keep the user informed that the process is indeed still "working as intended" and not DoA"

    Here are a couple of ideas.

    If the file you open for processing has a fully readable filehandle, you can count the lines in the filehandle, to use as a max in the Tk::Progressbar, then rewind the filehandle to process it. Increment the progressbar 1 for each line processed. Here is an example that will make a progressbar for a couple of files.

    sub do_while { use Fcntl qw(:seek); #count lines and rewind while(<FILE0>){$linetotal++} seek FILE0, 0, SEEK_SET or die "Cannot rewind file: $!"; while(<FILE1>){$linetotal++} seek FILE1, 0, SEEK_SET or die "Cannot rewind file: $!"; while(<FILE2>){$linetotal++} seek FILE2, 0, SEEK_SET or die "Cannot rewind file: $!"; print "$linetotal\n"; $progressbar = $main->ProgressBar( -length => 200, # Actually width -width => 20, # Actually height -gap => 0, -value => 0, -colors => [0, 'pink'], )->pack(-pady => 5, -padx => 5); while (<FILE0>) { my $line = $_; chomp($line); do_opt0($line); if ( $line ne $token ) { print SFILE "$token"; } $progressbar->value($progressbar->value + (100/$linetotal) ); $main->update; } while (<FILE1>) { my $line = $_; chomp($line); do_opt1($line); if ( $line ne $token ) { print SFILE "$token"; } $progressbar->value($progressbar->value + (100/$linetotal) ); $main->update; } while (<FILE2>) { my $line = $_; chomp($line); do_opt2($line); if ( $line ne $token ) { print SFILE "$token"; } $progressbar->value($progressbar->value + (100/$linetotal) ); $main->update; } }

    If you don't know the size of the files, like if its coming in off of a network, you could use the Tk::ExecuteCommand module, which opens up a nice box, has a blinking cancel button, and shows STDOUT and STDERR. Here is a simple example:

    #!/usr/bin/perl use warnings; use strict; use Tk; use Tk::ExecuteCommand; my %commands = ( #buttontext => command sleep => 'sleep 20', dir => 'dir', ls => 'ls -la', ); my $mw = MainWindow->new; foreach my $key (keys %commands){ $mw->Button(-text => $key, -command=>[\&execute, $commands{$key}] )->pack; } $mw->Button(-text => 'Quit', -command=> sub{Tk::exit} )->pack; MainLoop; sub execute { my $command = shift; my $tl = $mw->Toplevel( ); $tl->title($command); $tl->Button(-text => "Close", -command => sub { $tl->withdraw })->pack; my $ec = $tl->ExecuteCommand( -command => $command, -entryWidth => 50, -height => 10, -label => '', -text => 'Execute', )->pack; $ec->bell; $ec->update; }

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

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://372392]
Approved by Crackers2
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (6)
As of 2014-09-16 10:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (10 votes), past polls