Sinatra has asked for the wisdom of the Perl Monks concerning the following question:
I'm having trouble getting a small graphical program together, I've got a data source that I want to monitor displaying the data in a nice graph which reguarly updates every N seconds. The graphical side of it doesn't need to be to complicated, maybe a half dozen options and a graph that updates itself.
Creating the actual graph itself isn't to hard, I've been using GD::Graph::lines to do this quite well for a while, getting the graph on-screen is proving more of a challange however. The closest I've come is using GD::Graph to save it to a file and then Gtk2::Image->new_from_file() to load it from disk and display it. Surely there must be a better way than this?
I thought about perl-tk, perl-gtk and perk-gtk2 and went for what looks like the newest one however none of the three seem to be in Redhat4.0, what is the normal package for doing this kind of thing? I can't believe there are no graphical perl programs in RedHat so am I missing another tool for doing this?
Re: Which tk to use and how to show images?
by traveler (Parson) on Nov 03, 2005 at 21:18 UTC
|
Gtk2-perl is probably easiest. Try
#!/usr/bin/perl
use Gtk2 '-init';
use GD::Graph;
use GD::Graph::bars;
# almost straignt from the pod
@data = (
["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
[ 1, 2, 5, 6, 3, 1.5, 1, 3, 4],
);
my $graph = GD::Graph::bars->new(400, 300);
$graph->set(
x_label => 'X Label',
y_label => 'Y label',
title => 'Some simple graph',
y_max_value => 8,
y_tick_number => 8,
y_label_skip => 2
) or die $my_graph->error;
my $gd = $graph->plot(\@data) or die $my_graph->error;
open(IMG, '>file.png') or die $!;
binmode IMG;
print IMG $gd->png;
close IMG;
my $win = Gtk2::Window->new;
my $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file("file.png") or die "no g
+raph";
my $img = Gtk2::Image->new_from_pixbuf($pixbuf);
my $vbox = Gtk2::VBox->new();
$vbox->pack_start($img, 0,0,0);
$win->add($vbox);
$win->show_all();
Gtk2->main;
| [reply] [d/l] |
Re: Which tk to use and how to show images?
by rcseege (Pilgrim) on Nov 04, 2005 at 03:12 UTC
|
Updated: to remove unnecessary use of IO::String from the second example based on comments from BrowserUk.
Updated again: Thanks to Zentara for pointing out an error in both examples that I missed by not using strict. my $gd = $graph->plot(\@data) or die $my_graph->error;. I corrected it, but it's still wrong in Traveler's and BrowserUk's nodes. I don't feels as bad. ;-)
I respectfully disagree with Traveler about GTk being the easiest, but it's a minor quibble at best as you'll see below. Nice example, though (++ as soon as the xp fairy visits). Also, with Courage - since I have my own petty gripes with the Tcl::Tk module, even though I have to admit it seems like the inevitable future (sigh...).
Since you are considering using GD::Graph, any one of the three options will all work reasonably well, with a roughly equivalent amount of code. Each may have additional modules or extensions for providing specific support for charts. I know that Perl/Tk, and Tcl::Tk (through external tcl/tk libraries such as blt) do. For this node, I'll stick to GD::Graph.
In the way of example, and borrowing from Traveler, here's one way it might be done using Tk.
use strict;
use Tk;
use Tk::PNG;
use GD::Graph;
use GD::Graph::bars;
## almost straight from the pod
my $data = [
[qw/1st 2nd 3rd 4th 5th 6th 7th 8th 9th/],
[qw/ 1 2 5 6 3 1.5 1 3 4/],
];
my $graph = GD::Graph::bars->new(400, 300);
$graph->set(
x_label => 'X Label',
y_label => 'Y label',
title => 'Some simple graph',
y_max_value => 8,
y_tick_number => 8,
y_label_skip => 2
) or die $graph->error;
my $gd = $graph->plot(\@data) or die $graph->error;
open(IMG, '>file.png') or die $!;
binmode IMG;
print IMG $gd->png;
close IMG;
## Up to this point, except for the the module
## imports it's nearly identical to the GTK Example
my $mw = MainWindow->new;
my $png = $mw->Photo(-format => 'png', -file => 'file.png');
$mw->Label(-image => $png)->pack;
MainLoop;
The same example, but this time without the temp file:
use strict;
use Tk;
use Tk::PNG;
use MIME::Base64;
use GD::Graph;
use GD::Graph::bars;
my $data = [
[qw/1st 2nd 3rd 4th 5th 6th 7th 8th 9th/],
[qw/ 1 2 5 6 3 1.5 1 3 4/],
];
my $graph = GD::Graph::bars->new(400, 300);
$graph->set(
x_label => 'X Label',
y_label => 'Y label',
title => 'Some simple graph',
y_max_value => 8,
y_tick_number => 8,
y_label_skip => 2
) or die $graph->error;
my $gd = $graph->plot($data) or die $graph->error;
my $mw = MainWindow->new;
my $png = $mw->Photo(-data => encode_base64($gd->png));
$mw->Label(-image => $png)->pack;
MainLoop;
I imagine that the Tcl::Tk option would be virtually identical to the above examples, though I haven't tested it.
As far as choosing toolkits, it will largely depend on what you are looking for in a toolkit. I'm sure you can find a few nodes on this site and others that compare and constrast them. I'll offer up a few, barely organized and biased thoughts of my own:
| [reply] [d/l] [select] |
|
my $png = $mw->Photo( -format => 'png', -data => $gd->png );
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
Shouldn't you be able to bypass the intermediate file by using the Tk::Photo -data option?
Well, I did bypass the intermediate file in the second example using the -data option, but I tried to use your snippet which looked shorter anyway. Unfortunately, it didn't work. I think I've tried this in the past, but hoped for a second that the internals had been modified when you mentioned it. For it to work, the data has to be base64 encoded, before it can be passed, otherwise Tk complains that it doesn't understand the format.
I'd be interested if you have a way around this. I've only occasionally used GD, and I'm definitely not an expert with it.
Rob
| [reply] |
|
|
Re: Which tk to use and how to show images?
by Anonymous Monk on Nov 03, 2005 at 19:59 UTC
|
I use perl-tk, because as far as I know, gtk won't run on a Pentium-75. :-)
Tk is most commonly used in conjuction with the Tcl (Tool Command Language) programming language; Perl-Tk is a port that seems awkward at times, because it tends to reflect the Tcl origins rather faithfully.
There are Tk libraries for lots of things; I even saw one years ago that did 3D rendering (a ray-traced teapot, if I recall correctly). I imagine that you could access those features from Perl somehow, though the setup I saw used Tcl instead.
Just my $0.02.
Hope it helps.
--
Ytrew | [reply] |
|
I guess I'll give Perl-Tk a go then. I've got a fair amount of code already written in perl to collect the data so switching to tcl would be a bit of a step backwards.
| [reply] |
Re: Which tk to use and how to show images?
by spiritway (Vicar) on Nov 03, 2005 at 19:24 UTC
|
I use Perl Tk. I don't remember whether it came with Perl, or whether I had to download it from CPAN, but downloading is a simple enough matter.
| [reply] |
Re: Which tk to use and how to show images?
by Courage (Parson) on Nov 03, 2005 at 19:39 UTC
|
I would recommend Tcl::Tk which have many similarities with perl/Tk, and a bit harder to start compared to perlTk, but much more flexible, widget-rich and you'll gain more after a bit slower beginning
Best regards,
Courage, the Cowardly Dog
| [reply] |
Re: Which tk to use and how to show images?
by zentara (Cardinal) on Nov 04, 2005 at 14:53 UTC
|
BUGS
GD::Graph objects cannot be reused. To create a new plot, you have to
+create a new GD::Graph object.
What that means, is that everytime you update your graph, you will get a memory increase. If it is a long running program, it will become unacceptable.
For something simple like a bar graph, you would be better
of making a Tk Canvas, and creating rectangles to show your bars. It won't leak memory when you update, and you would be able to add more color to the bars. Additionally, it will use less memory than a script that uses GD.
In this example, I have it upside down. You can add axis and text if you want. It is also just a "quick demo", and the design can be improved upon.
#!/usr/bin/perl
use warnings;
use strict;
use Tk;
use MeM;
my $w=20;
my $x=0;
my $y=0;
my %colors = (
0 => ['black','yellow'],
1 => ['yellow','black'],
2 => ['white','green'],
3 => ['green','white'],
4 => ['grey','red'],
5 => ['red','grey'],
6 => ['blue','white'],
7 => ['white','blue'],
8 => ['orange','grey45'],
9 => ['grey45','orange'],
);
my %bardata = (
0 => rand 200,
1 => rand 200,
2 => rand 200,
3 => rand 200,
4 => rand 200,
5 => rand 200,
6 => rand 200,
7 => rand 200,
8 => rand 200,
9 => rand 200,
);
my %bars;
my $mw=tkinit;
my $c = $mw->Canvas->pack;
for (0..9) {
$bars{$_} = $c->createRectangle($x,$y,$x+20,$bardata{$_},
-fill=> ${$colors{$_}}[0],
);
my $text = $c->createText($x+10,$y+10,
-anchor=>'center',
-fill => ${$colors{$_}}[1],
-text => $_
);
$x+=20;
}
$mw->Button(
-text => "Save",
-command => [sub {
$c->update;
my @capture=();
my ($x0,$y0,$x1,$y1)=$c->bbox('all');
@capture=('-x'=>$x0,'-y'=>$y0,-height=>$y1-$y0,-width=>$x1-$x
+0);
$c -> postscript(-colormode=>'color',
-file=>$0.'.ps',
-rotate=>90,
-width=>800,
-height=>500,
@capture);
}
] )->pack;
$mw->repeat(2000, sub{ &update });
MainLoop;
##########################################################
sub update{
$x=0;
$y=0;
%bardata = (
0 => rand 200,
1 => rand 200,
2 => rand 200,
3 => rand 200,
4 => rand 200,
5 => rand 200,
6 => rand 200,
7 => rand 200,
8 => rand 200,
9 => rand 200,
);
for (0..9) {
$c->delete( $bars{$_} );
$bars{$_} = $c->createRectangle($x,$y,$x+20,$bardata{$_},
-fill=> ${$colors{$_}}[0],
);
my $text = $c->createText($x+10,$y+10,
-anchor=>'center',
-fill => ${$colors{$_}}[1],
-text => $_
);
$x+=20;
}
}
I'm not really a human, but I play one on earth.
flash japh
| [reply] [d/l] [select] |
|
I think you are going to run into problems with this type of script, where you combine Tk and GD.
Hmm. Provided that you disard old things before creating new ones, the space should be reused.
I haven't put this in a tight loop and run it thousands of times, but I've clicked the button quite a lot (maybe a 100 times) whilst monitoring the memory, and I see no sign of a growth trend (on my system):
use Tk;
use Tk::PNG;
use MIME::Base64;
use GD::Graph;
use GD::Graph::bars;
my $data = [
[qw/1st 2nd 3rd 4th 5th 6th 7th 8th 9th/],
[qw/ 1 2 5 6 3 1.5 1 3 4/],
];
my $graph = GD::Graph::bars->new(400, 300);
$graph->set(
x_label => 'X Label',
y_label => 'Y label',
title => 'Some simple graph',
y_max_value => 8,
y_tick_number => 8,
y_label_skip => 2
) or die $my_graph->error;
my $gd = $graph->plot($data) or die $my_graph->error;
my $mw = MainWindow->new;
my $png = $mw->Photo(-data => encode_base64($gd->png));
$mw->Button( -text => 'doit', -command => sub{
$data->[ 1 ] = [ map{ rand 5 } 1 .. 9 ];
undef $graph;
$graph = GD::Graph::bars->new(400, 300);
$graph->set(
x_label => 'X Label',
y_label => 'Y label',
title => 'Some simple graph',
y_max_value => 8,
y_tick_number => 8,
y_label_skip => 2
) or die $my_graph->error;
my $gd = $graph->plot($data) or die $my_graph->error;
$png->blank;
$png->configure( -data => encode_base64($gd->png) );
$mw->update;
} )->pack;
$mw->Label(-image => $png)->pack;
MainLoop;
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
Yeah, you have it pretty close, by undef'ing $gd. I ran your code with a repeat and it leveled out after about 200 updates, only gaining about 200k, which is probably due to the total area of bar space displayed maxing out eventually. But I still think the plain canvas approach uses less cpu.
I'm not really a human, but I play one on earth.
flash japh
| [reply] |
|
|
|
|
It would appear you guys are good, I was wondering what would happen to the memory usage. Being new to perl I assumed the garbage collector would cleanup after me (that is what they are for) but I've had problems with programs using to much memory. I'm re-drawing the graph every two seconds over four or five hours and it's been getting a bit slow.
I've now added the approiate undef and so far it's looking better.
| [reply] |
|
|