Home     |     .Net Programming    |     cSharp Home    |     Sql Server Home    |     Javascript / Client Side Development     |     Ajax Programming

Ruby on Rails Development     |     Perl Programming     |     C Programming Language     |     C++ Programming     |     IT Jobs

Python Programming Language     |     Laptop Suggestions?    |     TCL Scripting     |     Fortran Programming     |     Scheme Programming Language


 
 
Cervo Technologies
The Right Source to Outsource

MS Dynamics CRM 3.0

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:

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:

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:

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.
                        |

Add to del.icio.us | Digg this | Stumble it | Powered by Megasolutions Inc