13.2. Gtk2::Helper - Countdown to Extinction

13.2.1. Objective

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.

Table 13-2. Gtk2 object classes used

Gtk2 Class Name
Gtk2::Window
Gtk2::Helper
Gtk2::Label

Table 13-3. Special Perl module used

Perl module name
IO::Pipe

13.2.2. Discussion

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 )

13.2.3. The Screenshot

Figure 13-1. Countdown to extinction

13.2.4. The Code

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 }

13.2.5. A last Point

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