#!/usr/bin/perl
use warnings;
use strict;
use threads;
use threads::shared;
use Tk;
my %shash;
#share(%shash); #will work only for first level keys
my %hash;
share ($shash{'go'});
share ($shash{'fileno'});
share ($shash{'pid'});
share ($shash{'die'});
$shash{'go'} = 0;
$shash{'fileno'} = -1;
$shash{'pid'} = -1;
$shash{'die'} = 0;
$hash{'thread'} = threads->new(\&work);
my $mw = MainWindow->new(-background => 'gray50');
my $text = $mw->Scrolled('Text')->pack();
my $startb = $mw->Button( -text => 'Start',
-command=>sub{
$shash{'go'} = 1;
$mw->after(100); #give pipe chance to startup
my $fileno = $shash{'fileno'};
print "fileno_m $fileno\n";
open (my $fh, "<&=$fileno") or warn "$!\n";
# filevent works but may not work on win32,
# but you can use a timer instead as shown below
$mw->fileevent(\*$fh, 'readable', );
while(<$fh>){
$text->insert('end',$_);
$text->see('end');
$mw->update;
}
# on Win32 (untested by me) you may need
# a timer instead of fileevent
# my $repeater;
# $repeater = $mw->repeat(10,
# sub {
# my $bytes = sysread( "<&=$fileno", my $buf, 8192);
# $text->insert('end',$buf);
# $text->see('end');
# if( $shash{'go'} == 0 ){ $repeater->cancel }
# }
# );
}
)->pack();
my $stopb = $mw->Button( -text => 'Stop/Exit',
-command=>sub{
$shash{'die'} = 1;
kill 9,$shash{'pid'};
$hash{'thread'}->join;
exit;
},
)->pack();
MainLoop;
##################################################################
sub work{
$|++;
while(1){
if($shash{'die'} == 1){ return };
if ( $shash{'go'} == 1 ){
#run your command here, and try to find a way to capture it's
+output
# into a filehandle, then use fileevent or a timer in the main
+ Tk program
# to read the filehandle
# on win32 you may need to use IPC::Run in the thread
#use a win32 command that gives continous output
# on linux I use top
my $pid = open(FH, "top -b |" ) or warn "$!\n";
#on win32 you can use system(1, $command) IIRC
# see link above
my $fileno = fileno(FH);
print "fileno_t->$fileno\n";
$shash{'fileno'} = $fileno;
$shash{'pid'} = $pid;
$shash{'go'} = 0; #turn off self before returning
}else
{ select(undef,undef,undef,.1) } #short sleep
}
}
| [reply] [d/l] |
You need to inject $mw->update; or DoOneEvent() calls into your work loop so that the window can get some CPU cycles.
Be sure to put some form of protection against clicking "go" multiple times so you don't get recursive.
| [reply] [d/l] [select] |
| [reply] |
I usually make a progress window that pops up and shows the percent completed. If you can predict or calculate in advance how many "work units" are to be done, I prefer that over just some raw count of lines processed as the user can tell how fast things are going.
Below is one code snippet from something I wrote many moons ago - just to demo the idea. You call something like this within your data processing loop. It is important to note that updating the GUI is an "expensive" operation and you don't want to be going crazy updating it otherwise actual work progress will grind to a snail's pace! Below I check if the percentage value has changed, if not then I return without doing a GUI operation. You will have to decide what kind of "throttle" makes sense for your application. I've got other code with an "abort" button - can't find it right now, but you can check if user has pressed that button and take appropriate action (like exit your processing loop). When I'm finished with the progress window, I just hide it instead of destroying it. When I need it again, I just put it back on the screen.
sub update_WorkingScreen_Progress
{
my $ratio = shift; #number like .32 means 32%
my $new_percent = int ($ratio*100);
#updating screen is expensive computationally
#if number hasn't changed, then forget it...
#let 0% go through first time
if ( ($new_percent == $percent) && ($new_percent > 0)) {return;}
$percent = $new_percent;
$text = "Program is working - $extra_text...\n\n".
"$percent percent complete\n";
$workingScreen->update(); #this is necessary for to
#get the new value of $text to display!!!!!
}
Update: I am updating this post as perhaps I assumed that you have more control over this cpu intensive loop than you really do? If you don't have much control, then things do get more complex and zentara's post is right on target. However from the problem statement, it sounds to me like you are translating spreadsheet lines into CSV. An Excel spreadsheet can get huge nowadays. But if you can put your program into a loop that say translates X lines at a time (10, 100, or whatever) and call some simple subroutine on each loop iteration, I think you'll be happy. Updating the screen only when you need to will make a HUGE difference. | [reply] [d/l] |