http://qs321.pair.com?node_id=702916
Category: GUI programming
Author/Contact Info zentara of perlmonks
Description: The Goo::Canvas is ideal for solving many graphing problems. Like multiple y-axis (not shown here :-), zooming, and saving to pdf. This demo shows how to reverse the y axis (typically negative on canvases), so your plots can be in the normal cartesian direction. This makes plotting easier, your x,y values are plotted without the need to fudge their direction. This comes at a small price of needing to invert some text in the group. Margins are also taken care of nicely, by translating the plotting group from the edges, so it all stays 0,0 for plotting.

2 ways of rotating text are shown. One is an actual rotation of the Y_axis_label; and the other is the x tick values, which are automatically wrapped by 'char', to width 1, to give a vertical number..... the vertical spacing is a bit off, compared to fully rotated text, but it is easy to read ( no need to cock your head sideways to read :-) )

Zooming and pdf saving are also shown. This is just my proof-of-concept demo, to test ideas before making a full-fledged Goo::Canvas::Graph module.

#!/usr/bin/perl -w
use strict;
use warnings;
use Goo::Canvas;
use Gtk2 '-init';
use Glib qw(TRUE FALSE);
use Gtk2::Gdk::Keysyms;

my $scale = 1;

my $window = Gtk2::Window->new('toplevel');
$window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
$window->set_size_request(800, 600);

my $swin = Gtk2::ScrolledWindow->new;
$swin->set_shadow_type('in');
$window->add($swin);

my ($cwidth,$cheight)= (1200,1200);
my $canvas = Goo::Canvas->new();
$canvas->set_size_request(800, 650);
$canvas->set_bounds(0, 0, $cwidth, $cheight);
my $black = Gtk2::Gdk::Color->new (0x0000,0x0000,0x0000);
my $white = Gtk2::Gdk::Color->new (0xFFFF,0xFFFF,0xFFFF);
$canvas->modify_base('normal',$white );
$swin->add($canvas);

my $root = $canvas->get_root_item();

my ($margin_x,$margin_y) = (100,100); 

my $g = Goo::Canvas::Group->new($root); 
$g->scale(1,-1); #reverse direction of y axis, so graphing will be nor
+mal cartesian
$g->translate(0 + $margin_x, -1200 + $margin_y); #notice translations 
+are reversed
$canvas->scroll_to(0,$cheight);

# add a background rect filling $g, so button press will be detected
# otherwise $g will be invisible to button-press-event
my $rect = Goo::Canvas::Rect->new(
    $g, 0, 0, $cwidth,$cheight,
    'line-width' => 1,
    'stroke-color' => 'white', #invisible on white bg
    'fill-color' => 'white',   # must be filled for mouse event sensit
+ivity
    );

# some key help
 my $markup = "<span font_family ='Arial '
               foreground = '#000000'  
               size = '12000' 
               weight = 'ultralight'> Keys: 'Z' zoom in,   'z' zoom ou
+t,   's' save a PDF </span>";

 my $text = Goo::Canvas::Text->new(
             $g, 
             $markup,
             -80 , 80 , -1, 
             'w',
             'use markup' => 1,
            );
             
            $text->scale(1,-1);




$g->signal_connect('button-press-event',
                      \&on_g_button_press);

$canvas->signal_connect_after('key_press_event', \&on_key_press);
$canvas->can_focus(TRUE);
$canvas->grab_focus($root);

&set_axis();
&plot();

$window->show_all();

Gtk2->main;

