Which tk to use and how to show images?
Sinatra
created: 2005-11-03 14:07:28
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?
created: 2005-11-03 14:24:32

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.

Re: Which tk to use and how to show images?
created: 2005-11-03 14:39:35
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

Re: Which tk to use and how to show images?
created: 2005-11-03 14:59:28
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

Re^2: Which tk to use and how to show images?
created: 2005-11-03 15:58:22
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.
Re: Which tk to use and how to show images?
created: 2005-11-03 16:18:55
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 graph";
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;

Re: Which tk to use and how to show images?
created: 2005-11-03 22:12:39

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:

TK

The good: Good community that is responsive on Perlmonks, the ptk mailing list, the c.l.p.tk newsgroup, and a [http://www.perltk.org|website]. There are plenty of decent articles, tutorials, and Mastering Perl/Tk is an outstanding reference. There are also a decent assortment of custom widgets of varying quality on CPAN and other locations on the net. I also have a very large library of my own custom widgets that mitigate some of my personal issues with the API.

The bad: I find the Tk distribution somewhat bloated, and a little crusty with lots of seemingly random odds and ends. The API lacks cohesion, is not released as often as it used to and for a distribution of its size, there seem to be a fairly small number of folks doing maintainance and pushing out releases regularly. I'm distressed about the number of memory leaks still in the distro, and other minor annoyances. In short: it has warts, but I still like and have a lot of fun with it.

Tcl::Tk

Unlike Tk, which attempts to replace much of the Tcl c code with Perl code, Tcl::Tk is a bridge that relies on a locally installed Tcl/Tk distribution. There are several advantages to this approach that cannot be understated. Very little C code to maintain, ability to take advantage of the latest and greatest that the Tcl/Tk community has to offer, which is quite a bit. I especially like the blt library that has a few nice charting widgets (Alas, this library is somewhat dated). However, unicode support is solid, threads too, I really like what is being done with tiles, and the Tcl/Tk maintainers are a really talented group of folks. (Too bad I never warmed to the language...)

The bad: Personally, I don't care that the module(s) allow me to interpret Tcl code. If I wanted to code in Tcl/Tk, then I would be writing a tcl script, and not a perl one. I realize that it has a Perl/Tk compatibility mode, that supports a syntax which is virtually identical to Perl/Tk. I find that this is basically a thin facade, however, that doesn't fully support the Perl/Tk mega widget model, and replaces it with one that I don't care for (last time I checked...) Tcl::Tk's poor support for Perl/Tk's megawidgets is a serious issue for me. Despite these things, I may end up grudgingly converting my library to Tcl::Tk, or more likely switch to GTk and/or Qt which I already use in C/C++.

GTk

I like GTk, and Qt, too, but I haven't really embraced them in Perl yet, beyond a few toys. I have used them a bit with c/c++. Both have a more cohesive API, and very active and dedicated user bases on the C side. The big strike for me is that I can't easily download a Perl distro from Activestate with support for either one that is already built in, but that is mostly laziness talking ;-). I've seen some impressive things in both, but really haven't dedicated the time to either that I have to Tk.

I encourage you to find some simple task and experiment with each one toolkit to implement the task. Decide for yourself which works best for your personal situation. I don't think there's a wrong choice, but plenty of different right ones that will depend on you, your requirements, and preferences. Hope this helps,

Re^2: Which tk to use and how to show images?
created: 2005-11-03 23:09:54

I cannot verify this myself as I upgraded to 5.8.7 and I am still working through rebuilding everything else, and I haven't done GD yet. Shouldn't you be able to bypass the intermediate file by using the Tk::Photo -data option?

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.
Re^3: Which tk to use and how to show images?
created: 2005-11-03 23:31:04
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
Re^4: Which tk to use and how to show images?
created: 2005-11-03 23:46:15
For it to work, I had to make sure the data is base64 encoded ...

Now you mention it, I think I had to go that route also. It was a while ago.

That said, logic suggests that you should be able to bypass IO::String, and pass the output from gd directly to encode_base64():

my $png = $mw->Photo(-data => encode_base64( $gd->png ));

I see no reason that shouldn't work as what ends up in the file is exactly what comes out of the image methods, hence the need for [binmode];

I've made quite a lot of use of GD (though I hardly qualify as an expert), but I am light on Tk;

(Grrr. I hate being unable to try stuff, and I hate the available upgrade paths for perl.)


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.
Re^5: Which tk to use and how to show images?
created: 2005-11-03 23:56:36
I see no reason that shouldn't work as what ends up in the file is exactly what comes out of the image methods, hence the need for binmode;

You're absolutely right -- it worked fine. Thanks for that! I've corrected the example to remove it.

Re: Which tk to use and how to show images?
created: 2005-11-04 09:53:11
I think you are going to run into problems with this type of script, where you combine Tk and GD.

From the Perldoc for GD::Graph

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-$x0);
         $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
Re^2: Which tk to use and how to show images?
created: 2005-11-04 10:32:12
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.
Re^3: Which tk to use and how to show images?
created: 2005-11-04 12:40:13
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
Re^4: Which tk to use and how to show images?
created: 2005-11-04 12:44:21
But I still think the plain canvas approach uses less cpu

Oh, I totally agree. If the graph is only ever to be displayed in Tk, and not re-used as an image elsewhere, a Canvas does make sense.

That said, GD::Graph does do some stuff with scaling and ticks, labels and pie-charts that are not trivial to reproduce.


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.
Re^5: Which tk to use and how to show images?
created: 2005-11-04 12:51:27

Plus if you wanted to make the graphs interactive (make elements in the graph responsive to Mouse events) within Tk, then it would be easier with Canvas implementation -- or Tk::Graph for that matter. I'd like to see that module adapted for Tk::Zinc. It would gain some nice additional capabilities over Canvas.

Re^6: Which tk to use and how to show images?
created: 2005-11-04 12:58:36

It would be nice if there was a Canvas method for dumping the image it renders, into the various graphic formats. I realise that you can query the pels and could format them up yourself, or import thme back into a GD object for outputting as an image, but that would be very slow.


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.
Re^7: Which tk to use and how to show images?
created: 2005-11-04 13:19:11
Well, there is: $canvas->postscript(...), but it tends to be imperfect -- especially dealing with fonts. It looks as though this is a future option for Zinc as well, but it isn't there yet.
Re^8: Which tk to use and how to show images?
created: 2005-11-04 13:21:11

The postscript option is okay if your wanting to print it, but not so great of you want to incorporate the graph into a web page or slide etc.


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.
Re^9: Which tk to use and how to show images?
created: 2005-11-04 13:36:45

You could always use Image::Magick to do a conversion I've never had a need to that myself, but it doesn't seem to tough from the API.

Also, another interesting post on dumping to PDF:

I also seem to vaguely recall that there are one or two other options on Win32, but can't remember.

Re^7: Which tk to use and how to show images?
created: 2005-11-05 07:49:31
There is Tk::WinPhoto, which captures to various formats. It has a few drawbacks, one of which is being slow. There is also a win32 version now Tk::WinPhoto for ActiveState.

I'm not really a human, but I play one on earth. flash japh
Re^3: Which tk to use and how to show images?
created: 2005-11-14 09:10:46
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.
Has anyone seen my bint?

perlmonks.org content © perlmonks.org and Anonymous Monk, BrowserUk, Courage, rcseege, Sinatra, spiritway, traveler, zentara

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03