File Coverage

blib/lib/Gtk2/Ex/Graph/GD.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Gtk2::Ex::Graph::GD;
2              
3             our $VERSION = '0.04';
4              
5 7     7   32947 use strict;
  7         16  
  7         233  
6 7     7   35 use warnings;
  7         12  
  7         171  
7 7     7   8316 use Data::Dumper;
  7         93412  
  7         528  
8 7     7   23857 use GD::Graph::bars;
  0            
  0            
9             use GD::Graph::pie;
10             use GD::Graph::lines;
11             use GD::Graph::linespoints;
12             use GD::Graph::area;
13             use Gtk2;
14             use Glib qw /TRUE FALSE/;
15              
16             sub new {
17             my ($class, $width, $height, $type) = @_;
18             my $self = {};
19             bless ($self, $class);
20             $type or $type = 'bars';
21             $self->{graph} = undef;
22             $self->{graphhash} = undef;
23             $self->{graphtype} = $type;
24             $self->{imagesize} = [$width, $height];
25             $self->{eventbox} = $self->_create_eventbox;
26             $self->{optionsmenu} = $self->_create_optionsmenu;
27             $self->_set_type($type);
28             $self->_init_tooltip;
29             return $self;
30             }
31              
32             sub signal_connect {
33             my ($self, $signal, $callback) = @_;
34             $self->{signals}->{$signal} = $callback;
35             }
36              
37             sub _create_eventbox {
38             my ($self) = @_;
39             my $eventbox = Gtk2::EventBox->new;
40             # $eventbox->add_events (['pointer-motion-mask', 'pointer-motion-hint-mask', 'button-press-mask']);
41             $eventbox->add_events ('pointer-motion-mask');
42             $eventbox->signal_connect ('motion-notify-event' =>
43             sub {
44             my ($widget, $event) = @_;
45             my ($x, $y) = ($event->x, $event->y);
46             my @imageallocatedsize = $self->{graphimage}->allocation->values;
47             $x -= ($imageallocatedsize[2] - $self->{imagesize}->[0])/2;
48             $y -= ($imageallocatedsize[3] - $self->{imagesize}->[1])/2;
49             if ($self->{graphtype} eq 'bars') {
50             my $hotspot = $self->_check_bars_hotspot($x,$y);
51             if ($hotspot) {
52             my ($measure, $xvalue, $yvalue) = @$hotspot;
53             my $tooltipstring = $measure
54             ? "($measure, $xvalue, $yvalue)"
55             : "($xvalue, $yvalue)";
56             $self->_show_tooltip($tooltipstring);
57             &{ $self->{signals}->{'mouse-over'} } ($hotspot)
58             if $self->{signals}->{'mouse-over'};
59             }
60             } elsif ($self->{graphtype} eq 'lines' or $self->{graphtype} eq 'linespoints') {
61             my $hotspot = $self->_check_lines_hotspot($x,$y);
62             if ($hotspot) {
63             my ($measure, $xvalue0, $yvalue0, $xvalue1, $yvalue1) = @$hotspot;
64             my $tooltipstring = $measure
65             ? "($measure, ($xvalue0, $yvalue0), ($xvalue1, $yvalue1))"
66             : "(($xvalue0, $yvalue0), ($xvalue1, $yvalue1))";
67             $self->_show_tooltip($tooltipstring);
68             &{ $self->{signals}->{'mouse-over'} } ($hotspot)
69             if $self->{signals}->{'mouse-over'};
70             }
71             }
72             }
73             );
74             $eventbox->signal_connect ('button-press-event' =>
75             sub {
76             my ($widget, $event) = @_;
77             my ($x, $y) = ($event->x, $event->y);
78             my @imageallocatedsize = $self->{graphimage}->allocation->values;
79             $x -= ($imageallocatedsize[2] - $self->{imagesize}->[0])/2;
80             $y -= ($imageallocatedsize[3] - $self->{imagesize}->[1])/2;
81             my $hotspot;
82             if ($self->{graphtype} eq 'bars') {
83             $hotspot = $self->_check_bars_hotspot($x,$y);
84             } elsif ($self->{graphtype} eq 'lines' or $self->{graphtype} eq 'linespoints') {
85             $hotspot = $self->_check_lines_hotspot($x,$y);
86             }
87             &{ $self->{signals}->{'clicked'} } ($hotspot)
88             if $self->{signals}->{'clicked'} && $hotspot;
89             return FALSE unless $event->button == 3;
90             $self->{optionsmenu}->popup(
91             undef, # parent menu shell
92             undef, # parent menu item
93             undef, # menu pos func
94             undef, # data
95             $event->button,
96             $event->time
97             );
98             }
99             );
100             return $eventbox;
101             }
102              
103             sub set {
104             my ($self, %hash) = @_;
105             $self->{graphhash} = \%hash;
106             $self->{graph}->set(%hash);
107             }
108              
109             sub _set_type {
110             my ($self, $type) = @_;
111             my ($width, $height) = @{$self->{imagesize}};
112             $self->{graphtype} = $type;
113             my $graph;
114             if ($type eq 'bars') {
115             $graph = GD::Graph::bars->new($width, $height);
116             } elsif ($type eq 'lines') {
117             $graph = GD::Graph::lines->new($width, $height);
118             } elsif ($type eq 'linespoints') {
119             $graph = GD::Graph::linespoints->new($width, $height);
120             } elsif ($type eq 'area') {
121             $graph = GD::Graph::area->new($width, $height);
122             } elsif ($type eq 'pie') {
123             $graph = GD::Graph::pie->new($width, $height);
124             }
125             $self->{graph} = undef;
126             $self->{graph} = $graph;
127             }
128              
129             sub _refresh {
130             my ($self) = @_;
131             $self->{graph}->set(%{$self->{graphhash}}) if $self->{graphhash};
132             $self->set_legend(@{$self->{graphlegend}}) if $#{@{$self->{graphlegend}}} >= 0;
133             $self->get_image($self->{graphdata});
134             }
135              
136             sub _init_tooltip {
137             my ($self) = @_;
138             my $tooltip_label = Gtk2::Label->new;
139             my $tooltip = Gtk2::Window->new('popup');
140             $tooltip->set_decorated(0);
141             $tooltip->set_position('mouse'); # We'll choose this to start with.
142             $tooltip->modify_bg ('normal', Gtk2::Gdk::Color->parse('yellow')); # The obligatory yellow
143             $tooltip->add($tooltip_label);
144             $self->{tooltip}->{window} = $tooltip;
145             $self->{tooltip}->{displayed} = FALSE;
146             $self->{tooltip}->{label} = $tooltip_label;
147             }
148              
149             sub set_legend {
150             my ($self, @legend_keys) = @_;
151             return if ($self->{graphtype} eq 'pie');
152             $self->{graph}->set_legend(@legend_keys);
153             $self->{graphlegend} = \@legend_keys;
154             }
155              
156             sub get_image {
157             my ($self, $data) = @_;
158             $self->{graphdata} = $data;
159             my $graph = $self->{graph};
160             $graph->plot($data) or warn $graph->error;
161             my $loader = Gtk2::Gdk::PixbufLoader->new;
162             $loader->write ($graph->gd->png);
163             $loader->close;
164             my $image = Gtk2::Image->new_from_pixbuf($loader->get_pixbuf);
165             $self->{graphimage} = $image;
166             my $hotspotlist;
167             if ($self->{graphtype} eq 'bars' or
168             $self->{graphtype} eq 'lines' or
169             $self->{graphtype} eq 'linespoints') {
170             foreach my $hotspot ($graph->get_hotspot) {
171             push @$hotspotlist, $hotspot if $hotspot;
172             }
173             }
174             $self->{hotspotlist} = $hotspotlist;
175             my $eventbox = $self->{eventbox};
176             my @children = $eventbox->get_children;
177             foreach my $child (@children) {
178             $eventbox->remove($child);
179             }
180             $eventbox->add ($image);
181              
182             $eventbox->signal_connect ('button-press-event' =>
183             sub {
184             my ($widget, $event) = @_;
185             return TRUE;
186             return FALSE unless $event->button == 3;
187             $self->{optionsmenu}->popup(
188             undef, # parent menu shell
189             undef, # parent menu item
190             undef, # menu pos func
191             undef, # data
192             $event->button,
193             $event->time
194             );
195             }
196             );
197             $eventbox->show_all;
198             return $eventbox;
199             }
200              
201             sub _show_tooltip {
202             my ($self, $tooltipstring) = @_;
203             $self->{tooltip}->{label}->set_label($tooltipstring);
204             if (!$self->{tooltip}->{displayed}) {
205             $self->{tooltip}->{window}->show_all;
206             my ($thisx, $thisy) = $self->{tooltip}->{window}->window->get_origin;
207             # I want the window to be a bit away from the mouse pointer.
208             # Just a personal choice
209             $self->{tooltip}->{window}->move($thisx, $thisy-20);
210             $self->{tooltip}->{displayed} = TRUE;
211             }
212             }
213              
214             sub _check_lines_hotspot {
215             my ($self, $x, $y) = @_;
216             my $i=0;
217             my $hotspotlist = $self->{hotspotlist};
218             foreach my $datameasure (@$hotspotlist){
219             my $j=0;
220             foreach my $hotspot (@$datameasure) {
221             my ($name, @coords) = @$hotspot;
222             if (_on_the_line($x, $y, @coords)) {
223             my $xvalue0 = $self->{graphdata}->[0]->[$j-1];
224             my $yvalue0 = $self->{graphdata}->[$i+1]->[$j-1];
225             my $xvalue1 = $self->{graphdata}->[0]->[$j];
226             my $yvalue1 = $self->{graphdata}->[$i+1]->[$j];
227             my $measure = $self->{graphlegend}->[$i];
228             return [$measure, $xvalue0, $yvalue0, $xvalue1, $yvalue1];
229             }
230             $j++;
231             }
232             $i++;
233             }
234             $self->{tooltip}->{window}->hide;
235             $self->{tooltip}->{displayed} = FALSE;
236             }
237              
238             sub _on_the_line {
239             my ($x, $y, @linecoords) = @_;
240             if (($x <= $linecoords[0] and $x <= $linecoords[2]) or
241             ($x >= $linecoords[0] and $x >= $linecoords[2]) or
242             ($y <= $linecoords[1] and $y <= $linecoords[3]) or
243             ($y >= $linecoords[1] and $y >= $linecoords[3]) ){
244             return FALSE;
245             }
246             my $slope_diff =
247             ($linecoords[1]-$linecoords[3])/($linecoords[0]-$linecoords[2])
248             - ($linecoords[3]-$y)/($linecoords[2]-$x);
249             if ($slope_diff > -0.1 and $slope_diff < 0.1) {
250             return TRUE;
251             }
252             return FALSE;
253             }
254              
255             sub _check_bars_hotspot {
256             my ($self, $x, $y) = @_;
257             my $i=0;
258             my $hotspotlist = $self->{hotspotlist};
259             foreach my $datameasure (@$hotspotlist){
260             my $j=0;
261             foreach my $hotspot (@$datameasure) {
262             my ($name, @coords) = @$hotspot;
263             if ($x >= $coords[0] && $x <= $coords[2] && $y >= $coords[1] && $y <= $coords[3]) {
264             my $xvalue = $self->{graphdata}->[0]->[$j];
265             my $yvalue = $self->{graphdata}->[$i+1]->[$j];
266             my $measure = $self->{graphlegend}->[$i];
267             return [$measure, $xvalue, $yvalue];
268             }
269             $j++;
270             }
271             $i++;
272             }
273             $self->{tooltip}->{window}->hide;
274             $self->{tooltip}->{displayed} = FALSE;
275             }
276              
277             sub _create_optionsmenu {
278             my ($self) = @_;
279             my $menu = Gtk2::Menu->new();
280              
281             my $bars = Gtk2::MenuItem->new("bars");
282             my $lines = Gtk2::MenuItem->new("lines");
283             my $linespoints = Gtk2::MenuItem->new("lines with points");
284             my $area = Gtk2::MenuItem->new("area");
285             my $pie = Gtk2::MenuItem->new("pie");
286             my $cumulate = Gtk2::MenuItem->new("cumulate");
287              
288             $bars->signal_connect(activate =>
289             sub {
290             $self->_set_type('bars');
291             $self->_refresh;
292             }
293             );
294             $lines->signal_connect(activate =>
295             sub {
296             $self->_set_type('lines');
297             $self->_refresh;
298             }
299             );
300             $linespoints->signal_connect(activate =>
301             sub {
302             $self->_set_type('linespoints');
303             $self->_refresh;
304             }
305             );
306             $area->signal_connect(activate =>
307             sub {
308             $self->_set_type('area');
309             $self->_refresh;
310             }
311             );
312             $pie->signal_connect(activate =>
313             sub {
314             $self->_set_type('pie');
315             $self->_refresh;
316             }
317             );
318             $cumulate->signal_connect(activate =>
319             sub {
320             if (exists($self->{graphhash}->{cumulate})) {
321             $self->{graphhash}->{cumulate} = !$self->{graphhash}->{cumulate};
322             } else {
323             $self->{graphhash}->{cumulate} = TRUE;
324             }
325             $self->_set_type($self->{graphtype});
326             $self->_refresh;
327             }
328             );
329            
330             $bars->show();
331             $lines->show();
332             $linespoints->show();
333             $area->show();
334             $pie->show();
335             $cumulate->show();
336              
337             $menu->append($bars);
338             $menu->append($lines);
339             $menu->append($linespoints);
340             $menu->append($area);
341             $menu->append($pie);
342             $menu->append($cumulate);
343            
344             return $menu;
345             }
346              
347             1;
348              
349             __END__