Perl en Español

  1. Home
  2. Tutoriales
  3. Foro
  4. Artículos
  5. Donativos
  6. Publicidad
 
Índice general » Mundo Perl » Intermedio » Un fork en perl-gtk2 Responder al tema
Nuevo tema


Página 1 de 1  [ 5 mensajes ] 
 
Nota 2011-12-07 07:00 @333

Perlero Nuevo
Registrado: 2009-12-13 16:43 @738
Mensajes: 6
Un fork en perl-gtk2
Hola.

He creado un proceso en un programilla de perl-gtk2 y Glade con la intención de que los widgets me sigan respondiendo mientras se desarrolla el proceso.

Tengo dos botones. Uno que arranca el proceso y otro que lo detiene. Al arrancar el proceso pongo el botón que lo arranca insensible ($btn_arrancar->sensitive('0')) y el cursor con un reloj de espera.

El caso es que si detengo el proceso con el botón de detener, tanto el cursor como el estado del otro botón vuelven a la normalidad. Sin embargo, cuando el proceso se detiene por sí mismo el estado del botón continúa insensible y el cursor de reloj se mantiene. No doy con el modo de que vuelva a la normalidad tras el fin natural del proceso.

Gracias por la atención.

Este es el código:

Syntax: [ Download ] [ Hide ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use Gtk2 'init';
  5. use Gtk2::GladeXML;
  6.  
  7. use vars qw($nproc);
  8.  
  9. my $glade_app      = Gtk2::GladeXML->new("$ENV{'HOME'}/bin/Glade/GtkCNMV/pruebas/gtkfork.xml");
  10. my $cursor_normal  = Gtk2::Gdk::Cursor->new('left_ptr');
  11. my $cursor_ocupado = Gtk2::Gdk::Cursor->new('watch');
  12.  
  13. my $win          = $glade_app->get_widget('window1');
  14. my $btn_arrancar = $glade_app->get_widget('button1');
  15. my $btn_detener  = $glade_app->get_widget('button2');
  16.  
  17. $btn_arrancar->signal_connect( 'clicked' => \&arrancar );
  18. $btn_detener->signal_connect( 'clicked' => \&detener );
  19. $win->signal_connect( 'delete_event' => sub { Gtk2->main_quit; } );
  20.  
  21. $win->show_all;
  22. Gtk2->main;
  23.  
  24. sub arrancar {
  25.     $btn_arrancar->set_sensitive('0');
  26.     $btn_arrancar->window->set_cursor($cursor_ocupado);
  27.  
  28.     my $PID = fork();
  29.     if ($PID) {
  30.         $nproc = $$;
  31.  
  32.         # Mi manera cutre de pasar el número de proceso :)
  33.         open( TMP, ">/tmp/nproc" ) or die "Error: $!\n";
  34.         print TMP $nproc;
  35.         close TMP;
  36.  
  37.         foreach ( 0 .. 3 ) {
  38.             print ">> $_\n";
  39.             sleep 1;
  40.             detener() if ( $_ >= 3 );
  41.         }
  42.     }
  43.     else { return "No hay fork: $!\n"; }
  44.  
  45.     return;
  46. }
  47.  
  48. sub detener {
  49.     $btn_arrancar->set_sensitive('1');
  50.     $btn_arrancar->window->set_cursor($cursor_normal);
  51.  
  52.     open( TMP, "</tmp/nproc" ) or die "Error: $!\n";
  53.     $nproc = $_ while (<TMP>);
  54.     close TMP;
  55.  
  56.     print "Terminando $nproc...\n";
  57.     kill TERM => $nproc;
  58.  
  59.     return;
  60. }
  61.  


Adjuntos:
Comentario: Adjunto el archivo glade
gtkfork.xml [3.11 KiB]
7 veces


Última edición por explorer el 2011-12-07 10:42 @487, editado 1 vez en total
Formateado de código con Perltidy
Nota 2011-12-07 12:29 @561
Avatar de Usuario
Administrador
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España
Mensajes: 10272
Re: Un fork en perl-gtk2
No lo he mirado mucho, pero veo algunas cosas...

En la línea 28 se hace el fork(). En ese momento se crea un proceso hijo que es idéntico al padre, y los dos siguen en ese mismo punto.

En la línea 29 se pregunta por el valor de $PID.

Si $PID está definido y es positivo, resulta que somos el padre ($PID es el número de proceso del hijo).

Entonces, el padre entra en el bucle de espera de 3 segundos, al final de los cuales, se detiene.

En cambio, el hijo al llegar a la línea 29, recibe un valor 0 (el $PID dentro del hijo vale 0), con lo que salta directamente a la línea 43, donde hace un return y termina el proceso asociado a la pulsación del botón, y se queda esperando. (No es cierto lo que pones en la línea 43, de que no se ha generado el fork(). Para saber si el fork() ha funcionado bien o no, debes comprobar si el valor devuelto por fork() está definido o no (undef).)

Más información en perldoc -f fork .

_________________
JF^D Perl programming


Nota 2011-12-08 10:29 @478
Avatar de Usuario
Perlero Frecuente
Registrado: 2008-01-03 15:19 @680
Mensajes: 175
Re: Un fork en perl-gtk2
El problema es que tanto el padre como el hijo están accediendo a la interfaz gráfica y eso no funciona.

La forma correcta de enfocar este tipo de problemas es hacer que un solo proceso (normalmente el padre) se encargue en exclusiva de controlar la interfaz gráfica a la vez que habla con el hijo (habitualmente mediante pipes) que hará otro tipo de tareas (algún cálculo, acceder a algún servicio de red, etc).

Una vez que el hijo termina, tiene que llamar a POSIX::_exit() para salir sin llamar a los métodos DESTROY ni a los bloques END, lo cual también interferiría con el GUI.


Nota 2011-12-11 13:07 @588

Perlero Nuevo
Registrado: 2009-12-13 16:43 @738
Mensajes: 6
Re: Un fork en perl-gtk2
Gracias por vuestras respuestas. He encontrado un par de scripts que muestran el uso de threads en Gtk2:
http://code.google.com/p/saaral-soft-search-spider/
http://cpansearch.perl.org/src/FLORA/Gtk2-1.223/examples/thread_usage.pl

El autor del primero dice que Gtk2::GladeXML no se lleva muy bien con los hilos.


Nota 2011-12-13 19:17 @845

Perlero Nuevo
Registrado: 2009-12-13 16:43 @738
Mensajes: 6
Re: Un fork en perl-gtk2
Parece que el uso de threads supone una solución muy simple y limpia. Aunque aún no lo he probado con glade, este es el ejemplo que me ha quedado:

Syntax: [ Download ] [ Hide ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2.  
  3. use strict;
  4. use warnings;
  5. use threads;
  6. use LWP::Simple;
  7. use Glib qw(TRUE FALSE);
  8. use Gtk2 qw/-init -threads-init/;
  9.  
  10.  
  11. my $hebra_down;
  12. my $deb = ( 'firefox_8.0+build1-0ubuntu0.11.10.3_amd64.deb' );
  13. my $firefox = ( "http://security.ubuntu.com/ubuntu/pool/main/f/firefox/$deb" );
  14.  
  15. die "Glib::Object thread safetly failed"
  16.         unless Glib::Object->set_threadsafe (TRUE);
  17.  
  18. my $win = Gtk2::Window->new;
  19. $win->signal_connect (destroy => sub { Gtk2->main_quit; });
  20. $win->set_title ($0);
  21. $win->set_border_width (10);
  22. $win->set_default_size (400, 200);
  23. $win->set_position('center');
  24.  
  25. my $hbox = Gtk2::HBox->new (FALSE, 6);
  26. $win->add ($hbox);
  27.  
  28. my $vbox = Gtk2::VBox->new (FALSE, 6);
  29. $hbox->pack_start ($vbox, FALSE, FALSE, 0);
  30.  
  31. my $btn_iniciar = Gtk2::Button->new ('Iniciar');
  32. $vbox->pack_start ($btn_iniciar, FALSE, FALSE, 0);
  33.  
  34. my $btn_parar = Gtk2::Button->new ('Parar');
  35. $vbox->pack_start ($btn_parar, FALSE, FALSE, 0);
  36. $btn_parar->set_sensitive(FALSE);
  37.  
  38. my $pending = Gtk2::Label->new ('Pulsar para iniciar la descarga');
  39. $vbox->pack_start ($pending, FALSE, FALSE, 0);
  40.  
  41. $btn_iniciar->signal_connect (clicked => sub {
  42.                 local $SIG{KILL} = sub { threads->exit };
  43.  
  44.                 $btn_iniciar->set_sensitive (FALSE);
  45.                 $btn_parar->set_sensitive(TRUE);
  46.                 $pending->set_label('Descargando');
  47.                
  48.                 $hebra_down = threads->create(\&procesa_descarga);
  49.                 });
  50.  
  51. $btn_parar->signal_connect (clicked => sub {
  52.                 $hebra_down->kill('KILL')->detach;
  53.                
  54.                 $btn_iniciar->set_sensitive (TRUE);
  55.                 $btn_parar->set_sensitive(FALSE);
  56.                 $pending->set_label('Descarga cancelada');
  57.                 });
  58.  
  59. $win->show_all;
  60. Gtk2->main;
  61.  
  62.  
  63. sub procesa_descarga
  64.   {
  65.         my $ua = LWP::UserAgent->new();
  66.         $ua->timeout(10);
  67.         my $response = $ua->get( $firefox );
  68.  
  69.         if ($response->is_success)
  70.           {
  71.           open (FF, ">$deb") or return "Error volcando a disco: $!\n";
  72.           print FF $response->decoded_content();
  73.           close FF;
  74.  
  75.           $btn_iniciar->set_sensitive(TRUE);
  76.           $btn_parar->set_sensitive(FALSE);
  77.  
  78.           if ($response->status_line eq '200 OK')
  79.             {
  80.             $pending->set_label("Descarga completa\n" . $response->status_line);
  81.             return $response->status_line;
  82.             }
  83.           else
  84.             {
  85.             $pending->set_label("Se ha producido un error\n" . $response->status_line);
  86.             return $response->status_line;
  87.             }
  88.           }
  89.         else {
  90.             $pending->set_label("Se ha producido un error\n" . $response->status_line);
  91.             return $response->status_line;
  92.             }
  93.   }
  94.  


Última edición por morago el 2011-12-14 05:41 @279, editado 1 vez en total

Responder al tema  [ 5 mensajes ] 

Reglas del Foro
No puedes abrir nuevos temas en este Foro
No puedes responder a temas en este Foro
No puedes editar tus mensajes en este Foro
No puedes borrar tus mensajes en este Foro
No puedes enviar adjuntos en este Foro

Publicidad

Socializa

Síguenos por Twitter

Suscríbete GRATUITAMENTE al Boletín de Perl en Español

Saltar a:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Traducción al español por Huan Manwë para phpbb-es.com
phpBB SEO