Gtk2::Helper
- Countdown to Extinction This section will introduce you to Gtk2::Helper
. It is a wrapper class which is preferred above the direct use of Glib::IO
. The sample program will start a simple script which counts backwards. Making use of IO::Pipe
, we pipe this program's STDOUT into a filehandle. This filehandle will then be read and displayed with the help of Gtk2::Helper
.
When using Glib::IO
to add a watch to a filehandle, you need to specify the $condition (Glib::IOCondition)
. Specify ['in']
if you want to read from it, and ['out']
if you want to write to the filehandle.
If you specify ['in']
, there are lots of circumstances that will trigger the callback, yet there will be nothing to read from the filehandle's buffer. Thus you end up polling the whole time, reading from the filehandle, and test to see if data came in.
This is not ideal, and wastes resources. We should rather use Gtk2::Helper
.
Gtk2::Helper
has a method called add_watch
. This method is a wrapper for Glib::IO->add_watch
. The callback is called every time when it's safe to read from or write to the watched filehandle.
$tag = Gtk2::Helper->add_watch ( $fd, $cond, $callback, $data )
$fd
: This is the file descriptor to use. Use the "fileno" function on the filehandle to get its descriptor. Perl's IO modules are very versatile to create filehandles from different sources.
$cond
: May be either 'in' or 'out', depending if you want to read from the filehandle ('in') or write to it ('out').
$callback
: A subroutine reference or closure, which is called, if you can safely operate on the filehandle, without the risk of blocking your application, because the filehandle is not ready for reading or writing.
The callback subroutine should always return true. Two signal watchers are connected internally (the I/O watcher, and a HUP watcher, which is called on eof() or other exceptions). Returning false from a watcher callback, automatically removes the correspondent watcher. Because we have two watchers internally, only one of them is removed, but probably not both. So always return true and use Gtk2::Helper->remove_watch
to disable a watcher, which was installed with Gtk2::Helper->add_watch
.
$data
: This data is passed to the callback.
$tag
: The method returns a tag which represents the created watcher. Later you need to pass this tag to Gtk2::Helper->remove_watch
to remove the watcher.
The program can be found here: 'Countdown to extinction'
1 #!/usr/bin/perl -w 2 3 use strict; 4 use Glib qw(TRUE FALSE); 5 use Gtk2 -init; 6 use Gtk2::Helper; 7 8 use IO::Pipe; 9 10 #open a pipe to a command 11 my $fh = new IO::Pipe; 12 $fh->reader("./counter.pl"); 13 14 #add a watch to this file handle 15 my $helper_tag = Gtk2::Helper->add_watch(fileno $fh, 'in',sub{ 16 &watch_callback($fh) 17 18 }); 19 20 #standard window creation, placement, and signal connecting 21 my $window = Gtk2::Window->new('toplevel'); 22 $window->signal_connect('delete_event' => sub { exit;}); 23 $window->set_border_width(5); 24 $window->set_position('center_always'); 25 26 my $lbl = Gtk2::Label->new(); 27 28 #add and show the vbox 29 $window->add($lbl); 30 $window->show_all(); 31 32 #our main event-loop 33 Gtk2->main; 34 35 sub watch_callback { 36 37 my ($fh) = @_; 38 39 my $line; 40 #read 1000 caracters of the buffer 41 $fh->sysread($line,1000); 42 #remove the newline 43 #print $line; 44 chomp $line; 45 46 47 if($line){ 48 $lbl->set_markup("<span font_family ='Eli 5.0b' 49 foreground='green' 50 background='black' 51 size='100000' 52 >$line</span>"); 53 }else{ 54 $lbl->set_markup("<span font_family ='Eli 5.0b' 55 foreground='red' 56 background='black' 57 size='100000' 58 >EXTINCTION</span>"); 59 Gtk2::Helper->remove_watch($helper_tag); 60 61 } 62 63 #important so we can loop again 64 return 1; 65 }
In some situations, you have to deviate from the "readline" style shown in Gtk2::Helper
's man page to read data from the filehandle. Our counter program's filehandle is one. If we follow the standard, Gtk2::Helper
will wait for the program that counts down to finish first, only then it will display the data in the buffer! This is not what we want, thus we use sysread
to read the data from the filehandle as soon as it arrives. This method is part of the IO::Handle
module. We ask for 1000 characters, but it will return only those available and flush the filehandle's buffer. We take this value and remove the newline character. When we stop receiving data, we can remove the watch using remove_watch