sub set_axis{

# x axis
my $xline = Goo::Canvas::Polyline->new(
         $g, TRUE,
         [0,0,900,0], 
         'stroke-color' => 'black',
        'line-width' => 3,
    );

#label
      my $markup = "<span font_family ='Arial '
               foreground = '#000000'  
               size = '18000' 
               weight = 'bold'> X axis Label </span>";

           my $text = Goo::Canvas::Text->new(
             $g, 
             $markup,
             450 , 80 , -1, 
             'center',
             'use markup' => 1,
            );
             
            $text->scale(1,-1);



for my $x (0..900){
       
       if ($x % 100 == 0){
          my $xtick = Goo::Canvas::Polyline->new(
             $g, TRUE,
             [$x,0,$x,-25], 
             'stroke-color' => 'black',
             'line-width' => 3,
        );

          $markup = "<span font_family ='Arial '
               foreground = '#0000ff'  
               size = '10000' 
               weight = 'light'> $x </span>";

            $text = Goo::Canvas::Text->new(
             $g, 
             $markup,
             $x-1 , 10 , 1, 
             'north-east',
             'use markup' => 1,
             'wrap' => 'char');
             
            $text->scale(1,-1);


        
          }elsif ($x % 10 == 0){
         
           my $xtick = Goo::Canvas::Polyline->new(
               $g, TRUE,
               [$x,0,$x,-15], 
               'stroke-color' => 'red',
               'line-width' => 1,
            );
       
         my $markup = "<span font_family ='Arial '
               foreground = '#ff0000'  
               size = '8000' 
               weight = 'ultralight'> $x </span>";

           my $text = Goo::Canvas::Text->new(
             $g, 
             $markup,
             $x-1 , 6 , 1, 
             'north-east',
             'use markup' => 1,
             'wrap' => 'char');
             
            $text->scale(1,-1);
            
        }
  }

# y axis
my $yline = Goo::Canvas::Polyline->new(
         $g, TRUE,
         [0,0,0,900], 
         'stroke-color' => 'black',
        'line-width' => 3,
    );


#label
       $markup = "<span font_family ='Arial '
               foreground = '#000000'  
               size = '18000' 
               weight = 'bold'> Y axis Label </span>";

           $text = Goo::Canvas::Text->new(
             $g, 
             $markup,
             -70 ,-450 , -1, 
             'center',
             'use markup' => 1,
             );
             
            $text->scale(1,-1);
            $text->rotate(-90,-70,-450);


for my $y (0..900){
       
       if ($y % 100 == 0){
          my $ytick = Goo::Canvas::Polyline->new(
             $g, TRUE,
             [0,$y,-25,$y], 
             'stroke-color' => 'black',
             'line-width' => 3,
        );

          $markup = "<span font_family ='Arial '
               foreground = '#0000ff'  
               size = '10000' 
               weight = 'light'> $y </span>";

            $text = Goo::Canvas::Text->new(
             $g, 
             $markup,
             -25 , -$y -8 , -1, 
             'north-east',
             'use markup' => 1,
             );
             
            $text->scale(1,-1);


        
          }elsif ($y % 10 == 0){
         
           my $ytick = Goo::Canvas::Polyline->new(
               $g, TRUE,
               [0,$y,-15,$y], 
               'stroke-color' => 'red',
               'line-width' => 1,
            );
       
         my $markup = "<span font_family ='Arial '
               foreground = '#ff0000'  
               size = '8000' 
               weight = 'ultralight'> $y </span>";

           my $text = Goo::Canvas::Text->new(
             $g, 
             $markup,
             -16 , -$y - 6 , -1, 
             'north-east',
             'use markup' => 1,
             );
             
            $text->scale(1,-1);
            
        }
  }


}

sub plot{

  my @points;
  
  for my $x (0..900){

      push @points,$x, 300 + 100*sin( $x /5);
   
   }

   my $poly = Goo::Canvas::Polyline->new(
        $g, FALSE,
         \@points, 
        'stroke-color' => 'green',
        'line-width' => 2,
       );
}

sub on_g_button_press {
     #print "@_\n";
     my ( $group, $widget, $event ) = @_;
     print $widget ,' ',$event->type, '  ','button',' ',$event->button
+,"\n";
     my ($x,$y) = ($event->x,$event->y);
      print "$x  $y\n";
    return 0;
}

sub on_key_press {
    # print "@_\n";
     my ( $canvas, $event ) = @_;
    # print $event->type,"\n";

    if ( $event->keyval == $Gtk2::Gdk::Keysyms{Z} ) {
        $scale += .1;
        $canvas->set_scale($scale);
        $canvas->scroll_to(0,$cheight);
    }

    if ( $event->keyval == $Gtk2::Gdk::Keysyms{z} ) {
        $scale -= .1;
        $canvas->set_scale($scale);
        $canvas->set_scale($scale);
        $canvas->scroll_to(0,$cheight);
    }

    if ( $event->keyval == $Gtk2::Gdk::Keysyms{s} ) {
        write_pdf($canvas);
    }

#    print "key was ", chr( $event->keyval ), "\n";

    return 0;
}


sub write_pdf {
    #print "@_\n";
    my $canvas = shift;
    print "Write PDF...\n";

    my $scale = $canvas->get_scale;
    print "scale->$scale\n"; 
    
    my $surface = Cairo::PdfSurface->create("$0-$scale.pdf", 
                    $scale*$cwidth, $scale*$cheight);
    
    my $cr = Cairo::Context->create($surface);

    # needed to save scaled version
    $cr->scale($scale, $scale);

    $canvas->render($cr, undef, 1);
    $cr->show_page;
    print "done\n";
    return TRUE;
}
__END__