crabbdean has asked for the wisdom of the Perl Monks concerning the following question:
Hi Monks,
Most simple perl programs are scripts run at the command line. To make them look a little more professional I'm writing a simple "standard gui" module with TK so that the scripts run in a nice gui style window. Its mostly written. But I have one problem I haven't been able to solve ...
Basically I want to capture STDOUT to a text-like widget. Now STDOUT is a stream, directed somewhere, for example to the terminal. I want to direct this to my text widget. I'm guessing I need to create a reference type glog of the STDOUT to my text-widget? Or open a filehandle to it or a pipe? Or call a subroutine in a loop that reads the STDOUT and appends it to the text-widget? As you can tell I'm not sure. Help?!
The bare guts of the code is below.
Thoughts/comments/suggestions?
Thanks
Dean
use Tk;
use Tk::Pane;
my $mw = MainWindow->new (-title => 'sgui');
$mw->resizable('false','true');
my $run = $mw -> Button(-text => 'Run!',
-width => 27,
-command => [\&that]
);
my $enter = $mw->Label(-text => "\nOutput");
my $pane = $mw->Scrolled("Pane",
Name => 'fred',
-scrollbars => 'osoe',
-sticky => 'we',
-width => 540,
-height => 580
);
my $text = $pane->Text(-width => 75, -height => 40);
my $quitButton = $mw->Button ( -text => 'Quit',
-width => 27,
-command => \&quit);
$enter->pack;
$pane->Frame;
$pane->pack;
$text->pack;
$text->insert('end', \*STDOUT);
$quitButton->pack;
MainLoop;
Re: Perl:TK - standard output to text widget
by bbfu (Curate) on Feb 27, 2004 at 21:25 UTC
|
You can tie STDOUT to a Tk::Text object to have anything printed in perl appear in the text widget. This doesn't handle input, of course, nor does it work with any external programs you might call (for those, see the previous suggestions).
#!/usr/bin/winperl
use warnings;
use strict;
use Tk;
my $mw = MainWindow->new();
my $tx = $mw->Text()->pack();
tie *STDOUT, 'Tk::Text', $tx;
$mw->repeat(1000, \&tick);
MainLoop;
my $count;
sub tick {
++$count;
print "$count\n";
}
bbfu
Black flowers blossom
Fearless on my breath
| [reply] [d/l] |
|
Man you are no less than a God!!! What a brilliant solution. Very clever. Something I didn't even consider. Works a treat. Now I just have to solve the other little myriad of problems and see if I can come up with a nice modular solution.
Its getting closer bit by bit.
Dean
| [reply] |
|
merlyn pointed out that this feature is undocumented. Thus, it might be subject to change, not 100% reliable, etc. I haven't personally used it much, so I can't vouch for its reliability, either. If you have to use it in production code, you should keep a good test suite that you can re-run after every update.
bbfu
Black flowers blossom
Fearless on my breath
| [reply] |
Re: Perl:TK - standard output to text widget
by matija (Priest) on Feb 27, 2004 at 19:07 UTC
|
Capture the STDOUT, sure. But STDOUT of what?
If it is output from your own script, do yourself a favor, and simply change all the relevant print statements with calls to a routine that appends to the widget.
If not, are you trying to catch the output of some command that completes quickly? (like, say, man or ls)
In that case, use $var=`command` to capture the stdout of the command into your variable.
If it is the output of a program that will take a while to execute, and produce output at various points in time, then you should open that program with a pipe, like so: open(COM,"command |") || die "Could not execute command\n";
.
In the last case you will have to arrange something that will check for new output on a regular basis. File::Tail
may be of some use with that. | [reply] [d/l] [select] |
Re: Perl:TK - standard output to text widget
by kvale (Monsignor) on Feb 27, 2004 at 19:10 UTC
|
If you want to dump all output to the text object, try the backticks command:
my $out = `command`;
$text->insert('end', $out);
If you want to process things line by line, try opening a pipe:
open OUT "command |"
or die "Could not open command: $!\n";
while (my $line = <OUT>) {
# process $line ...
$text->insert('end', $line);
}
| [reply] [d/l] [select] |
Re: Perl:TK - standard output to text widget
by zentara (Archbishop) on Feb 28, 2004 at 16:40 UTC
|
I'm not sure what your project entails, but if you are using Tk, it would be good to be aware of fileevent. It gives you non-blocking reads.
#!/usr/bin/perl
use warnings;
use strict;
use Tk;
my $mw = new MainWindow;
my $t = $mw->Text(-width => 80, -height => 25, -wrap => 'none');
$t->pack(-expand => 1);
open(CHILD, "./read-stdout-piper 2>&1 |") or die "Can't open: $!";
$mw->fileevent(\*CHILD, 'readable', [\&fill_text_widget,$t]);
MainLoop;
sub fill_text_widget {
my($widget) = @_;
$_ = <CHILD>;
if((defined $_) && ($_ > 5)){print chr(07)}
$widget->insert('end', $_);
$widget->yview('end');
}
And the sender script
!/usr/bin/perl
$|++;
for my $i ( 0 .. 10) {
print $i, "\n";
sleep 1;
}
print "sleeping 5\n";
sleep 5;
for my $i ( 0 .. 10) {
print $i, "\n";
sleep 1;
}
I'm not really a human, but I play one on earth.
flash japh
| [reply] [d/l] [select] |
|
Now how can I modify this program to do a tail on a file. I have multiple tables in my main GUI window and need to tail the summary, detail and error file! I am stuck so any help would be greatly appreciated! Thanks in advance!
| [reply] |
|
You are better off getting rid of the tie to STDOUT, that is a hack of last resort. :-)Look at this. You could open multiple IPC connected filehandles, watch them with multiple fileevents, and insert the output into the same text widget, with different colored text.
#!/usr/bin/perl
use strict;
use Tk;
use IO::Handle;
my $H=IO::Handle->new;
open($H,"tail -f -n 50 z.txt |") or die $!;
my $main = MainWindow->new;
my $t = $main-> Scrolled('Text',
-wrap=>'none')->pack(-expand=>1);
$main->fileevent(\*$H,'readable',[\&fill,$t]);
MainLoop;
sub fill {
my ($w) = @_;
my $text;
my $text =<$H>;
$w->insert('end',$text);
$w->yview('end');
}
| [reply] [d/l] |
|
|
|