Gtk2-Ex-GraphViz
view release on metacpan or search on metacpan
lib/Gtk2/Ex/GraphViz.pm view on Meta::CPAN
package Gtk2::Ex::GraphViz;
our $VERSION = '0.01';
use strict;
use warnings;
use Glib qw(TRUE FALSE);
use Data::Dumper;
use GraphViz;
use Gtk2;
use XML::Simple;
use Math::Geometry::Planar;
use GD;
use GD::Polyline;
use Carp;
sub new {
my ($class, $graph) = @_;
my $self = {};
bless ($self, $class);
$self->_set_graph($graph);
return $self;
}
sub get_widget {
my ($self) = @_;
my $vbox = Gtk2::VBox->new(FALSE, 0);
$vbox->pack_start ($self->{eventbox}, FALSE, FALSE, 0);
my $hbox = Gtk2::HBox->new(FALSE, 0);
$hbox->pack_start ($vbox, FALSE, FALSE, 0);
return $hbox;
}
sub signal_connect {
my ($self, $signal, $callback) = @_;
my $allowedsignals = [
'mouse-enter-node',
'mouse-exit-node',
'mouse-enter-edge',
'mouse-exit-edge',
];
my %hash = map { $_ => 1 } @$allowedsignals;
unless ($hash{$signal}) {
my $str = "Warning !! No such signal $signal. Allowed signals are\n";
$str .= join "\n", @$allowedsignals;
warn $str."\n";
}
$self->{signals}->{$signal} = $callback;
}
sub _set_graph {
my ($self, $graph) = @_;
my $pngimage = GD::Image->newFromPngData($graph->as_png);
my $svgdata = XMLin($graph->as_svg);
my (@bounds) = split ' ', $svgdata->{viewBox};
my $width = $bounds[2] - $bounds[0];
my $height = $bounds[3] - $bounds[3];
$self->{pngimage} = $pngimage;
$self->{svgdata} = $svgdata;
$self->{node}->{polygons} = _extract_node_polygons($svgdata);
$self->{node}->{ellipses} = _extract_node_ellipses($svgdata);
$self->{edge}->{edges} = _extract_edge_coords($svgdata);
my $loader = Gtk2::Gdk::PixbufLoader->new;
$loader->write ($pngimage->png);
$loader->close;
my $image = Gtk2::Image->new_from_pixbuf($loader->get_pixbuf);
my $eventbox = Gtk2::EventBox->new;
$eventbox->add($image);
my ($ratiox, $ratioy);
$eventbox->signal_connect('realize' =>
sub {
my @imageallocatedsize = $image->allocation->values;
$ratiox = $imageallocatedsize[2]/$width;
$ratioy = $imageallocatedsize[2]/$width;
$self->{ratiox} = $ratiox;
$self->{ratioy} = $ratioy;
}
);
$eventbox->add_events ('pointer-motion-mask');
$eventbox->signal_connect ('motion-notify-event' =>
sub {
my ($widget, $event) = @_;
#my $r = $self->{eventbox}->allocation;
#print $r->x." ".$r->y." ".$r->width." ".$r->height." \n";
my ($x, $y) = ($event->x, $event->y);
$x = int($x/$ratiox);
$y = int($y/$ratioy);
return if $self->_check_inside_node($x, $y);
return if $self->_check_on_edge($x, $y);
}
);
$self->{eventbox} = $eventbox;
}
sub _inside_ellipse {
my ($x0, $y0, $a, $b, $x, $y) = @_;
return TRUE if
($b*$b*($x-$x0)*($x-$x0) + $a*$a*($y-$y0)*($y-$y0) <= $a*$a*$b*$b);
return FALSE;
}
sub _highlight_edge {
my ($self, $line) = @_;
my $polyline = new GD::Polyline;
my ($ratiox, $ratioy) = ($self->{ratiox}, $self->{ratioy});
foreach my $bit (@$line) {
$polyline->addPt($bit->[0]*$ratiox, $bit->[1]*$ratioy);
}
my $im = $self->{pngimage}->clone;
$im->setThickness(3);
( run in 1.351 second using v1.01-cache-2.11-cpan-39bf76dae61 )