|
|
 |
 |
 |
 |
Perl Programming Language
|
 |
 |
 |
 |
 |
 |
 |
 |
stumped by graphics display problem...
I need to generate and view a large number of data plots. My plan for this was to have a loop that at each iteration would generate the plot, display it, and wait for keyboard input before proceeding to the next iteration. (FWIW, this code is to run on a remote Linux system, and the display should take place on a local Mac OS X workstation running an X11 server.) The code I've written succeeds in displaying the graph, but somehow the displaying of the graph the script's execution, so the script does not get to request for keyboard input to move on to the next iteration. The relevant portion of the code is shown below. (In this snippet I have omitted all error checking and other such details for clarity; still, I confirmed that this simplified version of the code displays the behavior I described above.) The place where the problem occurs is indicated with "###". # unbuffer selected output handle (STDOUT) $| = 1; for my $data ( @data ) { my $chart = GD::Graph::lines->new( 400, 300 ); $chart->set( transparent => 0 ); # next line produces a GD::Image object my $gd = $chart->plot( $data ); open my $IMG, '|-', 'display'; print $IMG $gd->png; # display won't happen without this line! close $IMG; ### script stalls before executing the next line print "Press any key to continue... "; ReadMode 'cbreak'; ReadKey( 0 ); ReadMode 'normal'; print "\n"; } If someone could explain to me why this snippet is not behaving as I expect it to I'd be most thankful. BTW, I found it surprising that I needed to close the $IMG handle for the display to appear; turning on autoflush on the handle was not enough. Maybe the problem with the code above has more to do with a peculiarity of the 'display' command (from the ImageMagick suite) than with the Perl code? If so, is there a better way to achieve what I trying to do? Any suggestions or comments would be much appreciated. TIA! kj -- NOTE: In my address everything before the first period is backwards; and the last period, and everything after it, should be discarded.
In article <f3mk0m$e3@reader2.panix.com>, kj <s@987jk.com.invalid> wrote: : [...] : # display won't happen without this line! : close $IMG; : : ### script stalls before executing the next line The perlfunc(1) manpage's section on the close operator makes the following note: "Closing a pipe also waits for the process executing on the pipe to complete . . ." Hope this helps, Greg -- War is no longer merely a crime; it is an absurdity. It is no longer merely immoral and cruel; it is stupid. It is no longer merely murder on a large scale; it is suicide and voluntary ruin. -- Frederic Passy
On 05/31/2007 08:53 AM, kj wrote:
> [...] > open my $IMG, '|-', 'display'; > print $IMG $gd->png; > # display won't happen without this line! > close $IMG; > ### script stalls before executing the next line > print "Press any key to continue... "; > ReadMode 'cbreak'; > ReadKey( 0 ); > ReadMode 'normal'; > print "\n"; > } > [...]
The method used to remotely display X programs is different from the method used to remotely display console programs, and Term::ReadKey doesn't have internal support for remote displays. Trying to mix console type user-interaction with graphical user-interaction will confound you. It's best just to make your program fully graphical with one of the toolkits available: Gtk, Perl-Tk, etc.
In <oOD7i.11968$296.7@newsread4.news.pas.earthlink.net> "Mumia W." <paduille.4061.mumia.w+nos@earthlink.net> writes:
>On 05/31/2007 08:53 AM, kj wrote: >> [...] >> open my $IMG, '|-', 'display'; >> print $IMG $gd->png; >> # display won't happen without this line! >> close $IMG; >> ### script stalls before executing the next line >> print "Press any key to continue... "; >> ReadMode 'cbreak'; >> ReadKey( 0 ); >> ReadMode 'normal'; >> print "\n"; >> } >> [...] >The method used to remotely display X programs is different from the >method used to remotely display console programs, and Term::ReadKey >doesn't have internal support for remote displays. >Trying to mix console type user-interaction with graphical >user-interaction will confound you. It's best just to make your program >fully graphical with one of the toolkits available: Gtk, Perl-Tk, etc.
There's no shortage of fully graphical programs for viewing PNG files, but what I need is a viewer that I can control *entirely* from the keyboard. Are you saying that this would be too difficult to program? kj -- NOTE: In my address everything before the first period is backwards; and the last period, and everything after it, should be discarded.
On 05/31/2007 12:42 PM, Mumia W. wrote: > [...] > Trying to mix console type user-interaction with graphical > user-interaction will confound you [....]
And me too. Mr. Bacon's comment is correct. The user must close 'display' before your program continues. However, I would still avoid using both Term::ReadKey and X11 to interact with the same user.
In article <f3n312$ev@reader2.panix.com>, kj <s @987jk.com.invalid> wrote: >>> open my $IMG, '|-', 'display'; >>> print $IMG $gd->png; >>> # display won't happen without this line! >>> close $IMG; >There's no shortage of fully graphical programs for viewing PNG >files, but what I need is a viewer that I can control *entirely* >from the keyboard. Are you saying that this would be too difficult >to program?
(You can't quit the "display" program using the keyboard?) What you're trying to do is separate closing the pipe from waiting on the child process. The magical '|-' open ties those 2 actions together because most of the time, having them tied together is convenient. If you want them separate, you have to give up the magic and write it the long way with pipe() and fork(). # Create a pipe and a child process my ($rd, $wr); pipe($rd, $wr) or die "pipe: $!\n"; my $pid=fork(); defined($pid) or die "fork: $!\n"; if(!$pid) { # Child process reads from the pipe on stdin open(STDIN, '<&', $rd); # Child process doesn't write to the pipe, so close that end. close($wr); #{ exec 'display' } # I don't have "display" { exec 'xli', 'stdin' } # But I do have xli die "exec: $!\n"; }
# Parent process doesn't read from the pipe, so close that end. close($rd); # Parent process feeds child the image print $wr $imgdata; # Because the pipe was not created with a magic open, this close just closes # the pipe, and doesn't wait on the child process. close($wr); print "Press any key to continue... "; ReadMode 'cbreak'; ReadKey( 0 ); ReadMode 'normal'; print "\n"; # The image viewer is still running, but our user is done looking at it. # It won't die on its own; something must take it down. Hopefully it responds # correctly to SIGTERM. kill 15, $pid; # Because the child process was not created with a magic open, perl will not # clean up automatically when it dies. Without the following waitpid() we'd # have a zombie process left over. waitpid($pid,0); print "All done\n"; -- Alan Curry pac@world.std.com
It seems like that code ought to be a lot simpler. I tried using mjd's Secret Passage trick[*], but display never got the chance to start because the pipe filled up while I tried to write the image data to it. [*] <URL:http://perl.plover.com/LOD/199906.html#9> Hmm.. what about a double-fork.. That gets pretty close, but it leaves the display process running. #! /usr/local/bin/perl use warnings; use strict; open my $png, "<", "out.png" or die "$0: open: $!"; my $pid = open my $display, "|-"; die "$0: fork: $!" unless defined $pid; if ($pid) { print { $display } <$png>; close $display or warn "$0: pipe exited $?"; } else { unless (fork) { exec "display", "-" or die "$0: exec: $!"; } exit 0; } print "Press enter to exit...\n"; my(undef) = scalar <>; Without the curlies around $display in the parent's print, I get a syntax error near "$png>" with perl v5.8.5. Weird. Greg -- The people cannot delegate to government the power to do anything which would be unlawful for them to do themselves. -- John Locke
In <f3nbv4$b0@pcls4.std.com> pac@TheWorld.com (Alan Curry) writes: >In article <f3n312$ev @reader2.panix.com>, >What you're trying to do is separate closing the pipe from waiting on the >child process. The magical '|-' open ties those 2 actions together because >most of the time, having them tied together is convenient. If you want them >separate, you have to give up the magic and write it the long way with pipe() >and fork(). Thanks, that worked like gangbusters. ># Create a pipe and a child process >my ($rd, $wr); >pipe($rd, $wr) or die "pipe: $!\n"; >my $pid=fork(); >defined($pid) or die "fork: $!\n"; >if(!$pid) { > # Child process reads from the pipe on stdin > open(STDIN, '<&', $rd); > # Child process doesn't write to the pipe, so close that end. > close($wr); > #{ exec 'display' } # I don't have "display" > { exec 'xli', 'stdin' } # But I do have xli > die "exec: $!\n"; >}
I'm curious: why the curly brackets around the exec statement? Thanks again, kj -- NOTE: In my address everything before the first period is backwards; and the last period, and everything after it, should be discarded.
In article <f3njgd$gf@reader2.panix.com>, kj <s @987jk.com.invalid> wrote: >In <f3nbv4$b0 @pcls4.std.com> pac @TheWorld.com (Alan Curry) writes: >> { exec 'xli', 'stdin' } # But I do have xli >> die "exec: $!\n"; >>} >I'm curious: why the curly brackets around the exec statement?
Because there's a warning about any code after an exec, to helpfully remind you that the code won't be reached if the exec succeeds. As if it was a bad idea to check for the failure. I see now that the warning doesn't show up if the statement after the exec is a die, but in real life error handling can be more complicated than that. Let's see... $ perl -we 'exec "/bin/nosuchcommand"; die "oops";' Can't exec "/bin/nosuchcommand": No such file or directory at -e line 1. oops at -e line 1. $ perl -we 'exec "/bin/nosuchcommand"; exit(99);' Can't exec "/bin/nosuchcommand": No such file or directory at -e line 1. $ echo $? 99 $ perl -MPOSIX -we 'exec "/bin/nosuchcommand"; POSIX::_exit(99);' Statement unlikely to be reached at -e line 1. (Maybe you meant system() when you said exec()?) Can't exec "/bin/nosuchcommand": No such file or directory at -e line 1. $ echo $? 99 $ So exit() has special exemption from the rule but _exit() doesn't. That's odd since _exit() is more correct after a fork that hasn't been followed by a successful exec. Or is there no difference at all since perl added buffer-flushing magic to fork()? The 'Can't exec "/bin/nosuchcommand": No such file or directory' warning is another annoyance. When open() fails, or unlink(), or rename(), or almost any other system call that can fail with ENOENT, you don't get a warning blasted to stderr. Why doesn't perl trust the programmer to handle exec failures? -- Alan Curry pac@world.std.com
On Thu, 31 May 2007 20:42:12 +0000, Alan Curry wrote: > (You can't quit the "display" program using the keyboard?)
'q' HTH, M4
On Thu, 31 May 2007 18:09:38 +0000 (UTC), kj <s@987jk.com.invalid> wrote:
> In <oOD7i.11968$296.7 @newsread4.news.pas.earthlink.net> "Mumia W." <paduille.4061.mumia.w+nos @earthlink.net> writes: >>On 05/31/2007 08:53 AM, kj wrote: >>> [...] >>> open my $IMG, '|-', 'display'; >>> print $IMG $gd->png; >>> # display won't happen without this line! >>> close $IMG; >>> ### script stalls before executing the next line >>> print "Press any key to continue... "; >>> ReadMode 'cbreak'; >>> ReadKey( 0 ); >>> ReadMode 'normal'; >>> print "\n"; >>> } >>> [...] >>The method used to remotely display X programs is different from the >>method used to remotely display console programs, and Term::ReadKey >>doesn't have internal support for remote displays. >>Trying to mix console type user-interaction with graphical >>user-interaction will confound you. It's best just to make your program >>fully graphical with one of the toolkits available: Gtk, Perl-Tk, etc. > There's no shortage of fully graphical programs for viewing PNG > files, but what I need is a viewer that I can control *entirely* > from the keyboard. Are you saying that this would be too difficult > to program?
You could always use the Image::Magick module in a child process and control that child from the parent via a pipe, or signals if control is simple enough. The Image::Magick module can do pretty much everything the command line tools can do as well. Of course, you can also fork and exec a child for the display command line program, and then control that with a signal. Martien -- | Martien Verbruggen | If it isn't broken, it doesn't have enough | features yet. |
|
 |
 |
 |
 |
|