TheAmigo TheAmigo - 3 months ago 23
Perl Question

Histogram in Chart::Clicker

I'm new to using

and am having trouble creating a histogram. Using the following code:

#!/usr/bin/perl -w

use strict;

use Gtk3 -init;
use Chart::Clicker;
use Chart::Clicker::Renderer::Bar;

my $data = {
Alpha => 3,
Bravo => 1,
Charlie => 4,
Delta => 1,
Echo => 5,
};
my $counter = 0;

my $chart = Chart::Clicker->new;
my $context = $chart->get_context('default');
$context->range_axis->range(Chart::Clicker::Data::Range->new(lower => 0));
for my $value (sort keys %$data) {
$chart->add_data($value, {$counter++ => $data->{$value}});
}
my $xaxis = Chart::Clicker::Axis->new(orientation => 'horizontal', position => 'bottom');
$xaxis->{tick_values} = [0 .. $counter-1];
$xaxis->{tick_labels} = [sort keys %$data];
$context->{domain_axis} = $xaxis;
$chart->set_renderer(Chart::Clicker::Renderer::Bar->new);
$chart->legend->visible(0);
$chart->draw;

my $win = Gtk3::Window->new;
my $img = Gtk3::Image->new_from_surface($chart->driver->surface);
$win->add($img);
$win->signal_connect('delete-event' => sub{Gtk3::main_quit} );
$win->show_all;
Gtk3::main;


Produces the following output:

Undesirable result

What I want is something more like this (graphically edited):

Desired result

I don't care about color on the x-axis labels. While I'd prefer the bars to be in the order I specify, I don't care that much about order.

Edit:

Trying @Borodin's suggestion produces the following:

enter image description here

It's better, but not quite right. There are now 25 data points, but 20 are zero so they don't show, but they still take up space on the X axis. Two are cut off on the left and two are cut off on the right (the missing ones happen to be zeroes in this chart so it's not as bad as it might be in others).

I tried extending the X axis one tick in each direction so nothing would get cut off, but that causes data values to overlap. I changed the zero values to non-zero to illustrate what's happening:

enter image description here

Answer

I got it to work by switching to a single series, but that comes at the cost of color... oh well, can't have everything.

Fixed a couple other issues:

  • Adjusted X-axis ticks so that the left-most and right-most bars aren't cut off.
  • Forced Y-axis ticks to be integers (since it's counting occurrences, the data will always be integers).
  • Added workaround for Gtk3 < 3.10.

New code:

#!/usr/bin/perl -w

use strict;
use Gtk3 -init;
use Chart::Clicker;
use Chart::Clicker::Renderer::Bar;
use POSIX qw/ceil/;

my $data = {
    Alpha   => 3,
    Bravo   => 1,
    Charlie => 4,
    Delta   => 1,
    Echo    => 5,
    Foxtrot => 9,
};

my $points = keys %$data;
my $counter = 0;
my $max = 0;
my $chart = Chart::Clicker->new;
for my $series (sort keys %$data) {
    $chart->add_data('histogram', {++$counter => $data->{$series}});
    $max = $data->{$series} if $data->{$series} > $max;
}
my $context = $chart->get_context('default');
my $yaxis = $context->range_axis;
$yaxis->range(Chart::Clicker::Data::Range->new(lower => 0));

# Force Y-axis ticks to integers
$yaxis->clear_tick_values;
my $increment = ceil($max / 10);
for(my $tick = 0; $tick <= $max; $tick += $increment) {
    $yaxis->add_to_tick_values($tick);
}
$yaxis->format('%d');

# Fix cropped bars on the left and right end of the X-axis
my $xaxis = $context->domain_axis;
$xaxis->tick_values([1 .. $counter]);
$xaxis->tick_labels([sort keys %$data]);
$xaxis->range(Chart::Clicker::Data::Range->new(lower => .5, upper => $counter+.5));
$context->domain_axis($xaxis);

$chart->set_renderer(Chart::Clicker::Renderer::Bar->new);
$chart->legend->visible(0);
$chart->draw;

my $win = Gtk3::Window->new;
my $loader = Gtk3::Gdk::PixbufLoader->new;
$chart->driver->surface->write_to_png_stream(sub {
    $loader->write([unpack 'C*', $_[1]]);
});
$loader->close;
my $img = Gtk3::Image->new_from_pixbuf($loader->get_pixbuf);
$win->add($img);
$win->signal_connect('delete-event' => sub{Gtk3::main_quit} );
$win->show_all;
Gtk3::main;

Which looks like this:

enter image description here