Re: spawn functions?



On Thu, 12 Apr 2012 21:46:47 +0300
haratron <haratron gmail com> wrote:

Hello,

it seems that the spawn* functions of Gtk have not been ported to gtk2-perl.
I have this problem where I need to launch an external process from a
button's click callback. It's a TTS (text to speech) application that
has play, pause and stop buttons. The actual speech is been done by a
python script that I need to execute (don't ask..). The GUI in
gtk-perl shouldn't block, so that I am able to e.g. press pause/stop
while the voice speaks. I've tried a lot of ways but it always ends up
to one of the following:
- GUI gets blocked and I can't press pause/stop buttons
- GUI doesn't block (usually when I'm forking from within the
callback) but once the python script is done speaking, the gtk-perl
GUI is closed as well. I read this is a problem of gtk and there are
the spawn* functions to compensate for that.

How can I do this?

My first thought would be to run the Python script, via IPC::Open3
and setup Glib::IO->add_watch on the IPC::Open3 filehandles.
Since IPC::Open3 is a forked process, you shouldn't have problems
with blocking.
For instance, here is some code to look at. This is part of another
set of scripts, so it is for educational purposes only, to show
the workings of IPC::Open3. You should be able to work your
Python script in, instead of ssh. Also, you don't need the hash
for stdin, stdout, and stderr, but that is the way I wrote it.

#!/usr/bin/perl
use warnings;
use strict;
use Glib qw(TRUE FALSE);
use Gtk2 -init;
use FileHandle;
use IPC::Open3;


my $user = shift || 'z';
my $host = 'localhost';
my %hash;

$hash{$host}{'stdin'} = FileHandle->new;
$hash{$host}{'stdout'} = FileHandle->new;
$hash{$host}{'stderr'} = FileHandle->new;


Gtk2::Rc->parse_string(<<__);

style "my_entry" {
  font_name ="arial 30"
  text[NORMAL] = "#FF0000"
}

widget "*Entry*" style "my_entry"
__


my $window = Gtk2::Window->new;
$window->signal_connect( delete_event => sub { exit } );
$window->set_default_size( 400, 300 );

my $vbox = Gtk2::VBox->new;
$window->add($vbox);

my $scroller = Gtk2::ScrolledWindow->new;
$vbox->add($scroller);
my $textview = Gtk2::TextView->new;
$textview ->set_editable (0); #read-only text
$textview ->can_focus(0);     #
my $buffer   = $textview->get_buffer;
$buffer->create_mark( 'end', $buffer->get_end_iter, FALSE );
$buffer->signal_connect(
    insert_text => sub {
        $textview->scroll_to_mark( $buffer->get_mark('end'), 0.0, TRUE, 0,
            0.5 );
    }
);
$scroller->add($textview);


my $entry = Gtk2::Entry->new();

$vbox->pack_start( $entry, FALSE, FALSE, 0 );
$vbox->set_focus_child ($entry); # keeps cursor in entry
$window->set_focus_child ($entry); # keeps cursor in entry

$entry->signal_connect ('key-press-event' => sub {
         my ($widget,$event)= @_;
         if( $event->keyval() == 65293){  # a return key press
                my $text = $entry->get_text;
                print  $user.'->'. $text, "\n";
                 $entry->set_text('');
                 $entry->set_position(0);
         
         }
   });

#$entry->set_editable(0);


my $button = Gtk2::Button->new('Connect');
$button->signal_connect( clicked => \&init_connect );

$vbox->pack_start( $button, FALSE, FALSE, 0 );
$window->show_all;
Gtk2->main;
exit;


sub init_connect{

  $hash{$host}{'pid'} =  open3($hash{$host}{'stdout'},$hash{$host}{'stdin'},0,"ssh  $user\ $host ") or warn 
"@!\n";
            #if \*ERROR is false (0), STDERR is sent to STDOUT 

#    $socket = IO::Socket::INET->new(
#          PeerAddr => $host,
#          PeerPort => $port,
#          Proto    => 'tcp',
#       );
#       
#       if( ! defined $socket){ 
#         #die "ERROR: Can't connect to port $port on $host: $!\n";
#            my $buffer = $textview->get_buffer;
#            $buffer->insert( $buffer->get_end_iter, 
#            "ERROR: Can't connect to port $port on $host: $!\n" );
#        # die "ERROR: Can't connect to port $port on $host: $!\n";
#        return;
#        }
      
   
      
      $button->set_label('Connected');
      $button->set_state('insensitive');
 
        # install an io watch for this stream and
        # return immediately to the main caller, who will return
        # immediately to the event loop.  the callback will be
        # invoked whenever something interesting happens.
        Glib::IO->add_watch( fileno $hash{$host}{'stdout'} , [qw/in hup err/], \&watch_callback, 
$hash{$host}{'stdout'} );

       #turn on entry widget
       $entry->set_editable(1);
    #   $window->set_focus_child ($entry); # keeps cursor in entry
       $entry->grab_focus;
       Gtk2->main_iteration while Gtk2->events_pending; 
    

}


sub watch_callback {
    my ( $fd, $condition, $fh ) = @_;
    if ( $condition >= 'in' ) {

        # there's data available for reading.  we have no
        # guarantee that all the data is there, just that
        # some is there.  however, we know that the child
        # will be writing full lines, so we'll assume that
        # we have lines and will just use <>.
        my $data = scalar <$fh>;

        if ( defined $data ) {
            # do something useful with the text.
            my $buffer = $textview->get_buffer;
            $buffer->insert( $buffer->get_end_iter, $data );
        }
    }
    if ( $condition >= 'hup' or $condition >= 'err' ) {

        # End Of File, Hang UP, or ERRor.  that means
        # we're finished.
        # stop ability to send 
        $entry->set_editable(0);
        my $buffer = $textview->get_buffer;
        
        $buffer->insert( $buffer->get_end_iter, "Server connection lost !!\n" );
    
        #close socket
        $fh->close;
        $fh = undef;
        
        #allow for new connection
        $button->set_label('Connect');
        $button->set_sensitive(1);
        $button->set_state('active');
        $button->grab_focus;
        Gtk2->main_iteration while Gtk2->events_pending; 
    
    
    }

    if ($fh) {

        # the file handle is still open, so return TRUE to
        # stay installed and be called again.
        return TRUE;
    }
    else {

        # we're finished with this job.  start another one,
        # if there are any, and uninstall ourselves.  if there's
        # more in the queue, kick_off_job() will install a new
        # watch for the new file handle.
        return FALSE;
    }
}

__END__















[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]